web-dev-qa-db-fra.com

jsonb_array_elements () dans la sélection semble transformer une jointure gauche en jointure interne

PostgreSQL 9.6.

create table jon.vins (vin citext primary key);
insert into jon.vins values
('3GNAXUEV1KL221776'),
('3GNAXHEV2KS548975');

CREATE TABLE jon.describe_vehicle (
  vin citext primary key,
  response jsonb);

jon.describe_vehicle contient des données pour 1 seul vin, 3GNAXHEV2KS548975;

Cette requête:

    select a.vin,
      b.response->'attributes'->>'bestMakeName' as make
    from jon.vins a
    left join jon.describe_vehicle b on a.vin = b.vin;

Renvoie ce que j'attends, une ligne pour chaque vin dans jon.vins:

        vin        |   make    
-------------------+-----------
 3GNAXUEV1KL221776 | 
 3GNAXHEV2KS548975 | Chevrolet
(2 rows)

Mais cette requête:

    select a.vin,
      jsonb_array_elements(b.response->'style')->'attributes'->>'name' as style
    from jon.vins a
    left join jon.describe_vehicle b on a.vin = b.vin;

Retour:

        vin        |      style       
-------------------+------------------
 3GNAXHEV2KS548975 | FWD 4dr LS w/1LS
(1 row)

C'est comme si le jsonb_array_elements dans la sélection transforme la jointure gauche en jointure interne.

Je m'attendais à voir une ligne pour vin 3GNAXUEV1KL221776 avec une valeur nulle pour le style.

Qu'est-ce que je fais mal?

3
jon

Les fonctions de retour d'ensemble doivent être placées dans la clause from. Les placer dans la clause SELECT est autorisé, mais peut entraîner un résultat étrange (comme vous l'avez remarqué).

L'approche propre serait:

select a.vin, r.data ->'attributes'->>'name' as style
from vins a
  left join describe_vehicle b on a.vin = b.vin
  left join jsonb_array_elements(b.response->'style') as r(data) on true

Exemple en ligne: https://rextester.com/RGY32895

4

Il exécute toujours une jointure gauche, c'est juste que certaines lignes sont filtrées avant de remonter au niveau supérieur. Voici comment définir les fonctions de retour dans la liste de sélection d'un travail de requête. Cela ferait la même chose si vous aviez une ligne physique contenant une valeur NULL littérale, plutôt qu'une valeur NULL générée par une jointure gauche.

Une façon de contourner ce problème consiste à remplacer les valeurs NULL par un tableau JSON factice approprié avec un élément:

select a.vin,
  jsonb_array_elements(coalesce(b.response->'style','[{}]'))->'attributes'->>'name' as style
from jon.vins a
left join jon.describe_vehicle b on a.vin = b.vin;

Si vous exécutiez la version 10 ou supérieure, une autre option serait d'utiliser une fonction de retour d'ensemble factice qui renvoie toujours une ligne et de l'ajouter à votre liste de sélection:

select a.vin,
  jsonb_array_elements(b.response->'style')->'attributes'->>'name' as style,
  generate_series(1,1) as ignore_me
from jon.vins a
left join jon.describe_vehicle b on a.vin = b.vin;

Mais cela ne fonctionne pas en 9.6.

1
jjanes