2.2 WHERE
子句
WHERE
子句
SQL WHERE
的语法是
WHERE search_condition
这里的
“search_condition“
是任意返回一个
boolean
类型值的值表达式(参阅
值表达式
)。
在完成对
FROM
子句的处理之后,生成的虚拟表的每一行都会对根据搜索条件进行检查。如果该条件的结果
是真,那么该行被保留在输出表中;否则(也就是说,如果结果是假或空)就把它抛弃。搜索条件通常至少
要引用一些在
FROM
子句里生成的列;虽然这不是必须的,但如果不引用这些列,那么
WHERE
子句就没什
么用了。
Note:
内连接的连接条件既可以写在
WHERE
子句也可以写在
JOIN
子句里。例如,这些表表达式是等效的:
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
语法可能不那么容易移植到其它
SQL
数据库管理系统
中。对于外部连接而言没有选择:它们必须在
FROM
子句中完成。外部连接的
ON
或
USING
子句不等于
WHERE
条件,因为它导致最终结果中行的增加(对那些不匹配的输入行)和减少。
WHERE
子句的例子:
SELECT
...
FROM fdt WHERE c1
>
5
SELECT
...
FROM fdt WHERE c1 IN (
1
,
2
,
3
)
SELECT
...
FROM fdt WHERE c1 IN (SELECT c1 FROM t2)
SELECT
...
FROM fdt WHERE c1 IN (SELECT c3 FROM t2 WHERE c2
=
fdt
.
c1
+
10
)
SELECT
...
FROM fdt WHERE c1 BETWEEN (SELECT c3 FROM t2 WHERE c2
=
fdt
.
c1
+
10
)
AND
100
SELECT
...
FROM fdt WHERE EXISTS (SELECT c1 FROM t2 WHERE c2
>
fdt
.
c1)
在上面的例子里,
fdt
是从
FROM
子句中派生的表。那些不符合
WHERE
子句的搜索条件的行会被从
fdt
中删除。请注意把标量子查询当做一个值表达式来用。和任何其它查询一样,子查询里可以使用复杂的表表
达式。同时还请注意
fdt
在子查询中也被引用。只有在
c1
也是作为子查询输入表的生成表的列时,才必
须把 c1
限定成
fdt.c1
。但限定列名字可以增加语句的清晰度,即使有时候不是必须的。这个例子展示了
一个外层查询的列名范围如何扩展到它的内层查询。
2.3 GROUP BY
和
HAVING
子句
在通过了
WHERE
过滤器之后,生成的输入表可以使用
GROUP BY
子句进行分组,然后用
HAVING
子句删
除一些分组行。
SELECT select_list
FROM
...
[WHERE
...
]
GROUP BY grouping_column_reference [, grouping_column_reference]
...
GROUP BY
子句被用来把表中在所列出的列上具有相同值的行分组在一起。这些列的列出顺序并没有什么关
系。其效果是把每组具有相同值的行组合为一个组行,它代表该组里的所有行。这样就可以删除输出里的重
复和
/
或计算应用于这些组的聚集。例如:
=>
SELECT
*
FROM test1;
x
|
y
---+---
a
|
3
c
|
2
b
|
5
a
|
1
(
4
rows)
=>
SELECT x FROM test1 GROUP BY x;
x
---
a
b
c
(
3
rows)
在第二个查询里,不能写成
SELECT * FROM test1 GROUP BY x
,因为列
y
里没有哪个值可以和每个
组相关联起来。被分组的列可以在选择列表中引用是因为它们在每个组都有单一的值。
通常,如果一个表被分了组,那么没有在
GROUP BY
中列出的列都不能被引用,除非在聚集表达式中被引
用。一个用聚集表达式的例子是:
=>
SELECT x,
sum
(y) FROM test1 GROUP BY x;
x
|
sum
---+-----
a
|
4
b
|
5
c
|
2
(
3
rows)
这里的
sum
是一个聚集函数,它在整个组上计算出一个单一值。有关可用的聚集函数的更多信息可以在
聚集函数
。
Tip:
没有聚集表达式的分组实际上计算了一个列中可区分值的集合。也可以用
DISTINCT
子句实现(参阅
DISTINCT
)。
此例计算每种产品的总销售额(而不是所有产品的总销售额):
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(...)
)里使用,它代表一组产品的销售额。对于每种产品,这个查询都返回一个该产品的所有销售额
的总和行。
如果产品表被建立起来,例如
product_id
是主键,那么在上面的例子中用
product_id
来分组就够了,
因为名称和价格都是函数依赖于产品
ID
,并且关于为每个产品
ID
分组返回哪个名称和价格值就不会有歧义。
在严格的
SQL
里,
GROUP BY
只能对源表的列进行分组,但
KingbaseES
把这个扩展为也允许
GROUP BY
去
根据选择列表中的列分组。也允许对值表达式进行分组,而不仅是简单的列名。
如果一个表已经用
GROUP BY
子句分了组,然后你又只对其中的某些组感兴趣,那么就可以用
HAVING
子
句,它很象
WHERE
子句,用于从结果中删除一些组。其语法是:
SELECT select_list FROM
...
[WHERE
...
] GROUP BY
...
HAVING boolean_expression
在
HAVING
子句中的表达式可以引用分组的表达式和未分组的表达式(后者必须涉及一个聚集函数)。
例子:
=>
SELECT x,
sum
(y) FROM test1 GROUP BY x HAVING
sum
(y)
>
3
;
x
|
sum
---+-----
a
|
4
b
|
5
(
2
rows)
=>
SELECT x,
sum
(y) FROM test1 GROUP BY x HAVING x
<
'c'
;
x
|
sum
---+-----
a
|
4
b
|
5
(
2
rows)
再次,一个更现实的例子:
SELECT product_id, p
.
name, (
sum
(s
.
units)
*
(p
.
price
-
p
.
cost)) AS profit
FROM products p LEFT JOIN sales s USING (product_id)
WHERE s
.
date
>
CURRENT_DATE
-
INTERVAL
'4 weeks'
GROUP BY product_id, p
.
name, p
.
price, p
.
cost
HAVING
sum
(p
.
price
*
s
.
units)
>
5000
;
在上面的例子里,
WHERE
子句用那些非分组的列选择数据行(表达式只是对那些最近四周发生的销售为真)。
而
HAVING
子句限制输出为总销售收入超过
5000
的组。请注意聚集表达式不需要在查询中的所有地方都一
样。
如果一个查询包含聚集函数调用,但是没有
GROUP BY
子句,分组仍然会发生:结果是一个单一行(或者
根本就没有行,如果该单一行被
HAVING
所消除)。它包含一个
HAVING
子句时也是这样,即使没有任何
聚集函数调用或者
GROUP BY
子句。