【问题标题】:Match results in SQL table where not in same order as joined tableSQL 表中的匹配结果与连接表的顺序不同
【发布时间】:2021-11-25 11:55:07
【问题描述】:

我有两个表,其中包含大量数据。我从两个表中提取了一行来提供这个示例。

我需要将表 1 中的任何用户记录与专业匹配的表 2 进行匹配。

我不能使用 IN 子句,因为我的结果需要完全将表 1 'specialties' 列中的内容与表 2 中的 'specialties' 匹配。

问题是表2中的专业顺序与表1中专业的顺序不匹配。

我希望表 1 中的 userID = 1 与表 2 中的这一行相匹配,因为它们具有相同的特性。有谁知道我怎样才能让这些数据匹配?

表 1

select
   userID
   ,specialties
from table1
where userID = 1
userID specialties
1 Water & Environment, Built Environment, Flood & Flood risk, Civil & Environmental, Specialists, Environmental

表 2:

specialties
Built Environment, Water & Environment, Civil & Environmental, Environmental, Specialists, Flood & Flood risk

【问题讨论】:

  • 永远不要将数据存储为逗号分隔的项目。它只会给你带来很多问题。
  • 您的数据模型有缺陷,Specialties 应该是一个带有 UserId FK 的表,并且每个 row 将是一个单一的专业 - 那么您的任务会简单得多(并且性能更高)
  • 这不是我的数据模型,可悲的是它是一个客户。我知道像这样存储数据的问题,但是它们正在迁移到我们自己

标签: sql sql-server-2016


【解决方案1】:

您需要使用的功能如下。 top(100) 是那里的 SQL Server 限制。如果您的“带逗号的字符串”中有超过 100 个逗号,则需要将此顶部增加到 1000 个或更多。

create function sort_str(@in_str as nvarchar(max))
returns nvarchar(max) as
begin
declare @out_str as nvarchar(max)
select @out_str = string_agg(value,',') from
(
select top(100) value from
    (select value from string_split(@in_str,',')) x
order by value
) m
return @out_str
end

例子:

select dbo.sort_str(cast('1,2,5,4,7,6,3' as nvarchar(max)))

结果: 1,2,3,4,5,6,7

当比较表中的字符串时,这样做:

where dbo.sort_str(cast(string_from_table_1 as nvarchar(max)))=dbo.sort_str(cast(string_from_table_2 as nvarchar(max)))

【讨论】:

    【解决方案2】:

    您不应该在同一列中存储多个值,它们应该在单独的表中的单独行中。但鉴于您对不修改它的限制,我们可以拆分查询中的值。

    这就变成了Relational Division Without Remainder的问题。

    有很多解决方案,这里有两个:

    解决方案 1:

    • EXISTS 中,获取拆分值并将它们连接到table2 的拆分值...
    • ...首先使用窗口函数计算了table2 中的总行数
    • 然后统计行数,确保加入的总行数与加入前table2的总行数相同
    SELECT
      t1.userID,
      t1.specialties
    FROM table1 t1
    WHERE t1.userID = 1
      AND EXISTS (SELECT 1
        FROM STRING_SPLIT(t1.specialties, ',') s1
        JOIN (
            SELECT
              s.value,
              TotalRows = COUNT(*) OVER ()
            FROM table2 t2
            CROSS APPLY STRING_SPLIT(t2.specialties, ',') s
        ) t2 ON TRIM(t2.value) = TRIM(s1.value)
        GROUP BY ()
        HAVING COUNT(*) = MIN(t2.TotalRows)
    );
    

    解决方案 2:

    • NOT EXISTS 中,将拆分值和FULL JOIN 取为table2 的拆分值。所以任何不匹配的都变为空
    • 然后获取任一列中包含 null 的任何行。
    • 由此产生的任何结果都意味着排除外行
    SELECT
      t1.userID,
      t1.specialties
    FROM table1 t1
    WHERE t1.userID = 1
      AND NOT EXISTS (SELECT 1
        FROM STRING_SPLIT(t1.specialties, ',') s1
        FULL JOIN table2 t2
            CROSS APPLY STRING_SPLIT(t2.specialties, ',') s2
          ON TRIM(s2.value) = TRIM(s1.value)
        WHERE s1.value IS NULL OR s2.value IS NULL
    );
    

    db<>fiddle

    【讨论】:

      猜你喜欢
      • 2019-07-27
      • 2014-08-05
      • 1970-01-01
      • 2012-10-19
      • 1970-01-01
      • 1970-01-01
      • 2014-11-20
      • 2019-05-11
      • 1970-01-01
      相关资源
      最近更新 更多