postgresql restituisce 0 se il valore restituito è nullo


102

Ho una query che restituisce avg (prezzo)

  select avg(price)
  from(
      select *, cume_dist() OVER (ORDER BY price desc) from web_price_scan
      where listing_Type='AARM'
        and u_kbalikepartnumbers_id = 1000307
        and (EXTRACT(Day FROM (Now()-dateEnded)))*24 < 48
        and price>( select avg(price)* 0.50
                    from(select *, cume_dist() OVER (ORDER BY price desc)
                         from web_price_scan
                         where listing_Type='AARM'
                           and u_kbalikepartnumbers_id = 1000307
                           and (EXTRACT(Day FROM (Now()-dateEnded)))*24 < 48
                        )g
                   where cume_dist < 0.50
                 )
        and price<( select avg(price)*2
                    from( select *, cume_dist() OVER (ORDER BY price desc)
                          from web_price_scan
                          where listing_Type='AARM'
                            and u_kbalikepartnumbers_id = 1000307
                            and (EXTRACT(Day FROM (Now()-dateEnded)))*24 < 48
                        )d
                    where cume_dist < 0.50)
     )s

  having count(*) > 5

come fare in modo che restituisca 0 se non è disponibile alcun valore?


1
Sei sicuro che la tua query sia ben formata?
Luc M

2
@ LucM: non può essere una query ben strutturata. (clausola "avere" senza una clausola "group by".)
Mike Sherrill "Cat Recall",

tutto funziona bene, tranne che a volte, quando le regole non sono rispettate, non restituisce nulla. Inoltre, come posso fare la media, non credo sia possibile || qual è il punto? Le selezioni multiple from web_price_scansono selezioni separate; non sei sicuro di quale sia il problema qui?
Andrew

Va bene usare una havingclausola senza group by(che per impostazione predefinita è un singolo gruppo). Agisce come una whereclausola sui risultati aggregati. In questo caso, le righe vengono restituite solo se più di 5 righe vengono restituite dalla sottoquery di primo livello.
bruceskyaus

Risposte:


180

usa la coalescenza

COALESCE(value [, ...])
The COALESCE function returns the first of its arguments that is not null.  
Null is returned only if all arguments are null. It is often
used to substitute a default value for null values when data is
retrieved for display.

modificare

Ecco un esempio di COALESCEcon la tua query:

SELECT AVG( price )
FROM(
      SELECT *, cume_dist() OVER ( ORDER BY price DESC ) FROM web_price_scan
      WHERE listing_Type = 'AARM'
        AND u_kbalikepartnumbers_id = 1000307
        AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
        AND COALESCE( price, 0 ) > ( SELECT AVG( COALESCE( price, 0 ) )* 0.50
                                     FROM ( SELECT *, cume_dist() OVER ( ORDER BY price DESC )
                                           FROM web_price_scan
                                           WHERE listing_Type='AARM'
                                             AND u_kbalikepartnumbers_id = 1000307
                                             AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
                                         ) g
                                    WHERE cume_dist < 0.50
                                  )
        AND COALESCE( price, 0 ) < ( SELECT AVG( COALESCE( price, 0 ) ) *2
                                     FROM( SELECT *, cume_dist() OVER ( ORDER BY price desc )
                                           FROM web_price_scan
                                           WHERE listing_Type='AARM'
                                             AND u_kbalikepartnumbers_id = 1000307
                                             AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
                                         ) d
                                     WHERE cume_dist < 0.50)
     )s
HAVING COUNT(*) > 5

IMHO COALESCEnon dovrebbe essere utilizzato con AVGperché modifica il valore. NULLsignifica sconosciuto e nient'altro. Non è come usarlo SUM. In questo esempio, se sostituiamo AVGcon SUM, il risultato non viene distorto. L'aggiunta di 0 a una somma non fa male a nessuno, ma calcolando una media con 0 per i valori sconosciuti, non si ottiene la media reale.

In tal caso, vorrei aggiungere price IS NOT NULLnella WHEREclausola per evitare questi valori sconosciuti.


1
@Andrew Stavo cercando di darti un esempio usando la tua query. Ma mi perdo. Dubito che questa query funzioni. from web_price_scan...sembra ripetuto ...
Luc M

Per coloro che si chiedono, NULLIF(v1, v2)fa praticamente l'opposto di COALESCEin quanto restituisce NULLse è v1uguale v2.
sm

24

(questa risposta è stata aggiunta per fornire esempi più brevi e più generici alla domanda, senza includere tutti i dettagli specifici del caso nella domanda originale).


