【问题标题】:SQL select some rows in a table so that they sum up to certain valueSQL 选择表中的某些行,以便它们总和为某个值
【发布时间】:2012-03-20 12:11:43
【问题描述】:

我怎样才能只选择下表中的一些行,以便它们总和为某个值?

Table
-----
id | qty1 | qty2 | qty3 | qty4
------------------------------
1  | 0.0  | 0.0  | 10   | 20
2  | 1.5  | 0.0  | 7.5  | 18
3  | 1.0  | 2.0  | 7.5  | 18
4  | 0.0  | 0.5  | 5    | 13

比方说,我想要的最高值是 57...

所以我需要从上一个表中选择行,使得每行的 qty1+qty2+qty3+qty4,直到 57 值,然后丢弃其他行。在这个例子中,我会得到以下信息:

id | qty1 | qty2 | qty3 | qty4
------------------------------
1  | 0.0  | 0.0  | 10   | 20
2  | 1.5  | 0.0  | 7.5  | 18

因为 10+20+1.5+7.5+18 = 57,所以我舍弃了第 3 行和第 4 行...

现在我希望最高值为 50,那么我应该得到:

id | qty1 | qty2 | qty3 | qty4
------------------------------
1  | 0.0  | 0.0  | 10   | 20
2  | 1.5  | 0.0  | 7.5  | 11

由于这些值的总和为 50,并且 row2,qty4 中的 7 被忽略... (顺便说一句,行以这种特殊方式排序,因为这是我希望计算 qtys 总和的顺序......例如,先总结第一行,然后是 3,然后是 2,然后是 4,这是无效的......它们应始终按 1、2、3、4 的顺序相加...)

如果我想要这个的补充怎么办?我的意思是,我在最后一个结果中没有得到的另外两行。

第一种情况:

id | qty1 | qty2 | qty3 | qty4
------------------------------
3  | 1.0  | 2.0  | 7.5  | 18
4  | 0.0  | 0.5  | 5    | 13

第二种情况:

id | qty1 | qty2 | qty3 | qty4
------------------------------
2  | 0.0  | 0.0  | 0.0  | 7
3  | 1.0  | 2.0  | 7.5  | 18
4  | 0.0  | 0.5  | 5    | 13

(如果第二种情况太复杂,获取如何:

id | qty1 | qty2 | qty3 | qty4
------------------------------
1  | 0.0  | 0.0  | 10   | 20

因为将第 2 行的原始数量加起来会超过 50 的值,所以我将其丢弃... 这种情况下的补码应该是:

id | qty1 | qty2 | qty3 | qty4
------------------------------
2  | 1.5  | 0.0  | 7.5  | 18
3  | 1.0  | 2.0  | 7.5  | 18
4  | 0.0  | 0.5  | 5    | 13

)

【问题讨论】:

  • 我写了很多复杂的查询,我喜欢接受挑战,但这是极少数情况之一,只需要你用你选择的语言编写程序代码。
  • 即使是简化的第二种情况?文章末尾括号中的最后一个...?
  • 简化的第二种情况可用作查询。如果您修改您的问题以仅要求该问题(或创建一个仅要求该问题的新问题),我可以提供帮助。
  • fo 300 我将不得不向您解释我所面临的实际问题的每一个细节,而不是在这里仅展示抽象的特定细节作为示例,因此您提供的解决方案不仅仅是概括我必须翻译或改编,但我需要解决我的问题的实际解决方案。我会让你签署一份保密协议,因为这些细节是我工作中的事情,不应该为任何人所知...... ;)

标签: mysql select sum


【解决方案1】:

括号里的简化选项还不错:

SELECT foo1.*
  FROM foo AS foo1
  JOIN foo AS foo2
    ON foo2.id <= foo1.id
 GROUP
    BY foo1.id
HAVING SUM(foo2.qty1 + foo2.qty2 + foo2.qty3 + foo2.qty4) <= 57
;

(你没有提到表的名称,所以我选择了foo。)

补语是:

SELECT *
  FROM foo
 WHERE id NOT IN
        ( SELECT foo1.id
            FROM foo AS foo1
            JOIN foo AS foo2
              ON foo2.id <= foo1.id
           GROUP
              BY foo1.id
          HAVING SUM(foo2.qty1 + foo2.qty2 + foo2.qty3 + foo2.qty4) <= 57
        )
;

不带括号的选项要复杂得多;这是可行的,但你最好使用stored procedure

