表表達式里的 WHERE,GROUP BY,和 HAVING 子句聲明一系列對源自 FROM 子句的表的轉換操作.轉換最后生成的表提供用于產生輸出行的 輸入,這些輸出行都是在選擇列表的列表達式中生成的.
FROM 子句從一個用逗號分隔的表引用列表中的一個或更多個其它表 中生成一個表.
FROM table_reference [, table_reference [, ...]]
表引用可以是一個表名字或者是一個生成的表,比如子查詢,一個 表連接,或者這些東西的復雜組合.如果在 FROM 子句中列出了多于一個表, 那么它們被 CROSS JOIN (見下文)形成一個派生表,該表可以進行 WHERE,GROUP BY 和 HAVING 子句的轉換處理,并最后生成所有表表達式的結果.
如果一個表引用是一個簡單的表名字并且它是表繼承級別中的 超級表,那么該表的行包括所有它的后代子表的行,除非你在 該表名字前面加 ONLY 關鍵字.這樣的話,這個引用就只生成 出現在命名表中的列 --- 任何在子表中追加的列都會被忽略.
一個連接表是根據特定的連接類型的規則從兩個其它表(真實表或生成表) 中排生的表.我們支持 INNER,OUTER,和 CROSS JOIN 類型.
連接類型
- CROSS JOIN
T1 CROSS JOIN T2
對每個從 T1 和 T2 來的行的組合, 生成的表將包含這樣一行:它包含所有 T1 里面的列后面跟著所有 T2 里面的列. 如果該表分別有 N 和 M 行,連接成的表將有 N * M 行.一次 cross join (交叉連接)實際上是一個 INNER JOIN ON TRUE.
技巧: FROM T1 CROSS JOIN T2 等效于 FROM T1, T2.
- 條件 JOIN
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 ON boolean_expressionT1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 USING ( join column list )
T1 NATURAL { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2
INNER 和 OUTER 對所有連接(JOIN) 類型都是可選的.INNER 是缺省; LEFT,RIGHT,和 FULL 只用于 OUTER JOIN.
連接條件在 ON 或 USING 子句里聲明, 或者用關鍵字 NATURAL 隱含地聲明.連接條件判斷來自兩個源表 中的那些行是“匹配”的,這些我們將在下面詳細解釋.
ON 子句是最常見的連接條件的類型∶它接收一個和 WHERE 子句里用的一樣的 布爾值表達式.如果兩個分別來自 T1 和 T2 的行在 ON 表達式上運算的 結果為真,那么它們就算是匹配的行.
USING 是縮寫的概念∶它接收一個用逗號分隔的字端名子列表, 這些字段必須是連接表共有的,最終形成一個連接條件,表示 這些字段對必須相同.最后,JOIN USING 的輸出會為每一對相等 的輸入字段輸出一個字段,后面跟著來自各個表的所有其它字段. 因此,USING (a, b, c) 等效于 ON (t1.a = t2.a AND t1.b = t2.b AND t1.c = t2.c) 只不過是如果使用了 ON,那么在結果里會有兩個 a,b,和 c 字段, 而用 USING 的時候每個字段就只會有一個.
最后,NATURAL 是 USING 的縮寫形式∶它形成一個 USING 列表, 該列表由那些在兩個表里都出現了的字段名字組成.和 USING 一樣, 這些字段只在輸出表里出現一次.
條件 JOIN 的可能類型是∶
- INNER JOIN
對于 T1 的每一行 R1,生成的連接表都有一行對應 T2 中的 每一個滿足和 R1 的連接條件的行.
- LEFT OUTER JOIN
首先,執行一次 INNER JOIN.然后,為 T1 里那些和 T2 里任何一行都不滿足連接條件的行返回一個連接行, 同時該連接行里對應 T2 的列用 NULL 補齊.因此, 生成的連接表里無條件地包含來自 T1 里的每一行至少 一個副本.
- RIGHT OUTER JOIN
首先,執行一次 INNER JOIN.然后,為 T2 里那些和 T1 里任何一行都不滿足連接條件的行返回一個連接行, 同時該連接行里對應 T1 的列用 NULL 補齊.因此, 生成的連接表里無條件地包含來自 T2 里的每一行.
- FULL OUTER JOIN
首先,執行一次 INNER JOIN.然后,為 T1 里那些和 T2 里任何一行都不滿足連接條件的行返回一個連接行, 同時該連接行里對應 T2 的列用 NULL 補齊. 同樣,為 T2 里那些和 T1 里的任何行都不滿足連接條件的 行返回一個連接行,該行里對應 T1 的列用 NULL 補齊.
如果 T1 和 T2 有一個或者都是可以 JOIN 的表, 那么所有類型的連接都可以串在一起或嵌套在一起. 你可以在JOIN子句周圍使用圓括弧來控制連接順序, 如果沒有圓括弧,那么 JOIN 子句是從左向右嵌套的.
聲明一個派生表的子查詢必須包圍在圓括弧里并且必須 用 AS 子句命名.(參閱 Section 2.1.1.3.)
FROM (SELECT * FROM table1) AS alias_name
這個例子等效于 FROM table1 AS alias_name. 更有趣的例子是在子查詢里面有分組或聚集的時候, 這個時候子查詢不能歸納成一個簡單的連接.
你可以給一個表或復雜表引用一個臨時的名字,用于在后面的 處理過程中引用那些派生的表.這樣做叫做 表別名.
FROM table_reference AS alias
在這里,
alias 可以是任何規則的標識符. 這個別名成為當前查詢里該表引用的新名字 -- 同時我們也不能再用原來的 名字引用該表了(如果該表引用是一個普通的基本表).因此
SELECT * FROM my_table AS m WHERE my_table.a > 5;
是非法的 SQL 語法.作為
Postgres 對標準的擴展,這里實際發生的事情是那個隱含的表引用加入到 FROM 子句中, 所以該查詢會象寫成下面這樣處理
SELECT * FROM my_table AS m, my_table AS my_table WHERE my_table.a > 5;
表別名主要是為了表示簡便,但是如果我們要連接一個表自身, 那么使用它就是必須的,比如,
SELECT * FROM my_table AS a CROSS JOIN my_table AS b ...
另外,如果表引用是一個子查詢,那么也需要別名.
圓括弧用于解決歧義.下面的語句將把別名 b 賦與連接的結果,這是和前面的例子不同的:
SELECT * FROM (my_table AS a CROSS JOIN my_table) AS b ...
FROM table_referencealias
這個形式等效于前面那個;
AS 關鍵字是噪音.
FROM table_reference [AS] alias ( column1 [, column2 [, ...]] )
在這種形式里, 除了象上面那樣給表重命名外,還給該表的列賦予了臨時名字. 如果聲明的列別名比表里實際的列少,那么后面的列就沒有重命名. 這個語法對于自連接或子查詢特別有用.
如果用這些形式中的任何一種給一個 JOIN 子句的輸出附加了一個別名, 那么該別名就在 JOIN 里隱藏了其原始的名字.比如
SELECT a.* FROM my_table AS a JOIN your_table AS b ON ...
是合法 SQL,但是
SELECT a.* FROM (my_table AS a JOIN your_table AS b ON ...) AS c
是不合法的∶表別名 A 在別名 C 外面是看不到的.
FROM T1 INNER JOIN T2 USING (C)
FROM T1 LEFT OUTER JOIN T2 USING (C)
FROM (T1 RIGHT OUTER JOIN T2 ON (T1C1=T2C1)) 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 子句是如何 重命名或命名一個派生表的以及隨后的逗號分隔的列表是如何給 那些列命名或重命名的.最后兩個 FROM 子句從 T1,T2,和 T3 中生成同樣的派生表.在命名隨后的 DT1 時省略了 AS 關鍵字. 關鍵字 OUTER 和 INNER 都是擺設,也可以省略.
WHERE 子句的語法是
WHERE search_condition
這里的
search condition 是定義在
Section 1.3 里的任意表達式,它返回一個 類型為
boolean的值.
在完成對 FROM 子句的處理之后,生成的每一行都會對搜索條件進行檢查. 如果該條件的結果是真,那么該行輸出到輸出表中,否則(也就是說, 如果結果是假或 NULL)就把它拋棄.搜索條件通常至少要引用一些在 FROM 子句里生成的列;這不是必須的,但如果不是這樣的話,那么 WHERE 子句就沒什么用了.
注意: 在 JOIN 語法實現以前,我們必須把 inner join(內部連接)的連接條件放在 WHERE 子句里. 比如,這些表表達式是等效的:
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 語法可能不那么 容易移植到其它產品中.對于外部連接(outer 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 中刪除.請注意我們把標量 子查詢當做一個值表達式來用(假設 C2 UNIQUE (唯一)). 就好象任何其它查詢一樣,子查詢里可以使用復雜的表表達式. 請注意 FDT 是如何引用子查詢的.把 C1 修飾成 FDT.C1 只有 在 C1 是該子查詢生成的列的名字時才是必須的. 修飾列名字可以增加語句的準確性,即使有時候不是必須的. 列名字的作用范圍從外層查詢擴展到它的內層查詢.
在通過了 WHERE 過濾器之后,生成的輸出表可以繼續用 GROUP BY 子句進行分組,然后用 HAVING 子句刪除一些分組行.
SELECT select_list FROM ... [WHERE ...] GROUP BY grouping_column_reference [, grouping_column_reference]...
GROUP BY 子句用于把一個表中在所列出的列上共享相同值的行聚集在一起. 這些列的列出順序如果并沒有什么關系(和 ORDER BY 子句相反). 目的是把每組共享相同值的行縮減為一個組行,它代表該組里的所有行. 這樣就可以刪除輸出里的重復和/或獲取應用于這些組的聚集.
一旦對一個表分了組,那么沒有包含在分組中的列就不能引用, 除了在一個聚集表達式中以外,因為在那些列中的一個特定值是 模糊的 - 它應該來自哪個分組的行?我們可以在一個選擇列表 的列表達式中引用 group-by 的列,因為它們對每個組都有一個 已知的常量值.在未分組的列上使用聚集函數提供的是組內的 聚集值,而不是整個表的.比如,一個在由產品代碼分組的表上的 sum(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 只能對源表的列進行分組,但 Postgres 把這個擴展為也允許 GROUP BY 那些在選擇列表中的選則列.也允許 對值表達式進行分組,而不僅是簡單的列.
SELECT select_list FROM ... [WHERE ...] GROUP BY ... HAVING boolean_expression
如果一個表已經用 GROUP BY 子句分了組,然后你又只對其中的某些 組感興趣,那么就可以用 HAVING 子句,很象 WHERE 子句,以刪除 一個分了組的表中的一些組.對于一些查詢,Postgres 允許不帶 GROUP BY 子句使用 HAVING 子句,這時候它的作用就象另外一個 WHERE 子句,但是那么用 HAVING 的目的并不清晰.因為 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 子句選擇那些單價超過 5000 的組的行.