Newsgroup: it.comp.software.database
Titolo: Come si fa a fare le somme?

Ok, sono nuovo qui, e arrivare e fare subito domande stupide non è la 
migliore delle tattiche, lo so ;-)
Perlomeno mi sembra che il problema non sia così banale come può 
apparire dal subject... vi spiego:
ho a che fare con dei dati importati da un gestionale su cui degli 
importi numerici sono così rappresentati:
segno | valore
------|--------
   +   |  12.45
   +   |  10.23
   -   |   9.23
e così via. In altre parole, ho un campo char(1) contenente i due 
caratteri '+' e '-' e un campo numerico con il valore assoluto dell'importo.
Vorrei riuscire a usare la SUM() per fare delle query aggregate... ma 
non mi viene in mente un modo migliore che non sdoppiare le query in due 
parti, una che mi fa la sum dei valori col '+', una di quelli col '-', e 
poi sottrarre i risultati. Così è ovviamente bruttarello e soprattutto 
poco efficiente (e le righe sono tantine, vorrei riuscire a fare una 
query vispa).
Il DB su cui lavoro è PostgreSQL; se a qualcuno viene in mente un'idea 
che funzionerebbe su altri DB comunque provate a dirmela lo stesso, poi 
vedo io se riesco a farcela girare ;-)
Grazie in anticipo, e ciao a tutti!
-- 
Fabrizio "Hermooz" Ermini

 caratteri '+' e '-' e un campo numerico con il valore assoluto dell'importo.
Se il database lo supporta potresti usare qualche cosa del tipo
select sum( if(campo='+') then valore else -valore)...
Verifica nella documentazione del database se esiste una funzione "IF"
o "CASE" che faccia al caso tuo.
Davide

 o "CASE" che faccia al caso tuo.
L'IF lo supporta solo all'interno delle stored procedure, che pero' non 
ho mai fatto in Pgsql e non avrei voglia di cominciare apposta per 
questa applicazione... il CASE non lo conoscevo, ma in effetti c'e'. 
Penso vada benissimo (spero solo sia accettabile come prestazioni)!
PS: grazie e benritrovato, Davide :-)
-- 
Fabrizio "Hermooz" Ermini

poi sottrarre i risultati. 
Purtroppo non conosco PostGres, ma su MySQL farei:
   select sum(concat(segno,valore)) from tuatabella;
concat concatena i valori dei 2 campi: in tal modo "+" e "12.45"
diviene "+12.45", "-" e "0,23" diventa "-9.23" e sum ne fa la somma
algebrica.
Dovresti vedere se su PostGres esiste concat o qualcosa di analogo.
Facci sapé.
Matteo

Vorrei riuscire a usare la SUM() per fare delle query aggregate...
SELECT SUM(CASE WHEN segno='-' THEN -valore ELSE valore END) AS totale
	FROM tabella;
SELECT SUM(CASE WHEN segno='-' THEN -valore ELSE valore END) AS totale
	FROM ( SELECT segno, SUM(valore) FROM tabella ) AS foo;
La seconda sintassi FORSE e' un po' piu' veloce, perche' il CASE viene
eseguito una volta sola. Pero' c'e' il materialize della subselect, mi
sa. Bisognerebbe fare delle prove. Probabilmente su tanti records, con
indice su 'segno', conviene; su pochi, no.
SELECT SUM(segno||valore) AS totale FROM tabella;
e' piu' semplice da scrivere, ma IMHO lunga come un giorno senza pane,
dato che per ogni riga deve fare
	valore::->varchar
	concat
	::->float
e puo' toccarti di dover scrivere
SELECT SUM((segno||(valore::text))::float8) AS totale FROM tabella;
per evitare noie in conversione (es. troncamento ad int).
Leonardo
-- 
".signature": bad command or file name

 Dovresti vedere se su PostGres esiste concat o qualcosa di analogo.
Yep, c'e' l'operatore di concatenazione (la sintassi la mette sotto 
leonardo). Su quello pero' ho paura di avere problemi di prestazioni, 
faccio delle verifiche e poi vedo...
Grazie e Ciao!
-- 
Fabrizio "Hermooz" Ermini

faccio delle verifiche e poi vedo...
Ho visto il post di Leonardo: interessante per il suo commento sulle
prestazioni.
Ciao
Matteo

 e' piu' semplice da scrivere, ma IMHO lunga come un giorno senza pane,
