Ⅰ.select通用语法
SELECT 选择列表 FROM 表表达式 [排序声明] [截取]
Ⅱ.表表达式
1.from子句
FROM table_reference [, table_reference [, ...]]
FROM子句从一个逗号分隔的table_reference列表中,通过jion,生成一个虚拟表。
table_reference可以是一个表名字或者一个生成的表,比如“子查询、表连接、或这些东西的复杂组合”。
如果在FROM子句中列出了多于一个表,那么它们“jion”(连接类型,见下文)形成一个派生表。该派生表可以进行WHERE, GROUP BY, HAVING子句的转换处理,并最后生成表表达式的结果。
交叉连接:
T1 CROSS JOIN T2
T1 CROSS JOIN T2对每个来自T1和T2 的行进行组合(也就是,一个笛卡尔积),连接成的表将包含这样的行:所有T1里面的字段后面跟着所有T2 里面的字段。
如果两表分别有 N 和 M 行,连接成的表将有 N*M 行。
FROM T1 CROSS JOIN T2 等效于FROM T1,T2。它还等效于FROM T1 INNER JOIN T2 ON TRUE(见下文)。
条件连接
内连接
T1 [INNER] JOIN T2 ON boolean_expression T1 [INNER] JOIN T2 USING ( join column list ) T1 NATURAL [INNER] JOIN T2
对于 T1 中的每一行 R1 ,如果能在 T2 中找到一个或多个满足连接条件的行,那么这些满足条件的每一行都在连接表中生成一行。
外连接
T1 { LEFT | RIGHT | FULL } [OUTER] JOIN T2 ON boolean_expression T1 { LEFT | RIGHT | FULL } [OUTER] JOIN T2 USING ( join column list ) T1 NATURAL { LEFT | RIGHT | FULL } [OUTER] JOIN T2
左外连接(LEFT OUTER JOIN)。首先执行一次内连接。然后为每一个 T1 中无法在 T2 中找到匹配的行生成一行,该行中对应 T2 的列用 NULL 补齐。因此,生成的连接表里总是包含来自 T1 里的每一行至少一个副本。
右外连接(RIGHT OUTER JOIN)。首先执行一次内连接。然后为每一个 T2 中无法在 T1 中找到匹配的行生成一行,该行中对应 T1 的列用 NULL 补齐。因此,生成的连接表里总是包含来自 T2 里的每一行至少一个副本。
全连接(FULL OUTER JOIN)。首先执行一次内连接。然后为每一个 T1 与 T2 中找不到匹配的行生成一行,该行中无法匹配的列用 NULL 补齐。因此,生成的连接表里无条件地包含 T1 和 T2 里的每一行至少一个副本。
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的时候就只会有一个(如果使用了SELECT * 的话,他们会优先发生)。
最后,NATURAL是USING的缩写形式:它自动形成一个由两个表中同名的字段组成的USING列表(同名字段只出现一次)。如果没有同名的字段,NATURAL的行为会像CROSS JOIN。
例子:
t1:
num | name -----+------ 1 | a 2 | b 3 | c
t2:
num | value -----+------- 1 | xxx 3 | yyy 5 | zzz
运行:
=> SELECT * FROM t1 CROSS JOIN t2; num | name | num | value -----+------+-----+------- 1 | a | 1 | xxx 1 | a | 3 | yyy 1 | a | 5 | zzz 2 | b | 1 | xxx 2 | b | 3 | yyy 2 | b | 5 | zzz 3 | c | 1 | xxx 3 | c | 3 | yyy 3 | c | 5 | zzz (9 rows) => SELECT * FROM t1 INNER JOIN t2 ON t1.num = t2.num; num | name | num | value -----+------+-----+------- 1 | a | 1 | xxx 3 | c | 3 | yyy (2 rows) => SELECT * FROM t1 INNER JOIN t2 USING (num); num | name | value -----+------+------- 1 | a | xxx 3 | c | yyy (2 rows) => SELECT * FROM t1 NATURAL INNER JOIN t2; num | name | value -----+------+------- 1 | a | xxx 3 | c | yyy (2 rows) => SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num; num | name | num | value -----+------+-----+------- 1 | a | 1 | xxx 2 | b | | 3 | c | 3 | yyy (3 rows) => SELECT * FROM t1 LEFT JOIN t2 USING (num); num | name | value -----+------+------- 1 | a | xxx 2 | b | 3 | c | yyy (3 rows) => SELECT * FROM t1 RIGHT JOIN t2 ON t1.num = t2.num; num | name | num | value -----+------+-----+------- 1 | a | 1 | xxx 3 | c | 3 | yyy | | 5 | zzz (3 rows) => SELECT * FROM t1 FULL JOIN t2 ON t1.num = t2.num; num | name | num | value -----+------+-----+------- 1 | a | 1 | xxx 2 | b | | 3 | c | 3 | yyy | | 5 | zzz (4 rows) => SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num AND t2.value = ‘xxx‘; num | name | num | value -----+------+-----+------- 1 | a | 1 | xxx 2 | b | | 3 | c | | (3 rows)
请注意,将限制放在在WHERE子句中将会产生不同的结果:
=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num WHERE t2.value = ‘xxx‘; num | name | num | value -----+------+-----+------- 1 | a | 1 | xxx (1 row)
PS:注意最后两个select语句:
用ON声明的连接条件也可以包含与连接不直接相关的条件。这种功能可能对某些查询很有用,但是需要我们仔细想清楚。
这是因为限制放在ON子句中时是先于连接处理的,而限制放在WHERE子句中时是后于连接处理的。
内连接的连接条件既可以写在WHERE子句里也可以写在JOIN子句里。最终效果一样。
对于外连接而言,我们没有选择:连接条件必须在FROM子句中完成。外连接的ON或USING子句不等于WHERE条件,因为它导致最终结果中行的增(那些不匹配的输入行)和删。
2.where子句
WHERE search_condition
这里的search_condition是一个返回类型为 boolean 的值表达式。
在完成对FROM子句的处理之后,生成的每一行都会按照搜索条件进行检查。如果结果是真,那么该行保留在输出表中,否则(也就是结果是假或NULL)就把它抛弃。
PS:这里需要注意的是 where语句中的子查询对from语句中表的字段的引用问题。例如:
SELECT ... FROM fdt WHERE c1 IN (SELECT c3 FROM t2 WHERE c2 = fdt.c1 + 10)
3.group by 子句
SELECT select_list FROM ... [WHERE ...] GROUP BY grouping_column_reference [, grouping_column_reference]...
在通过了WHERE过滤器之后,生成的输入表可以继续用GROUP BY 子句进行分组,然后用HAVING子句选取一些分组行。
GROUP BY 子句子句用于把那些所有列出的 grouping_column_reference值都相同的行聚集在一起,缩减为一行,这样就可以删除输出里的重复或计算聚集。这些字段的列出顺序无关紧要。
group by对select引用字段的限制
凡是在select后面出现的、同时未在聚合函数中出现的字段,必须同时出现在group by后面.被分组的字段可以在选择列表中引用是因为它们每个组都有单一的数值。
例子:
它计算每种产品的总销售额(而不是所有产品的总销售额)。
SELECT product_id, p.name, (sum(s.units) * p.price) AS sales FROM products p LEFT JOIN sales s USING (product_id) GROUP BY product_id, p.name, p.price;
在这个例子里,字段product_id,p.name 和p.price必须在GROUP BY子句里,因为它们都在查询选择列表里被引用了(但见下文)。s.units字段不必在 GROUP BY列表里,因为它只是在一个聚集表达式(sum(…))里使用,它代表一组产品的销售总额。对于每种产品,这个查询都返回一个该产品的总销售额。
4.having 子句
SELECT select_list FROM ... [WHERE ...] GROUP BY ... HAVING boolean_expression
如果一个表已经用GROUP BY分了组,然后你又只对其中的某些组感兴趣,那么就可以用HAVING子句筛选分组。
groupby对HAVING子句引用字段的限制 同 groupby对Select引用字段的限制
如果一个查询调用了聚合函数,但没有GROUP BY子句,分组仍然发生:结果是单一组行(或者如果单一行被HAVING所淘汰,那么也许没有行)。
同样,它可以只包含一个HAVING子句,没有任何聚合函数的调用或GROUP BY子句。
Ⅲ.排序
SELECT select_list FROM table_expression ORDER BY sort_expression1 [ASC | DESC] [, sort_expression2 [ASC | DESC] ...]
在查询生成输出表之后,也就是在处理完选择列表之后,你还可以对输出表进行排序。如果没有排序,那么行将以不可预测的顺序返回.
sort_expression 是任何可用于选择列表的表达式,例如:
SELECT a, b FROM table1 ORDER BY a + b, c;
如果指定了多个排序表达式,那么仅在前面的表达式排序相等的情况下才使用后面的表达式做进一步排序。每个表达式都可以跟一个可选的ASC(升序,默认) 或DESC(降序)以设置排序方向。升序先输出小的数值,这里的"小"是以<操作符的角度定义的。类似的是,降序是以>操作符来判断的。
注意,排序选项对于每个排序列是相对独立的。例如ORDER BY x, y DESC 意思是说ORDER BY x ASC, y DESC,不同于ORDER BY x DESC, y DESC。
一个sort_expression也可以是字段编号,如:
SELECT a,b, c FROM table1 ORDER BY 1;
Ⅳ.limit截取
SELECT select_list FROM table_expression [ ORDER BY ... ] [ LIMIT { number | ALL } ]
LIMIT子句允许你只取出查询结果中的一部分数据行。LIMIT ALL和省略LIMIT子句是一样的。
使用LIMIT的同时使用ORDER BY子句把结果行约束成一个唯一的顺序是一个好主意。否则你就会得到一个不可预料的子集。
Ⅴ.select 语句大体执行顺序:
1.from子句:通过对表进行各种join,生成一个虚拟表T1。
2.where子句:对虚拟表T1中的每行数据,进行where子句计算,返回True,则表里本行,返回False,则删除本行。过滤后形成虚拟表T2。
3.group by子句:对虚拟表T2进行分组,分组依据是group by 后的表达式。
4.having子句:对分组后的数据,进修再过滤,只能直接引用group by中的字段或者将其他字段作为聚集函数参数使用。形成选择列表T3。
5.order by子句:对T3进行排序。
6.limit子句:截取相应行数。
在这些关键字中,只有在Order By语句中才可以使用最终视图的列名,如:
SELECT FruitName, ProductPlace, Price, ID AS IDE, Discount FROM T_TEST_FRUITINFO WHERE (ProductPlace = N‘china‘) ORDER BY IDE
这里只有在ORDER BY语句中才可以使用IDE,其他条件语句中如果需要引用列名则只能使用ID,而不能使用IDE。
以上内容是根据postgresql文档整理,因为部分语法可能只适用于postgresql,但原理相似。