【问题标题】:How can I Select the Top and Bottom Record, Per Group?如何选择每组的顶部和底部记录?
【发布时间】:2010-10-18 00:48:43
【问题描述】:

说,我在数据库(SQL Server 2008)中有一个表,其中的数据与此类似 (但要大得多):

| ID | SCORE | GROUP |
-----------------------
| 10 |     1 | A     |
| 6  |     2 | A     |
| 3  |     3 | A     |
|----|-------|-------|
| 8  |     5 | B     |
|----|-------|-------|
| 4  |     1 | C     |
| 9  |     3 | C     |
| 2  |     4 | C     |
| 7  |     4 | C     |
|----|-------|-------|
| 12 |     3 | D     |
| 1  |     3 | D     |
| 11 |     4 | D     |
| 5  |     6 | D     |

我想获取每个GROUP 的顶部和底部记录的ID,其中每个组的记录按SCORE 排序(以及补充,ID),如下所示:

| GROUP | MIN_ID | MAX_ID  |
----------------------------
| A     | 10     | 3       |
| B     | 8      | 8       |
| C     | 4      | 7       |
| D     | 1      | 5       |

问题是:我怎样才能做到这一点?

到目前为止,我一直在尝试基于 RANK() 函数的解决方案,但还没有找到一个既能产生正确输出又具有模糊效率或可维护性的查询。


注意事项:

这个例子被简化了。我的“表”实际上是一个已经很复杂的查询的输出,我希望将最后阶段添加到该查询中。我宁愿只从表格中选择一次。

如果可能,最好有一个通用的解决方案,让我可以选择每个组的顶部和底部 n 值。

IDs 的顺序不方便。

【问题讨论】:

  • 您是否也想要分数,或者只是让输出按分数排序?
  • 前 1 名还是前 n 名?大不同...
  • @j_random_hacker:我不在乎分数。
  • @gbn:是的,我知道。我会对前 1 名感到满意。
  • @A J Lane:如果你不关心分数,你最好把它们排除在画面之外。查看答案,您的问题会以不同的方式解释。

标签: sql sql-server-2008


【解决方案1】:

您可以使用子查询 (SELECT TOP 1)...

【讨论】:

    【解决方案2】:
    DECLARE @YourTable TABLE (ID INTEGER, Score INTEGER, [Group] VARCHAR(1))
    INSERT INTO @YourTable VALUES (10, 1, 'A')
    INSERT INTO @YourTable VALUES (6 , 2, 'A')
    INSERT INTO @YourTable VALUES (3 , 3, 'A')
    INSERT INTO @YourTable VALUES (8 , 5, 'B')
    INSERT INTO @YourTable VALUES (4 , 1, 'C')
    INSERT INTO @YourTable VALUES (9 , 3, 'C')
    INSERT INTO @YourTable VALUES (2 , 4, 'C')
    INSERT INTO @YourTable VALUES (7 , 4, 'C')
    INSERT INTO @YourTable VALUES (12, 3, 'D')
    INSERT INTO @YourTable VALUES (1 , 3, 'D')
    INSERT INTO @YourTable VALUES (11, 4, 'D')
    INSERT INTO @YourTable VALUES (5 , 6, 'D')  
    

    SELECT [Group], MIN([Min_ID]), MAX([Max_ID])
    FROM (
      SELECT [score].[Group], [Min_ID] = [min].ID, [Max_ID] = [max].ID
      FROM (
        SELECT [Group], [Min_Score] = MIN(Score), [Max_Score] = MAX(Score)
        FROM @YourTable
        GROUP BY [GROUP]) score
        INNER JOIN @YourTable [min] ON [min].[Group] = [score].[Group] AND [min].[Score] = [score].[Min_Score]
        INNER JOIN @YourTable [max] ON [max].[Group] = [score].[Group] AND [max].[Score] = [score].[Max_Score] 
      ) yourtable
    GROUP BY [yourtable].[Group]
    

    【讨论】:

      【解决方案3】:

      如果可能,最好有一个通用的解决方案,让我可以选择每个组的顶部和底部 n 值。

      WITH q AS
              (
              SELECT  m.*,
                      ROW_NUMBER() OVER (PARTITION BY Group ORDER BY Score) AS rn_asc,
                      ROW_NUMBER() OVER (PARTITION BY Group ORDER BY Score DESC) AS rn_desc
              FROM    mytable m
              )
      SELECT  *
      FROM    q
      WHERE   rn_asc BETWEEN 1 AND 10
              OR rn_desc BETWEEN 1 AND 10
      

      【讨论】:

        【解决方案4】:

        任何解决方案还需要一些关于组和分数的良好索引,但包括 ID

        SELECT
            foo.[Group],
            m1.ID AS Min_ID,
            m2.ID AS Max_ID
        FROM
            (
            SELECT
               [Group], MIN(Score) AS MinScore, MAX(Score) AS MaxScore
            FROM
               mytable
            GROUP BY
               [Group]
            ) foo
            JOIN
            mytable m1 ON foo.[Group] = m1.[Group] AND foo.MinScore = m1.Score
            JOIN
            mytable m2 ON foo.[Group] = m2.[Group] AND foo.MaxScore = m2.Score
        

        但是,在您的示例数据中,这也有效,因为 ID 和分数是按顺序对齐的:

        SELECT
            [Group],
            MIN(ID) AS Min_ID,
            MAX(ID) AS Max_ID
        FROM
            mytable
        GROUP BY
            [Group]
        

        【讨论】:

        • +1。阅读您的解决方案后,我想我终于明白了 OP 想要什么。根据他在示例中提出的值,我的解决方案与您的解决方案给出的结果相同。
        • 谢谢。是的,他添加了一条评论来澄清。
        • 我认为我有 TDD-litis... 创造最简单的可能可行的东西。
        猜你喜欢
        • 2010-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多