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;

Demo Fiddle

结果:

请注意,如果每组有超过 1 个“真”,则可以使用 dense_rank

使用 union all

select * from #y where flag=1
union all
select * from #y
where (select sum(flag) from #y)=0

demo link

您可以使用 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