每组最大 n,同时还返回内容
Posted
技术标签:
【中文标题】每组最大 n,同时还返回内容【英文标题】:Greatest n per group while also returning content 【发布时间】:2017-11-18 11:30:19 【问题描述】:我查看了其他答案,但无法将答案应用到我的申请中。我有一个返回如下内容的查询:
SELECT *
FROM MYTABLE T;
__________________________________________________________
| ID | AORB | ....OTHER (UNIQUE) CONTENT..... |
|---------------------------------------------------------|
| 1 | A | ....CONTENT |
| 1 | B | ....CONTENT |
| 2 | A | ....CONTENT |
| 3 | A | ....CONTENT |
| 3 | B | ....CONTENT |
| 4 | A | ....CONTENT |
| 5 | B | ....CONTENT |
| 6 | A | ....CONTENT |
| 6 | B | ....CONTENT |
-----------------------------------------------------------
如您所见,有时最多有两行具有 相同的 ID 但内容不同,AORB
为 A 或 B。有时会有“唯一”ID 的计数为 1 和 AORB
可以是 A
或 B
。
为了使数据结构清晰,您可以通过过滤“AORB”来拆分表,如下所示:
全A:
SELECT *
FROM MYTABLE T
WHERE T.AORB = 'A';
__________________________________________________________
| ID | AORB | ....OTHER (UNIQUE) CONTENT..... |
|---------------------------------------------------------|
| 1 | A | ....CONTENT |
| 2 | A | ....CONTENT |
| 3 | A | ....CONTENT |
| 4 | A | ....CONTENT |
| 6 | A | ....CONTENT |
----------------------------------------------------------
所有 B:
SELECT *
FROM MYTABLE T
WHERE T.AORB = 'B';
__________________________________________________________
| ID | AORB | ....OTHER (UNIQUE) CONTENT..... |
|---------------------------------------------------------|
| 1 | B | ....CONTENT |
| 3 | B | ....CONTENT |
| 5 | B | ....CONTENT |
| 6 | B | ....CONTENT |
-----------------------------------------------------------
我需要一个类似于以下内容的查询,但将所有其他内容行返回到右侧:
SELECT ID, MIN(AORB)
FROM MYTABLE T
GROUPBY ID;
我的要求是,如果有两行具有相同的id,则返回AORB
的行A
。如果给定 id 只有一行,则返回该行,而不考虑 AORB
。
我尝试对 a 和 b 查询进行 UNION,但由于内容不同,它仍然返回“重复”ID:
SELECT *
FROM MYTABLE T
WHERE T.AORB = 'B'
UNION
SELECT *
FROM MYTABLE T
WHERE T.AORB = 'A';
【问题讨论】:
【参考方案1】:我想这就是你想要的:
SELECT T.*
FROM MYTABLE T
WHERE T.AORB = 'A' UNION ALL
SELECT T.*
FROM MYTABLE T
WHERE T.AORB = 'B' AND
NOT EXISTS (SELECT 1 FROM MYTABLE T2 WHERE T2.ID = T.ID AND T2.AORB = 'A');
这是一个优先级查询。它返回所有“A”。如果没有对应的“A”,则所有“B”。
【讨论】:
这真的很棒,正是我需要的。我用WITH
替换了MYTABLE
,因为我的实际查询要复杂得多。【参考方案2】:
您可以使用row_number
优先处理order by
中的条件。
SELECT * FROM (
SELECT T.*, ROW_NUMBER() OVER(PARTITION BY T.ID ORDER BY CASE WHEN T.AORB = 'A' THEN 1 ELSE 2 END) AS RNUM
FROM MYTABLE T
) T
WHERE RNUM=1
如果 AORB 值只有 A,B,则查询可以简化为
SELECT * FROM (
SELECT T.*, ROW_NUMBER() OVER(PARTITION BY T.ID ORDER BY T.AORB) AS RNUM
FROM MYTABLE T
) T
WHERE RNUM=1
【讨论】:
【参考方案3】:为了多样化,这里有另一种方法:
with mins as (
select id, min(aorb) as min_aorb
from mytable
)
select t.*
from mytable t
inner join mins on mins.id=t.id and mins.min_aorb=t.aorb;
这避免了将“A”和“B”直接编码到查询中。
【讨论】:
【参考方案4】:select id, min(aorb) as min_aorb,
min(content) keep (dense_rank first order by aorb) as content
from ...
group by id
将在一次遍历数据时为您提供答案(与大多数其他解决方案相比,节省大量 I/O)。
【讨论】:
【参考方案5】:我投票支持 vkp 的答案,因为只要 AORB 是字母表,它就更有可能得到你需要的东西。如果不需要,我不喜欢硬编码条件。我也认为AORB应该有一个不同的名字。今天,可能的值是 A 和 B,但根据经验,我可以告诉你,最终有人会添加 C、D、E 或 9。然后你将不得不返回并重新编码 UNION/EXISTS 逻辑,因为附加值会出现。只要 AORB 保持您需要的整理顺序,ROW_NUMBER() 方法就可以工作。我还要注意,如果数据库排序规则发生变化,它会改变你的结果。
查看排序效果:
IF OBJECT_ID('tempdb.dbo.#myStuff', 'U') IS NOT NULL
DROP TABLE #myStuff ;
CREATE TABLE #myStuff (
ID int
, AORB char(1) /* Watch collation. This is case-sensitive */ collate SQL_Latin1_General_CP1_CS_AS
, otherContent varchar(120)
) ;
INSERT INTO #myStuff (ID, AORB, otherContent)
VALUES
(1,'a','Lorem ipsum dolor sit amet, consectetur adipiscing elit.')
, (1,'A','Nulla malesuada tellus a arcu ultrices suscipit.')
, (1,'B','Proin lacinia laoreet pretium.')
, (2,'a','Nam commodo, elit sit amet efficitur rutrum, nisi magna semper neque, vitae fermentum nulla erat a felis.')
, (3,'A','Vivamus eget augue in felis luctus gravida congue et lectus.')
, (3,'B','Phasellus ullamcorper vehicula ornare.')
, (4,'A','Vivamus in facilisis nisl.')
, (5,'B','Duis accumsan elit nisi, eu sodales metus fermentum ut.')
, (6,'D','Aenean ultrices suscipit dui sit amet mollis.')
, (6,'C','Maecenas feugiat, ligula et tristique venenatis, lectus lorem ornare dui, id convallis felis sapien non justo.')
, (7,'A','Suspendisse vitae mattis leo.')
, (7,'9','Nam eu nunc tincidunt, hendrerit elit at, semper diam.')
, (7,'a','Quisque ornare erat justo, id venenatis est congue eu. ')
;
SELECT t2.id, t2.AORB, t2.otherContent, rn
FROM (
SELECT t1.id, t1.AORB, t1.otherContent, ROW_NUMBER() OVER(PARTITION BY t1.ID ORDER BY t1.AORB) AS rn
FROM #myStuff t1
) t2
WHERE t2.rn=1
;
SELECT t2.id, t2.AORB, t2.otherContent, rn
FROM (
SELECT t1.id, t1.AORB, t1.otherContent, ROW_NUMBER() OVER(PARTITION BY t1.ID ORDER BY t1.AORB) AS rn
FROM #myStuff t1
) t2
WHERE t2.rn=1
;
这是区分大小写的拉丁语的排序规则。默认 SQL 通常不区分大小写。
【讨论】:
以上是关于每组最大 n,同时还返回内容的主要内容,如果未能解决你的问题,请参考以下文章