SELECT语法

MaxCompute支持通过SELECT语句查询数据。本文为您介绍SELECT命令格式及如何实现嵌套查询、分组查询、排序等操作。

执行SELECT操作前需要具备目标表的读取数据权限(SELECT)。授权操作请参见MaxCompute权限

功能介绍

SELECT语句用于从表中选取满足指定条件的数据。您可以根据实际场景结合以下功能完成多样化的查询操作。

类型

功能

子查询(SUBQUERY)

在某个查询的执行结果基础上进一步执行查询操作时,可以通过子查询操作实现。

交集(INTERSECT)、并集(UNION)和补集(EXCEPT)

对查询结果数据集执行取交集、并集或补集操作。

JOIN

通过JOIN操作连接表并返回符合连接条件和查询条件的数据信息。

SEMI JOIN(半连接)

通过右表过滤左表的数据,右表的数据不出现在结果集中。

MAPJOIN HINT

对一个大表和一个或多个小表执行JOIN操作时,可以在SELECT语句中显式指定MAPJOIN Hint提示以提升查询性能。

DISTRIBUTED MAPJOIN

DISTRIBUTED MAPJOINMAPJOIN的升级版,适用于小表JOIN大表的场景。

SKEWJOIN HINT

当两张表JOIN存在热点,导致出现长尾问题时,您可以通过取出热点key,将数据分为热点数据和非热点数据两部分处理,最后合并的方式,提高JOIN效率。

Lateral View

通过Lateral View与UDTF(表生成函数)结合,将单行数据拆成多行数据。

GROUPING SETS

对数据进行多维度的聚合分析。

SELECT TRANSFORM

SELECT TRANSFORM语法允许您启动一个指定的子进程,将输入数据按照一定的格式通过标准输入至子进程,并且通过解析子进程的标准输出获取输出数据。

Split Size Hint

通过修改Split Size来控制并发度数量。

Time travel查询与增量查询

对于Delta类型的表,支持:

  • 通过TimeTravel查询,可以回溯到源表某个历史时间或者版本进行历史Snapshot查询。

  • 通过Incremental查询,指定源表某个历史时间区间或者版本区间进行历史增量查询。

使用限制

  • 当使用SELECT语句时,屏显最多只能显示10000行结果,同时返回结果要小于10 MB。当SELECT语句作为子句时则无此限制,SELECT子句会将全部结果返回给上层查询。

  • SELECT语句查询分区表时默认禁止全表扫描。

    自2018年1月10日20:00:00后,在新创建的项目上执行SQL语句时,默认情况下,针对该项目里的分区表不允许执行全表扫描操作。在查询分区表数据时必须指定分区,由此减少SQL的不必要I/O,从而减少计算资源的浪费以及按量计费模式下不必要的计算费用。

    如果您需要对分区表进行全表扫描,可以在全表扫描的SQL语句前加上命令SET odps.sql.allow.fullscan=true;,并和SQL语句一起提交执行。假设sale_detail表为分区表,需要同时执行如下语句进行全表查询:

    SET odps.sql.allow.fullscan=true;
    SELECT * from sale_detail;

    如果整个项目都需要开启全表扫描,项目空间Owner执行如下命令打开开关:

    SETPROJECT odps.sql.allow.fullscan=true;
  • 当查询聚簇表(cluster表)时,目前版本只对单表扫描分区数小于等于400时进行分桶裁剪优化。当分桶裁剪优化未生效时,会导致扫描数据增加。如果您使用的是按需付费模式,则导致费用增加;如果您使用包年包月付费模式,则会导致SQL计算性能下降。

命令格式

[WITH <cte>[, ...] ]
SELECT [ALL | DISTINCT] <SELECT_expr>[, <EXCEPT_expr>][, <REPLACE_expr>] ...
       FROM <TABLE_reference>
       [WHERE <WHERE_condition>]
       [GROUP BY {<col_list>|ROLLUP(<col_list>)}]
       [HAVING <HAVING_condition>]
       [WINDOW <WINDOW_clause>]
       [ORDER BY <ORDER_condition>]
       [DISTRIBUTE BY <DISTRIBUTE_condition> [SORT BY <SORT_condition>]|[ CLUSTER BY <CLUSTER_condition>] ]
       [LIMIT <number>]

