SQL 服务器:基于 2 列查找集合的最有效方法
SQL Server: Most efficient way to look for set based on 2 columns
编辑:
我意识到我问错了问题。我真正想问的是,"given a set of values for [Column B]
, check if there is a value in [Column A]
that matches all of the set and no others."
我不确定我想做的事情是否有正式名称,所以很难搜索我的问题。
给定一个包含 2 列和一个值列表的 table,我想看看 table.
中是否存在这个组合(并且只有这个组合)
例如,给定这个 table(假设它是整个 table):
|----------|----------|
| Column A | Column B |
|----------|----------|
| 12345 | abcde |
|----------|----------|
| 12345 | xyz |
|----------|----------|
| 12345 | abcd |
|----------|----------|
| 12345 | abbba |
|----------|----------|
并给定此输入参数:
Declare @columnBs Varchar(Max) = '["abcde","xyz","abcd","abbba"]';
对于那个集合,我想要 return 12345
。所以,基本上,我想 运行 检查并查看 [Column A]
中的任何值是否与 [Column B]
中的所有值匹配 @columnBs
[=41] 中的所有值=]并且没有其他值。
如果没有 [Column A]
的值作为起点,我什至无法构思一个长格式的解决方案。
如果有助于更好地概念化,这是一个消息传递解决方案,其中:
[Column A]
表示线程的主键
[Column B]
表示分配给线程的用户
因此,如果收到一组用户的新消息,我想查看是否存在针对 @columnBs
提供的所有用户的现有线程,而没有其他用户。
如果您使用 SQL Server 2016 及更高版本,请使用 STRING_SPLIT()。如果您使用早期版本的 SQL 服务器,您可以使用此 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)
我读到您需要在 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,因为它完全匹配。
编辑:
我意识到我问错了问题。我真正想问的是,"given a set of values for [Column B]
, check if there is a value in [Column A]
that matches all of the set and no others."
我不确定我想做的事情是否有正式名称,所以很难搜索我的问题。
给定一个包含 2 列和一个值列表的 table,我想看看 table.
中是否存在这个组合(并且只有这个组合)例如,给定这个 table(假设它是整个 table):
|----------|----------|
| Column A | Column B |
|----------|----------|
| 12345 | abcde |
|----------|----------|
| 12345 | xyz |
|----------|----------|
| 12345 | abcd |
|----------|----------|
| 12345 | abbba |
|----------|----------|
并给定此输入参数:
Declare @columnBs Varchar(Max) = '["abcde","xyz","abcd","abbba"]';
对于那个集合,我想要 return 12345
。所以,基本上,我想 运行 检查并查看 [Column A]
中的任何值是否与 [Column B]
中的所有值匹配 @columnBs
[=41] 中的所有值=]并且没有其他值。
如果没有 [Column A]
的值作为起点,我什至无法构思一个长格式的解决方案。
如果有助于更好地概念化,这是一个消息传递解决方案,其中:
[Column A]
表示线程的主键[Column B]
表示分配给线程的用户
因此,如果收到一组用户的新消息,我想查看是否存在针对 @columnBs
提供的所有用户的现有线程,而没有其他用户。
如果您使用 SQL Server 2016 及更高版本,请使用 STRING_SPLIT()。如果您使用早期版本的 SQL 服务器,您可以使用此 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)
我读到您需要在 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,因为它完全匹配。