SELECT
功能描述
SELECT用于从表或视图中读取数据。
SELECT语句就像叠加在数据库表上的过滤器,利用SQL关键字从数据表中过滤出用户需要的数据。
注意事项
- 使用SELECT请遵循SELECT操作规范。
- SELECT支持普通表与HDFS之间的JOIN,不支持普通表与GDS外表之间的JOIN。即SELECT语句中不能同时出现普通表和GDS外表。
- 必须对每个在SELECT命令中使用的字段有SELECT权限,使用FOR UPDATE或FOR SHARE还要求UPDATE权限。
语法格式
主句语法格式如下,其中包含的FROM子句、GROUP BY子句中的语法格式单独定义,如需了解各子句的含义,可以单击以下代码中的链接进入。

1
|
TABLE { ONLY {(table_name)| table_name} | table_name [ * ]}; |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[ WITH [ RECURSIVE ] with_query [, ...] ] SELECT [/*+ plan_hint */] [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ] { * | {expression [ [ AS ] output_name ]} [, ...] } [ FROM from_item [, ...] ] --跳转到FROM子句 [ WHERE condition ] [ GROUP BY grouping_element [, ...] ] --跳转到GROUP BY子句 [ HAVING condition [, ...] ] [ WINDOW {window_name AS ( window_definition )} [, ...] ] [ { UNION | INTERSECT | EXCEPT | MINUS } [ ALL | DISTINCT ] select ] [ ORDER BY {expression [ [ ASC | DESC | USING operator ] | nlssort_expression_clause ] [ NULLS { FIRST | LAST } ]} [, ...] ] [ { [ LIMIT { count | ALL } ] [ OFFSET start [ ROW | ROWS ] ] } | { LIMIT start, { count | ALL } } ] [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ] [ {FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ]} [...] ]; |