命令中各字段的执行语序请参见SELECT语序

示例数据

为便于理解使用方法,本文为您提供源数据,基于源数据提供相关示例。创建表sale_detail,并添加数据,命令示例如下。

--创建一张分区表sale_detail。
CREATE TABLE IF NOT EXISTS sale_detail
(
shop_name     STRING,
customer_id   STRING,
total_price   DOUBLE
)
PARTITIONED BY (sale_date STRING, region STRING);

--向源表增加分区。
ALTER TABLE sale_detail ADD PARTITION (sale_date='2013', region='china');

--向源表追加数据。
INSERT INTO sale_detail PARTITION (sale_date='2013', region='china') VALUES ('s1','c1',100.1),('s2','c2',100.2),('s3','c3',100.3);

查询分区表sale_detail中的数据,命令示例如下:

SELECT * FROM sale_detail;
--返回结果。
+------------+------------+------------+------------+------------+
| shop_name  | price      | customer   | sale_date  | region     |
+------------+------------+------------+------------+------------+
| s1         | 100.1      | c1         | 2013       | china      |
| s2         | 100.2      | c2         | 2013       | china      |
| s3         | 100.3      | c3         | 2013       | china      |
+------------+------------+------------+------------+------------+

WITH子句(cte)

可选。WITH子句包含一个或多个常用的表达式CTE。CTE充当当前运行环境中的临时表,您可以在之后的查询中引用该表。CTE使用规则如下:

  • 在同一WITH子句中的CTE必须具有唯一的名字。

  • 在WITH子句中定义的CTE仅对在同一WITH子句中的其他CTE可以使用。

    假设A是子句中的第一个CTE,B是子句中的第二个CTE:

    • A引用A:无效。错误命令示例如下。

      WITH 
      A AS (SELECT 1 FROM A) 
      SELECT * FROM A;

      返回结果如下:

      FAILED: ODPS-0130161:[1,6] Parse exception - recursive cte A is invalid, it must have an initial_part and a recursive_part, which must be connected by UNION ALL
    • A引用B,B引用A:无效,不允许循环引用。错误命令示例如下

      WITH 
      A AS (SELECT * FROM B ), 
      B AS (SELECT * FROM A ) 
      SELECT * FROM B;

      返回结果如下:

      FAILED: ODPS-0130071:[1,26] Semantic analysis exception - while resolving view B - [1,51]recursive function call is not supported, cycle is A->B->A

正确命令示例如下。

WITH 
A AS (SELECT 1 AS C),
B AS (SELECT * FROM A) 
SELECT * FROM B;

返回结果如下。

+---+
| c |
+---+
| 1 |
+---+

列表达式(SELECT_expr)

