使用多个 IN 运算符的子查询
Subquery using multiple IN operator
我正在尝试获取列表 1 中的所有 ID,并使用列表 1 中的这些 ID,我正在尝试获取列表 2 中的所有值以及基于列表 2 中值的计数。
DECLARE @Table1 AS TABLE (
id int,
l1 varchar(20)
);
INSERT INTO @Table1 VALUES
(1,'sun'),
(2,'shine'),
(3,'moon'),
(4,'light'),
(5,'earth'),
(6,'revolves'),
(7,'flow'),
(8,'fire'),
(9,'fighter'),
(10,'sun'),
(10,'shine'),
(11,'shine'),
(12,'moon'),
(1,'revolves'),
(10,'revolves'),
(2,'air'),
(3,'shine'),
(4,'fire'),
(5,'love'),
(6,'sun'),
(7,'rises');
/*
OPERATION 1
fetch all distinct ID's that has values from List 1
List1
sun
moon
earth
Initial OUTPUT1:
distinct_id list1_value
1 sun
3 moon
5 earth
10 sun
12 moon
6 sun
OPERATION2
fetch all the id, count_of_list2_values, list2_values
based on the id's that we recieved from OPERATION1
List2
shine
revolves
Expected Output:
id list1-value count_of_list2_values, list2_values
1 sun 1 revolves
3 moon 1 shine
5 earth 0 NULL
10 sun 2 shine,revolves
12 moon 0 NULL
6 sun 1 revolves
*/
我的查询:
这是我试过的
select id, count(l1),l1
from @table1
where id in ('shine','revolves') and id in ('sun','moon','earth')
我怎样才能做到这一点。
我知道这应该是一个子查询,有多个in。如何实现?
SQLfiddleLink:
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=7a85dbf51ca5b5d35e87d968c46300bb
foo
foo
有了这个:
with
cte as(
select t1.id, t2.l1
from table1 t1 left join (
select * from table1 where l1 in ('shine','revolves')
) t2 on t2.id = t1.id
where t1.l1 in ('sun','moon','earth')
),
cte1 as(
select
c.id,
stuff(( select ',' + cte.l1 from cte where id = c.id for xml path(''), type).value('.', 'NVARCHAR(MAX)'), 1, 1, '') col
from cte c
)
select
id,
count(col) count_of_list2_values,
max(col) list2_values
from cte1
group by id
第一次 CTE 给出了这些结果:
id | l1
-: | :-------
1 | revolves
3 | shine
5 | null
10 | shine
10 | revolves
12 | null
6 | revolves
并且第二个对这些结果进行操作以连接 l1
.
的公共分组值
最后,我对第二次 CTE 的结果使用 group by id
和聚合。
查看demo
结果:
id | count_of_list2_values | list2_values
-: | --------------------: | :-------------
1 | 1 | revolves
3 | 1 | shine
5 | 0 | null
6 | 1 | revolves
10 | 2 | shine,revolves
12 | 0 | null
如果您使用的是 Sql Server 2017,那么您可以使用 string_agg
function and outer apply
运算符:
select
l1.id,
l1.l1,
l2.cnt as count_of_list2_values,
l2.l1 as list2_values
from @Table1 as l1
outer apply (
select
count(*) as cnt,
string_agg(tt.l1, ',') as l1
from @Table1 as tt
where
tt.l1 in ('shine','revolves') and
tt.id = l1.id
) as l2
where
l1.l1 in ('sun','moon','earth')
在以前的版本中,我不确定是否可以在不为此创建特殊函数的情况下一次性汇总和计数。当然,您可以使用 xquery
这样做,但这可能有点矫枉过正(至少我不会在生产代码中这样做):
select
l1.id,
l1.l1,
l2.data.value('count(l1)', 'int'),
stuff(l2.data.query('for $i in l1 return concat(",",$i/text()[1])').value('.','nvarchar(max)'),1,1,'')
from @Table1 as l1
outer apply (
select
tt.l1
from @Table1 as tt
where
tt.l1 in ('shine','revolves') and
tt.id = l1.id
for xml path(''), type
) as l2(data)
where
l1.l1 in ('sun','moon','earth')
如果您不介意通过 table 的双重扫描/搜索来完成此操作,那么您可以使用@forpas 答案或执行以下操作:
with cte_list2 as (
select tt.l1, tt.id
from @Table1 as tt
where
tt.l1 in ('shine','revolves')
)
select
l1.id,
l1.l1,
l22.cnt as count_of_list2_values,
stuff(l21.data.value('.', 'nvarchar(max)'),1,1,'') as list2_values
from @Table1 as l1
outer apply (
select
',' + tt.l1
from cte_list2 as tt
where
tt.id = l1.id
for xml path(''), type
) as l21(data)
outer apply (
select count(*) as cnt
from cte_list2 as tt
where
tt.id = l1.id
) as l22(cnt)
where
l1.l1 in ('sun','moon','earth')
有几种方法可以做到这一点。以下是我的做法:
首先设置数据:
DECLARE @Table1 AS TABLE (
id int,
l1 varchar(20)
) ;
INSERT INTO @Table1 VALUES
(1,'sun'),
(2,'shine'),
(3,'moon'),
(4,'light'),
(5,'earth'),
(6,'revolves'),
(7,'flow'),
(8,'fire'),
(9,'fighter'),
(10,'sun'),
(10,'shine'),
(11,'shine'),
(12,'moon'),
(1,'revolves'),
(10,'revolves'),
(2,'air'),
(3,'shine'),
(4,'fire'),
(5,'love'),
(6,'sun'),
(7,'rises') ;
因为这是一个已知列表,所以将 "target" 数据设置为它自己的集合。 (在 SQL 中,表格几乎总是比疯狂的列表更好用。糟糕,打字错误!我的意思是 delimited 列表。)
DECLARE @Targets AS TABLE (
l2 varchar(20)
) ;
INSERT INTO @Targets VALUES
('sun'),
('moon'),
('earth') ;
操作 1
从列表 1 中获取所有具有值的不同 ID
(太阳、月亮、地球)
连接很简单:
SELECT Id
from @Table1 t1
inner join @Targets tg
on tg.l2 = t1.l1
操作 2
获取所有 id,count_of_list2_values,list2_values
基于我们从 OPERATION1
收到的 ID
如果我正确地遵循了所需的逻辑,那么(先阅读 "join" 评论):
SELECT
tt.Id
-- This next counts how many items in the Operation 1 list are not in the target list
-- (Spaced out, to make it easier to compare with the next line)
,sum( case when tg2.l2 is null then 1 else 0 end)
-- And this concatenates them together in a string (in later editions of SQL Server)
,string_agg(case when tg2.l2 is null then tt.l1 else null end, ', ')
from @Table1 tt
inner join (-- Operation 1 as a subquery, produce list of the Ids to work with
select t1.id
from @Table1 t1
inner join @Targets tg
on tg.l2 = t1.l1
) xx
on xx.id = tt.id
-- This is used to identify the target values vs. the non-target values
left outer join @Targets tg2
on tg2.l2 = tt.l1
-- Aggregate, because that's what we need to do
group by tt.Id
-- Order it, because why not?
order by tt.Id
我正在尝试获取列表 1 中的所有 ID,并使用列表 1 中的这些 ID,我正在尝试获取列表 2 中的所有值以及基于列表 2 中值的计数。
DECLARE @Table1 AS TABLE (
id int,
l1 varchar(20)
);
INSERT INTO @Table1 VALUES
(1,'sun'),
(2,'shine'),
(3,'moon'),
(4,'light'),
(5,'earth'),
(6,'revolves'),
(7,'flow'),
(8,'fire'),
(9,'fighter'),
(10,'sun'),
(10,'shine'),
(11,'shine'),
(12,'moon'),
(1,'revolves'),
(10,'revolves'),
(2,'air'),
(3,'shine'),
(4,'fire'),
(5,'love'),
(6,'sun'),
(7,'rises');
/*
OPERATION 1
fetch all distinct ID's that has values from List 1
List1
sun
moon
earth
Initial OUTPUT1:
distinct_id list1_value
1 sun
3 moon
5 earth
10 sun
12 moon
6 sun
OPERATION2
fetch all the id, count_of_list2_values, list2_values
based on the id's that we recieved from OPERATION1
List2
shine
revolves
Expected Output:
id list1-value count_of_list2_values, list2_values
1 sun 1 revolves
3 moon 1 shine
5 earth 0 NULL
10 sun 2 shine,revolves
12 moon 0 NULL
6 sun 1 revolves
*/
我的查询: 这是我试过的
select id, count(l1),l1
from @table1
where id in ('shine','revolves') and id in ('sun','moon','earth')
我怎样才能做到这一点。 我知道这应该是一个子查询,有多个in。如何实现?
SQLfiddleLink: https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=7a85dbf51ca5b5d35e87d968c46300bb foo foo
有了这个:
with
cte as(
select t1.id, t2.l1
from table1 t1 left join (
select * from table1 where l1 in ('shine','revolves')
) t2 on t2.id = t1.id
where t1.l1 in ('sun','moon','earth')
),
cte1 as(
select
c.id,
stuff(( select ',' + cte.l1 from cte where id = c.id for xml path(''), type).value('.', 'NVARCHAR(MAX)'), 1, 1, '') col
from cte c
)
select
id,
count(col) count_of_list2_values,
max(col) list2_values
from cte1
group by id
第一次 CTE 给出了这些结果:
id | l1
-: | :-------
1 | revolves
3 | shine
5 | null
10 | shine
10 | revolves
12 | null
6 | revolves
并且第二个对这些结果进行操作以连接 l1
.
的公共分组值
最后,我对第二次 CTE 的结果使用 group by id
和聚合。
查看demo
结果:
id | count_of_list2_values | list2_values
-: | --------------------: | :-------------
1 | 1 | revolves
3 | 1 | shine
5 | 0 | null
6 | 1 | revolves
10 | 2 | shine,revolves
12 | 0 | null
如果您使用的是 Sql Server 2017,那么您可以使用 string_agg
function and outer apply
运算符:
select
l1.id,
l1.l1,
l2.cnt as count_of_list2_values,
l2.l1 as list2_values
from @Table1 as l1
outer apply (
select
count(*) as cnt,
string_agg(tt.l1, ',') as l1
from @Table1 as tt
where
tt.l1 in ('shine','revolves') and
tt.id = l1.id
) as l2
where
l1.l1 in ('sun','moon','earth')
在以前的版本中,我不确定是否可以在不为此创建特殊函数的情况下一次性汇总和计数。当然,您可以使用 xquery
这样做,但这可能有点矫枉过正(至少我不会在生产代码中这样做):
select
l1.id,
l1.l1,
l2.data.value('count(l1)', 'int'),
stuff(l2.data.query('for $i in l1 return concat(",",$i/text()[1])').value('.','nvarchar(max)'),1,1,'')
from @Table1 as l1
outer apply (
select
tt.l1
from @Table1 as tt
where
tt.l1 in ('shine','revolves') and
tt.id = l1.id
for xml path(''), type
) as l2(data)
where
l1.l1 in ('sun','moon','earth')
如果您不介意通过 table 的双重扫描/搜索来完成此操作,那么您可以使用@forpas 答案或执行以下操作:
with cte_list2 as (
select tt.l1, tt.id
from @Table1 as tt
where
tt.l1 in ('shine','revolves')
)
select
l1.id,
l1.l1,
l22.cnt as count_of_list2_values,
stuff(l21.data.value('.', 'nvarchar(max)'),1,1,'') as list2_values
from @Table1 as l1
outer apply (
select
',' + tt.l1
from cte_list2 as tt
where
tt.id = l1.id
for xml path(''), type
) as l21(data)
outer apply (
select count(*) as cnt
from cte_list2 as tt
where
tt.id = l1.id
) as l22(cnt)
where
l1.l1 in ('sun','moon','earth')
有几种方法可以做到这一点。以下是我的做法:
首先设置数据:
DECLARE @Table1 AS TABLE (
id int,
l1 varchar(20)
) ;
INSERT INTO @Table1 VALUES
(1,'sun'),
(2,'shine'),
(3,'moon'),
(4,'light'),
(5,'earth'),
(6,'revolves'),
(7,'flow'),
(8,'fire'),
(9,'fighter'),
(10,'sun'),
(10,'shine'),
(11,'shine'),
(12,'moon'),
(1,'revolves'),
(10,'revolves'),
(2,'air'),
(3,'shine'),
(4,'fire'),
(5,'love'),
(6,'sun'),
(7,'rises') ;
因为这是一个已知列表,所以将 "target" 数据设置为它自己的集合。 (在 SQL 中,表格几乎总是比疯狂的列表更好用。糟糕,打字错误!我的意思是 delimited 列表。)
DECLARE @Targets AS TABLE (
l2 varchar(20)
) ;
INSERT INTO @Targets VALUES
('sun'),
('moon'),
('earth') ;
操作 1 从列表 1 中获取所有具有值的不同 ID (太阳、月亮、地球)
连接很简单:
SELECT Id
from @Table1 t1
inner join @Targets tg
on tg.l2 = t1.l1
操作 2
获取所有 id,count_of_list2_values,list2_values
基于我们从 OPERATION1
如果我正确地遵循了所需的逻辑,那么(先阅读 "join" 评论):
SELECT
tt.Id
-- This next counts how many items in the Operation 1 list are not in the target list
-- (Spaced out, to make it easier to compare with the next line)
,sum( case when tg2.l2 is null then 1 else 0 end)
-- And this concatenates them together in a string (in later editions of SQL Server)
,string_agg(case when tg2.l2 is null then tt.l1 else null end, ', ')
from @Table1 tt
inner join (-- Operation 1 as a subquery, produce list of the Ids to work with
select t1.id
from @Table1 t1
inner join @Targets tg
on tg.l2 = t1.l1
) xx
on xx.id = tt.id
-- This is used to identify the target values vs. the non-target values
left outer join @Targets tg2
on tg2.l2 = tt.l1
-- Aggregate, because that's what we need to do
group by tt.Id
-- Order it, because why not?
order by tt.Id