【问题标题】:Select x rows from table having total < y SQL Server从总计 < y SQL Server 的表中选择 x 行
【发布时间】:2024-01-14 02:12:01
【问题描述】:

我有Table A 有以下数据:

Id  Value
1   100
2   63
4   50
6   24
7   446

我想选择带有SUM(value) &lt;= 200 的第一行。

所以期望的输出应该是:

Id Value
1   100
4   50
6   24

【问题讨论】:

  • 哪个版本的sql-server?在 2012 年,您可以使用 ROWS UNBOUNDED PRECEDING 来获取累计总和。 *.com/questions/2120544/how-to-get-cumulative-sum
  • @TimSchmelter,似乎 OP 只想让Value 小于或等于 200,而不是累积总和。
  • SELECT Id, Value FROM TableName WHERE Value &lt;=200
  • 我认为您需要在数据样本中添加更多值以使其更清晰。现在很混乱。
  • 我的意思是值的总和是

标签: sql sql-server sum


【解决方案1】:

简单的答案

您需要找到每一行的累积和,并且由于您想要尽可能多的行,因此您需要从最小值开始 (ORDER BY Value):

WITH Data AS
(   SELECT  Id, 
            Value,
            CumulativeValue = SUM(Value) OVER(ORDER BY Value, Id)
    --FROM  (VALUES (1, 100), (2, 300), (4, 50), (6, 24), (7, 446)) AS t (Id, Value)
    FROM    TableA AS t
)
SELECT  d.Id, d.Value
FROM    Data AS d
WHERE   d.CumulativeValue <= 200
ORDER BY d.Id;  

完整答案

如果您想更有选择性地选择总和小于 200 的行,那么它会变得有点复杂,例如,在您的新样本数据中:

Id  Value
1   100
2   63
4   50
6   24
7   446

共有 3 种不同的组合允许总数少于 200:

Id  Value
1   100
2   63
6   24

--> 187

Id  Value
2   63
4   50
6   24

--> 137

Id  Value
1   100
4   50
6   24

--> 174

这样做的唯一方法是获取总和小于 200 的所有组合,然后选择所需的组合,为此,您需要使用递归公用表表达式来获取所有组合:

WITH TableA AS
(   SELECT  Id, Value
    FROM    (VALUES (1, 100), (2, 63), (4, 50), (6, 24), (7, 446)) t (Id, Value)
), CTE AS
(   SELECT  Id,
            IdList = CAST(Id AS VARCHAR(MAX)), 
            CumulativeValue = Value,
            ValueCount = 1
    FROM    TableA AS t
    UNION ALL
    SELECT  T.ID, 
            IdList = CTE.IDList + ',' + CAST(t.ID AS VARCHAR(MAX)),
            CumulativeValue = CTE.CumulativeValue + T.Value,
            ValueCount = CTE.ValueCount + 1
    FROM    CTE
            INNER JOIN TableA AS T
                ON ',' + CTE.IDList + ',' NOT LIKE '%,' + CAST(t.ID AS VARCHAR(MAX)) + ',%'
                AND CTE.ID < T.ID
    WHERE   T.Value + CTE.CumulativeValue <= 200
)
SELECT  *
FROM    CTE
ORDER BY ValueCount DESC, CumulativeValue DESC;

此输出(已删除单行)

Id  IdList  CumulativeValue ValueCount
-------------------------------------
6   1,2,6       187         3
6   1,4,6       174         3
6   2,4,6       137         3
2   1,2         163         2
4   1,4         150         2
6   1,6         124         2
4   2,4         113         2
6   2,6         87          2
6   4,6         74          2

因此,您需要选择最符合您要求的行组合,例如,如前所述,如果您希望值尽可能接近 200 的行数最多,那么您需要选择顶部结果,如果您想要最低的总数,那么您需要更改排序。

那么你就可以通过EXISTS获取你原来的输出,获取IdList中存在的记录:

WITH TableA AS
(   SELECT  Id, Value
    FROM    (VALUES (1, 100), (2, 63), (4, 50), (6, 24), (7, 446)) t (Id, Value)
), CTE AS
(   SELECT  Id,
            IdList = CAST(Id AS VARCHAR(MAX)), 
            CumulativeValue = Value,
            ValueCount = 1
    FROM    TableA AS t
    UNION ALL
    SELECT  T.ID, 
            IdList = CTE.IDList + ',' + CAST(t.ID AS VARCHAR(MAX)),
            CumulativeValue = CTE.CumulativeValue + T.Value,
            ValueCount = CTE.ValueCount + 1
    FROM    CTE
            INNER JOIN TableA AS T
                ON ',' + CTE.IDList + ',' NOT LIKE '%,' + CAST(t.ID AS VARCHAR(MAX)) + ',%'
                AND CTE.ID < T.ID
    WHERE   T.Value + CTE.CumulativeValue <= 200
), Top1 AS
(   SELECT  TOP 1 IdList, CumulativeValue
    FROM    CTE
    ORDER BY ValueCount DESC, CumulativeValue DESC -- CHANGE TO MEET YOUR NEEDS
)
SELECT  *
FROM    TableA AS t
WHERE   EXISTS
        (   SELECT  1
            FROM    Top1
            WHERE   ',' + Top1.IDList + ',' LIKE '%,' + CAST(t.ID AS VARCHAR(MAX)) + ',%'
        );

这不是很有效,但我目前看不到更好的方法。

返回

Id  Value
1   100
2   63
6   24

这是您可以获得的最接近 200 的行数。由于有多种方法可以实现“x 个总和小于 200 的行数”,因此编写查询的方法也有多种。您需要更具体地了解您的组合偏好,以便获得所需的确切答案。

【讨论】:

  • 您的代码似乎工作正常,但这部分 FROM (VALUES (1, 100), (2, 300), (4, 50), (6, 24), (7, 446)) AS t (Id, Value) 我不知道如何选择
  • @user1753385:这只是示例数据。用你的真实桌子替换它。
  • 为什么是 ORDER BY Value, Id 而不仅仅是 ORDER BY Id ? OP 希望 ti 获得按 ID 排序的累积总和 “选择总和 )。
  • WITH 数据为 (SELECT Id, Value, CumulativeValue = SUM(Value) OVER(ORDER BY Value, Id) FROM (select id, value from TableA)) SELECT d.Id, d.Value FROM数据 AS d WHERE d.CumulativeValue
  • @TimSchmelter 按 ID 顺序的累计总数为 100, 163, 213, 237, 683,因此 &lt;= 200 的过滤器将仅返回前两行,而不添加第三行的 24 in,这确实与 OP 的预期输出不匹配。
【解决方案2】:

这应该返回预期的结果:

WITH TableWithTotals AS
(SELECT 
 id, 
 value,
 SUM (Value) OVER (ORDER BY Value, Id) as total
FROM myTable)
SELECT * FROM TableWithTotals
WHERE total <=200;

此代码将最大化符合 200 限制的记录数,因为运行总计是根据有序值计算的。

SQL Fiddle

【讨论】:

  • 好吧,我原来的答案的问题是OVER不能用在WHERE中,通过CTE很容易修复,想法是一样的。
  • Thnx 这是我想要的,如果我的问题不清楚,抱歉:)
  • @Bulat:但无论如何这都是 Gareth 的重复答案
  • 我同意,这就是为什么赞成他的回答。 @user1753385 如果你接受 Gareth 的回答,我会删除我的。
【解决方案3】:

根据我了解您的要求,我认为以下查询应该对您有所帮助。

WITH FinalResult AS
(SELECT 
 id, 
 value,
 SUM (Value) OVER (ORDER BY Value, Id) as ValueCount
FROM TableA)
SELECT * FROM FinalResult
WHERE ValueCount <=200;

【讨论】:

  • 这行不通。您在选择中有没有聚合函数的 group by 子句。
【解决方案4】:

你应该试试这个:-

    SELECT Id, Value FROM TableName WHERE Value <=200

【讨论】:

  • 这不提供 OP 要求的结果,即 sum
最近更新 更多