必填。SELECT_expr格式为col1_name, col2_name, 列表达式,...,表示待查询的普通列、分区列或正则表达式。列表达式使用规则如下:

  • 用列名指定要读取的列。

    读取表sale_detail的列shop_name。命令示例如下。

    SELECT shop_name FROM sale_detail;

    返回结果如下。

    +------------+
    | shop_name  |
    +------------+
    | s1         |
    | s2         |
    | s3         |
    +------------+
  • 用星号(*)代表查询所有的列。可配合WHERE子句指定过滤条件。

    • 读取表sale_detail中所有的列。命令示例如下。

      --开启全表扫描,仅此Session有效。
      SET odps.sql.allow.fullscan=true;
      SELECT * FROM sale_detail;

      返回结果如下。

      +------------+-------------+-------------+------------+------------+
      | shop_name  | customer_id | total_price | sale_date  | region     |
      +------------+-------------+-------------+------------+------------+
      | s1         | c1          | 100.1       | 2013       | china      |
      | s2         | c2          | 100.2       | 2013       | china      |
      | s3         | c3          | 100.3       | 2013       | china      |
      +------------+-------------+-------------+------------+------------+
    • WHERE子句中指定过滤条件。命令示例如下。

      SELECT * FROM sale_detail WHERE shop_name='s1';

      返回结果如下。

      +------------+-------------+-------------+------------+------------+
      | shop_name  | customer_id | total_price | sale_date  | region     |
      +------------+-------------+-------------+------------+------------+
      | s1         | c1          | 100.1       | 2013       | china      |
      +------------+-------------+-------------+------------+------------+
  • 可以使用正则表达式。

    • 选出sale_detail表中所有列名以sh开头的列。命令示例如下。

      SELECT `sh.*` FROM sale_detail;

      返回结果如下。

      +------------+
      | shop_name  |
      +------------+
      | s1         |
      | s2         |
      | s3         |
      +------------+
    • 选出sale_detail表中列名不为shop_name的所有列。命令示例如下。

      SELECT `(shop_name)?+.+` FROM sale_detail;

      返回结果如下。

      +-------------+-------------+------------+------------+
      | customer_id | total_price | sale_date  | region     |
      +-------------+-------------+------------+------------+
      | c1          | 100.1       | 2013       | china      |
      | c2          | 100.2       | 2013       | china      |
      | c3          | 100.3       | 2013       | china      |
      +-------------+-------------+------------+------------+
    • 选出sale_detail表中排除shop_namecustomer_id两列的其他列。命令示例如下。

      SELECT `(shop_name|customer_id)?+.+` FROM sale_detail;

      返回结果如下。

      +-------------+------------+------------+
      | total_price | sale_date  | region     |
      +-------------+------------+------------+
      | 100.1       | 2013       | china      |
      | 100.2       | 2013       | china      |
      | 100.3       | 2013       | china      |
      +-------------+------------+------------+
    • 选出sale_detail表中排除列名以t开头的其他列。命令示例如下。

      SELECT `(t.*)?+.+` FROM sale_detail;

      返回结果如下。

      +------------+-------------+------------+------------+
      | shop_name  | customer_id | sale_date  | region     |
      +------------+-------------+------------+------------+
      | s1         | c1          | 2013       | china      |
      | s2         | c2          | 2013       | china      |
      | s3         | c3          | 2013       | china      |
      +------------+-------------+------------+------------+
      说明

      在排除多个列时,如果col2是col1的前缀,则需保证col1写在col2的前面(较长的col写在前面)。例如,一个表有2个分区无需被查询,一个分区名为ds,另一个分区名为dshh,由于前者是后者的前缀,正确表达式为SELECT `(dshh|ds)?+.+` FROM t;;错误表达式为SELECT `(ds|dshh)?+.+` FROM t;

  • 在选取的列名前可以使用DISTINCT去掉重复字段,只返回去重后的值。使用ALL会返回字段中所有重复的值。不指定此选项时,默认值为ALL

    • 查询表sale_detail中region列数据,如果有重复值时仅显示一条。命令示例如下。

      SELECT DISTINCT region FROM sale_detail;

      返回结果如下。

      +------------+
      | region     |
      +------------+
      | china      |
      +------------+
    • 去重多列时,DISTINCT的作用域是SELECT的列集合,不是单个列。命令示例如下。

      SELECT DISTINCT region, sale_date FROM sale_detail;

      返回结果如下。

      +------------+------------+
      | region     | sale_date  |
      +------------+------------+
      | china      | 2013       |
      +------------+------------+
    • distinct可以对窗口函数的计算结果进行去重,即distinct可以配合窗口函数使用。命令示例如下:

      SET odps.sql.allow.fullscan=true;
      SELECT DISTINCT sale_date, ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY total_price) AS rn FROM sale_detail;

      返回结果如下。

      +-----------+------------+
      | sale_date | rn         |
      +-----------+------------+
      | 2013      | 1          |
      +-----------+------------+

      目前不支持DISTINCTGROUP BY联合使用,例如执行如下命令会报错。

      SELECT DISTINCT shop_name FROM sale_detail GROUP BY shop_name;
      --报错信息: GROUP BY cannot be used with SELECT DISTINCT

排除列(EXCEPT_expr)

可选。EXCEPT_expr格式为EXCEPT(col1_name, col2_name, ...)。当您希望读取表内大多数列的数据,同时要排除表中少数列的数据时,可以通过SELECT * EXCEPT(col1_name, col2_name, ...) FROM ...;语句实现,表示读取表数据时会排除指定列(col1、col2)的数据。

命令示例如下。

--读取sale_detail表的数据,并排除region列的数据。
SELECT * EXCEPT(region) FROM sale_detail;