Condivido la tua impressione. Pero' dalla explain non mi viene fuori 
l'eventuale costo di questa operazione, quindi non saprei dire; comunque 
la prova non la posso fare perchè nel campo "valore", a volte, ho un 
segno negativo, e con la notazione di sopra funziona tutto uguale, 
mentre con la concatenazione mi dà errore perchè non riesce a convertire 
roba scritta così: "+-9999" (continuo a chiedermi chi diavolo ha 
progettato quel DB e come faccia a funzionare)...
Grazie del supporto, e ciao!
-- 
Fabrizio "Hermooz" Ermini

   ->  Seq Scan on tabella  (cost=0.00..4607.45 rows=66945 width=24)
Ma questo e' se hai appunto 66945 valori senza indici.
Aggregate  (cost=6660.30..6660.30 rows=1 width=13)
  ->  Subquery Scan foo  (cost=6316.24..6643.92 rows=6554 width=13)
        ->  Aggregate  (cost=6316.24..6643.92 rows=6554 width=13)
              ->  Group  (cost=6316.24..6480.08 rows=65536 width=13)
                    ->  Sort  (cost=6316.24..6316.24 rows=65536 width=13)
                          ->  Seq Scan on test2  (cost=0.00..1073.36
rows=65536 
Creando un indice su test2:
Aggregate  (cost=2604.77..2604.77 rows=1 width=13)
  ->  Subquery Scan foo  (cost=0.00..2588.38 rows=6554 width=13)
        ->  Aggregate  (cost=0.00..2588.38 rows=6554 width=13)
              ->  Group  (cost=0.00..2424.54 rows=65536 width=13)
                    ->  Index Scan using test2_ndx on test2
(cost=0.00..2260.70 rows=65536 width=13)
che insomma e' gia' qualcosa.
Se si puo' modificare la tabella, meglio ancora:
ALTER TABLE test2 ADD COLUMN signo double precision;
=> EXPLAIN UPDATE test2 SET signo=-1 WHERE segno='-';
NOTICE:  QUERY PLAN:
Seq Scan on test2  (cost=0.00..1237.20 rows=32375 width=19)
EXPLAIN
=> EXPLAIN UPDATE test2 SET signo=1 WHERE segno='+';
NOTICE:  QUERY PLAN:
Seq Scan on test2  (cost=0.00..1237.20 rows=33161 width=19)
EXPLAIN
A quel punto puoi lavorare con "signo*valore" :-)


:
Prestazioni:
EXPLAIN ANALYZE SELECT SUM(CASE WHEN segno='-' THEN -valore ELSE valore
END) AS totale FROM test2;
NOTICE:  QUERY PLAN:
Aggregate  (cost=1237.20..1237.20 rows=1 width=13) (actual
time=678.32..678.32 rows=1 loops=1)
  ->  Seq Scan on test2  (cost=0.00..1073.36 rows=65536 width=13) (actual
time=53.84..411.42 rows=65536 loops=1)
Total runtime: 678.66 msec
EXPLAIN ANALYZE SELECT SUM(CASE WHEN segno='-' THEN -valore ELSE valore
END) AS totale FROM (SELECT segno, SUM(valore) AS valore FROM test2 GROUP
BY segno) AS foo;
NOTICE:  QUERY PLAN:
Aggregate  (cost=2604.77..2604.77 rows=1 width=13) (actual
time=1467.55..1467.55 rows=1 loops=1)
  ->  Subquery Scan foo  (cost=0.00..2588.38 rows=6554 width=13) (actual
time=762.32..1467.41 rows=2 loops=1)
        ->  Aggregate  (cost=0.00..2588.38 rows=6554 width=13) (actual
time=762.29..1467.35 rows=2 loops=1)
              ->  Group  (cost=0.00..2424.54 rows=65536 width=13) (actual
time=0.15..1306.43 rows=65536 loops=1)
                    ->  Index Scan using test2_ndx on test2
(cost=0.00..2260.70 rows=65536 width=13) (actual time=0.14..963.73
rows=65536 loops=1)
Total runtime: 1468.14 msec
Senza indice e' un pianto:
DROP INDEX test2_ndx;
DROP
EXPLAIN ANALYZE SELECT SUM(CASE WHEN segno='-' THEN -valore ELSE valore
END) AS totale FROM (SELECT segno, SUM(valore) AS valore FROM test2 GROUP
BY segno) AS foo;
NOTICE:  QUERY PLAN:
Aggregate  (cost=6660.30..6660.30 rows=1 width=13) (actual
time=2295.35..2295.35 rows=1 loops=1)
  ->  Subquery Scan foo  (cost=6316.24..6643.92 rows=6554 width=13) (actual
time=1742.93..2295.25 rows=2 loops=1)
        ->  Aggregate  (cost=6316.24..6643.92 rows=6554 width=13) (actual
time=1742.90..2295.19 rows=2 loops=1)
              ->  Group  (cost=6316.24..6480.08 rows=65536 width=13)
