SQL Server:基于2列查找集合的最有效方法

时间:2017-11-30 00:10:04

标签: sql-server set-based

编辑: 我意识到我问错了。我真正要问的是,“给定[Column B]的一组值,检查[Column A]中是否有与所有集合匹配的值,而不是其他值。”

我不确定我想做的事情是否有正式名称,所以搜索我的问题一直很困难。

给定一个包含2列和值列表的表,我想看看表中是否存在这种组合(并且只有这种组合)。

例如,给定此表(假设它是整个表):

|----------|----------|
| Column A | Column B |
|----------|----------|
| 12345    | abcde    |
|----------|----------|
| 12345    | xyz      |
|----------|----------|
| 12345    | abcd     |
|----------|----------|
| 12345    | abbba    |
|----------|----------|

并给出此输入参数:

Declare @columnBs Varchar(Max) = '["abcde","xyz","abcd","abbba"]';

对于那个集合,我想返回12345。所以,基本上,我想运行检查,看看[Column A]中的任何值是否与[Column B]中的所有值匹配@columnBs 中的所有值且没有其他值即可。

如果没有[Column A]的值作为起点,我甚至无法构思出长篇解决方案。

<小时/> 如果有助于更好地概念化,这是消息传递的解决方案,其中:

  • [Column A]代表主题的主键
  • [Column B]表示分配给该主题的用户

因此,如果一组用户收到新消息,我想查看@columnBs提供的所有用户是否存在现有线程,而不是其他用户。

2 个答案:

答案 0 :(得分:1)

我读到这一点,你需要在ColumnA中找到一个值,它与查询中的相同值完全相同,不多也不少。因此,您需要加入搜索值,确保单个ColumnA存在所有搜索值,然后确保不再存在。你可以通过交叉连接来实现它,但是对于更大的数据集,这将具有可怕的性能。这可能会好一些:

-- Set up the source data.
create table MyTable (
        ColumnA int,
        ColumnB nvarchar(max)
    )
insert MyTable
    (ColumnA, ColumnB)
    Values
    -- Value 1 contains exactly the same values as we'll be looking for
    (1, 'abcde'),
    (1, 'xyz'),
    (1, 'abcd'),
    (1, 'abbba'),
    -- Value 2 contains all of them, plus one more different value
    (2, 'abcde'),
    (2, 'xyz'),
    (2, 'abcd'),
    (2, 'abbba'),
    (2, 'xxxxx'),
    -- Value 3 contains one less value
    (3, 'abcde'),
    (3, 'xyz'),
    (3, 'abcd'),
    -- Value 4 contains one different value
    (4, 'abcde'),
    (4, 'xyz'),
    (4, 'abcd'),
    (4, 'xxxxxxxxx')


-- These are the values we are looking for:
create table #searchValues (
            value nvarchar(max)
        )
insert #searchValues
    (value) values
    ('abcde'),
    ('xyz'), 
    ('abcd'), 
    ('abbba')

declare @valueCount int = (select COUNT(*) from #searchValues)


select  t.ColumnA
    from (
        -- This inner query finds all ColumnA values
        -- that link to all of the specified ColumnB values.
        select  tab.ColumnA
            from #searchValues t
            join MyTable tab on
                t.value = tab.ColumnB
            group by tab.ColumnA
            having COUNT(*) = @valueCount
        ) x
    -- And this outer join and group by will filter out those that 
    -- have all the values, plus some more.
    join MyTable t on
        t.ColumnA = x.ColumnA
    group by t.ColumnA
    having COUNT(*) = @valueCount



drop table #searchValues
drop table MyTable

这样会产生值1,因为它完全匹配。

答案 1 :(得分:0)

如果您使用的是SQL Server 2016及更高版本,请使用STRING_SPLIT()。如果使用早期版本的SQL Server,则可以使用此http://www.sqlservercentral.com/articles/Tally+Table/72993/将CSV解析为列式结果列表

declare @table table
(
    ColumnA int,
    ColumnB varchar(6)
)

insert into @table select 12345, 'abcde'
insert into @table select 12345, 'xyz'
insert into @table select 12345, 'abcd'
insert into @table select 12345, 'abbba'

declare @columnBs varchar(max) = 'abcde,xyz,abcd,abbba';

; with cte as
(
    select  value, cnt = count(*) over()
    from    string_split(@columnBs, ',')
)
select  ColumnA
from    cte c
    inner join @table t on  c.value = t.ColumnB
group by ColumnA
having count(*) = max(c.cnt)