|
2.2. 表表达式表表达式 声明一个表.该表表达式 包含一个 FROM 子句,该子句可以根据需要选用 WHERE,GROUP BY, 和 HAVING 子句.大部分的表表达式只是指向磁盘上的一个表, 一个所谓的基本表,但是我们可以用更复杂的表表达式以各种方法修改或 组合基本表. 表表达式里的 WHERE,GROUP BY,和 HAVING 子句声明一系列对源自 FROM 子句的表的转换操作.转换最后生成的表提供用于产生输出行的 输入,这些输出行都是在选择列表的列表达式中生成的. 2.2.1. FROM 子句FROM 子句从一个用逗号分隔的表引用列表中的一个或更多个其它表 中生成一个表. FROM
table_reference
[
,
table_reference
[
, ...
]
]
表引用可以是一个表名字或者是一个生成的表,比如子查询,一个 表连接,或者这些东西的复杂组合.如果在 FROM 子句中列出了多于一个表, 那么它们被 cross join (见下文)形成一个派生表,该表可以进行 WHERE,GROUP BY 和 HAVING 子句的转换处理,并最后生成所有表表达式的结果. 如果一个表引用是一个简单的表名字并且它是表继承级别中的 超级表,那么该表的行包括所有它的后代子表的行,除非你在 该表名字前面加 ONLY 关键字.这样的话,这个引用就只生成 出现在命名表中的列 --- 任何在子表中追加的列都会被忽略. 2.2.1.1. 连接表一个连接表是根据特定的连接类型的规则从两个其它表(真实表或生成表) 中排生的表.我们支持 INNER,OUTER,和 CROSS JOIN 类型.
连接类型
如果 T1 和 T2 有一个或者都是可以连接(join)的表, 那么所有类型的连接都可以串在一起或嵌套在一起. 你可以在JOIN子句周围使用圆括弧来控制连接顺序, 如果没有圆括弧,那么 JOIN 子句是从左向右嵌套的. 2.2.1.2. 子查询声明一个派生表的子查询必须包围在圆括弧里并且 必须 用 AS 子句命名.(参阅 Section 2.2.1.3 .) FROM (SELECT * FROM table1) AS alias_name 这个例子等效于 FROM table1 AS alias_name . 更有趣的例子是在子查询里面有分组或聚集的时候, 这个时候子查询不能归纳成一个简单的连接. 2.2.1.3. 表和列别名你可以给一个表或复杂表引用一个临时的名字,用于在后面的 处理过程中引用那些派生的表.这样做叫做 表别名 . FROM table_reference AS alias 在这里, alias 可以是任何规则的标识符. 这个别名成为当前查询里该表引用的新名字 -- 同时我们也不能再用原来的 名字引用该表了(如果该表引用是一个普通的基本表).因此 SELECT * FROM my_table AS m WHERE my_table.a > 5; 是非法的 SQL 语法.作为 PostgreSQL 对标准的扩展,这里实际发生的事情是那个隐含的表引用加入到 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_reference alias 这个形式等效于前面那个; 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 外面是看不到的. 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 子句是如何 重命名或命名一个派生表的以及随后的逗号分隔的列表是如何给 那些列命名或重命名的.最后两个 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 子句里生成的列;这不是必须的,但如果不是这样的话,那么 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 是该子查询生成的列的名字时才是必须的. 修饰列名字可以增加语句的准确性,即使有时候不是必须的. 列名字的作用范围从外层查询扩展到它的内层查询. 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 子句相反). 目的是把每组共享相同值的行缩减为一个组行,它代表该组里的所有行. 这样就可以删除输出里的重复和/或获取应用于这些组的聚集. 一旦对一个表分了组,那么没有包含在分组中的列就不能引用, 除了在一个聚集表达式中以外,因为在那些列中的一个特定值是 模糊的 - 它应该来自哪个分组的行?我们可以在一个选择列表 的列表达式中引用 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 只能对源表的列进行分组,但 PostgreSQL 把这个扩展为也允许 GROUP BY 那些在选择列表中的选则列.也允许 对值表达式进行分组,而不仅是简单的列.
SELECT
select_list
FROM ...
[
WHERE ...
]
GROUP BY ... HAVING
boolean_expression
如果一个表已经用 GROUP BY 子句分了组,然后你又只对其中的某些 组感兴趣,那么就可以用 HAVING 子句,很象 WHERE 子句,以删除 一个分了组的表中的一些组.对于一些查询,PostgreSQL 允许不带 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 的组的行. |