【问题标题】:Greatest n per group while also returning content每组最大 n,同时还返回内容
【发布时间】: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';

【问题讨论】:

    标签: sql oracle group-by greatest-n-per-group


    【解决方案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 通常不区分大小写。

          【讨论】:

            猜你喜欢
            • 2018-06-02
            • 2019-10-21
            • 1970-01-01
            • 2013-04-16
            • 2017-03-02
            • 2019-11-20
            • 1970-01-01
            • 2022-01-06
            • 2013-05-27
            相关资源
            最近更新 更多