每组最大 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 可以是 AB

为了使数据结构清晰,您可以通过过滤“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,同时还返回内容的主要内容,如果未能解决你的问题,请参考以下文章

只返回每组oracle中的最大值

SQLite:仅返回每组中的前 2 个结果

每组最大 n,具有多个最大标准

Oracle分组查询取每组排序后的前N条记录

返回一个整数数组中最大子数组的和

返回一维整数数组中最大子数组的和