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)
)
这里是 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)
)