2012-03-01 20 views
10

Come posso selezionare solo alcune righe nella seguente tabella in modo che riassumano un determinato valore?SQL seleziona alcune righe in una tabella in modo che sommano fino a un determinato valore

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 

Diciamo, il valore alto che voglio è 57 ...

quindi ho bisogno di selezionare le righe della tabella precedente in modo tale che qty1 + qty2 + qty3 + qty4 di ogni riga, ottenere fino quel valore 57 e scartare le altre righe. In questo esempio, vorrei avere il seguente:

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

Perché 10 + 20 + 1,5 + 7,5 + 18 = 57, quindi scartare le righe 3 & 4 ...

Ora vorrei che la parte superiore valore è 50, quindi dovrei ottenere:

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

poiché questi valori si sommano a 50, e il 7 da row2, qty4 è lasciato fuori ... (BTW le file sono ordinate in questo particolare modo perché questo è l'ordine in cui desidero rendere conto delle somme di qtys ... Non è valido riassumere prima r ow1, quindi 3, poi 2 poi 4, ad esempio ... Dovrebbero sempre essere ordinati nell'ordine 1,2,3,4 ...)

E se desiderassi il completamento di questo? Voglio dire, le altre due file che non ho avuto nell'ultimo risultato.

Primo caso:

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

Secondo caso:

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 

(Se il secondo caso è troppo complicato, come su come ottenere:

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

Perché sommando le qtys originali della riga 2 supererebbe il valore 50, lo scarterei ... Il complemento in questo caso dovrebbe essere solo:

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 

)

+1

ho scritto un sacco di query complesse, e mi piace assumendo una sfida, ma questo è uno di quei rari casi che chiede solo per voi a scrivere codice procedurale nella lingua che preferisci. –

+0

anche per il secondo caso semplificato? gli ultimi tra parentesi alla fine del post ...? –

+1

Il secondo caso semplificato è fattibile come una query. Se modifichi la tua domanda per chiederlo (o crea una nuova domanda che ti chieda solo questo), posso aiutarti. –

risposta

7

Diciamo così: se SQL fosse una religione andrei all'inferno per fornire questa soluzione. SQL non ha lo scopo di risolvere questo tipo di problemi, quindi qualsiasi soluzione sarebbe orribile.Il mio è non fa eccezione :)

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 

Per i dati forniti, questo si traduce in:

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

e il complemento:

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 

Per i dati forniti, questo si traduce in:

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

Divertiti!

+0

ho appena provato le soluzioni fornite, ma per il caso del complemento, usando lo stesso campione dati, non ho ottenuto il risultato che mostri, ma solo le 4 righe originali dalla tabella dati ... –

+0

ora funziona! Non so cosa ho fatto di sbagliato l'ultima volta :) Fammi provare e applicalo al mio caso reale e ti darò una risposta sulla taglia;) –

+0

Certo, prenditi il ​​tuo tempo :) –

13

L'opzione semplificata tra parentesi non è male:

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 
; 

(lei non ha citato il nome del tavolo, così sono andato con foo).

Il complemento sarebbe:

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

L'opzione non personalizzata è molto più complicata; è fattibile, ma starai molto meglio usando lo stored procedure.

4

Diamo caricare i dati di esempio dalla domanda

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> 

query finale che funziona completamente

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

FINALE COMPLEMENT query che funziona in maniera completamente

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; 

Qui è l'uscita per il 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> 

Ecco l'output per 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> 

Ricordatevi di impostare il numero per mylimit nel (SELECT 50 mylimit) sottoquery due volte.

informi Ho ottenuto questo uno ...

+0

grazie :) tuttavia questo risolve solo il caso semplice che ho proposto, non quello complesso;) –

+0

@Javier Qual è il complesso? Se intendete usare un altro numero come 50, basta sostituire '(SELECT 57 mylimit)' con '(SELECT 50 mylimit)' in entrambe le query (il COMPLETO FINALE e FINALE) e funzioneranno perfettamente. Altrimenti, per favore spiega il caso complesso. – RolandoMySQLDBA

+0

Proverò a spiegare con l'esempio, utilizzando i dati di esempio che ho dato. Con mylimit = 50, il risultato della query deve dare per id = 2: qty1 = 1.5, qty2 = 0.0, qty3 = 7.5, qty4 = 11. Tuttavia, le tue query non mi danno alcuna riga per id = 2. Come puoi vedere, qty4 = 11 non è ciò che la tabella ha nei suoi dati, ma è il risultato dell'aggiunta dei valori di qty1 + qty2 + qty3 + qty4 su ogni riga finché non viene raggiunto il valore mylimit. Cioè, 50 viene raggiunto quando aggiungete: row1.qty1 + row1.qty2 + row1.qty3 + row1.qty4 quindi + row2.qty1 + row2.qty2 + row2.qty3 quindi +7 che è sottratto dalla riga originale2.qty4 = 18, che dà il 7 che vedi sul risultato desiderato –

4

Si dovrebbe regolare solo la @limit di inizializzazione variabile nel init sottoquery. La prima query restituisce i dati fino al limite, la secnd query ne fornisce il complemento.

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:[email protected] + LEAST(@limit, @limit:=0)), 
     IF(@limit-qty2 >=0, 
      @limit:=(@limit-(@qty2:=qty2)), 
      @qty2:[email protected] + LEAST(@limit, @limit:=0)), 
     IF(@limit-qty3 >=0, 
      @limit:=(@limit-(@qty3:=qty3)), 
      @qty3:[email protected] + LEAST(@limit, @limit:=0)), 
     IF(@limit-qty4 >=0, 
      @limit:=(@limit-(@qty4:=qty4)), 
      @qty4:[email protected] + LEAST(@limit, @limit:=0))),0) 
; 

Il complemento:

SELECT 
    id, 
    IF([email protected], qty1, [email protected]) as qty1, 
    IF([email protected], qty2, [email protected]) as qty2, 
    IF([email protected], qty3, [email protected]) as qty3, 
    IF([email protected], qty4, [email protected]) 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:[email protected] + LEAST(@limit, @limit:=0)), 
     IF(@limit-qty2 >=0, 
     @limit:=(@limit-(@qty2:=qty2)), 
     @qty2:[email protected] + LEAST(@limit, @limit:=0)), 
     IF(@limit-qty3 >=0, 
     @limit:=(@limit-(@qty3:=qty3)), 
     @qty3:[email protected]it + LEAST(@limit, @limit:=0)), 
     IF(@limit-qty4 >=0, 
     @limit:=(@limit-(@qty4:=qty4)), 
     @qty4:[email protected] + LEAST(@limit, @limit:=0)), 
     @limit), 0, 1) 
; 
+0

grazie! funziona con i dati di esempio. fammelo testare con i miei dati reali e ti risponderò della taglia ... –

Problemi correlati