condition和expression中可以使用输出列中表达式的别名。
- 只能同一层引用。
- 只能引用输出列中的别名。
- 只能是后面的表达式引用前面的表达式。
- 不能包含volatile函数。
- 不能包含Window function函数。
- 不支持在join on条件中引用别名。
- 输出列中有多个要应用的别名则报错。
SELECT主句参数说明
参数 |
描述 |
取值范围或示例 |
||||||
---|---|---|---|---|---|---|---|---|
WITH [ RECURSIVE ] with_query [, ...] |
用于声明一个或多个在主查询中通过名字引用的子查询(公共表达式CTE),相当于临时表,当需要多次引用相同子查询结果时,使用WITH可简化代码逻辑,提高编码效率。 如果声明了RECURSIVE,则允许递归查询,即允许SELECT子查询通过名字引用它自己。 其中with_query的详细格式为:
|
|
||||||
plan_hint子句 |
以/*+ */的形式在SELECT关键字后,用于对SELECT对应的语句块生成的计划进行hint调优 ,详细用法请参见使用Plan Hint进行调优。 |
- |
||||||
ALL |
声明返回所有符合条件的行,是默认行为,可以省略该关键字。 |
- |
||||||
DISTINCT [ ON ( expression [, ...] ) ] |
从SELECT的结果集中删除所有重复的行,确保结果集中每行都是唯一的。 ON ( expression [, ...] ) 只保留那些在给出的表达式上运算出相同结果的行当中的第一行。
须知:
DISTINCT ON表达式是使用与ORDER BY相同的规则进行解释的。除非使用了ORDER BY来保证需要的行首先出现,否则,"第一行" 是不可预测的。 |
- |
||||||
SELECT列表 |
指定查询表中列名,可以是部分列或者是全部(使用通配符*表示)。 通过使用子句AS output_name可以为输出字段取个别名,这个别名通常用于输出字段的显示。 |
列名可以用下面几种形式表达:
|
||||||
FROM子句 |
为SELECT声明一个或者多个源表。具体请参见表2。 |
- |
||||||
WHERE子句 |
WHERE子句构成一个行选择表达式,用来缩小SELECT的查询范围。 |
具体使用约束请参见WHERE子句使用约束。 |
||||||
GROUP BY子句 |
将查询结果按某一列或多列的值分组,值相等的为一组。 |
详情请参见表4。 |
||||||
HAVING子句 |
用于过滤分组(GROUP BY)后的结果,它与WHERE类似,但是通常与GROUP BY子句配合使用来过滤出特殊的组。HAVING子句将组的一些属性与一个常数值进行比较,只有满足HAVING子句中的逻辑表达式的组才会被提取出来。 |
- |
||||||
WINDOW子句 |
用于定义窗口函数,一般格式为WINDOW window_name AS ( window_definition ) [, ...],window_name是可以被随后的窗口定义所引用的窗口名称,window_definition可以是以下格式:
frame_clause为窗函数定义一个窗口框架window frame,窗函数(并非所有)依赖于框架,window frame是当前查询行的一组相关行。frame_clause可以是以下的格式:
frame_start和frame_end可以是:
须知:
对列存表的查询目前只支持row_number窗口函数,不支持frame_clause。 |
- |
||||||
UNION子句 |
UNION计算多个SELECT语句返回行集合的并集。 UNION子句有如下约束条件:
一般表达式:
|
- |
||||||
INTERSECT子句 |
INTERSECT计算多个SELECT语句返回行集合的交集,不含重复的记录。 INTERSECT子句有如下约束条件:
一般形式:
select_statement可以是任何没有FOR UPDATE子句的SELECT语句。 |
- |
||||||
EXCEPT子句 |
EXCEPT操作符计算存在于左边SELECT语句的输出而不存在于右边SELECT语句输出的行。 EXCEPT子句有如下的通用形式:
|
- |
||||||
MINUS子句 |
与EXCEPT子句具有相同的功能和用法。 |
- |
||||||
ORDER BY子句 |
对SELECT语句检索得到的数据进行升序或降序排序。对于ORDER BY表达式中包含多列的情况:
须知:
|
- |
||||||
nlssort_expression_clause |
设置排序方式,其格式为:
NLS_SORT:指定某字段按照特殊方式排序。目前仅支持中文拼音格式排序和不区分大小写排序。取值如下:
|
|||||||
[ { [ LIMIT { count | ALL } ] [ OFFSET start [ ROW | ROWS ] ] } | { LIMIT start, { count | ALL } } ] |
LIMIT子句由两个独立的Limit子句、Offset子句和一个多参Limit子句构成:
其中,count声明返回的最大行数,而start声明跳过前面的start行数。如果这两个参数都指定了,会计算跳过start行之后返回的count行数。多参Limit子句不可和单参的Limit子句或Offset子句共同出现。 例如:
|
- |
||||||
FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY |
FETCH子句限定返回查询结果从第一行开始的总行数。 |
如果不指定count,默认值为1。 |
||||||
FOR UPDATE子句 |
FOR UPDATE子句将对SELECT检索出来的行进行加锁,这样避免在当前事务结束前被其它事务修改或者删除。即执行UPDATE、 DELETE、 SELECT FOR UPDATE这些行的事务将被阻塞,直到当前事务结束。 NOWAIT选项:避免操作等待其它事务提交,如果被选择的行不能立即被锁,执行SELECT FOR UPDATE NOWAIT将会立即报错。
须知:
|
- |
FROM子句参数说明
1 2 3 4 5 6 |
{[ ONLY ] table_name [ * ] [ partition_clause ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ] |( select ) [ AS ] alias [ ( column_alias [, ...] ) ] |with_query_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ] |function_name ( [ argument [, ...] ] ) [ AS ] alias [ ( column_alias [, ...] | column_definition [, ...] ) ] |function_name ( [ argument [, ...] ] ) AS ( column_definition [, ...] ) |from_item [ NATURAL ] join_type from_item [ ON join_condition | USING ( join_column [, ...] ) ]} |
参数 |
描述 |
取值范围或示例 |
||
---|---|---|---|---|
table_name |
表名或视图名,名称前可加上模式名,如:schema_name.table_name。 |
字符串,要符合标识符命名规范。 具体请参见标识符命名规范。 |
||
alias |
给表或复杂的表引用起一个临时的表别名,以便被其余的查询引用。 别名用于缩写或者在自连接中消除歧义。如果提供了别名,它就会完全隐藏表的实际名字。 |
字符串,要符合标识符命名规范。 具体请参见标识符命名规范。 |
||
column_alias |
列别名。 |
字符串,要符合标识符命名规范。 具体请参见标识符命名规范。 |
||
partition_clause |
PARTITION子句,查询某个分区表中相应分区的数据。格式如下:
须知:
指定分区只适合普通表。 |
示例: SELECT * FROM tpcds.reason_p PARTITION (P_05_BEFORE); |
||
subquery |
FROM子句中可以出现子查询,创建一个临时表保存子查询的输出。 |
例如,将子查询结果取别名为e,并作为FROM数据源:
|
||
with_query_name |
WITH子句同样可以作为FROM子句的源,可以通过WITH查询的名字对其进行引用。 |
字符串,要符合标识符命名规范。 具体请参见标识符命名规范。 举例,WITH子句取名为cte,并作为FROM的引用源:
|
||
function_name |
函数名称。函数调用也可以出现在FROM子句中。 |
字符串,要符合标识符命名规范。 具体请参见标识符命名规范。 |
||
join_type |
JOIN的类型。 |
支持五种JOIN类型:
详情请参见表3。 |
||
ON join_condition |
连接条件,用于限定连接中的哪些行是匹配的。如:ON left_table.a = right_table.a。 |
- |
||
USING(join_column[,...]) |
ON left_table.a = right_table.a AND left_table.b = right_table.b ... 的简写。要求对应的列必须同名。 |
- |
||
NATURAL |
NATURAL是具有相同名称的两个表的所有列的USING列表的简写。 |
- |
||
from item |
用于连接的查询源对象的名称。 |
- |
参数 |
描述 |
---|---|
[ INNER ] JOIN |
一个JOIN子句由两个FROM项组合而成。可使用圆括弧以决定嵌套的顺序。如果没有圆括弧,JOIN从左向右嵌套。在任何情况下,JOIN都比逗号分隔的FROM项绑定得更紧。 |
LEFT [ OUTER ] JOIN |
返回笛卡尔积中所有符合连接条件的行,再加上左表中通过连接条件没有匹配到右表行中的那些行。这样,左边的行将扩展为生成表的全长,方法是在那些右表对应的字段位置填充上NULL。 需要注意的是,只在计算匹配的时候,才使用JOIN子句的条件,外层的条件是在计算完成之后施加的。 |
RIGHT [ OUTER ] JOIN |
返回所有内连接的结果行,加上每个不匹配的右边行(左边用NULL扩展)。这只是一个符号上的方便,因为总是可以将其转换成一个LEFT OUTER JOIN,只需要把左边和右边的输入互换位置即可。 |
FULL [ OUTER ] JOIN |
返回所有内连接的结果行,加上每个不匹配的左边行(右边用NULL扩展),再加上每个不匹配的右边行(左边用NULL扩展)。 |
CROSS JOIN |
CROSS JOIN等效于INNER JOIN ON(TRUE) ,即没有被条件删除的行。这种连接类型只是符号上的方便,与简单的FROM和WHERE的效果相同。
说明:
|
GROUP BY子句参数说明
1 2 3 4 5 6 |
( ) | expression | ( expression [, ...] ) | ROLLUP ( { expression | ( expression [, ...] ) } [, ...] ) | CUBE ( { expression | ( expression [, ...] ) } [, ...] ) | GROUPING SETS ( grouping_element [, ...] ) |
参数 |
描述 |
示例 |
||||||||
---|---|---|---|---|---|---|---|---|---|---|
ROLLUP ( { expression | ( expression [, ...] ) } [, ...] ) |
ROLLUP是计算一个有序的分组列在GROUP BY中指定的标准聚集值,然后从右到左进一步创建高层次的部分和,最后创建了累积和。一个分组能够看做一系列的分组集。例如:
等价于:
ROLLUP子句中的元素可以是单独的字段或表达式,也可以是使用括号包含的列表。如果是括号中的列表,产生分组集时它们必须作为一个整体。例如:
等价于:
|
|||||||||
CUBE ( { expression | ( expression [, ...] ) } [, ...] ) |
CUBE是自动对group by子句中列出的字段进行分组汇总,结果集将包含维度列中各值的所有可能组合,以及与这些维度值组合相匹配的基础行中的聚合值。它会为每个分组返回一行汇总信息, 用户可以使用CUBE来产生交叉表值。比如,在CUBE子句中给出三个表达式(n = 3),运算结果为2n = 23 = 8组。 以n个表达式的值分组的行称为常规行,其余的行称为超级聚集行。例如:
等价于:
CUBE子句中的元素可以是单独的字段或表达式,也可以是使用括号包含的列表。如果是括号中的列表,产生分组集时它们必须作为一个整体。例如:
等价于: GROUP BY GROUPING SETS ((a,b,c,d), (a,b,c), (a), ( )) |
|||||||||
GROUPING SETS ( grouping_element [, ...] ) |
GROUPING SETS子句是GROUP BY子句的进一步扩展,它可以使用户指定多个GROUP BY选项。选项用于定义分组集,每个分组集都需要包含在单独的括号中,空白的括号(())表示将所有数据当作一个组处理。 这样做可以通过裁剪用户不需要的数据组来提高效率。 用户可以根据需要指定所需的数据组进行查询。
须知:
如果SELECT列表的表达式中引用了那些没有分组的字段,则会报错,除非使用了聚集函数,因为对于未分组的字段,可能返回多个数值。 |
WHERE子句使用约束
- 对于WHERE子句的LIKE操作符,当LIKE中要查询特殊字符“%”、“_”、“\”的时候需要使用反斜杠“\”来进行转义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
CREATE TABLE tt01 (id int,content varchar(50)); INSERT INTO tt01 values (1,'Jack say ''hello'''); INSERT INTO tt01 values (2,'Rose do 50%'); INSERT INTO tt01 values (3,'Lilei say ''world'''); INSERT INTO tt01 values (4,'Hanmei do 100%'); SELECT * FROM tt01; id | content ----+------------------- 3 | Lilei say 'world' 4 | Hanmei do 100% 1 | Jack say 'hello' 2 | Rose do 50% (4 rows) SELECT * FROM tt01 WHERE content like '%''he%'; id | content ----+------------------ 1 | Jack say 'hello' (1 row) SELECT * FROM tt01 WHERE content like '%50\%%'; id | content ----+------------- 2 | Rose do 50% (1 row)
- WHERE子句中可以通过指定"(+)"操作符的方法将表的连接关系转换为外连接。但是不建议用户使用这种用法,因为这并不是SQL的标准语法,在做平台迁移的时候可能面临语法兼容性的问题。使用"(+)"有很多限制如下:
- "(+)"只能出现在where子句中。
- 如果from子句中已经有指定表连接关系,那么"(+)"不能在where子句中使用。
- "(+)"只能作用在表或者视图的列上,不能作用在表达式上。
- 如果表A和表B有多个连接条件,那么必须在所有的连接条件中指定"(+)",否则"(+)"将不会生效,表连接会转化成内连接,并且不给出任何提示信息。
- "(+)"作用的连接条件中的表不能跨查询或者子查询。如果"(+)"作用的表,不在当前查询或者子查询的from子句中,则会报错。如果"(+)"作用的对端的表不存在,则不报错,同时连接关系会转化为内连接。
- "(+)"作用的表达式不能直接通过"OR"连接。
- 如果"(+)"作用的列是和一个常量的比较关系, 那么这个表达式会成为join条件的一部分。
- 同一个表不能对应多个外表。
- "(+)"只能出现"比较表达式","NOT表达式",“ANY表达式”,“ALL表达式”,“IN表达式”,“NULLIF表达式”,“IS DISTINCT FROM表达式”,“IS OF”表达式。"(+)"不能出现在其他类型表达式中,并且这些表达式中不允许出现通过“AND”和“OR”连接。
- "(+)"只能转化为左外连接或者右外连接,不能转化为全连接,即不能在一个表达式的两个表上同时指定"(+)"。
示例:使用WITH子句实现子查询
先通过子查询得到一张临时表temp_t,然后查询表temp_t中的所有数据。
1
|
WITH temp_t(name,isdba) AS (SELECT usename,usesuper FROM pg_user) SELECT * FROM temp_t; |
为名为temp_t的with_query显示指定MATERIALIZED,然后查询表temp_t中的所有数据。
1
|
WITH temp_t(name,isdba) AS MATERIALIZED (SELECT usename,usesuper FROM pg_user) SELECT * FROM temp_t; |
为名为temp_t的with_query显示指定NOT MATERIALIZED,然后查询表temp_t中的所有数据。
1 2 3 4 |
WITH temp_t(name,isdba) AS NOT MATERIALIZED (SELECT usename,usesuper FROM pg_user) SELECT * FROM temp_t t1 WHERE name LIKE 'A%' UNION ALL SELECT * FROM temp_t t2 WHERE name LIKE 'B%'; |
示例:其他SELECT常用场景
查询tpcds.reason表的所有r_reason_sk记录,且去除重复。
1
|
SELECT DISTINCT(r_reason_sk) FROM tpcds.reason; |
LIMIT子句示例:获取表中一条记录。
1
|
SELECT * FROM tpcds.reason LIMIT 1; |
LIMIT子句示例:获取表中第三条记录。
1
|
SELECT * FROM tpcds.reason LIMIT 1 OFFSET 2; |
LIMIT子句示例:获取表中前两条记录。
1
|
SELECT * FROM tpcds.reason LIMIT 2; |
查询所有记录,且按字母升序排列。
1
|
SELECT r_reason_desc FROM tpcds.reason ORDER BY r_reason_desc; |
通过表别名,从pg_user和pg_user_status这两张表中获取数据。
1
|
SELECT a.usename,b.locktime FROM pg_user a,pg_user_status b WHERE a.usesysid=b.roloid; |
FULL JOIN子句示例:将pg_user和pg_user_status这两张表的数据进行全连接显示,即数据的合集。
1
|
SELECT a.usename,b.locktime,a.usesuper FROM pg_user a FULL JOIN pg_user_status b on a.usesysid=b.roloid; |
GROUP BY子句示例:根据查询条件过滤,并对结果进行分组。
1
|
SELECT r_reason_id, AVG(r_reason_sk) FROM tpcds.reason GROUP BY r_reason_id HAVING AVG(r_reason_sk) > 25; |
GROUP BY子句示例:通过group by别名来对结果进行分组。
1
|
SELECT r_reason_id AS id FROM tpcds.reason GROUP BY id; |
UNION子句示例:将表tpcds.reason里r_reason_desc字段中的内容以W开头和以N开头的进行合并。
1 2 3 4 5 6 7 |
SELECT r_reason_sk, tpcds.reason.r_reason_desc FROM tpcds.reason WHERE tpcds.reason.r_reason_desc LIKE 'W%' UNION SELECT r_reason_sk, tpcds.reason.r_reason_desc FROM tpcds.reason WHERE tpcds.reason.r_reason_desc LIKE 'N%'; |
示例:使用递归查询(WITH RECURSIVE)查询某公司三层部门的归属关系
以某公司的三层部门的递归为例,在一个包含一级、二级、三级部门的排序不规则的表中,通过递归查询(使用关键字RECURSIVE),查询出某个部门(例如技术部)下的所有二、三级部门。
准备数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
DROP TABLE IF EXISTS departments; CREATE TABLE departments (id varchar(10), pid varchar(10), dept_name varchar(50)); INSERT INTO departments VALUES('0',null,'Head office'); INSERT INTO departments VALUES('1','0','Technology'); -- 一级部门 INSERT INTO departments VALUES('2','0','Marketing'); INSERT INTO departments VALUES('3','0','HR'); INSERT INTO departments VALUES('4','0','Business'); INSERT INTO departments VALUES('5','0','Maintenance'); INSERT INTO departments VALUES('1-1','1','R&D Center'); -- 二级部门 INSERT INTO departments VALUES('1-2','1','Pre-Research'); INSERT INTO departments VALUES('1-3','1','Network'); INSERT INTO departments VALUES('1-4','1','Test'); INSERT INTO departments VALUES('1-5','1','Architecture'); INSERT INTO departments VALUES('1-1-1','1-1','Frontend'); -- 三级部门 INSERT INTO departments VALUES('1-1-2','1-1','Backend'); INSERT INTO departments VALUES('1-1-3','1-1','Design'); INSERT INTO departments VALUES('2-1','2','Branding'); INSERT INTO departments VALUES('2-2','2','Pricing'); INSERT INTO departments VALUES('2-3','2','Training'); |
查询表的原始数据,结果显示排序不规则,很难看出三层部门之间的归属关系。
1
|
SELECT * FROM departments; |
此时使用WITH RECURSIVE关键字,查询出技术部下面所有的二、三级部门,可以使用如下SQL:
1 2 3 4 5 6 7 8 9 10 11 |
WITH RECURSIVE t1 AS ( --定义一个公共表达式名称为t1 SELECT id,pid,dept_name FROM departments WHERE id='1' --初始化递归的根节点:查询id为1的记录,即技术部 UNION ALL --递归合并结果(保留重复项) SELECT --递归部分:逐层向下查询 t2.id, --子节点id,各一级部门名称 t2.pid, --父节点id,即总公司 t2.dept_name --子节点名称 FROM departments t2 INNER JOIN t1 ON t2.pid = t1.id --通过父节点ID连接递归结果 ) SELECT id,dept_name FROM t1 ORDER BY id; --最终查询,按ID排序显示所有二、三级部门 |
以上查询结果看起来还不太直观,可以将结果再稍微改进,将各个层级展示出来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
WITH RECURSIVE t1 AS ( SELECT --初始化递归的根节点,查询id为1的记录,即技术部,显示转换为text类型 id, pid, dept_name::text --显式转换为text类型(为后续路径拼接做准备) FROM departments WHERE id='1' UNION ALL SELECT --递归部分:拼接层级路径 t2.id, t2.pid, (t1.dept_name||'->'||t2.dept_name)::text AS dept_name --拼接父级路径与当前节点名称,如Technology -> R&D Center FROM departments t2 INNER JOIN t1 ON t2.pid = t1.id ) SELECT id,dept_name FROM t1 ORDER BY id; --最终查询,显示带层级路径的结果 |
示例:通过NLSSORT子句实现查询结果按中文拼音首字母进行排序
1 2 3 4 |
DROP TABLE IF EXISTS stu_pinyin_info; CREATE TABLE stu_pinyin_info (id bigint, name text) DISTRIBUTE BY REPLICATION; INSERT INTO stu_pinyin_info VALUES (1, '李雷'),(2, '石祥传'), (3,'安南鹏'); SELECT * FROM stu_pinyin_info ORDER BY NLSSORT (name, 'NLS_SORT = SCHINESE_PINYIN_M' ); |
示例:通过NLSSORT子句实现查询结果不区分大小写排序
1 2 3 4 5 6 7 8 |
CREATE TABLE stu_icase_info (id bigint, name text) DISTRIBUTE BY REPLICATION; INSERT INTO stu_icase_info VALUES (1, 'aaaa'),(2, 'AAAA'); SELECT * FROM stu_icase_info ORDER BY NLSSORT (name, 'NLS_SORT = generic_m_ci'); id | name ----+------ 1 | aaaa 2 | AAAA (2 rows) |
示例:查询分区表的数据
创建分区表tpcds.reason_p,并插入数据,再从tpcds.reason_p的表分区P_05_BEFORE中获取数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
CREATE TABLE tpcds.reason_p ( r_reason_sk integer, r_reason_id character(16), r_reason_desc character(100) ) PARTITION BY RANGE (r_reason_sk) ( partition P_05_BEFORE values less than (05), partition P_15 values less than (15), partition P_25 values less than (25), partition P_35 values less than (35), partition P_45_AFTER values less than (MAXVALUE) ); INSERT INTO tpcds.reason_p values(3,'AAAAAAAABAAAAAAA','reason 1'),(10,'AAAAAAAABAAAAAAA','reason 2'),(4,'AAAAAAAABAAAAAAA','reason 3'),(10,'AAAAAAAABAAAAAAA','reason 4'),(10,'AAAAAAAABAAAAAAA','reason 5'),(20,'AAAAAAAACAAAAAAA','reason 6'),(30,'AAAAAAAACAAAAAAA','reason 7'); SELECT * FROM tpcds.reason_p PARTITION (P_05_BEFORE); r_reason_sk | r_reason_id | r_reason_desc -------------+------------------+------------------------------------ 4 | AAAAAAAABAAAAAAA | reason 3 3 | AAAAAAAABAAAAAAA | reason 1 (2 rows) ——查询分区P_15的行数: SELECT count(*) FROM tpcds.reason_p PARTITION (P_15); count -------- 3 (1 row) |
GROUP BY子句示例:按r_reason_id分组统计tpcds.reason_p表中的记录数。
1 2 3 4 5 6 |
SELECT COUNT(*),r_reason_id FROM tpcds.reason_p GROUP BY r_reason_id; count | r_reason_id -------+------------------ 2 | AAAAAAAACAAAAAAA 5 | AAAAAAAABAAAAAAA (2 rows) |
HAVING子句示例:按r_reason_id分组统计tpcds.reason_p表中的记录,并只显示r_reason_id个数大于2的信息。
1 2 3 4 5 |
SELECT COUNT(*) c,r_reason_id FROM tpcds.reason_p GROUP BY r_reason_id HAVING c>2; c | r_reason_id ---+------------------ 5 | AAAAAAAABAAAAAAA (1 row) |
IN子句示例:按r_reason_id分组统计tpcds.reason_p表中的r_reason_id个数,并只显示r_reason_id值为AAAAAAAABAAAAAAA或AAAAAAAADAAAAAAA的个数。
1 2 3 4 5 |
SELECT COUNT(*),r_reason_id FROM tpcds.reason_p GROUP BY r_reason_id HAVING r_reason_id IN('AAAAAAAABAAAAAAA','AAAAAAAADAAAAAAA'); count | r_reason_id -------+------------------ 5 | AAAAAAAABAAAAAAA (1 row) |
INTERSECT子句示例:查询r_reason_id等于AAAAAAAABAAAAAAA,并且r_reason_sk小于5的信息。
1 2 3 4 5 6 |
SELECT * FROM tpcds.reason_p WHERE r_reason_id='AAAAAAAABAAAAAAA' INTERSECT SELECT * FROM tpcds.reason_p WHERE r_reason_sk<5; r_reason_sk | r_reason_id | r_reason_desc -------------+------------------+------------------------------------ 4 | AAAAAAAABAAAAAAA | reason 3 3 | AAAAAAAABAAAAAAA | reason 1 (2 rows) |
EXCEPT子句示例:查询r_reason_id等于AAAAAAAABAAAAAAA,并且去除r_reason_sk小于4的信息。
1 2 3 4 5 6 7 8 |
SELECT * FROM tpcds.reason_p WHERE r_reason_id='AAAAAAAABAAAAAAA' EXCEPT SELECT * FROM tpcds.reason_p WHERE r_reason_sk<4; r_reason_sk | r_reason_id | r_reason_desc -------------+------------------+------------------------------------ 10 | AAAAAAAABAAAAAAA | reason 2 10 | AAAAAAAABAAAAAAA | reason 5 10 | AAAAAAAABAAAAAAA | reason 4 4 | AAAAAAAABAAAAAAA | reason 3 (4 rows) |
通过在where子句中指定"(+)"来实现左连接。
1 2 3 4 5 6 |
select t1.sr_item_sk ,t2.c_customer_id from store_returns t1, customer t2 where t1.sr_customer_sk = t2.c_customer_sk(+) order by 1 desc limit 1; sr_item_sk | c_customer_id ------------+--------------- 18000 | (1 row) |
通过在where子句中指定"(+)"来实现右连接。
1 2 3 4 5 6 |
select t1.sr_item_sk ,t2.c_customer_id from store_returns t1, customer t2 where t1.sr_customer_sk(+) = t2.c_customer_sk order by 1 desc limit 1; sr_item_sk | c_customer_id ------------+------------------ | AAAAAAAAJNGEBAAA (1 row) |
通过在where子句中指定"(+)"来实现左连接,并且增加连接条件。
1 2 3 4 5 |
select t1.sr_item_sk ,t2.c_customer_id from store_returns t1, customer t2 where t1.sr_customer_sk = t2.c_customer_sk(+) and t2.c_customer_sk(+) < 1 order by 1 limit 1; sr_item_sk | c_customer_id ------------+--------------- 1 | (1 row) |
不支持在where子句中指定"(+)"的同时使用内层嵌套AND/OR的表达式。
1 2 3 |
select t1.sr_item_sk ,t2.c_customer_id from store_returns t1, customer t2 where not(t1.sr_customer_sk = t2.c_customer_sk(+) and t2.c_customer_sk(+) < 1); ERROR: Operator "(+)" can not be used in nesting expression. LINE 1: ...tomer_id from store_returns t1, customer t2 where not(t1.sr_... |
where子句在不支持表达式宏指定"(+)"会报错。
1 2 |
select t1.sr_item_sk ,t2.c_customer_id from store_returns t1, customer t2 where (t1.sr_customer_sk = t2.c_customer_sk(+))::bool; ERROR: Operator "(+)" can only be used in common expression. |
where子句在表达式的两边都指定"(+)"会报错。
1 2 3 |
select t1.sr_item_sk ,t2.c_customer_id from store_returns t1, customer t2 where t1.sr_customer_sk(+) = t2.c_customer_sk(+); ERROR: Operator "(+)" can't be specified on more than one relation in one join condition HINT: "t1", "t2"...are specified Operator "(+)" in one condition. |
示例:使用GROUP BY进行分组查询(ROLLUP、CUBE、GROUPING SETS)
在一些报表场景中,经常会对数据做分组统计(group by),例如对一级部门下辖的二级部门员工数进行统计。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
DROP TABLE IF EXISTS emp; CREATE TABLE emp( id int primary key, --工号 name text, --员工名 dep_1 text, --一级部门 dep_2 text --二级部门 ); INSERT INTO emp VALUES (01, 'liwei', 'Product', 'SRE'), (02, 'liming', 'Product', 'U1'), (03, 'hanlei', 'Product', 'U1'), (04, 'hanmeimei', 'Product', 'SRE'), (05, 'dingjun', 'Product', 'SRE'), (06, 'huxue', 'Product', 'E1'), (07, 'liuyi', 'Product', 'E1'), (08, 'luhua', 'Product', 'E1'), (09, 'yangjing', 'Product', 'U1'), (10, 'binbin', 'Product', 'U1'), (11, 'jim', 'Product', 'SRE'), (12, 'leo', 'Product', 'SER'); SELECT COUNT(*), dep_2 FROM emp GROUP BY dep_2; --统计二级部门的员工人数 |
常见的统计报表业务中,通常需要进一步计算一级部门的“合计”人数,也就是二级部门各分组的累加,就可以借助于rollup,如下所示,比前面的分组计算结果多了一行合计的数据。如下图的“12 Null”,表示一级部门总人数12。
1
|
SELECT COUNT(*), dep_2 FROM emp GROUP BY ROLLUP(dep_2) ORDER BY 1; |
ROLLUP,CUBE,GROUPING SETS的用法如下:
- 使用ROLLUP:
首先创建一张用户信息表customer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
DROP TABLE IF EXISTS customer; CREATE TABLE customer ( c_id char(16) not null, --用户iD c_name char(20) , --用户名 c_age integer , --年龄 c_country varchar(20) , --地区 c_class char(10), --用户级别 c_gender text, --性别 c_balance numeric ); --余额 INSERT INTO customer VALUES(1, 'tom', '20', 'Region A', '1', 'male', 300); INSERT INTO customer VALUES(2, 'jack', '30', 'Region B', '1', 'male', 100); INSERT INTO customer VALUES(3, 'rose', '40', 'Region C', '1', 'female', 200); INSERT INTO customer VALUES(4, 'Frank', '60', 'Region D', '1', 'male', 100); INSERT INTO customer VALUES(5, 'Leon', '20', 'Region A', '2', 'male', 200); INSERT INTO customer VALUES(6, 'Lucy', '20', 'Region A', '1', 'female', 500);
ROLLUP是在分组计算基础上增加了合计,从字面意思理解,就是从最小聚合级开始,聚合单位逐渐扩大,例如如下语句。
1 2
--首先对(c_country, c_class)进行GROUP BY,接着对(c_country)进行GROUP BY,最后对全表进行GROUP BY操作。 SELECT c_country, c_class, sum(c_balance) FROM customer GROUP BY ROLLUP(c_country, c_class) ORDER BY 1,2,3;
以上语句等价于:
1 2 3 4
SELECT c_country, c_class, SUM(c_balance) FROM customer GROUP BY c_country, c_class UNION ALL SELECT c_country, null, SUM(c_balance) FROM customer GROUP BY c_country UNION ALL SELECT null, null, SUM(c_balance) FROM customer ORDER BY 1,2,3;
- 使用CUBE:
CUBE从字面意思理解就是各个维度的意思,也就是说全部组合,即聚合键中所有字段的组合的分组统计结果,例如如下语句:
1 2
--首先对(c_country,c_class)进行GROUP BY,然后依次对(c_country)、(c_class)进行GROUP BY,最后对全表进行GROUP BY操作。 SELECT c_country, c_class, SUM(c_balance) FROM customer GROUP BY CUBE(c_country, c_class) ORDER BY 1,2,3;
以上语句等价于:
1 2 3 4 5 6 7
SELECT c_country, c_class, SUM(c_balance) FROM customer GROUP BY c_country, c_class union all SELECT c_country, null, SUM(c_balance) FROM customer GROUP BY c_country union all SELECT null, null, SUM(c_balance) FROM customer union all SELECT NULL, c_class, SUM(c_balance) FROM customer GROUP BY c_class order by 1,2,3;
- 使用GROUPING SETS:
GROUPING SETS区别于ROLLUP和CUBE,并没有总体的合计功能,相当于从ROLLUP和CUBE的结果中提取出部分记录,例如如下语句:
1 2
--分别对(c_class)、(c_country)进行GROUP BY计算 SELECT c_country, c_class, SUM(c_balance) FROM customer GROUP BY GROUPING SETS(c_country, c_class) ORDER BY 1,2,3;
以上语句等价于:
1 2 3
SELECT c_country, null, SUM(c_balance) FROM customer GROUP BY c_country UNION ALL SELECT null, c_class, sum(c_balance) FROM customer GROUP BY c_class ORDER BY 1,2,3;