返回结果如下。

+-----------+-------------+-------------+-----------+
| shop_name | customer_id | total_price | sale_date |
+-----------+-------------+-------------+-----------+
| s1        | c1          | 100.1       | 2013      |
| s2        | c2          | 100.2       | 2013      |
| s3        | c3          | 100.3       | 2013      |
+-----------+-------------+-------------+-----------+

修改列(REPLACE_expr)

可选。REPLACE_expr格式为REPLACE(exp1 [as] col1_name, exp2 [as] col2_name, ...)。当您希望读取表内大多数列的数据,同时要对表中少数列的数据进行修改时,可以通过SELECT * REPLACE(exp1 as col1_name, exp2 as col2_name, ...) from ...;实现,表示读取表数据时会将col1的数据修改为exp1,将col2的数据修改为exp2。

命令示例如下。

--读取sale_detail表的数据,并修改total_price、region两列的数据。
SELECT * REPLACE(total_price+100 AS total_price, 'shanghai' AS region) FROM sale_detail;

返回结果如下。

+-----------+-------------+-------------+-----------+--------+
| shop_name | customer_id | total_price | sale_date | region |
+-----------+-------------+-------------+-----------+--------+
| s1        | c1          | 200.1       | 2013      | shanghai |
| s2        | c2          | 200.2       | 2013      | shanghai |
| s3        | c3          | 200.3       | 2013      | shanghai |
+-----------+-------------+-------------+-----------+--------+

目标表信息(TABLE_reference)

必填。TABLE_reference表示查询的目标表信息。目标表使用规则如下:

  • 直接指定目标表名。命令示例如下。

    SELECT customer_id FROM sale_detail;

    返回结果如下。

    +-------------+
    | customer_id |
    +-------------+
    | c1          |
    | c2          |
    | c3          |
    +-------------+
  • 嵌套子查询。命令示例如下。

    SELECT * FROM (SELECT region,sale_date FROM sale_detail) t WHERE region = 'china';

    返回结果如下。

    +------------+------------+
    | region     | sale_date  |
    +------------+------------+
    | china      | 2013       |
    | china      | 2013       |
    | china      | 2013       |
    +------------+------------+

WHERE子句(WHERE_condition)

可选。WHERE子句为过滤条件。如果表是分区表,可以实现列裁剪。使用规则如下:

  • 配合关系运算符,筛选满足指定条件的数据。关系运算符包含:

    • ><=>=<=<>

    • LIKERLIKE

    • INNOT IN

    • BETWEEN…AND

    详情请参见关系运算符

    WHERE子句中,您可以指定分区范围,只扫描表的指定部分,避免全表扫描。命令示例如下。

    SELECT * 
    FROM sale_detail
    WHERE sale_date >= '2008' AND sale_date <= '2014';
    --等价于如下语句。
    SELECT * 
    FROM sale_detail 
    WHERE sale_date BETWEEN '2008' AND '2014';

    返回结果如下。

    +------------+-------------+-------------+------------+------------+
    | shop_name  | customer_id | total_price | sale_date  | region     |
    +------------+-------------+-------------+------------+------------+
    | s1         | c1          | 100.1       | 2013       | china      |
    | s2         | c2          | 100.2       | 2013       | china      |
    | s3         | c3          | 100.3       | 2013       | china      |
    +------------+-------------+-------------+------------+------------+
    说明

    您可以通过EXPLAIN语句查看分区裁剪是否生效。普通的UDF或JOIN的分区条件写法都有可能导致分区裁剪不生效,详情请参见分区剪裁合理性评估

  • 通过UDF实现分区裁剪,将UDF语句先当作一个小作业执行,再将执行的结果替换到原来UDF出现的位置。

    • 实现方式

      • 在编写UDF的时候,UDF类上加入Annotation。

        @com.aliyun.odps.udf.annotation.UdfProperty(isDeterministic=true)
        说明

        com.aliyun.odps.udf.annotation.UdfProperty定义在odps-sdk-udf.jar文件中。您需要把引用的odps-sdk-udf版本提高到0.30.x或以上。

      • 在SQL语句前增加SET odps.sql.udf.ppr.deterministic = true;语句,此时SQL中所有的UDF均被视为deterministic。该操作执行的原理是进行执行结果回填,但是结果回填最多回填1000个分区。因此,如果UDF类加入Annotation,则可能会导致出现超过1000个回填结果的报错。此时如果您需要忽视此错误,可以通过设置SET odps.sql.udf.ppr.to.subquery = false;全局关闭此功能。关闭后,UDF分区裁剪也会失效。

    • 注意事项

      使用UDF实现分区裁剪时,UDF必须在查询表的WHERE条件里才能生效。

      • 用UDF实现分区裁剪正确示例如下。

        --UDF必须放在查询的源表的where条件中:
        SELECT key, value FROM srcp WHERE udf(ds) = 'xx';
      • 用UDF实现分区裁剪错误示例如下。

        --放在join on后面分区裁剪不会生效
        SELECT A.c1, A.c2 FROM srcp1 A  JOIN srcp2  B ON A.c1 = B.c1 AND udf(A.ds) ='xx';
  • 列表达式(SELECT_expr)中,如果被重命名的列字段(赋予了列别名)使用了函数,则不能在WHERE子句中引用列别名。错误命令示例如下。

    SELECT  task_name
            ,inst_id
            ,settings
            ,GET_JSON_OBJECT(settings, '$.SKYNET_ID') AS skynet_id
            ,GET_JSON_OBJECT(settings, '$.SKYNET_NODENAME') AS user_agent
    FROM    Information_Schema.TASKS_HISTORY
    WHERE   ds = '20211215' AND skynet_id IS NOT NULL
    LIMIT 10;