(actual time=1261.62..1987.13 rows=65536 loops=1)
                    ->  Sort  (cost=6316.24..6316.24 rows=65536 width=13)
(actual time=1261.53..1372.39 rows=65536 loops=1)
                          ->  Seq Scan on test2  (cost=0.00..1073.36
rows=65536 width=13) (actual time=37.38..464.58 rows=65536 loops=1)
Total runtime: 2340.63 msec
Il tempo minimo possibile per le operazioni indispensabili e':
EXPLAIN ANALYZE SELECT SUM(valore) FROM test2;
NOTICE:  QUERY PLAN:
Aggregate  (cost=1237.20..1237.20 rows=1 width=8) (actual
time=457.64..457.65 rows=1 loops=1)
  ->  Seq Scan on test2  (cost=0.00..1073.36 rows=65536 width=8) (actual
time=37.19..319.90 rows=65536 loops=1)
Total runtime: 457.92 msec


 A quel punto puoi lavorare con "signo*valore" :-)
Sì, si può, e infatti mi hai dato un'idea che si può anche estendere... 
a questo punto aggiungo una colonna in cui salvare direttamente il 
valore segno*importo, che mi calcolo in fase di import della tabella, 
così posso a posteriori farci dei conti ammodo...  :-)
Ciao, alla prossima!
-- 
Fabrizio "Hermooz" Ermini

 I dati mi vengono da un DB su AS400 dove sono fatti in quel modo.
Scusa, ma se ti arrivano da un'AS non puoi far modificare la
procedura di estrazione in modo da avere il segno *NEL* campo ?
Davide

 procedura di estrazione in modo da avere il segno *NEL* campo ?
Per quello che ho capito, il procedimento con sui si estraggono da AS i 
dati sotto forma di file ascii tipo CSV (che poi io reimporto in SQL) 
non prevede nessun tipo di elaborazione: gli dici che code page usare, 
che delimitatore, e qualche altro parametro e fine lì, poi rispecchia 
fedelemente quello che c'era nel file originario.
Ci metto molto meno io a smanettarmi i dati dalla mia parte come mi pare 
  ;-)
-- 
Fabrizio "Hermooz" Ermini

 dati sotto forma di file ascii tipo CSV (che poi io reimporto in SQL) 
Ok, allora ci hai un file ascii che poi devi importare, non puoi fare un
cerca-e-sostituisci *PRIMA* di importarlo ?
Se ti interessa, per risolvere questo tipo di problemi, io mi sono
scritto la mia utility di importazione in Java.
http://www.soft-land.org/archivio/dbi.zip
Davide

 
http://www.soft-land.org/archivio/dbi.zip
Purtroppo, per ora, java lo mastico pochino :-(
Anch'io ho una utility di import, ma me la sono fatta in C. Non la 
distribuisco pero' perchè me ne vergogno, è troppo spaghettosa come 
programmazione ;-)


qualcosa che non c'entra nulla con quello che volevi fare...
Never underestimate the power of the regexp :-))) [1]
Yogurt
[1] puoi avere una specifica precisissima di ogni singolo campo e cosa
    farne, e avere le righe che matchano trasformate in SQL e il resto
    (errori, incertezze ecc.) segnalato, sottolineato e spedito subito
    per email sul tuo palmare :-)))


Leonardo Serni wrote:
 >
 > Never underestimate the power of the regexp :-))) [1]
 >
Assolutamente, non smetto mai di stupirmi quando vedo alcune tecniche
basate su regexp che sconfinano nell'opera di puro ingegno...
Pero' a me quest'arte continua a sfuggirmi un po', e ancora non sono 
riuscito a convincermi che i pur eccellenti risultati che si possono 
ottenere valgano lo sforzo necessario per padroneggiarla, anche 
considerando il grado di illeggibilità e immanutenibilità delle 
espressioni che si tirano fuori ;-)
bye!
-- 
Fabrizio "Hermooz" Ermini