1

"短縮版

と の 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_v1image_v2

私は PostgreSQL 8.4 を使用していることに注意してください。

4

3 に答える 3

1

複数の述語で結合を使用します。内部結合でも構いませんが、どの項目でも使われていない画像を見たい場合は以下のように外部結合にしてください。

Select i.serial imgId, i.text imgName, 
  case when t.im_left is Null and t.im_right Is Null then 'None'
       When t.im_left Is Null Then 'Right'
       When t.im_right Is Null Then 'Left' Else 'Both' End Source,
  t.serial itmId, t.text itmName
From Image i 
   Left Join item t 
     On t.im_left = i.serial Or
        t.im_right = i.serial  
于 2013-04-08T13:49:56.197 に答える
0

あなたが最初に試したものと同様に、あなたのために働くことができる別の解決策があります. 左側と右側の結合を分離するだけです。

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
(
    SELECT 
      image.id as image_id, 
      image.name as image_name,
      li.id as li_id,
      li.name as li_name,
      NULL as ri_id,
      NULL as ri_name
    FROM image
    LEFT OUTER JOIN item AS li ON li.im_left=image.id
  UNION ALL
   SELECT 
      image.id as image_id, 
      image.name as image_name,
      NULL as li_id,
      NULL as li_name,
      ri.id as ri_id,
      ri.name as ri_name
    FROM image
    LEFT OUTER JOIN item AS ri ON ri.im_right=image.id

) image

GROUP BY image_id, image_name
ORDER BY image_name ASC

SQLFiddle デモ

于 2013-04-08T14:51:33.407 に答える