GROUP BY分组查询(col_list)

可选。通常,GROUP BY聚合函数配合使用,根据指定的普通列、分区列或正则表达式进行分组。GROUP BY使用规则如下:

  • GROUP BY操作优先级高于SELECT操作,因此GROUP BY的取值是SELECT输入表的列名或由输入表的列构成的表达式。需要注意的是:

    • GROUP BY取值为正则表达式时,必须使用列的完整表达式。

    • SELECT语句中没有使用聚合函数的列必须出现在GROUP BY中。

    使用示例:

    • 直接使用输入表列名region作为GROUP BY的列,即以region值分组。命令示例如下。

      SELECT region FROM sale_detail GROUP BY region;

      返回结果如下。

      +------------+
      | region     |
      +------------+
      | china      |
      +------------+
    • 以region值分组,返回每一组的销售额总量。命令示例如下。

      SELECT SUM(total_price) FROM sale_detail GROUP BY region;

      返回结果如下。

      +------------+
      | _c0        |
      +------------+
      | 300.6      |
      +------------+
    • 以region值分组,返回每一组的region值(组内唯一)及销售额总量。命令示例如下。

      SELECT region, SUM (total_price) FROM sale_detail GROUP BY region;

      返回结果如下。

      +------------+------------+
      | region     | _c1        |
      +------------+------------+
      | china      | 300.6      |
      +------------+------------+
    • SELECT列的别名分组,命令示例如下。

      SELECT region AS r FROM sale_detail GROUP BY r;
      --等效于如下语句。
      SELECT region AS r FROM sale_detail GROUP BY region;

      返回结果如下。

      +------------+
      | r          |
      +------------+
      | china      |
      +------------+
    • 以列表达式分组,命令示例如下。

      SELECT 2 + total_price AS r FROM sale_detail GROUP BY 2 + total_price;

      返回结果如下。

      +------------+
      | r          |
      +------------+
      | 102.1      |
      | 102.2      |
      | 102.3      |
      +------------+
    • SELECT的所有列中没有使用聚合函数的列,必须出现在GROUP BY中,否则返回报错。错误命令示例如下。

      SELECT region, total_price FROM sale_detail GROUP BY region;

      正确命令示例如下。

      SELECT region, total_price FROM sale_detail GROUP BY region, total_price;

      返回结果如下。

      +------------+-------------+
      | region     | total_price |
      +------------+-------------+
      | china      | 100.1       |
      | china      | 100.2       |
      | china      | 100.3       |
      +------------+-------------+
  • 当SQL语句设置了属性,即SET odps.sql.groupby.position.alias=true;GROUP BY中的整型常量会被当作SELECT的列序号处理。命令示例如下。

    --与下一条SQL语句一起执行。
    SET odps.sql.groupby.position.alias=true;
    --1代表SELECT的列中第一列即region,以region值分组,返回每一组的region值(组内唯一)及销售额总量。
    SELECT region, SUM(total_price) FROM sale_detail GROUP BY 1;

    返回结果如下。

    +------------+------------+
    | region     | _c1        |
    +------------+------------+
    | china      | 300.6      |
    +------------+------------+

