他のバージョンの文書 15 | 14 | 13 | 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

2.2. テーブル式

テーブル式でテーブルを指定します。テーブル式には FROM 句があり、オプションとして WHERE 句、GROUP BY 句、HAVING 句を付けることができます。単純なテーブル式は、単にディスク上のいわゆる基本テーブルと呼ばれるテーブルを参照するだけです。しかし、さまざまな方法で基本テーブルを修正したり、組み合わせたりするため更に複雑な式を使用することができます。

テーブル式のオプションで指定する WHERE 句、GROUP BY 句、HAVING 句は、FROM 句で派生されたテーブル上に対して次々に変換されて実行されるパイプラインです。これらすべての変換によって生成される派生テーブルは、列評価式の選択リストで指定されたように出力行を算出するための入力行を用意します。

2.2.1. FROM句

FROM 句は、カンマで分けられたテーブル参照リストで与えられる 1 つ以上複数ののテーブルから、1 つのテーブルを派生させます。

FROM table_reference [, table_reference [, ...]]

テーブル参照は、テーブル名、または、副問い合わせ、テーブル結合、それらの複雑な組み合わせなどから派生されたテーブルです。FROM 句に複数のテーブル参照がある場合、それらは、あとで WHERE 句、GROUP BY 句、HAVING 句によって変換される派生テーブルを作るためのクロス結合(CROSS JOIN、下記を参照)で、最終的にはすべてのテーブル式の結果となります。

テーブル参照で、テーブルの継承階層のスーパーテーブルの名前を指定すると、テーブル名の前に ONLY キーワードがない場合は、テーブル参照はそのテーブルだけでなくそのサブテーブルに継承されたすべての列を生成します。しかし、参照は、名前を指定したテーブルに現れた列のみを生成し、サブテーブルで追加された列は無視されます。

2.2.1.1. 結合テーブル

結合テーブルは、2 つの(実、または、派生)テーブルから、結合の規則に従って派生したテーブルです。内部結合(INNER JOIN)、外部結合(OUTER JOIN)、クロス結合(CROSS JOIN)がサポートされています。

結合の種類

クロス結合
T1 CROSS JOIN T2

どの T1T2 の行の組み合わせについても、T1 のすべての列に続き、 T2 のすべての列を含む行が派生テーブルに含まれます。2 つのテーブルが N 行と M 行で構成されているとすると、結合されたテーブルの行数はN×M 行となります。クロス結合は INNER JOIN ON TRUE と同じです。

Tip: FROM T1 CROSS JOIN T2FROM T1, T2 と等価です。

