SQL выбирает несколько строк в таблице, чтобы они суммировались до определенного значения - PullRequest
10 голосов
/ 02 марта 2012

Как я могу выбрать только несколько строк в следующей таблице, чтобы они суммировали до определенного значения?

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, а 7 из строки 2, qty4 опущено ... (Кстати, строки упорядочены именно таким образом, потому что это порядок, в котором я хочу учесть суммы qtys ... Недопустимо суммировать первую строку 1, затем 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

Поскольку сложение исходных qtys строки 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

)

Ответы [ 4 ]

13 голосов
/ 02 марта 2012

Упрощенный вариант в скобках не так уж и плох:

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
        )
;

Опция без скобок намного сложнее; это выполнимо, но вам будет гораздо лучше использовать хранимую процедуру .

7 голосов
/ 06 марта 2012

Скажем так: если бы SQL был религией, я бы попал в ад за это решение.SQL не предназначен для решения такого рода проблем, поэтому любое решение будет ужасным.Шахта не исключение, это приводит к:

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

Наслаждайтесь!

4 голосов
/ 07 марта 2012

Вы должны настроить только инициализацию переменной @limit в подзапросе init. Первый запрос выводит данные до предела, второй запрос выводит свое дополнение.

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 голосов
/ 07 марта 2012

Давайте загрузим ваши образцы данных из вопроса

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);

ЗАКЛЮЧИТЕЛЬНЫЙ ЗАПРОС ДОПОЛНИТЕЛЬНО, ЧТО ПОЛНОСТЬЮ РАБОТАЕТ1010 *

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>

Пожалуйста, не забудьте установить число для mylimit в(SELECT 50 mylimit) подзапрос дважды каждый.

Пожалуйста, скажите, что я получил этот ...

...