【问题标题】:Second maximum and minimum values第二个最大值和最小值
【发布时间】:2017-11-20 14:45:01
【问题描述】:

给定一个包含多行 int 字段和相同标识符的表,是否可以从表中返回第二个最大值和第二个最小值。

一张表由

组成
ID      |   number
------------------------
1       |     10
1       |     11
1       |     13
1       |     14
1       |     15
1       |     16

最终结果是

ID      |   nMin    |   nMax
--------------------------------
1       |     11    |    15

【问题讨论】:

  • 什么版本的 SQL?很多方法...
  • 2008.越便宜越好……数百万行
  • 如果插入 2|17 和 2|12 行应该是什么结果?
  • @danihp 我假设您的问题是关于是否只有 2 行。理想情况下应该输出 2|12|17。令人敬畏的问题还没有考虑过。 1 行 nMax 和 nMin 应该 = 1 值(即 2|12|12)

标签: sql sql-server-2008


【解决方案1】:

您可以使用row_number 为每个 ID 分配排名。然后您可以group by id 并选择具有您所追求的排名的行。以下示例选择 第二 最低和 第三 最高:

select  id
,       max(case when rnAsc = 2 then number end) as SecondLowest
,       max(case when rnDesc = 3 then number end) as ThirdHighest
from    (
        select  ID
        ,       row_number() over (partition by ID order by number) as rnAsc
        ,       row_number() over (partition by ID order by number desc) as rnDesc
        ) as SubQueryAlias
group by
        id

max只是挑出一个非空值;您可以将其替换为min 甚至avg,并且不会影响结果。

【讨论】:

  • +1 我不敢相信只有五分之一的答案实际上管理了这个......并且也允许使用不同的 ID。
  • 要获得第二高,为什么不直接select X.* from (select row_number() over partition by id order by number) as rn, id, number) X where rn = 2? (然后要获得第二低,添加“desc”来排序?
  • @MakeMinePanacea:OP 在一行中要求两者,因此答案必须将两个特定行“旋转”成两列
  • 这没有给出正确的结果。它使用1|11|14 而不是1|11|15。不过,应该是一个简单的编辑 (rnDesc = 2)。
  • 运行良好。我确信这里和那里都会有一些调整(我实际上是在几列上做的。还使用 Dense_Rank 而不是 rowNumber 以便我可以考虑多个条目。非常感谢!
【解决方案2】:

这会起作用,但请注意注意事项:

SELECT Id, number
INTO #T
FROM (
  SELECT 1 ID, 10 number
  UNION
  SELECT 1 ID, 10 number
  UNION
  SELECT 1 ID, 11 number
  UNION
  SELECT 1 ID, 13 number
  UNION
  SELECT 1 ID, 14 number
  UNION
  SELECT 1 ID, 15 number
  UNION
  SELECT 1 ID, 16 number
) U;

WITH EX AS (
  SELECT Id, MIN(number) MinNumber, MAX(number) MaxNumber
  FROM #T
  GROUP BY Id
)
SELECT #T.Id, MIN(number) nMin, MAX(number) nMax
FROM #T INNER JOIN
     EX ON #T.Id = EX.Id
WHERE #T.number <> MinNumber AND #T.number <> MaxNumber
GROUP BY #T.Id

DROP TABLE #T;

如果您有两个相同的 MAX 值,这将不会拾取它们。因此,根据您的数据呈现方式,您可能会丢失正确的结果。

【讨论】:

  • @gbn:一个查询找到 MIN/MAX,第二个查询使用它们 :) 顶部的废话只是设置,因此可以通过复制运行并粘贴,但我相信你知道这一点。
  • 我的目的是强调自联接和聚合相比使用 ROW_NUMBER(或 DENSE_RANK 联合第二最大值等)更优雅。
  • @gbn:仅供参考,实际执行计划表明我的示例比使用@Andomar 提供的答案使用ROW_NUMBER() 优化得更好。
  • 在 SQL Server 2005 或 SQL Server 2008 上? '2005 的聚合往往更好,窗口功能随着更高版本而改进
  • @gbn:我使用的是 2K8 R2。如果您想聊天,我可以粘贴屏幕截图,以便您了解我的意思。批量运行时,拆分为 37%,而聚合为 63%。
【解决方案3】:

您可以使用以下方法选择下一个最小值:

SELECT MAX(Number)
FROM
(
  SELECT  top 2 (Number) 
   FROM table1 t1 
   WHERE ID = {MyNumber}
   order by Number
)a

仅当您可以使用 where 子句限制内部查询时才有效

【讨论】:

    【解决方案4】:

    这将是一个更好的方法。我很快把它放在一起,但如果你能把这两个查询结合起来,你会得到你正在寻找的东西。

    select *
    from
    (
        select
            myID,
            myNumber,
            row_number() over (order by myID) as myRowNumber
        from MyTable
    ) x
    where x.myRowNumber = 2
    
    select *
    from
    (
        select
            myID,
            myNumber,
            row_number() over (order by myID desc) as myRowNumber
        from MyTable
    ) y
    where x.myRowNumber = 2
    

    【讨论】:

      【解决方案5】:

      让表名是 tblName。 select max(number) from tblName where number not in (select max(number) from tblName);

      与 min 相同,只需将 max 替换为 min。

      【讨论】:

        【解决方案6】:

        正如我自己今天了解到的,解决方案是使用 LIMIT。您对结果进行排序,以使最高值位于顶部并将结果限制为 2。然后选择该子选择并以相反的方式对其进行排序,只取第一个。

        SELECT somefield FROM (
        SELECT somefield from table
        ORDER BY somefield DESC LIMIT 2) 
        ORDER BY somefield ASC LIMIT 1
        

        【讨论】:

        • 问题出在MSSQL 2K8上,没有LIMIT
        • @Yuck 哦,我开始写的时候已经添加了评论。废话。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-11-28
        • 1970-01-01
        • 2014-12-07
        • 2017-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多