web-dev-qa-db-ja.com

1つの行の別のテーブルに一致するレコードがある1つのテーブルから複数の値を選択する

次の2つのテーブルがあります。

create table first (id int, name varchar(20));
create table second (id int, value char(1),fid int);  

これらのテーブルには次のレコードがあります。

insert into first values (1,'Ahmad'),(2,'Sami'),(3,'Khan');
insert into second values
  (1,'a',1)
, (2,'b',1)
, (3,'c',2)
, (4,'d',1)
, (5,'e',2)
, (6,'f',3);  

単一行のテーブルsecondに一致するレコードがあるテーブルfirstからレコードを選択したい。

望ましい出力は次のようになります。

 id |名前|値|値|値
 --- + ------- + ------- + ------- + ------- 
 1 |アフマド| a | b | d 
 2 |サミ| c | e | 
 3 |カーン| f | | 

テーブルfirstの単一のレコードの値の数は制限されず、テーブルnの単一のレコードのfirstの数の値が存在する可能性があります。

nは不明ですが、テーブルvalueに一致するレコードがあるテーブルsecondの各値に対して列firstを繰り返す必要があります。上記のようなテーブル全体が必要ですThe desired result。テーブルsecondによって参照される行のみが必要です。 Postgresのバージョンが最新になります。

1

実際、以来

テーブルnの単一のレコードにfirst個の値が存在する可能性があります。

返す列の数はnot完全に任意です。最大数の列があり、これはクリーンな解決策を持っています- Postgresがテーブルに許可するよりも多くの列がない場合:

カラムのタイプに応じて250〜1600

ちなみに、通常、thirdテーブルにはvalueのすべての可能な値がリストされており、そのすべてが古典的な多対多の関係を実装しています。

CASEステートメントを使用できます。より洗練された方法として、追加モジュールtablefunccrosstab()関数を使用できます。慣れていない場合は、最初に以下の基本的な手順をお読みください。

実際の値の右側の列はNULLで埋められます。最大5つの可能な値を想定し、この設定に基づいて構築します。

_CREATE TABLE first  (id int, name text);
CREATE TABLE second (id int, value "char", fid int);  

INSERT INTO first  VALUES
  (1,'Ahmad')
, (2,'Sami')
, (3,'Khan')
, (4,'Nobody');  -- Added to demonstrate difference
INSERT INTO second VALUES
  (1,'a',1)
, (2,'b',1)
, (3,'c',2)
, (4,'d',1)
, (5,'e',2)
, (6,'f',3);
_

crosstab(text)(1パラメータ形式)を使用して、もう一度テーブルfirstに結合します。

_SELECT id, f.name, value1, value2, value3, value4, value5
FROM   crosstab(
        'SELECT f.id, 1 AS dummy_category, s.value
         FROM   first  f
         JOIN   second s ON s.fid = f.id
         ORDER  BY f.id, s.value'
       ) ct (id int
           , value1 "char", value2 "char", value3 "char", value4 "char", value5 "char")
JOIN first f USING (id);
_

または、crosstab(text, text)(2パラメータ形式)を使用して、値のダミーカテゴリを生成します。

_SELECT *
FROM   crosstab(
        'SELECT f.id, f.name
              , row_number() OVER (PARTITION BY f.id ORDER BY s.value) AS dummy_category
              , s.value
         FROM   first  f
         JOIN   second s ON s.fid = f.id
         ORDER  BY f.id, s.value'
      , ('SELECT generate_series(1,5)')
       ) ct (id int, name text
           , value1 "char", value2 "char", value3 "char", value4 "char", value5 "char");
_

結果はどちらの方法でも同じです。

_ id | name  | value1 | value2 | value3 | value4 | value5
----+-------+--------+--------+--------+--------+--------
  1 | Ahmad | a      | b      | d      |        |
  2 | Sami  | c      | e      |        |        |
  3 | Khan  | f      |        |        |        |
_

テーブルfirstからall行を含める場合は、 _LEFT [OUTER] JOIN_:

_         ...
         LEFT JOIN second s ON s.fid = f.id
         ...
_

次に、上記の例の結果行を1つ追加します。

_  4 | Nobody |        |        |        |        |
_
5

これは、通常のSQLを使用して行の値に基づいて任意の数の列を取得することはできません。

あなたはあなたが望むものと同様の結果を得ることができます:

Select f.id, f.name, array_agg(s.value)
From first f inner join second s on f.id = s.fid
Group by f.id, f.name

これは3つの列を返し、3番目の列は配列になります。

1
JoshRagem