この簡単なシナリオで問題が発生しています。私はおそらくいくつかの基本的な概念を見逃しています...
最初の「もの」と2番目の注文の2つのテーブルがあります。私はsqlfiddleを作成しました: http://sqlfiddle.com/#!17/e9d19/6/
クエリ:
select t1.*, t2.*
from things t1
left join things_orders t2
on t1.id = t2.thing_id
結果を各アイテムと個人の行に表示したい。私の例では、アダムはリンゴ、バナナ2個、チェリー3個を注文しました。ベンはリンゴ(データベースに行がない)、バナナ2個、チェリー3個を注文しました。 Iwant結果は6行になります(簡略化された出力):
Apple Adam 1
Banana Adam 2
Cherry Adam 3
Apple Ben null <-- wanted row, but not showing
Banana Ben 2
Cherry Ben 3
2番目のテーブルにnull値を含む行が必要であるとは思いませんでしたが、多分必要です。
これは、sqlfiddleにもあるDDLです。
CREATE TABLE things (
id smallint NOT NULL,
name text COLLATE pg_catalog."default",
CONSTRAINT things_pkey PRIMARY KEY (id)
);
CREATE TABLE things_orders (
person text COLLATE pg_catalog."default" NOT NULL,
thing_id smallint NOT NULL,
qty integer,
CONSTRAINT things_orders_pk PRIMARY KEY (person, thing_id)
);
INSERT INTO things VALUES
(1, 'Apple')
, (2, 'Banana')
, (3, 'Cherry')
;
INSERT INTO things_orders VALUES
('Adam', 1, 1)
, ('Adam', 2, 2)
, ('Adam', 3, 3)
, ('Ben', 2, 2)
, ('Ben', 3, 3)
;
通常、テーブルpeople
には、関連する人物ごとに1つの行だけが保持されます。 (例のような「ベン」はほとんど一意ではありません。)そうでない場合は、作成することを検討してください。標準の多対多の関係に焼き付けます。
次に、_CROSS JOIN
_と_LEFT JOIN
_から_things_orders
_を使用して、人とモノからデカルト積を作成します。
_SELECT p.id AS person_id, p.person, t.id as thing_id, t.name as thing, tp.qty
FROM people p
CROSS JOIN things t
LEFT JOIN things_orders tp ON tp.thing_id = t.id
AND tp.person_id = p.id;
_
関連:
Meanwhile、_things_orders
_が大きく、1人あたりの行数が多い場合、一意の人物を取得するプレーンDISTINCT
は高価です。そして、_(person)
_のインデックスは役立ちますが、Postgresはまだサポートしていませんindex skip scans。 (Postgres 12に入らなかったかもしれませんが、おそらくPostgres 13に入るでしょう...)
それまでは、再帰CTEは高速回避策です:
_WITH RECURSIVE persons AS (
(
SELECT person
FROM things_orders
ORDER BY person
LIMIT 1
)
UNION ALL
SELECT x.person
FROM persons p
CROSS JOIN LATERAL (
SELECT person
FROM things_orders
WHERE person > p.person
ORDER BY person
LIMIT 1
) x
)
SELECT p.person, t.thing_id, t.name, tp.qty
FROM persons p
CROSS JOIN (SELECT id AS thing_id, name FROM things) t
LEFT JOIN things_orders tp USING (thing_id, person);
_
db <> fiddle ここ
NULL
ではなくCOALESCE(tp.qty, 0)
に_0
_を表示させたい場合があります...
関連:
余談ですが、「id」と「name」は一般的に役に立たない識別子です。
あなたは食べ物のリストを持っています。そのため、注文に存在しない場合でも、それらのすべてを(左結合を使用して)取得します。
しかし、人のリストはありません。そのため、注文に含まれていなくてもそれらすべてを取得したい場合は、リストを作成する必要があります。サブクエリまたはCTE。
2つのリストがあれば、ペアの完全なリスト(食品-人)を作成できます。次に、そのリストにジョイン注文を残します-必要な結果が得られます。
SELECT t0.person, t1.name, t2.qty
FROM ( SELECT DISTINCT person
FROM things_orders ) t0
CROSS JOIN things t1
LEFT JOIN things_orders t2 ON t1.id = t2.thing_id
AND t0.person = t2.person