web-dev-qa-db-ja.com

selectのjsonb_array_elements()は、左結合を内部結合に変えているようです

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には、1ビン、3GNAXHEV2KS548975のデータのみが含まれます。

このクエリ:

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

私が期待するものを返します。jon.vinsのvinごとに1行です。

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

しかし、このクエリ:

    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;

戻り値:

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

選択内のjsonb_array_elementsは、左結合を内部結合に変換するかのようです。

私は、スタイルにnull値を持つvin 3GNAXUEV1KL221776の行が表示されることを期待していました。

何が悪いのですか?

3
jon

セットを返す関数はfrom句に入れる必要があります。それらをSELECT句に入れることは許可されていますが、(気づいたように)奇妙な結果になる可能性があります。

クリーンなアプローチは次のとおりです。

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

オンラインの例: https://rextester.com/RGY32895

それはまだ左結合を実行しています、それはそれらがトップレベルにバブルアップする前にいくつかの行がフィルターにかけられるということです。これは、クエリの選択リストでリターン関数を設定する方法です。左結合によってNULL値が生成されるのではなく、リテラルNULL値を含む物理行がある場合も同じことを行います。

これを回避する1つの方法は、NULL値を1つの要素を持つ適切なダミーJSON配列に置き換えることです。

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;

バージョン10以上を実行している場合、別のオプションは、常に1行を返すダミーセットを返す関数を使用して、それを選択リストに追加することです。

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;

しかし、それは9.6では機能しません。

1
jjanes