postgresql renvoie 0 si la valeur renvoyée est nulle

100

J'ai une requête qui renvoie avg (prix)

  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

comment le faire retourner 0 si aucune valeur n'est disponible?

Andrew
la source
1
Êtes-vous sûr que votre requête est bien formée?
Luc M
2
@LucM: Ce ne peut pas être une requête bien formée. (clause «ayant» sans clause «group by».)
Mike Sherrill 'Cat Recall'
tout fonctionne bien, sauf que parfois, lorsque les règles ne sont pas respectées, cela ne renvoie rien. De plus, comment puis-je me faire une idée de la moyenne, je ne pense pas que ce soit possible || quel est le point? Les sélections multiples from web_price_scansont des sélections distinctes; ne sais pas quel est le problème ici?
Andrew
Il est correct d'utiliser une havingclause sans a group by(qui par défaut est un seul groupe). Il agit comme une whereclause sur les résultats agrégés. Dans ce cas, les lignes ne sont renvoyées que si plus de 5 lignes sont renvoyées par la sous-requête de 1er niveau.
bruceskyaus

Réponses:

178

utiliser coalesce

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.

Éditer

Voici un exemple de COALESCEavec votre requête:

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 COALESCEne doit pas être utilisé avec AVGcar il modifie la valeur. NULLsignifie inconnu et rien d'autre. Ce n'est pas comme l'utiliser SUM. Dans cet exemple, si nous remplaçons AVGpar SUM, le résultat n'est pas déformé. Ajouter 0 à une somme ne fait de mal à personne, mais en calculant une moyenne avec 0 pour les valeurs inconnues, vous n'obtenez pas la moyenne réelle.

Dans ce cas, je voudrais ajouter price IS NOT NULLà la WHEREclause pour éviter ces valeurs inconnues.

Luc M
la source
1
@Andrew J'essayais de vous donner un exemple en utilisant votre requête. Mais je me perds. Je doute que cette requête fonctionne. from web_price_scan...semble répété ...
Luc M
Pour ceux qui se demandent, NULLIF(v1, v2)fait à peu près le contraire de COALESCEen ce qu'il retourne NULLsi v1égal v2.
sm
24

(Cette réponse a été ajoutée pour fournir des exemples plus courts et plus génériques à la question - sans inclure tous les détails spécifiques au cas dans la question initiale).


Il y a deux "problèmes" distincts ici, le premier est si une table ou une sous-requête n'a pas de lignes, le second est s'il y a des valeurs NULL dans la requête.

Pour toutes les versions que j'ai testées, postgres et mysql ignoreront toutes les valeurs NULL lors du calcul de la moyenne, et il renverra NULL s'il n'y a rien à faire la moyenne. Cela a généralement du sens, car NULL doit être considéré comme "inconnu". Si vous souhaitez remplacer cela, vous pouvez utiliser coalesce (comme suggéré par 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)

bien sûr, "from foo" peut être remplacé par "from (... toute logique compliquée ici ...) as foo"

Maintenant, la ligne NULL du tableau doit-elle être comptée comme 0? Ensuite, la fusion doit être utilisée dans l'appel avg.

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

Je peux penser à 2 façons d'y parvenir:

  • IFNULL ():

    La fonction IFNULL () renvoie une valeur spécifiée si l'expression est NULL.Si l'expression est NOT NULL, cette fonction renvoie l'expression.

Syntaxe:

IFNULL(expression, alt_value)

Exemple d'IFNULL () avec votre requête:

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
  • SE FONDRE()

    La fonction COALESCE () renvoie la première valeur non nulle d'une liste.

Syntaxe:

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

Exemple de COALESCE () avec votre requête:

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
Joish
la source
1
IFNULL () n'est pas une fonction dans Postgres. Cela pourrait fonctionner dans d'autres bases de données, mais la question concerne spécifiquement Postgres.
Jon Wilson