从表中选择x行,总数<是SQL Server

时间:2015-08-12 09:02:29

标签: sql sql-server sum

我有Table A,其中包含以下数据:

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

我想选择带SUM(value) <= 200的第一行。

所以期望的输出应该是:

Id Value
1   100
4   50
6   24

4 个答案:

答案 0 :(得分:8)

简单回答

您需要找到每行的累积总和,并且由于您需要尽可能多的行,因此您需要从最低值(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的最可能的行。由于有多种方法可以实现总和小于200&#34;的行数,因此还有多种编写查询的方法。您需要更加具体地了解您的组合偏好,以便获得所需的确切答案。

答案 1 :(得分:4)

这应返回预期结果:

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

答案 2 :(得分:1)

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

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

答案 3 :(得分:-1)

你应该试试这个: -

    SELECT Id, Value FROM TableName WHERE Value <=200