HAVING子句(HAVING_condition)

可选。通常HAVING子句与聚合函数一起使用,实现过滤。命令示例如下。

--为直观展示数据呈现效果,向sale_detail表中追加数据。
INSERT INTO sale_detail PARTITION (sale_date='2014', region='shanghai') VALUES ('null','c5',null),('s6','c6',100.4),('s7','c7',100.5);
--使用having子句配合聚合函数实现过滤。
SELECT region,SUM(total_price) FROM sale_detail 
GROUP BY region 
HAVING SUM(total_price)<305;

返回结果如下。

+------------+------------+
| region     | _c1        |
+------------+------------+
| china      | 300.6      |
| shanghai   | 200.9      |
+------------+------------+

ORDER BY全局排序(ORDER_condition)

可选。ORDER BY用于对所有数据按照指定普通列、分区列或指定常量进行全局排序。ORDER BY使用规则如下:

  • 默认对数据进行升序排序,如果降序排序,需要使用DESC关键字。

  • ORDER BY默认要求带LIMIT数据行数限制,没有LIMIT会返回报错。如您需要解除ORDER BY必须带LIMIT的限制,详情请参见LIMIT NUMBER限制输出行数>解除ORDER BY必须带LIMIT的限制

    • 查询表sale_detail的信息,并按照total_price升序排列前2条。命令示例如下。

      SELECT * FROM sale_detail ORDER BY total_price LIMIT 2;

      返回结果如下。

      +------------+-------------+-------------+------------+------------+
      | shop_name  | customer_id | total_price | sale_date  | region     |
      +------------+-------------+-------------+------------+------------+
      | s1         | c1          | 100.1       | 2013       | china      |
      | s2         | c2          | 100.2       | 2013       | china      |
      +------------+-------------+-------------+------------+------------+
    • 查询表sale_detail的信息,并按照total_price降序排列前2条。命令示例如下。

      SELECT * FROM sale_detail ORDER BY total_price DESC LIMIT 2;

      返回结果如下。

      +------------+-------------+-------------+------------+------------+
      | shop_name  | customer_id | total_price | sale_date  | region     |
      +------------+-------------+-------------+------------+------------+
      | s3         | c3          | 100.3       | 2013       | china      |
      | s2         | c2          | 100.2       | 2013       | china      |
      +------------+-------------+-------------+------------+------------+
  • 在使用ORDER BY排序时,NULL会被认为比任何值都小,这个行为与MySQL一致,但是与Oracle不一致。

    查询表sale_detail的信息,并按照total_price升序排列前2条。命令示例如下。

    SELECT * FROM sale_detail ORDER BY total_price LIMIT 2;

    返回结果如下。

    +------------+-------------+-------------+------------+------------+
    | shop_name  | customer_id | total_price | sale_date  | region     |
    +------------+-------------+-------------+------------+------------+
    | s1         | c1          | 100.1       | 2013       | china      |
    | s2         | c2          | 100.2       | 2013       | china      |
    +------------+-------------+-------------+------------+------------+
  • ORDER BY后面需要加上SELECT列的别名。当SELECT某列时,如果没有指定列的别名,则列名会被作为列的别名。

    ORDER BY加列的别名。命令示例如下。

    SELECT total_price AS t FROM sale_detail ORDER BY total_price LIMIT 3;
    --等效于如下语句。
    SELECT total_price AS t FROM sale_detail ORDER BY t LIMIT 3;

    返回结果如下。

    +------------+
    | t          |
    +------------+
    | 100.1      |
    | 100.2      |
    | 100.3      |
    +------------+
  • 当SQL语句设置了属性,即SET odps.sql.orderby.position.alias=true;ORDER BY中的整型常量会被当作SELECT的列序号处理。命令示例如下。

    --与下一条SQL语句一起执行。
    SET odps.sql.orderby.position.alias=true;
    SELECT * FROM sale_detail ORDER BY 3 LIMIT 3;

    返回结果如下。

    +------------+-------------+-------------+------------+------------+
    | shop_name  | customer_id | total_price | sale_date  | region     |
    +------------+-------------+-------------+------------+------------+
    | s1         | c1          | 100.1       | 2013       | china      |
    | s2         | c2          | 100.2       | 2013       | china      |
    | s3         | c3          | 100.3       | 2013       | china      |
    +------------+-------------+-------------+------------+------------+
  • OFFSET可以和ORDER BY...LIMIT语句配合使用,用于指定跳过的行数,格式为ORDER BY...LIMIT m OFFSET n,也可以简写为ORDER BY...LIMIT n, m。其中:LIMIT m控制输出m行数据,OFFSET n表示在开始返回数据之前跳过的行数。OFFSET 0与省略OFFSET子句效果相同。

    将表sale_detail按照total_price升序排序后,输出从第3行开始的3行数据。命令示例如下。

    SELECT customer_id,total_price FROM sale_detail ORDER BY total_price LIMIT 3 OFFSET 2;
    --等效于如下语句。
    SELECT customer_id,total_price FROM sale_detail ORDER BY total_price LIMIT 2, 3;

    返回结果如下。

    +-------------+-------------+
    | customer_id | total_price |
    +-------------+-------------+
    | c3          | 100.3       |
    +-------------+-------------+

    由于查询到的数据从第3行开始仅剩1行数据,不足3行,所以返回结果只有1行。

  • Range Clustering可以用来做全局排序加速。在普通的ORDER BY场景,为保证全局有序,所有的排序数据合并到一个单独的Instance运行,这就无法发挥并行处理的优势。利用Range Clustering的PARTITION步骤,可以实现并发多路全排序。首先对数据取样并划分Range,然后对各个Range做并发排序,最后得到的就是全局有序的结果,详情请参见全局排序加速

