T-SQL 根据另一表列过滤一列上的多个值

Posted

技术标签:

【中文标题】T-SQL 根据另一表列过滤一列上的多个值【英文标题】:T-SQL Filter multiple values on one column based on another table column 【发布时间】:2016-03-22 22:05:26 【问题描述】:

请帮忙。

我正在使用 SQL Server 并且熟悉 CONTAINS 关键字。

我有一个表“Table1”,列值如下

1,'bla1 bla2 bla3 String1 bla4 bla5 bla6 String2 bla7 bla8 bla9'
2,'bla3 String1 bla4 String2 bla7 bla8 bla1'
3,'bla3 String2 bla4 String3'

我有另一个表“Table2”,只有一列,但具有动态值,例如

1,bla1
2,string1
3,bla3

1,string2
2,bla5
3,bla1
4,bla4

我只是想根据 Table2 中存在的所有值返回 Table1 中的行,这意味着它不是OR,而是AND - Table2 中的所有值都应该存在于 Table1 的列值中(它可以顺序不限)。

在上面的例子中,

如果根据 Table1 检查 Table2 的第一组,那么它应该只返回 Table1 的前 2 行 如果根据 Table1 检查 Table2 的第二组,那么它应该只返回 Table1 的第一行

因为 Table2 中的值之一在 Table1 的列值中不可用

由于这两个表都是临时表变量,我无法在它们上添加FULL-TEXT INDEX,否则我可以使用 CONTAINS(Column, 'SearchString1 AND SearchString2 AND SearchString3 AND so on')

以下代码可以正常工作,但最多只能搜索 2 个搜索字符串。超过2,它不起作用。例如,如果我在下面的代码中有这样的

declare @str nvarchar(100) = ' Dr clark Nick '

DECLARE @Tab TABLE(Col NVARCHAR(50))
INSERT INTO @Tab
SELECT ' Dr. Nick Clark' UNION ALL
SELECT ' Dr. Nick SPACE Clark' UNION ALL
SELECT ' Dr. Clark SPACE Nick' UNION ALL
SELECT ' Dr. Clark Nick' UNION ALL
SELECT ' Dr. Nick' UNION ALL
SELECT ' Dr. Clark '

declare @str nvarchar(100) = '  Nick clark '

set @str = ltrim(rtrim(@str))

DECLARE @Search1 VARCHAR(MAX), @Search2 VARCHAR(MAX) 

declare @t table(sno int, splitdata nvarchar(100))
insert into @t
SELECT 
    row_number() over (order by ltrim(rtrim(o.splitdata))) as sno,
    ltrim(rtrim(o.splitdata)) AS splitdata
FROM
    (SELECT CAST('<X>'+REPLACE(@str,' ','</X><X>')+'</X>' AS XML) AS Filter)F1
     CROSS APPLY
          ( SELECT fdata.D.value('.','varchar(MAX)') AS splitdata 
          FROM f1.Filter.nodes('X') AS fdata(D)
    ) O

SELECT @Search1 = COALESCE(@Search1 + '%', '') + Splitdata FROM @t order by sno
SELECT @Search2 = COALESCE(@Search2 + '%', '') + Splitdata FROM @t order by sno desc

select * from @tab where col like '%'+@Search1+'%' or col like '%'+@Search2+'%'

【问题讨论】:

如果 table1 上的一行是“NickClarkNick”(没有空格),你会期待匹配吗? 任何数量的 Nick 或 Clark 都可以...但 Nick 和 Clark 都应该在场...有或没有空格.. 【参考方案1】:
set nocount on;

declare @Tab table (col nvarchar(50));

insert @Tab (Col)
values (' Dr. Nick Clark')
     , (' Dr. Nick SPACE Clark')
     , (' Dr. Clark SPACE Nick')
     , (' Dr. Clark Nick')
     , (' Dr. Nick')
     , (' Dr. Clark ');

declare @str nvarchar(100) = '  Nick clark ';
set @str = ' ' + ltrim(rtrim(@str)) + ' ';

with Tally
as (
    select row_number() over (order by t1.column_id) as 'N'
      from sys.columns t1
      join sys.columns t2
        on 1=1
   )
, Needles
as (
    select substring(@str,N+1,charindex(' ',@str,N+1)-N-1) 'Needle'
      from Tally
     where N < len(@str)
       and substring(@str,N,1) = ' '
   )
, NotSelected
as (
    select tab.col
      from @tab tab
      join Needles
        on 1=1
     where charindex(Needles.Needle,tab.col) = 0
   )
   select Tab.Col
     from @Tab Tab
left join NotSelected
       on NotSelected.Col = Tab.Col
    where NotSelected.Col is null;

好的,这是我的方法:我们将您的搜索字符串转换为一个记录集,并用空格分隔。我使用 Tally-table 方法来解决这个问题,但是我使用基于 sys.columns 的 Common Table Expression 而不是 Real tally 表......基本上给了我一个充满整数的表,我可以用它来快速分隔你的空间- 分隔字符串到记录集中。我称它为针头,因为我们正在@tab haystack 中寻找这些针头。

我创建了一个针和干草堆的笛卡尔记录集以及haystackneedle 的第一个位置的CHARINDEX 值。任何零值都告诉我针不在大海捞针中。这也是一个名为“NotSelected”的通用表表达式 - 现在列出了所有未找到针头的 @Tab.Col 值。

最后,将 Not Selected 行与整个表进行左连接将显示所有与 Not Selected 值不匹配的记录。

【讨论】:

哇...它的工作原理...我只是用值(排列和组合)进行了测试。但是要理解逻辑...感谢您的解决方案... 现在理解了逻辑...为了我的理解将 CTE 重命名为“SNo_Gen”、“Search_Str_Splitter”和“Not_Exists”(从上到下)...再次感谢。

以上是关于T-SQL 根据另一表列过滤一列上的多个值的主要内容,如果未能解决你的问题,请参考以下文章

当另一列具有特定值时,列上的 NOT NULL 约束

基于另一列中的值的一列上的pyspark滞后函数

Elasticsearch 多个聚合或术语和同一列上的聚合

MySQL:加入表并根据另一列上的聚合函数从一行返回一列[重复]

基于另一列的最大值的列上的 SQL 内连接 [重复]

MySQL sum 和 distinct 在具有多个连接的另一列上