使用 SQL Query 生成多个最大值和最小值

Posted

技术标签:

【中文标题】使用 SQL Query 生成多个最大值和最小值【英文标题】:Producing multiple maximum and minimum values with SQL Query 【发布时间】:2011-03-22 15:32:25 【问题描述】:

我对 SQL 的一个奇怪的限制感到沮丧 - 它显然无法将一条记录与聚合函数之外的另一条记录关联起来。我的问题就是这样总结的。 我有一张桌子,已经整理好了。我需要找到它的最大值(注意复数!)和最小值。不,我不是在寻找单个最大值或单个最小值。更具体地说,我正在尝试生成数字序列的局部峰值列表。生成它的算法的粗略描述是:

WHILE NOT END_OF_TABLE
 IF RECORD != FIRST_RECORD AND RECORD != LAST_RECORD THEN
  IF ((RECORD(Field)<RECORD_PREVIOUS(Field) AND RECORD(Field)<RECORD_NEXT(Field)) OR
      RECORD(Field)>RECORD_PREVIOUS(Field) AND RECORD(Field)>RECORD_NEXT(Field)) THEN
     ADD_RESULT RECORD
  END IF
 END IF
END WHILE

看到问题了吗?我需要查询给定记录必须与前一个和下一个记录的值进行比较。这甚至可以在标准 SQL 中完成吗?

【问题讨论】:

表格是如何排序的?它有主键吗? 示例数据可能会有所帮助,表架构也可以... @Andomar 是按自变量排序的。 :) 这表示数学函数 f(x)=y 的输出。 x 是它的排序依据。它有一个主键“id”。但是输入的数据已经排序了。 @Martin 我目前正在使用 Microsoft Access 2007,但我可能会迁移到当前版本的 mysql。理想的解决方案对两者都足够通用。 @gbn 我不确定什么是合适的样本集。桌子相当大。我正在对仅具有相关项和独立项的未知函数进行数值分析。 f(x)=y 有一个键,但它可以是任何唯一标识符。假设它是随机值。 :) 有“x”和“y”字段,数据库按“x”字段(升序)排序。 “x”和“y”都是任意精度的浮点值。 【参考方案1】:

您的挫败感与许多人一样;虽然 SQL 非常适合处理 general 集,但在尝试处理特定于 ordered 集的问题时(无论它是在表中物理排序还是隐含或明确的逻辑顺序无关)。有些事情可以提供帮助(例如,rank()row_number() 函数),但解决方案可能因 RDBMS 不同而有所不同。

如果您能具体说明您正在使用哪个平台,我或其他人可以提供更详细的答案。

【讨论】:

我认为完整的 ANSI OVER 子句可能会允许这种事情,所以它不是 SQL 本身的限制。但 SQL Server 还没有完全实现它。 over 在 SQL Server 2008 中可用。 @antisanity。关键字是“完整的”。在连接站点by Itzik Ben Gan 上有大量建议可以进一步扩展它。 @Martin:我不知道 SQL Server 的窗口函数实现(在 2008 年及以后)是如何不完整的;您是否知道缺少某些东西? @Adam - Itzik 在insidetsql.com/OVER_Clause_and_Ordered_Calculations.doc 的这篇论文中对此进行了很好的介绍。我不确定这些建议是否符合 OP 的要求,但它们的方向相同。【参考方案2】:

你必须自加入两次并生成一个没有间隙的行号:

在 T-SQL 中:

WITH ordered AS (
    SELECT ROW_NUMBER() OVER (ORDER BY your_sort_order) AS RowNumber
           ,* -- other columns here
)
SELECT *
FROM ordered
LEFT JOIN ordered AS prev
    ON prev.RowNumber = ordered.RowNumber - 1
LEFT JOIN ordered AS next
    ON next.RowNumber = ordered.RowNumber + 1
WHERE -- here you put in your local min/local max and end-point handling logic - end points will have NULL in next/prev

【讨论】:

有趣的解决方案。您能否更详细地解释它的工作原理以供将来参考?它是否与 Microsoft Access 2007 和 MySQL 兼容? 该技术适用于任何事情 - Access 不支持 WITH 构造来制作公用表表达式,不确定 MySQL。两者都可能不支持 ROW_NUMBER()。您基本上是将集合转换为有序集合并根据顺序分配一个数字。然后简单地加入自身两次以获得前驱行和后继行。【参考方案3】:

是的。您需要自联接 - 但没有数据库模式,很难具体说明解决方案。

具体来说,我想知道您提到的“排序”事情 - 但我假设我们可以使用一个“ID”字段。

(哦,我使用的是老式连接语法,因为我是恐龙)。

select *
from   myTable   main,
       myTable   previous,
       myTable   next
where  previous.id  = main.id - 1
and    next.id      = main.id + 1
and    previous.record > main.record
and    next.record     < main.record

(我认为我在大于/小于子句中正确解释了您的要求,但请根据口味调整)。

【讨论】:

这只有在有明确的方法来确定什么下一个和上一个记录的 ID 是(在您的情况下,它们是连续的)时才有效。如果有间隙,这将不起作用。 是的 - 在没有模式的情况下,我做了一些假设。如果有差距,您可以将“+1”和“-1”替换为子查询,以查找小于当前 ID 的最大/最小值。 WHERE 子句的最后两个条件需要更正。最大值 = (Main > Previous AND Main > Next), Minima = (Main 嗯。我以为我已经为 Microsoft Access 标记了这个,但它也可能在 MySQL 中完成。目前更容易获得访问权限。并且表格可以认为是3个字段,int ID,X值,Y值。 Y 值字段是需要考虑的字段。该表已经在 X 值字段上按升序排序。这种帮助。 x 值不一定是整数,也可以代替索引 :) @adam 是的。 id 字段确实存在,并且可能是连续的,但也有可能不是连续的。【参考方案4】:
SELECT
  current.RowID,
  current.Value,
  CASE WHEN
    (
     (current.Value < COALESCE(previous.Value,   current.Value + 1))
     AND
     (current.Value < COALESCE(subsequent.Value, current.Value + 1))
    )
  THEN
    'Minima'
  ELSE
    'Maxima'
  END
FROM
  myTable  current
LEFT JOIN
  myTable  previous
    ON previous.RowID = (SELECT MAX(RowID) FROM myTable WHERE RowID < current.ROWID)
LEFT JOIN
  myTable  subsequent
    ON subsequent.RowID = (SELECT MIN(RowID) FROM myTable WHERE RowID > current.ROWID)
WHERE
  (
   (current.Value < COALESCE(previous.Value,   current.Value + 1))
   AND
   (current.Value < COALESCE(subsequent.Value, current.Value + 1))
  )
  OR
  (
   (current.Value > COALESCE(previous.Value,   current.Value - 1))
   AND
   (current.Value > COALESCE(subsequent.Value, current.Value - 1))
  )

注意: 逻辑是从您那里复制而来的,但不适用于在一个或多个连续记录中相等的局部最大值/最小值。

注意:我创建了一个虚构的 RowID 来按顺序连接记录,重要的是连接获取“前一个”和“后一个”记录。

注意: LEFT JOIN 和 COALESCE 语句导致第一个和最后一个值始终被计为最大值或最小值。

【讨论】:

不幸的是,不能假设排序值是整数。在这种情况下,它是一个浮点数。例如,我当前使用的序列开始(仅 x):0,.5,1.5,2.5,3.5...,239.5,240 请注意结束值与其他值不同?这是一个真实世界的数据集;没有帮助。 :( 这个解决方案也可以在 Microsoft Access 2007 和 MySQL 中工作吗?而且,是的,我考虑了 (Value = Previous AND Value = Subsequent) 的问题。这些代表拐点而不是极值。需要单独处理。 如果您没有一种机制来代数推导“下一个”和“上一个”排序值,您有两种选择:创建一个(物理上,例如标识列,或虚拟地,例如如在其他答案中提到的 ROW_NUMBER() 的使用),或使用相关的子查询。我已经用相关的子查询更新了答案。

以上是关于使用 SQL Query 生成多个最大值和最小值的主要内容,如果未能解决你的问题,请参考以下文章

Prim算法和Kruskal算法(图论中的最小生成树算法)

[基础] 在Python中获得字典列表中最大值与最小值

[BJOI2010] 严格次小生成树

随便说说堆——二叉堆

Oracle SQL:从组中选择最大值和最小值

sql 几个字段最小值