DISTRIBUTE BY哈希分片(DISTRIBUTE_condition)

可选。DISTRIBUTE BY用于对数据按照某几列的值做Hash分片。

DISTRIBUTE BY控制Map(读数据)的输出在Reducer中是如何划分的,如果不希望Reducer的内容存在重叠,或需要对同一分组的数据一起处理,您可以使用DISTRIBUTE BY来保证同组数据分发到同一个Reducer中。

必须使用SELECT的输出列别名,当SELECT某列时,如果没有指定列的别名,则列名会被作为列的别名。命令示例如下:

--查询表sale_detail中的列region值并按照region值进行哈希分片。
SELECT region FROM sale_detail DISTRIBUTE BY region;
--等价于如下语句。
SELECT region AS r FROM sale_detail DISTRIBUTE BY region;
SELECT region AS r FROM sale_detail DISTRIBUTE BY r;

SORT BY局部排序(SORT_condition)

可选。通常,配合DISTRIBUTE BY使用。SORT BY使用规则如下:

  • SORT BY默认对数据进行升序排序,如果降序排序,需要使用DESC关键字。

  • 如果SORT BY语句前有DISTRIBUTE BYSORT BY会对DISTRIBUTE BY的结果按照指定的列进行排序。

    • 查询表sale_detail中的列region和total_price的值并按照region值进行哈希分片,然后按照total_price对哈希分片结果进行局部升序排序。命令示例如下。

      --为直观展示数据呈现效果,向sale_detail表中追加数据。
      INSERT INTO sale_detail PARTITION (sale_date='2014', region='shanghai') VALUES ('null','c5',null),('s6','c6',100.4),('s7','c7',100.5);
      SELECT region,total_price FROM sale_detail DISTRIBUTE BY region SORT BY total_price;

      返回结果如下。

      +------------+-------------+
      | region     | total_price |
      +------------+-------------+
      | shanghai   | NULL        |
      | china      | 100.1       |
      | china      | 100.2       |
      | china      | 100.3       |
      | shanghai   | 100.4       |
      | shanghai   | 100.5       |
      +------------+-------------+
    • 查询表sale_detail中的列region和total_price的值并按照region值进行哈希分片,然后按照total_price对哈希分片结果进行局部降序排序。命令示例如下。

      SELECT region,total_price FROM sale_detail DISTRIBUTE BY region SORT BY total_price DESC;

      返回结果如下。

      +------------+-------------+
      | region     | total_price |
      +------------+-------------+
      | shanghai   | 100.5       |
      | shanghai   | 100.4       |
      | china      | 100.3       |
      | china      | 100.2       |
      | china      | 100.1       |
      | shanghai   | NULL        |
      +------------+-------------+
  • 如果SORT BY语句前没有DISTRIBUTE BYSORT BY会对每个Reduce中的数据进行局部排序。

    保证每个Reduce的输出数据都是有序的,从而增加存储压缩率,同时读取时如果有过滤,能够减少真正从磁盘读取的数据量,提高后续全局排序的效率。命令示例如下。

    SELECT region,total_price FROM sale_detail SORT BY total_price DESC;

    返回结果如下。

    +------------+-------------+
    | region     | total_price |
    +------------+-------------+
    | china      | 100.3       |
    | china      | 100.2       |
    | china      | 100.1       |
    | shanghai   | 100.5       |
    | shanghai   | 100.4       |
    | shanghai   | NULL        |
    +------------+-------------+
