SQL - 如果为 TRUE,则从组中选择一条记录,如果为 FALSE,则选择所有记录
SQL - Pick one record from group if TRUE, all records if FALSE
我可以暴力破解,但我觉得有更有效的方法。对于下面的每个组 ID,我想 return 标记为 TRUE 的一条记录。或者,如果组中没有记录被标记为 TRUE,那么我想 return 所有 FALSE 记录。我使用的是最新版本的 SQL 服务器。
select 500 id, 100 group_id, 0 flag into #y
union select 501, 100, 0
union select 502, 100, 0
union select 503, 100, 0
union select 504, 100, 1
union select 505, 101, 0
union select 506, 101, 0
union select 507, 101, 0
union select 508, 102, 0
union select 509, 102, 1
union select 510, 102, 0
期望的结果是 return ID 的 504 和 509(分别在组 100 和 102 中的单个 TRUE 记录)和 ID 的 505、506、507(组 101 中的所有记录,因为在组)。
select * from #y where id in (504, 505, 506, 507, 509)
我尝试了一些使用子查询和 window 函数的方法,但感觉有更直接的方法。谢谢。
一个简单的方法是(假设 group_id 只能有一行,其中 flag = 1):
SELECT
*
FROM table
WHERE flag = 1
UNION ALL
SELECT
*
FROM table
WHERE group_id NOT IN (SELECT group_id FROM table WHERE flag = 1)
使用 window 函数的一种解决方案是使用 Row_number 对行进行排序,并使用 Max 确定分区中的行是否没有标志:
with cte as (
select *,
case when
Max(flag) over(partition by group_id) = 0 then 1
else
Row_Number() over(partition by group_id order by flag desc)
end rn
from #y
)
select Id, Group_id, Flag
from cte
where rn = 1;
结果:
请注意,如果每组有超过 1 个“真”,则可以使用 dense_rank。
使用 union all
select * from #y where flag=1
union all
select * from #y
where (select sum(flag) from #y)=0
您可以使用 NOT EXISTS
:
SELECT t1.*
FROM #y t1
WHERE t1.flag = 1
OR NOT EXISTS (
SELECT *
FROM #y t2
WHERE t2.group_id = t1.group_id AND t2.flag = 1
);
或者,使用 RANK()
window 函数:
WITH cte AS (
SELECT *, RANK() OVER (PARTITION BY group_id ORDER BY flag DESC) rn
FROM #y
)
SELECT id, group_id, flag FROM cte WHERE rn = 1;
参见demo。
使用 OR
条件可以很容易地完成,如下所示:
SELECT *
FROM #y
WHERE flag = 1
OR group_id NOT IN (SELECT group_id FROM #y WHERE flag = 1)
结果:
504 100 1
505 101 0
506 101 0
507 101 0
509 102 1
工作演示:DB Fiddle
我可以暴力破解,但我觉得有更有效的方法。对于下面的每个组 ID,我想 return 标记为 TRUE 的一条记录。或者,如果组中没有记录被标记为 TRUE,那么我想 return 所有 FALSE 记录。我使用的是最新版本的 SQL 服务器。
select 500 id, 100 group_id, 0 flag into #y
union select 501, 100, 0
union select 502, 100, 0
union select 503, 100, 0
union select 504, 100, 1
union select 505, 101, 0
union select 506, 101, 0
union select 507, 101, 0
union select 508, 102, 0
union select 509, 102, 1
union select 510, 102, 0
期望的结果是 return ID 的 504 和 509(分别在组 100 和 102 中的单个 TRUE 记录)和 ID 的 505、506、507(组 101 中的所有记录,因为在组)。
select * from #y where id in (504, 505, 506, 507, 509)
我尝试了一些使用子查询和 window 函数的方法,但感觉有更直接的方法。谢谢。
一个简单的方法是(假设 group_id 只能有一行,其中 flag = 1):
SELECT
*
FROM table
WHERE flag = 1
UNION ALL
SELECT
*
FROM table
WHERE group_id NOT IN (SELECT group_id FROM table WHERE flag = 1)
使用 window 函数的一种解决方案是使用 Row_number 对行进行排序,并使用 Max 确定分区中的行是否没有标志:
with cte as (
select *,
case when
Max(flag) over(partition by group_id) = 0 then 1
else
Row_Number() over(partition by group_id order by flag desc)
end rn
from #y
)
select Id, Group_id, Flag
from cte
where rn = 1;
结果:
请注意,如果每组有超过 1 个“真”,则可以使用 dense_rank。
使用 union all
select * from #y where flag=1
union all
select * from #y
where (select sum(flag) from #y)=0
您可以使用 NOT EXISTS
:
SELECT t1.*
FROM #y t1
WHERE t1.flag = 1
OR NOT EXISTS (
SELECT *
FROM #y t2
WHERE t2.group_id = t1.group_id AND t2.flag = 1
);
或者,使用 RANK()
window 函数:
WITH cte AS (
SELECT *, RANK() OVER (PARTITION BY group_id ORDER BY flag DESC) rn
FROM #y
)
SELECT id, group_id, flag FROM cte WHERE rn = 1;
参见demo。
使用 OR
条件可以很容易地完成,如下所示:
SELECT *
FROM #y
WHERE flag = 1
OR group_id NOT IN (SELECT group_id FROM #y WHERE flag = 1)
结果:
504 100 1
505 101 0
506 101 0
507 101 0
509 102 1
工作演示:DB Fiddle