"短縮版
と の 2 つのテーブルがimage
ありitem
ます。アイテムは 2 つの画像で構成されています: それらleft
を and と呼びましょうright
(アイテムの GUI は、たとえば、2 つの画像を並べたキャンバスなどにすることができます)。どのアイテムがそれらを「使用」しているかを示す、画像に関するレポートをセットアップしようとしています。
+------------+ +------------+
| image | | item |
+------------+ +------------+
| id <------------+ | id |
| name | | | name |
+------------+ +-------- im_left |
+-------- im_right |
+------------+
アイテムが画像への参照を 1 つしか持っていない場合、クエリを理解するのは簡単ですが、これを「二重参照」の場合に拡張するのは簡単ではありません。ビジネス上の制約)。
今までLEFT OUTER JOIN
、左と右の列を使用して、エイリアスを持つ 2 つの を使用して画像をアイテムに結合していました。しかし、この構成は、同じ画像が左右の両方で使用されている場合に失敗します (以下に示すように、結果を実際に説明することはできません)。
これはデータベース設計の非常に一般的なパターンであるため、このようなシナリオでは、各画像とそれが使用されている各アイテムを表示し、どの列から参照されているかを示すビューをどのように設計しますか?
例えば
- image_1 は item_1 と item_2 (左) と item_3 (右) で使用されます
- image_2 は none (左) と item_4 (右) で使用されます
- image_3 は item_5 と item_6 (左) と item_6 (右) で使用されます
長いバージョン(ローカライズされすぎている可能性がありますが、私の試行と特定の問題を示しています)。
これが私の2つのテーブルの定義です:
CREATE TABLE image (
id serial PRIMARY KEY,
name text
);
INSERT INTO image(name) VALUES ('image 1');
INSERT INTO image(name) VALUES ('image 2');
INSERT INTO image(name) VALUES ('image 3');
CREATE TABLE item (
id serial PRIMARY KEY,
name text,
im_left int,
FOREIGN KEY (im_left) REFERENCES image(id),
im_right int,
FOREIGN KEY (im_right) REFERENCES image(id)
);
INSERT INTO item(name,im_left,im_right) VALUES ('item 1',1,2);
INSERT INTO item(name,im_left,im_right) VALUES ('item 2',1,3);
INSERT INTO item(name,im_left,im_right) VALUES ('item 3',2,3);
今日まで、私はこのクエリを使用してビューを構築していました。
CREATE VIEW imagev_v1 AS (
SELECT image.id, image.name,
array_agg(li.id) AS left_ids,
array_agg(li.name) AS left_names,
array_agg(ri.id) AS right_ids,
array_agg(ri.name) AS right_names
FROM image
LEFT OUTER JOIN item AS li ON li.im_left=image.id
LEFT OUTER JOIN item AS ri ON ri.im_right=image.id
GROUP BY image.id, image.name
ORDER BY name ASC
);
そして、それは非常にうまくいきました:
SELECT * FROM imagev_v1;
id | name | left_ids | left_names | right_ids | right_names
----+---------+-------------+---------------------+-------------+---------------------
1 | image 1 | {1,2} | {"item 1","item 2"} | {NULL,NULL} | {NULL,NULL}
2 | image 2 | {3} | {"item 3"} | {1} | {"item 1"}
3 | image 3 | {NULL,NULL} | {NULL,NULL} | {2,3} | {"item 2","item 3"}
(3 rows)
左と右の列で同じ画像を参照する卑劣なアイテムを追加するまで:
INSERT INTO item(name,im_left,im_right) VALUES ('item 4',3,3);
SELECT * FROM imagev_v1;
id | name | left_ids | left_names | right_ids | right_names
----+---------+----------+------------------------------+-------------+------------------------------
1 | image 1 | {1,2} | {"item 1","item 2"} | {NULL,NULL} | {NULL,NULL}
2 | image 2 | {3} | {"item 3"} | {1} | {"item 1"}
3 | image 3 | {4,4,4} | {"item 4","item 4","item 4"} | {2,3,4} | {"item 2","item 3","item 4"}
(3 rows)
結果の 3 行目は、控えめに言っても奇妙ですが、その動作を説明することはできません。
ビューの別のバージョンを試してみましたが、これは機能しますが、参照元を表示する機能がありません (画像がim_left
またはim_right
列によって参照されているかどうか):
CREATE VIEW imagev_v2 AS (
SELECT image.id, image.name,
array_agg(item.id) AS item_ids,
array_agg(item.name) AS item_names
FROM image
LEFT OUTER JOIN item ON item.im_left=image.id OR item.im_right=image.id
GROUP BY image.id, image.name
ORDER BY name ASC
);
SELECT * FROM image_v2 ;
id | name | item_ids | item_names
----+---------+----------+------------------------------
1 | image 1 | {1,2} | {"item 1","item 2"}
2 | image 2 | {1,3} | {"item 1","item 3"}
3 | image 3 | {2,3,4} | {"item 2","item 3","item 4"}
(3 rows)
imagev_v3
ここまで読んでくれてありがとう。これで実際の質問を見る権利が得られましimage_v1
たimage_v2
。
私は PostgreSQL 8.4 を使用していることに注意してください。