从表中返回加起来达到最大给定值的项目

Posted

技术标签:

【中文标题】从表中返回加起来达到最大给定值的项目【英文标题】:Return items that add up to a maximum given value from a table 【发布时间】:2014-01-08 23:11:25 【问题描述】:

我正在尝试构建一个 sql 查询,该查询将返回具有总和的 ID 列表,总和小于或大于使用最少项目数的给定值。

这是我将要查询的表的示例。

ID    Value 
-----------  
226   2.3   
331   3.1   
25    1.5   
28    1.5   
29    1.2   
52    5.2   
38    3.5   

这里是按Value asc排序的。

ID   Value
----------  
29   1.2  
25   1.5  
28   1.5  
226  2.3  
331  3.1  
38   3.5  
52   5.2  

示例 A:

如果我的值为 6,我希望查询返回 ID 29、25、28 和 226。 1.2 + 1.5 + 1.5 + 2.3 = 6.5

示例 B:

如果我的值为 19,我希望查询返回所有 ID(29、25、28、226、331、38、52)。 1.2 + 1.5 + 1.5 + 2.3 + 3.1 + 3.5 + 5.2 = 18.3

我已尝试此处找到的建议答案: SQL select elements where sum of field is less than N

但是,这并不能完全满足我的需要,因为它只返回加起来小于设定值的 ID。此外,它假设 ID 是升序的,当我按 asc 值排序时,情况并非如此。

这甚至可以在 sql 语句中实现吗?还是我必须做一个程序/功能来完成这项任务?

【问题讨论】:

你能写出这些示例返回这些 ID 的过程吗?对于示例 A,ID 52 和 29 的总数为 6.4,这比您的答案少,并且更接近您想要的总数。 您使用什么标准来决定接受一个高于您期望总数的总数,而不是停止一个小于您期望总数的总数? sqlfiddle.com/#!2/61d60/36 @SaifHamed - 那是 mysql 而不是 Oracle,OP 希望能够获得可能总计大于所需总数的结果(尽管他们没有指定在什么情况下这些总数是可以接受的)。 @MT0 哎呀!每天我都越来越讨厌那个主人。所以,The Oracle does not looks like MySQL。我应该换个学院。大声笑 【参考方案1】:

假设您正在按升序讨论值,并且您想在最接近所需总数时停止:

SQL Fiddle

Oracle 11g R2 架构设置

CREATE TABLE data ( ID, Value ) AS
          SELECT 226, 2.3 FROM DUAL
UNION ALL SELECT 331, 3.1 FROM DUAL
UNION ALL SELECT 25, 1.5 FROM DUAL
UNION ALL SELECT 28, 1.5 FROM DUAL
UNION ALL SELECT 29, 1.2 FROM DUAL
UNION ALL SELECT 52, 5.2 FROM DUAL
UNION ALL SELECT 38, 3.5 FROM DUAL;

查询 1

WITH differences AS (
  SELECT ID,
         Value, 
         ABS(
           SUM( Value ) OVER ( ORDER BY Value ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW )
           - 6 -- REPLACE THIS WITH :desired_total
         ) AS difference
 FROM    data
),
min_difference AS (
  SELECT MIN( Value ) KEEP ( DENSE_RANK FIRST ORDER BY difference ASC ) AS max_value,
         MIN( ID ) KEEP ( DENSE_RANK FIRST ORDER BY difference ASC ) AS max_id
  FROM differences
)
SELECT ID,
       Value
FROM   differences d
       INNER JOIN
       min_difference m
       ON (  d.value < max_value
          OR ( d.value = m.max_value AND d.id <= max_id ) )

Results

|  ID | VALUE |
|-----|-------|
| 226 |   2.3 |
|  25 |   1.5 |
|  28 |   1.5 |
|  29 |   1.2 |

编辑 - 当运行总数刚好大于或等于所需总数时停止

SQL Fiddle

查询 1

计算每行的运行总计(按值升序排列),然后选择运行总计小于所需总计的所有行以及下一行(总计大于或等于的最小运行总计到所需的总数)。

WITH running_totals AS (
  SELECT ID,
         Value,
         SUM( Value ) OVER ( ORDER BY Value ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS running_total
 FROM    data
)
SELECT ID,
       Value
FROM   running_totals
WHERE  running_total < 6
UNION ALL
SELECT MIN( id    ) KEEP ( DENSE_RANK FIRST ORDER BY running_total ),
       MIN( value ) KEEP ( DENSE_RANK FIRST ORDER BY running_total )
FROM   running_totals
WHERE  running_total >= 6

Results

|  ID | VALUE |
|-----|-------|
|  29 |   1.2 |
|  28 |   1.5 |
|  25 |   1.5 |
| 226 |   2.3 |

编辑 2 - 另一种方法:

WITH running_totals AS (
  SELECT ID,
         Value,
         SUM( Value ) OVER ( ORDER BY Value, ID ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS running_total,
         ROW_NUMBER() OVER ( ORDER BY Value, ID ) AS idx
  FROM   data
)
SELECT ID,
       Value
FROM   running_totals
WHERE  idx <= (SELECT MAX(idx) + 1 FROM running_totals WHERE running_total < 6 );

【讨论】:

所以我尝试了 SQL fiddle .. 起初它似乎正在工作。但是,当我将值切换到更高的值时。例如,如果我尝试使用值 10,它会返回 226、331、25、28、29,它们总共只能达到 9.6,并且不符合要求,因为它应该返回一个以使其超过 10。在我们的例子中,它返回列表中的 id 也应为 38。 我对原始问题发表了评论,询问停止列表的标准是什么 - 你还没有回答。如果你看一下这个答案开头的假设,那么我说它会在它达到最接近答案的总数时停止 - 因为 9.6 比 (9.6+3.5=13.1) 更接近 10,那么这就是这个答案将停止的地方.明天我会尝试给你一个编辑过的答案,但如果你指定你想要的算法标准,那么这意味着我们不必猜测。 哎呀,我想我忘了点击“添加评论”。无论如何,标准是查询应返回按 asc 值顺序加起来 >= 给定值 (n) 的 ID 列表,如果所有可用值的总和小于 n,则显示所有 ID。

以上是关于从表中返回加起来达到最大给定值的项目的主要内容,如果未能解决你的问题,请参考以下文章

返回列中给定值的第一行 - BigQuery

用于从表中选择具有最新时间戳的行的 JOOQ 代码

SQL Server - 重叠数据的累积总和 - 获取总和达到给定值的日期

给定一个数字列表和一个数字 k,返回列表中的任意两个数字加起来是不是为 k

SQL 函数,通过使用参数从表中返回值

如何让 LINQ 返回具有给定属性最大值的对象? [复制]