说明
  • ORDER BY|DISTRIBUTE BY|SORT BY的取值必须是SELECT语句的输出列,即列的别名。列的别名可以为中文。

  • 在MaxCompute SQL解析中,ORDER BY|DISTRIBUTE BY|SORT BY执行顺序在SELECT操作之后,因此它们的取值只能为SELECT语句的输出列。

  • ORDER BY不和DISTRIBUTE BYSORT BY同时使用,GROUP BY也不和DISTRIBUTE BYSORT BY同时使用。

LIMIT限制输出行数(number)

可选。LIMIT <number>中的number是常数,用于限制输出行数,取值范围为int32位取值范围,即最大值不可超过2,147,483,647。

说明

LIMIT基于分布式系统对数据进行扫描后过滤,您无法通过LIMIT减少返回数据量进而减少计算费用。

当您涉及到如下场景时,可参考对应解决方案处理:

  • 解除ORDER BY必须带LIMIT的限制。

    因为ORDER BY需要对单个执行节点做全局排序,所以默认带LIMIT限制,避免误用导致单点处理大量数据。如果您的使用场景确实需要ORDER BY放开LIMIT限制,可以通过如下两种方式实现:

    • Project级别:设置SETPROJECT odps.sql.validate.orderby.limit=false;关闭ORDER BY必须带LIMIT的限制。

    • Session级别:设置SET odps.sql.validate.orderby.limit=false;关闭ORDER BY必须带LIMIT的限制,需要与SQL语句一起提交。

      说明

      如果关闭ORDER BY必须带LIMIT的限制,在单个执行节点有大量数据排序的情况下,资源消耗或处理时长等性能表现会受到影响。

  • 解除屏显限制

    当使用无LIMITSELECT语句或LIMITNUMBER数量超过设置的屏显上限时,如果您直接从屏显窗口查看结果,最多只能输出屏显上限设置的行数。

    每个项目空间的屏显上限可能不同,您可以参考如下方法控制:

    • 如果关闭了项目空间数据保护,修改odpscmd_config.ini文件。

      设置odpscmd_config.ini文件中的use_instance_tunnel=true,如果不配置instance_tunnel_max_record参数,则屏显行数不受限制;否则,屏显行数受instance_tunnel_max_record参数值限制。instance_tunnel_max_record参数值上限为10000行。Instance Tunnel详情请参见使用说明

    • 如果开启了项目空间数据保护,屏显行数受READ_TABLE_MAX_ROW参数值限制,配置上限为10000行。

    说明

    您可以执行SHOW SecurityConfiguration;命令查看ProjectProtection属性配置。如果ProjectProtection=true,根据项目空间数据保护需求判断是否关闭数据保护机制。如果可以关闭,通过SET ProjectProtection=false;命令关闭。ProjectProtection属性默认不开启。项目空间数据保护机制详情请参见数据保护机制

窗口子句(window_clause)

详细窗口子句信息,请参见窗口函数语法