1
votes

Comment forcer postgres à renvoyer 0 même s'il n'y a pas de lignes correspondant à la requête, en utilisant la fusion, le groupement et la jointure

J'ai essayé désespérément d'obtenir l'instruction SQL suivante pour renvoyer les résultats de la requête et la valeur par défaut à 0 s'il n'y a pas de lignes correspondant à la requête.

Voici le résultat attendu:

select coalesce(vol,0) as vol, year
from (select sum(vol) as vol, year
      from schema.fact_data
         join schema.period_data
            on schema.fact_data.period_tag = schema.period_data.tag 
         join schema.product_data
            on schema.fact_data.product_tag = 
schema.product_data.tag
         join schema.market_data
            on schema.fact_data.market_tag = schema.market_data.tag
      where "retailer"='MadeUpRetailer'
        and "product_tag"='FakeProductTag'
        and "year"='2018' group by year
     ) as DerivedTable;

Au lieu de cela, j'obtiens:

 vol | year 
-----+------
(0 rows)

Voici l'instruction sql:

  vol  | year 
-------+------
   0   | 2018

Je connais le La requête fonctionne car elle renvoie des données lorsqu'il y a des données. N'a pas la valeur par défaut à 0 comme prévu ...

Toute aide pour trouver pourquoi c'est le cas serait très appréciée!


1 commentaires

Veuillez qualifier tous vos noms de colonnes.


3 Réponses :


2
votes

En utilisant votre sous-requête DerivedTable , vous pouvez écrire:

SELECT coalesce(DerivedTable.vol, 0) AS vol,
       y.year
FROM (VALUES ('2018'::text)) AS y(year)
   LEFT JOIN (SELECT ...) AS DerivedTable
      ON DerivedTable.year = y.year;


7 commentaires

Mais cela ne fonctionnera que pour l'année 2018. Correct? Je pense que 2018 était un exemple.


Eh bien, il y avait 2018 dans la requête de la question. Ce n'est pas un problème d'utiliser un paramètre à cet endroit.


@Neptune: si vous voulez faire cela pendant une plage d'années (par exemple, de 2000 à 2018 ), remplacez VALUES ('2018' :: text ) avec SELECT generate_series (2000, 2018) :: text .


@LaurenzAlbe merci pour votre réponse! Je n'arrive pas à faire fonctionner cet extrait de code, que remplaceriez-vous exactement ... par mon exemple?


Si vous remplacez le ... dans ma requête par votre sous-requête, vous obtenez le résultat souhaité, non? Si non, veuillez me dire l'erreur (je ne l'ai pas testée). Si oui, veuillez expliquer votre problème plus en détail.


Voici l'erreur que j'obtiens: ERROR: erreur de syntaxe à ou près de "y" LINE 2: y.year


Zounds! J'ai oublié une virgule à la fin de la première ligne. Pardon.



0
votes

D'après le code que vous avez publié, il n'est pas clair dans quel tableau vous avez la colonne année .
Vous pouvez utiliser UNION pour récupérer une seule ligne au cas où il n'y aurait pas de lignes dans ce tableau pour l'année 2018 comme ceci:

select sum(vol) as vol, year
from schema.fact_data innrt join schema.period_data
on schema.fact_data.period_tag = schema.period_data.tag 
inner join schema.product_data
on schema.fact_data.product_tag = schema.product_data.tag
inner join schema.market_data
on schema.fact_data.market_tag = schema.market_data.tag
where 
  "retailer"='MadeUpRetailer' and 
  "product_tag"='FakeProductTag' and 
  "year"='2018'
group by "year"
union 
select 0 as vol, '2018' as year
where not exists (
  select 1 from tablename where "year" = '2018' 
)

S'il y a des lignes pour l'année 2018, rien ne sera récupéré par la deuxième requête,


0 commentaires

1
votes

Supprimez le GROUP BY (et la requête externe):

select v.yyyy as year, coalesce(sum(vol), 0) as vol
from (values (2018), (2019)) v(yyyy) left join
     schema.fact_data f
     on f.year = v.yyyy left join  -- this is just an example.  I have no idea where year is coming from
     schema.period_data p
     on f.period_tag = p.tag left join
     schema.product_data pr
     on f.product_tag = pr.tag left join
     schema.market_data m
     on fd.market_tag = m.tag
group by v.yyyy

Une requête d'agrégation sans GROUP BY renvoie toujours exactement un ligne, donc cela devrait faire ce que vous voulez.

EDIT:

La requête ressemblerait à quelque chose comme ceci:

select 2018 as year, coalesce(sum(vol), 0) as vol
from schema.fact_data f join
     schema.period_data p
     on f.period_tag = p.tag join
     schema.product_data pr
     on f.product_tag = pr.tag join
     schema.market_data m
     on fd.market_tag = m.tag
where "retailer" = 'MadeUpRetailer' and
      "product_tag" = 'FakeProductTag' and
      "year" = '2018';

Cependant , vous devez déplacer les conditions where vers les clauses on appropriées. Je n'ai aucune idée d'où viennent les colonnes.


3 commentaires

Cette requête retourne bien une ligne, mais alors la colonne 2018 est toujours vide ce qui n'est pas le comportement souhaité


@ludo. . . Si vous voulez 0 au lieu de NULL , utilisez simplement coalesce () .


Merci pour ce @GordonLinoff. J'obtiens le résultat souhaité! Cependant, j'ai besoin que les résultats soient regroupés par année. Donc, si je mets where year in (2018,2019) , la requête se groupera sur l'année et renverra deux lignes, que les lignes contiennent des données ou non. Avez-vous une idée de comment je pourrais faire cela?