【讨论】:

    【解决方案2】:

    让我们这样说吧:如果 SQL 是一种宗教,我会因为提供这个解决方案而下地狱。 SQL 并不是为了解决这类问题,所以任何解决方案都会很糟糕。我的也不例外:)

    set @limitValue := 50;
    select id, newQty1, newQty2, newQty3, newQty4 from (
      select id,
      if(@limitValue - qty1 > 0, qty1, greatest(@limitValue, 0)) newQty1,
      @limitValue := @limitValue - qty1 Total1,
      if(@limitValue - qty2 > 0, qty2, greatest(@limitValue, 0)) newQty2,
      @limitValue := @limitValue - qty2 Total2,
      if(@limitValue - qty3 > 0, qty3, greatest(@limitValue, 0)) newQty3,
      @limitValue := @limitValue - qty3 Total3,
      if(@limitValue - qty4 > 0, qty4, greatest(@limitValue, 0)) newQty4,
      @limitValue := @limitValue - qty4 Total4
      from (
        select id, qty1, qty2, qty3, qty4,
          @rowTotal < @limitValue Useful,
          @previousRowTotal := @rowTotal PreviousRowTotal,
          @rowTotal := @rowTotal + qty1 + qty2 + qty3 + qty4 AllRowsTotal,
          @rowTotal - @previousRowTotal CurrentRowTotal 
        from t,
        (select @rowTotal := 0, @previousRowTotal := 0) S1
      ) MarkedUseful
      where useful = 1
    ) Final
    

    对于提供的数据,这会导致:

    +----+---------+---------+---------+---------+
    | ID | NEWQTY1 | NEWQTY2 | NEWQTY3 | NEWQTY4 |
    +----+---------+---------+---------+---------+
    |  1 | 0       |       0 | 10      |      20 |
    |  2 | 1.5     |       0 | 7.5     |      11 |
    +----+---------+---------+---------+---------+
    

    还有补语:

    set @limitValue := 50;
    select t1.id,
      coalesce(t1.qty1 - newQty1, t1.qty1) newQty1,
      coalesce(t1.qty2 - newQty2, t1.qty2) newQty2,
      coalesce(t1.qty3 - newQty3, t1.qty3) newQty3,
      coalesce(t1.qty4 - newQty4, t1.qty4) newQty4
    from t t1 left join (
        select id,
        if(@limitValue - qty1 > 0, qty1, greatest(@limitValue, 0)) newQty1,
        @limitValue := @limitValue - qty1 Total1,
        if(@limitValue - qty2 > 0, qty2, greatest(@limitValue, 0)) newQty2,
        @limitValue := @limitValue - qty2 Total2,
        if(@limitValue - qty3 > 0, qty3, greatest(@limitValue, 0)) newQty3,
        @limitValue := @limitValue - qty3 Total3,
        if(@limitValue - qty4 > 0, qty4, greatest(@limitValue, 0)) newQty4,
        @limitValue := @limitValue - qty4 Total4
        from (
          select id, qty1, qty2, qty3, qty4,
            @rowTotal < @limitValue Useful,
            @previousRowTotal := @rowTotal PreviousRowTotal,
            @rowTotal := @rowTotal + qty1 + qty2 + qty3 + qty4 AllRowsTotal,
            @rowTotal - @previousRowTotal CurrentRowTotal 
          from t,
          (select @rowTotal := 0, @previousRowTotal := 0) S1
        ) MarkedUseful
        where useful = 1
    ) Final
    on t1.id = final.id
    where Total1 < 0 or Total2 < 0 or Total3 < 0 or Total4 < 0 or final.id is null
    

    对于提供的数据,这会导致:

    +----+---------+---------+---------+---------+
    | ID | NEWQTY1 | NEWQTY2 | NEWQTY3 | NEWQTY4 |
    +----+---------+---------+---------+---------+
    |  2 |       0 | 0       | 0       |       7 |
    |  3 |       1 | 2       | 7.5     |      18 |
    |  4 |       0 | 0.5     | 5       |      13 |
    +----+---------+---------+---------+---------+
    

    享受吧!

    【讨论】:

    • 刚刚尝试了提供的解决方案,但是对于补充情况,使用相同的示例数据,我没有得到您显示的结果,而是数据表中的原始 4 行...
    • 现在可以了!我不知道我上次做错了什么:)让我测试一下并将其应用到我的真实案例中,我会给你一个关于赏金的答案;)
    【解决方案3】:

    您应该只调整init 子查询中的@limit 变量初始化。第一个查询输出数据达到限制,第二个查询输出其补码。

    SELECT
      id,
      @qty1 as qty1,
      @qty2 as qty2,
      @qty3 as qty3,
      @qty4 as qty4
    FROM quantities q,
      (SELECT @qty1:=0.0, @qty2:=0.0,
              @qty3:=0.0, @qty4:=0.0,
              @limit:=50.0) init
    WHERE
      IF(@limit > 0,
         GREATEST(1,
           IF(@limit-qty1 >=0,
              @limit:=(@limit-(@qty1:=qty1)),
              @qty1:=@limit + LEAST(@limit, @limit:=0)),
           IF(@limit-qty2 >=0,
              @limit:=(@limit-(@qty2:=qty2)),
              @qty2:=@limit + LEAST(@limit, @limit:=0)),
           IF(@limit-qty3 >=0,
              @limit:=(@limit-(@qty3:=qty3)),
              @qty3:=@limit + LEAST(@limit, @limit:=0)),
           IF(@limit-qty4 >=0,
              @limit:=(@limit-(@qty4:=qty4)),
              @qty4:=@limit + LEAST(@limit, @limit:=0))),0)
    ;
    

    补语:

    SELECT
      id,
      IF(qty1=@qty1, qty1, qty1-@qty1) as qty1,
      IF(qty2=@qty2, qty2, qty2-@qty2) as qty2,
      IF(qty3=@qty3, qty3, qty3-@qty3) as qty3,
      IF(qty4=@qty4, qty4, qty4-@qty4) as qty4
    FROM quantities q,
      (SELECT @qty1:=0.0, @qty2:=0.0,
              @qty3:=0.0, @qty4:=0.0,
              @limit:=50.0) init
    WHERE
      IF(
        LEAST(
          IF(@limit-qty1 >=0,
             @limit:=(@limit-(@qty1:=qty1)),
             @qty1:=@limit + LEAST(@limit, @limit:=0)),
          IF(@limit-qty2 >=0,
             @limit:=(@limit-(@qty2:=qty2)),
             @qty2:=@limit + LEAST(@limit, @limit:=0)),
          IF(@limit-qty3 >=0,
             @limit:=(@limit-(@qty3:=qty3)),
             @qty3:=@limit + LEAST(@limit, @limit:=0)),
          IF(@limit-qty4 >=0,
             @limit:=(@limit-(@qty4:=qty4)),
             @qty4:=@limit + LEAST(@limit, @limit:=0)),
          @limit), 0, 1)
    ;
    

    【讨论】:

    • 谢谢!它确实适用于样本数据。让我用我的真实数据测试一下,我会回答你关于赏金的问题......
    【解决方案4】:

    让我们从问题中加载您的示例数据

    mysql> drop database if exists javier;
    Query OK, 1 row affected (0.02 sec)
    
    mysql> create database javier;
    Query OK, 1 row affected (0.01 sec)
    
    mysql> use javier
    Database changed
    mysql> create table mytable
        -> (
        ->     id int not null auto_increment,
        ->     qty1 float,qty2 float,qty3 float,qty4 float,
        ->     primary key (id)
        -> );
    Query OK, 0 rows affected (0.08 sec)
    
    mysql> insert into mytable (qty1,qty2,qty3,qty4) values
        -> ( 0.0 , 0.0 , 10  , 20 ),( 1.5 , 0.0 , 7.5 , 18 ),
        -> ( 1.0 , 2.0 , 7.5 , 18 ),( 0.0 , 0.5 , 5   , 13 );
    Query OK, 4 rows affected (0.05 sec)
    Records: 4  Duplicates: 0  Warnings: 0
    
    mysql> select * from mytable;
    +----+------+------+------+------+
    | id | qty1 | qty2 | qty3 | qty4 |
    +----+------+------+------+------+
    |  1 |    0 |    0 |   10 |   20 |
    |  2 |  1.5 |    0 |  7.5 |   18 |
    |  3 |    1 |    2 |  7.5 |   18 |
    |  4 |    0 |  0.5 |    5 |   13 |
    +----+------+------+------+------+
    4 rows in set (0.00 sec)
    
    mysql>
    

    完全有效的最终查询

    select BBBB.* from (select id,sums FROM (select A.id,A.sums from
    (select id,(select sum(qty1+qty2+qty3+qty4) from mytable BB 
    where BB.id<=AA.id) sums from mytable AA order by id) A 
    INNER JOIN (SELECT 50 mylimit) B ON A.sums <= B.mylimit) AAA
    UNION
    (select A.id,A.sums from (select id,(select sum(qty1+qty2+qty3+qty4)  
    from mytable BB where BB.id<=AA.id) sums from mytable AA order by id) A
    where A.sums=(select min(A.sums) sums from (select id, 
    (select sum(qty1+qty2+qty3+qty4) from mytable BB where BB.id<=AA.id) sums 
    from mytable AA order by id) A INNER JOIN (SELECT 50 mylimit) B 
    ON A.sums >= B.mylimit))) AAAA JOIN mytable BBBB USING (id);
    

    完全有效的最终查询补充

    select BBBB.* from  mytable BBBB LEFT JOIN
    (select id,sums FROM (select A.id,A.sums from ( 
    select id,(select sum(qty1+qty2+qty3+qty4)  
    from mytable BB where BB.id<=AA.id) sums 
    from mytable AA order by id) A INNER JOIN
    (SELECT 50 mylimit) B ON A.sums <= B.mylimit) AAA
    UNION
    (select A.id,A.sums from (select id, 
    (select sum(qty1+qty2+qty3+qty4) from mytable BB 
    where BB.id<=AA.id) sums from mytable AA order by id) A
    where A.sums=(select min(A.sums) sums from ( 
    select id,(select sum(qty1+qty2+qty3+qty4) from mytable BB 
    where BB.id<=AA.id) sums from mytable AA order by id) A 
    INNER JOIN (SELECT 50 mylimit) B ON A.sums >= B.mylimit))) AAAA
    USING (id) WHERE AAAA.id IS NULL;
    

    这是 57 的输出

    mysql>     select BBBB.* from (select id,sums FROM (select A.id,A.sums from
        ->     (select id,(select sum(qty1+qty2+qty3+qty4) from mytable BB
        ->     where BB.id<=AA.id) sums from mytable AA order by id) A
        ->     INNER JOIN (SELECT 57 mylimit) B ON A.sums <= B.mylimit) AAA
        ->     UNION
        ->     (select A.id,A.sums from (select id,(select sum(qty1+qty2+qty3+qty4)
        ->     from mytable BB where BB.id<=AA.id) sums from mytable AA order by id) A
        ->     where A.sums=(select min(A.sums) sums from (select id,
        ->     (select sum(qty1+qty2+qty3+qty4) from mytable BB where BB.id<=AA.id) sums
        ->     from mytable AA order by id) A INNER JOIN (SELECT 57 mylimit) B
        ->     ON A.sums >= B.mylimit))) AAAA JOIN mytable BBBB USING (id);
    +----+------+------+------+------+
    | id | qty1 | qty2 | qty3 | qty4 |
    +----+------+------+------+------+
    |  1 |    0 |    0 |   10 |   20 |
    |  2 |  1.5 |    0 |  7.5 |   18 |
    +----+------+------+------+------+
    2 rows in set (0.00 sec)
    
    mysql>     select BBBB.* from  mytable BBBB LEFT JOIN
        ->     (select id,sums FROM (select A.id,A.sums from (
        ->     select id,(select sum(qty1+qty2+qty3+qty4)
        ->     from mytable BB where BB.id<=AA.id) sums
        ->     from mytable AA order by id) A INNER JOIN
        ->     (SELECT 57 mylimit) B ON A.sums <= B.mylimit) AAA
        ->     UNION
        ->     (select A.id,A.sums from (select id,
        ->     (select sum(qty1+qty2+qty3+qty4) from mytable BB
        ->     where BB.id<=AA.id) sums from mytable AA order by id) A
        ->     where A.sums=(select min(A.sums) sums from (
        ->     select id,(select sum(qty1+qty2+qty3+qty4) from mytable BB
        ->     where BB.id<=AA.id) sums from mytable AA order by id) A
        ->     INNER JOIN (SELECT 57 mylimit) B ON A.sums >= B.mylimit))) AAAA
        ->     USING (id) WHERE AAAA.id IS NULL;
    +----+------+------+------+------+
    | id | qty1 | qty2 | qty3 | qty4 |
    +----+------+------+------+------+
    |  3 |    1 |    2 |  7.5 |   18 |
    |  4 |    0 |  0.5 |    5 |   13 |
    +----+------+------+------+------+
    2 rows in set (0.00 sec)
    
    mysql>
    

    这是 50 的输出

    mysql>     select BBBB.* from (select id,sums FROM (select A.id,A.sums from
        ->     (select id,(select sum(qty1+qty2+qty3+qty4) from mytable BB
        ->     where BB.id<=AA.id) sums from mytable AA order by id) A
        ->     INNER JOIN (SELECT 50 mylimit) B ON A.sums <= B.mylimit) AAA
        ->     UNION
        ->     (select A.id,A.sums from (select id,(select sum(qty1+qty2+qty3+qty4)
        ->     from mytable BB where BB.id<=AA.id) sums from mytable AA order by id) A
        ->     where A.sums=(select min(A.sums) sums from (select id,
        ->     (select sum(qty1+qty2+qty3+qty4) from mytable BB where BB.id<=AA.id) sums
        ->     from mytable AA order by id) A INNER JOIN (SELECT 50 mylimit) B
        ->     ON A.sums >= B.mylimit))) AAAA JOIN mytable BBBB USING (id);
    +----+------+------+------+------+
    | id | qty1 | qty2 | qty3 | qty4 |
    +----+------+------+------+------+
    |  1 |    0 |    0 |   10 |   20 |
    |  2 |  1.5 |    0 |  7.5 |   18 |
    +----+------+------+------+------+
    2 rows in set (0.00 sec)
    
    mysql>     select BBBB.* from  mytable BBBB LEFT JOIN
        ->     (select id,sums FROM (select A.id,A.sums from (
        ->     select id,(select sum(qty1+qty2+qty3+qty4)
        ->     from mytable BB where BB.id<=AA.id) sums
        ->     from mytable AA order by id) A INNER JOIN
        ->     (SELECT 50 mylimit) B ON A.sums <= B.mylimit) AAA
        ->     UNION
        ->     (select A.id,A.sums from (select id,
        ->     (select sum(qty1+qty2+qty3+qty4) from mytable BB
        ->     where BB.id<=AA.id) sums from mytable AA order by id) A
        ->     where A.sums=(select min(A.sums) sums from (
        ->     select id,(select sum(qty1+qty2+qty3+qty4) from mytable BB
        ->     where BB.id<=AA.id) sums from mytable AA order by id) A
        ->     INNER JOIN (SELECT 50 mylimit) B ON A.sums >= B.mylimit))) AAAA
        ->     USING (id) WHERE AAAA.id IS NULL;
    +----+------+------+------+------+
    | id | qty1 | qty2 | qty3 | qty4 |
    +----+------+------+------+------+
    |  3 |    1 |    2 |  7.5 |   18 |
    |  4 |    0 |  0.5 |    5 |   13 |
    +----+------+------+------+------+
    2 rows in set (0.01 sec)
    
    mysql>
    

    请记住在 (SELECT 50 mylimit) 子查询中为 mylimit 设置数字,每次两次。

    请告诉我有这个...

    【讨论】:

    • 谢谢 :) 但这只是解决了我提出的简单案例,而不是复杂案例;)
    • @Javier 复杂的是什么?如果您的意思是使用其他数字,例如 50,只需在两个查询(最终和最终补码)中将 (SELECT 57 mylimit) 替换为 (SELECT 50 mylimit),它们将完美运行。否则,请解释复杂情况。
    • 我将尝试通过示例来解释,使用我提供的示例数据。在 mylimit=50 的情况下,id=2 的查询结果应为:qty1=1.5, qty2=0.0, qty3=7.5, qty4=11。但是,您的查询没有给我 id=2 的行。可以看到,qty4=11 不是表的数据,而是每行的qty1+qty2+qty3+qty4的值相加,直到达到mylimit值的结果。也就是说,当您添加时达到 50: row1.qty1+row1.qty2+row1.qty3+row1.qty4 然后 +row2.qty1+row2.qty2+row2.qty3 然后 +7 从原始 row2.qty4 中减去=18,这给出了您在所需结果上看到的 7
    • 嗨哈维尔,刚刚签到。请检查最终查询和最终查询补充。它们完全适用于 57 和 50。
    • 我清理了我的答案,以便将工作查询放在首位。我包括了 57 和 50 被执行的完整示例。
    猜你喜欢
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-26
    • 2011-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多