Ci sono due distinti "problemi" qui, il primo è se una tabella o una sottoquery non ha righe, il secondo è se ci sono valori NULL nella query.

Per tutte le versioni che ho testato, postgres e mysql ignoreranno tutti i valori NULL durante il calcolo della media e restituiranno NULL se non c'è nulla su cui calcolare la media. Ciò ha generalmente senso, poiché NULL deve essere considerato "sconosciuto". Se vuoi sovrascriverlo, puoi usare il coalesce (come suggerito da Luc M).

$ create table foo (bar int);
CREATE TABLE

$ select avg(bar) from foo;
 avg 
-----

(1 row)

$ select coalesce(avg(bar), 0) from foo;
 coalesce 
----------
        0
(1 row)

$ insert into foo values (3);
INSERT 0 1
$ insert into foo values (9);
INSERT 0 1
$ insert into foo values (NULL);
INSERT 0 1
$ select coalesce(avg(bar), 0) from foo;
      coalesce      
--------------------
 6.0000000000000000
(1 row)

ovviamente, "from foo" può essere sostituito da "from (... any complex logic here ...) as foo"

Ora, la riga NULL nella tabella dovrebbe essere contata come 0? Quindi deve essere utilizzato il coalesce all'interno della chiamata avg.

$ select coalesce(avg(coalesce(bar, 0)), 0) from foo;
      coalesce      
--------------------
 4.0000000000000000
(1 row)

2

Posso pensare a 2 modi per ottenere questo risultato:

  • IFNULL ():

    La funzione IFNULL () restituisce un valore specificato se l'espressione è NULL. Se l'espressione è NOT NULL, questa funzione restituisce l'espressione.

Sintassi:

IFNULL(expression, alt_value)

Esempio di IFNULL () con la tua query:

SELECT AVG( price )
FROM(
      SELECT *, cume_dist() OVER ( ORDER BY price DESC ) FROM web_price_scan
      WHERE listing_Type = 'AARM'
        AND u_kbalikepartnumbers_id = 1000307
        AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
        AND IFNULL( price, 0 ) > ( SELECT AVG( IFNULL( price, 0 ) )* 0.50
                                     FROM ( SELECT *, cume_dist() OVER ( ORDER BY price DESC )
                                           FROM web_price_scan
                                           WHERE listing_Type='AARM'
                                             AND u_kbalikepartnumbers_id = 1000307
                                             AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
                                         ) g
                                    WHERE cume_dist < 0.50
                                  )
        AND IFNULL( price, 0 ) < ( SELECT AVG( IFNULL( price, 0 ) ) *2
                                     FROM( SELECT *, cume_dist() OVER ( ORDER BY price desc )
                                           FROM web_price_scan
                                           WHERE listing_Type='AARM'
                                             AND u_kbalikepartnumbers_id = 1000307
                                             AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
                                         ) d
                                     WHERE cume_dist < 0.50)
     )s
HAVING COUNT(*) > 5
  • COALESCE ()

    La funzione COALESCE () restituisce il primo valore non nullo in un elenco.

Sintassi:

COALESCE(val1, val2, ...., val_n)

Esempio di COALESCE () con la tua query:

SELECT AVG( price )
FROM(
      SELECT *, cume_dist() OVER ( ORDER BY price DESC ) FROM web_price_scan
      WHERE listing_Type = 'AARM'
        AND u_kbalikepartnumbers_id = 1000307
        AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
        AND COALESCE( price, 0 ) > ( SELECT AVG( COALESCE( price, 0 ) )* 0.50
                                     FROM ( SELECT *, cume_dist() OVER ( ORDER BY price DESC )
                                           FROM web_price_scan
                                           WHERE listing_Type='AARM'
                                             AND u_kbalikepartnumbers_id = 1000307
                                             AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
                                         ) g
                                    WHERE cume_dist < 0.50
                                  )
        AND COALESCE( price, 0 ) < ( SELECT AVG( COALESCE( price, 0 ) ) *2
                                     FROM( SELECT *, cume_dist() OVER ( ORDER BY price desc )
                                           FROM web_price_scan
                                           WHERE listing_Type='AARM'
                                             AND u_kbalikepartnumbers_id = 1000307
                                             AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
                                         ) d
                                     WHERE cume_dist < 0.50)
     )s
HAVING COUNT(*) > 5

1
IFNULL () non è una funzione in Postgres. Questo potrebbe funzionare in altri database, ma la domanda riguarda specificamente Postgres.
Jon Wilson,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.