修飾付き結合
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 ON boolean_expression
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 USING ( join column list )
T1 NATURAL { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2

INNEROUTER は、省略可能です。 INNER がデフォルトとなります。LEFTRIGHTFULL を OUTER JOIN に指定できます。

結合条件は、ON 句か USING 句で指定するか、または暗黙的に NATURAL 記述で指定されます。結合条件は、以下で詳しく説明するように、2つの元となるテーブルのどの行が"一致するか"を決めます。

ON 句は最も一般的な結合条件であり、WHERE 句で使われるのと同じブール値評価式となります。ON で表現された部分が真となる場合、T1 と T2 の対応する行が対象となります。

USING は略記法です。それは、結合テーブルが共通で持つカンマで区切られた列名のリストから、各々の列の組み合わせをイコールとした結合条件を生成します。さらに、JOIN USING の出力は、入力列でイコールとなった列の組み合わせに対して、1 つの列があり、各テーブルの他のすべての列がそのあと続きます。つまり、USING (a, b, c)ON (t1.a = t2.a AND t1.b = t2.b AND t1.c = t2.c) と等価です。ただし、ON を使った場合は、結果において a、b、c は 2 つの列になりますが、USING を使うとそれぞれ 1 つの列になるという例外があります。

最後に NATURAL は USING の略記形式です。2つの入力テーブルの両方にある列名を持ったUSING 句リストを形成します。USING のように、これらの列は出力テーブルに一度だけ現われます。

修飾付き JOIN には次のものがあります。

内部結合(INNER JOIN)

T1 の各 R1 行に対して、T2 において R1 との結合条件を満たしている行が、結合されたテーブルに含まれます。

左外部結合(LEFT OUTER JOIN)

まず、内部結合が行われます。その後、T2 の任意の行で結合条件を満たさない T1 の各行については、T2 の列は NULL 値として、結合されたテーブルを作ります。したがって、連結されたテーブルは、無条件にT1の行それぞれに少なくとも1つの行があります。

右外部結合(RIGHT OUTER JOIN)

まず、内部結合が行われます。その後、T1 の任意の行で結合条件を満たさない T2 の各行については、T1 の列は NULL 値として、結合されたテーブルを作ります。これは左結合の反対です。結果のテーブルは、T2の 行が無条件に入ります。

完全外部結合(FULL OUTER JOIN)

まず、内部結合が行われます。その後、T1 の各行で結合条件を満たさない T2 の任意の行については、T2 の列は NULL 値として結合します。さらに、T2 の各行で結合条件を満たさない T1 の任意の行については、T1 の列は NULL 値として結合します。

すべての結合は、互いを連結したり、入れ子にしたりすることができます。 T1T2 のどちらか、あるいは両方が、結合テーブルになることがあります。JOIN 句を小かっこで囲んで結合の順序を制御できます。小かっこを付けない場合 JOIN 句は左から右に入れ子にします。

2.2.1.2. 副問い合わせ(Subqueries)

派生テーブルを指定する副問い合わせは、必ず小かっこでくくらなければなりません。また、名前には必ず AS 句を使わなければいけません(Section 2.2.1.3を参照)。

FROM (SELECT * FROM table1) AS alias_name

この例は、FROM table1 AS alias_nameと同じです。さらに興味深いケースとして、副問い合わせがグループ化や集約を含んでいる場合、単純結合にまとめることはできないということがあります。

2.2.1.3. テーブルと列の別名

テーブルや複雑なテーブル参照は、その先の処理で派生テーブルを参照するために一時的な名前を与えることができます。これをテーブルの別名 (table alias)と呼びます。

FROM table_reference AS alias

ここで、alias はどんな通常識別子でもかまいません。別名は、問い合わせでテーブル参照をするときの新しい名前になります。その場合は、オリジナルの名前でテーブルを参照することはできなくなります。つまり、以下の文は、正しい SQL の構文ではありません。

SELECT * FROM my_table AS m WHERE my_table.a > 5;

この場合、実際には、FROM 句に暗黙のテーブル参照が追加されます(これは SQL 標準に対する PostgreSQL 拡張です)。つまり、次のような問い合わせを書いたものとして処理されます。

SELECT * FROM my_table AS m, my_table AS my_table WHERE my_table.a > 5;

テーブルの別名は主に表記を簡単にするためにあります。しかし次のように、1 つのテーブルが自分自身と結合する場合は、必須となります。

SELECT * FROM my_table AS a CROSS JOIN my_table AS b ...

さらに、テーブル参照が副問い合わせの場合に別名が必要になります。

小かっこは、あいまいさをなくすために使われます。次の SQL 文は、前の例題と異なり、結合の結果に b という別名を与えます。

SELECT * FROM (my_table AS a CROSS JOIN my_table) AS b ...

FROM table_reference alias

この形式は前に書いたものと等価です。AS キーワードは省略可能です。

FROM table_reference [AS] alias ( column1 [, column2 [, ...]] )

この形式では、前に説明したようなテーブルの改名に加えてさらに、テーブルの列は、囲んでいる問い合わせの中で一時的な名前として与えられます。もし、実際のテーブルが持つ列よりも少ない数の列の別名が与えれられる場合、残りの列は改名されません。この構文は、自己結合あるいは副問い合わせで特に役立ちます。

別名が、JOIN 句の結果として適用される場合、これらの形式のいずれかを使うと、別名は JOIN の中の元の名前を隠します。たとえば、以下の文は正しい SQL 文です。

SELECT a.* FROM my_table AS a JOIN your_table AS b ON ...

しかし、以下の文は正しくありません。

SELECT a.* FROM (my_table AS a JOIN your_table AS b ON ...) AS c

テーブルの別名 A は、外側の別名 C では参照することができません。

2.2.1.4. 例

FROM T1 INNER JOIN T2 USING (C)
FROM T1 LEFT OUTER JOIN T2 USING (C)
FROM (T1 RIGHT OUTER JOIN T2 ON (T1.C1=T2.C1)) AS DT1
FROM (T1 FULL OUTER JOIN T2 USING (C)) AS DT1 (DT1C1, DT1C2)

FROM T1 NATURAL INNER JOIN T2
FROM T1 NATURAL LEFT OUTER JOIN T2
FROM T1 NATURAL RIGHT OUTER JOIN T2
FROM T1 NATURAL FULL OUTER JOIN T2

FROM (SELECT * FROM T1) DT1 CROSS JOIN T2, T3
FROM (SELECT * FROM T1) DT1, T2, T3

上記は、結合テーブルと複雑な派生テーブルの例です。AS 句がどのように改名、あるいは、派生テーブルに名前を付けるか、さらに、省略可能なカンマで区切られた列名をどのように改名するかに注目してください。最後の 2 つの FROM 句は T1、T2、T3 から同じ派生テーブルを作ります。副問い合わせに DT1 と名前付けする際の AS キーワードは省略されています。さらに、キーワードの OUTER、INNER も省略することができます。

2.2.2. WHERE 句

WHERE 句の構文は、以下のとおりです。

WHERE search_condition

search_condition には、 Section 1.3 で定義した、boolean 型を返すどのような評価式も指定できます。

FROM 句の処理が終わった後に、派生テーブルの各行は検索条件と照合されます。条件の結果が真の場合、その行は出力されます。そうでない(すなわち結果が偽またはNULLの)場合は、その行は捨てられます。一般的に検索条件は、FROM 句で生成されたテーブルのどれかの列を少なくともいくつか参照します。これは、必須ではありませんが、そうしないと FROM 句はまったく意味がなくなります。

Note: 結合(JOIN)構文が実装される以前では、WHERE 句の中に内部結合の結合条件を挿入する必要がありました。たとえば、以下の 3 つのテーブル式は等価です。

FROM a, b WHERE a.id = b.id AND b.val > 5

FROM a INNER JOIN b ON (a.id = b.id) WHERE b.val > 5

FROM a NATURAL JOIN b WHERE b.val > 5

どれを使うかは、主にスタイルの問題です。FROM 句の JOIN 構文は、おそらく、他の製品への移植性がありません。外部結合については、FROM 句でなければならなく、選択の余地はありません。外部結合の ON 句や USING 句は、WHERE 条件とは等しく ありません。なぜなら、最終結果から行を除去すると同様に、(マッチしない入力行に対する)行の追加も行うからです。

FROM FDT WHERE
    C1 > 5

FROM FDT WHERE
    C1 IN (1, 2, 3)
FROM FDT WHERE
    C1 IN (SELECT C1 FROM T2)
FROM FDT WHERE
    C1 IN (SELECT C3 FROM T2 WHERE C2 = FDT.C1 + 10)

FROM FDT WHERE
    C1 BETWEEN (SELECT C3 FROM T2 WHERE C2 = FDT.C1 + 10) AND 100

FROM FDT WHERE
    EXISTS (SELECT C1 FROM T2 WHERE C2 > FDT.C1)

上記の例で、FDT は FROM 句の中から派生されたテーブルです。WHERE 句の検索条件を満たさなかった行は、FDT から削除されます。評価式としてのスカラ副問い合わせの使い方に注目してください。他の問い合わせのように、副問い合わせは、複雑なテーブル式を使うことができます。副問い合わせの中でどのように FDT が参照されるかに注意してください。 C1FDT.C1 のように修飾することは、C1 が副問い合わせの入力テーブルから派生した列名でもあるときにだけ必要です。列名の修飾は、必須の場合ではなくても、明確にするために役立ちます。これは、外側の問い合わせの列名の有効範囲を、どのようにして内側の問い合わせまで拡張するかを示します。

2.2.3. GROUP BY 句と HAVING 句

WHERE フィルタを通した後、派生された入力テーブルを、GROUP BY 句でグループ化し、また、HAVING 句を使用して不要なグループを取り除くことができます。

SELECT select_list
    FROM ...
    [WHERE ...]
    GROUP BY grouping_column_reference [, grouping_column_reference]...

GROUP BY 句は、テーブル内で選択された全列で同じ値を共有する行をまとめてグループ化するために使用されます。(ORDER BY 句とは違い)指定された列の順番は関係ありません。この目的は、共通する値を持つ行を、代表となる 1 つのグループ行へまとめることにより、各グループの行を減らすことにあります。これにより、出力の冗長度を排除し、さらにまた、これらのグループに適用される集約が得られます。

いったんテーブルがグループ化されると、グループの中のどの行を使えばよいのかが曖昧となるので、集約式を除いてグループ化に使われない列は参照することができません。グループごとに既知の一定の値を持つので、選択リストの列評価式でグループ化された列は参照することができます。グループ化で使われない列の集約関数は、テーブル全体ではなく1 つのグループでの値を計算します。たとえば、製品コードでグループ化されたテーブルに対する sum(sales) は、各製品の売り上げ(sales)合計であり、すべての製品の売り上げ合計ではありません。グループ化されていない列を計算した集約はグループを代表しますが、グループ化されていない列の個別の値はそうではありません。

例を示します。

SELECT pid, p.name, (sum(s.units) * p.price) AS sales
  FROM products p LEFT JOIN sales s USING ( pid )
  GROUP BY pid, p.name, p.price;

この例では、pid 列、p.name 列、 p.price 列は必ず GROUP BY 句で指定しなければいけません。なぜなら、これらは、問い合わせ選択リストの中で使われているためです。s.units 列は GROUP BY で指定する必要はありません。これは、製品ごとの売り上げ計算の集約関数(sum()) の中だけで使われるためです。各製品に対して、製品のすべての販売に関する合計行が返されます。

厳密な SQL では、GROUP BY は、ソーステーブルの列によってのみグループ化できますが、 PostgreSQL はこれを、問い合わせ選択リストの列によるグループ化も許すように拡張しています。単純な列名の代わりに、評価式でグループ化することもできます。

SELECT select_list FROM ... [WHERE ...] GROUP BY ... HAVING boolean_expression

GROUP BY 句を使ってグループ化されたテーブルで、特定のグループのみ必要な場合、グループ化されたテーブルから不要なグループを除くのに、WHERE句 のように HAVING 句を使うことができます。PostgreSQL では HAVING 句を GROUP BY 句なしに使うことができますが、その場合、もう1つの WHERE 句のように振る舞います。このように HAVING を使うポイントは明確ではありませんが、目安としては、HAVING 条件が集約関数の結果を参照するということです。集約を含んでいない制約は、WHERE 句の中でより効果的に表記されます。

例を示します。

SELECT pid    AS "Products",
       p.name AS "Over 5000",
       (sum(s.units) * (p.price - p.cost)) AS "Past Month Profit"
  FROM products p LEFT JOIN sales s USING ( pid )
  WHERE s.date > CURRENT_DATE - INTERVAL '4 weeks'
  GROUP BY pid, p.name, p.price, p.cost
    HAVING sum(p.price * s.units) > 5000;

この例では、WHERE 句はグループ化する前の列の行を選択し、その後、HAVING 句が、総売上高の合計が 5,000 より大きいグループを抽出します。