SQL 拼图 - 没有 UNION ALL 的 UNION ALL

SQL Puzzle - UNION ALL without UNION ALL

这里是 SQL 挑战你的谜题:

编写一个查询,select 三个不同类别的前 5 条记录。

像这样:

select top 5 name, age from table1 where category = 22 order by age desc
union all
select top 5 name, age from table1 where category = 27 order by age desc
union all
select top 5 name, age from table1 where category = 53 order by age desc

但是 不用 使用 UNION 或 UNION ALL

如果您正在使用某些供应商特定的 SQL 扩展,请指定您正在使用的数据库。

If you are using some vendor-specific SQL extensions please specify the database you are using.

SQL服务器中,您可以使用排名函数ROW_NUMBER()PARTITION BY category为每组类别生成一个排名数字,并筛选您要查找的三个类别。然后您可以过滤以仅获取排名编号小于 5 的行,这将为您提供每个类别中最旧的 5 个名称:

SELECT name, age
FROM
(
    SELECT 
      name, age, 
      ROW_NUMBER() OVER(PARTITION BY category ORDER BY age DESC)AS RN
    FRO< table1 
    WHERE category IN(22,27, 53)
) AS t
WHERE RN <= 5;

经典top-n-per-group,不是吗?

使用 SQL 服务器语法。 ROW_NUMBER() 应该在 2015 年出现在所有像样的数据库中。

WITH
CTE
AS
(
    select 
        name
        ,age
        ,ROW_NUMBER() OVER (PARTITION BY category ORDER BY age desc) AS rn
    from table1
    where category IN (22, 27, 53)
)
SELECT
    name
    ,age
FROM CTE
WHERE rn <= 5
;

UNION 在某种意义上与 OR.

相同

如果您的 table 有一个主键 ID 您可以像这样重写查询:

SELECT name, age
FROM table1
WHERE
    ID IN (select top 5 ID from table1 where category = 22 order by age desc)
    OR
    ID IN (select top 5 ID from table1 where category = 27 order by age desc)
    OR
    ID IN (select top 5 ID from table1 where category = 53 order by age desc)

不过,通常情况下,UNION ALL 会比这更有效。

可以使用自联接模拟昂贵的排名。这是伪 SQL(即可能需要一些语法调整),用于使用子 select 进行自连接的解决方案。这是你的意思吗?

select name, age from ( 
  select a.name,a.age, count(*) as rank from 
    table1 a join table1 b on a.category=b.category 
  where a.category in (22,27,53) and 
        ((b.age+hash(b.name,b.age) >=a.age+hash(a.name,a.age))
) c
group by c.name, c.age  
where rank <= 5

如果您真的想要前 5 名,那么您可能需要决胜局。 name 每个 age 是唯一的吗?

select t.name, t.age from T t
where t.category in (22, 27, 53) and 5 >= (
    select count(*) from T t2
    where t2.category = t.category
        and (t2.age >= t.age or t2.age = t.age and t2.name >= t.name)
)