In MaxCompute, the clauses of a SELECT statement execute in a different order than they are written. Understanding this logical processing order helps you write correct queries and avoid errors such as referencing a column alias before it is defined.
How it works
A MaxCompute SELECT statement supports nine clauses: SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, DISTRIBUTE BY, SORT BY, and LIMIT.
These clauses are processed in one of two sequences, depending on which clauses are present.
Sequence 1 — used when GROUP BY, HAVING, or ORDER BY is present:
| Step | Clause | What it does |
|---|---|---|
| 1 | FROM |
Identifies the source table |
| 2 | WHERE |
Filters rows before aggregation |
| 3 | GROUP BY |
Groups the filtered rows |
| 4 | HAVING |
Filters groups based on aggregate conditions |
| 5 | SELECT |
Projects the columns and computes expressions |
| 6 | ORDER BY |
Sorts the result set |
| 7 | LIMIT |
Restricts the number of output rows |
Sequence 2 — used when DISTRIBUTE BY or SORT BY is present:
| Step | Clause | What it does |
|---|---|---|
| 1 | FROM |
Identifies the source table |
| 2 | WHERE |
Filters rows |
| 3 | SELECT |
Projects the columns |
| 4 | DISTRIBUTE BY |
Distributes rows across reducers using hash partitioning |
| 5 | SORT BY |
Sorts rows within each partition |
ORDER BYandGROUP BYcannot be used together withDISTRIBUTE BYorSORT BYin the same query.
Write SELECT statements in execution order
MaxCompute lets you write a SELECT statement with clauses in execution order, placing FROM before SELECT. This makes the logical flow explicit and easier to read.
Standard syntax (write order):
SELECT [ALL | DISTINCT] <select_expr>, <select_expr>, ...
FROM <table_reference>
[WHERE <where_condition>]
[GROUP BY <col_list>]
[HAVING <having_condition>]
[ORDER BY <order_condition>]
[DISTRIBUTE BY <distribute_condition> [SORT BY <sort_condition>]]
[LIMIT <number>]
Execution-order syntax (FROM first):
FROM <table_reference>
[WHERE <where_condition>]
[GROUP BY <col_list>]
[HAVING <having_condition>]
SELECT [ALL | DISTINCT] <select_expr>, <select_expr>, ...
[ORDER BY <order_condition>]
[DISTRIBUTE BY <distribute_condition> [SORT BY <sort_condition>]]
[LIMIT <number>]
Both forms produce identical results. The FROM-first form is recommended when you want to make the execution sequence visible in the code.
Sample data
The examples in this topic use the sale_detail table. Run the following statements to create and populate it:
-- Create a partitioned table
CREATE TABLE IF NOT EXISTS sale_detail
(
shop_name STRING,
customer_id STRING,
total_price DOUBLE
)
PARTITIONED BY (sale_date STRING, region STRING);
-- Add partitions
ALTER TABLE sale_detail ADD PARTITION (sale_date='2013', region='china') PARTITION (sale_date='2014', region='shanghai');
-- Insert data into the 2013/china partition
INSERT INTO sale_detail PARTITION (sale_date='2013', region='china')
VALUES ('s1','c1',100.1),('s2','c2',100.2),('s3','c3',100.3);
-- Insert data into the 2014/shanghai partition
INSERT INTO sale_detail PARTITION (sale_date='2014', region='shanghai')
VALUES ('null','c5',null),('s6','c6',100.4),('s7','c7',100.5);
To verify the inserted data:
SET odps.sql.allow.fullscan=true;
SELECT * FROM sale_detail;
Result:
+------------+-------------+-------------+------------+------------+
| 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 |
| null | c5 | NULL | 2014 | shanghai |
| s6 | c6 | 100.4 | 2014 | shanghai |
| s7 | c7 | 100.5 | 2014 | shanghai |
+------------+-------------+-------------+------------+------------+
Querying a partitioned table without specifying a partition requires SET odps.sql.allow.fullscan=true; before the statement. In production, specify partitions directly to avoid a full table scan.
Examples
Example 1: Sequence 1 (GROUP BY, HAVING, ORDER BY)
The following two queries are equivalent. The first uses standard write order; the second uses execution order.
-- Standard write order
SET odps.sql.allow.fullscan=true;
SELECT region, MAX(total_price)
FROM sale_detail
WHERE total_price > 100
GROUP BY region
HAVING SUM(total_price) > 300.5
ORDER BY region
LIMIT 5;
-- Execution order (FROM first) — equivalent to the query above
FROM sale_detail
WHERE total_price > 100
GROUP BY region
HAVING SUM(total_price) > 300.5
SELECT region, MAX(total_price)
ORDER BY region
LIMIT 5;
Result:
+------------+------------+
| region | _c1 |
+------------+------------+
| china | 100.3 |
+------------+------------+
How the query is processed step by step:
| Step | Clause | Intermediate result |
|---|---|---|
| 1 | FROM sale_detail |
All 6 rows from the table |
| 2 | WHERE total_price > 100 |
5 rows where total_price exceeds 100 (the NULL row is excluded) |
| 3 | GROUP BY region |
2 groups: china (3 rows) and shanghai (2 rows) |
| 4 | HAVING SUM(total_price) > 300.5 |
1 group: china (sum = 300.6) |
| 5 | SELECT region, MAX(total_price) |
1 row: (china, 100.3) |
| 6 | ORDER BY region |
Sorted ascending by region |
| 7 | LIMIT 5 |
At most 5 rows returned |
Example 2: Sequence 2 (DISTRIBUTE BY, SORT BY)
The following two queries are equivalent:
-- Standard write order
SET odps.sql.allow.fullscan=true;
SELECT shop_name,
total_price,
region
FROM sale_detail
WHERE total_price > 100.2
DISTRIBUTE BY region
SORT BY total_price;
-- Execution order (FROM first) — equivalent to the query above
FROM sale_detail
WHERE total_price > 100.2
SELECT shop_name,
total_price,
region
DISTRIBUTE BY region
SORT BY total_price;
Result:
+------------+-------------+------------+
| shop_name | total_price | region |
+------------+-------------+------------+
| s3 | 100.3 | china |
| s6 | 100.4 | shanghai |
| s7 | 100.5 | shanghai |
+------------+-------------+------------+
How the query is processed step by step:
| Step | Clause | Intermediate result |
|---|---|---|
| 1 | FROM sale_detail |
All 6 rows from the table |
| 2 | WHERE total_price > 100.2 |
3 rows: s3 (100.3), s6 (100.4), s7 (100.5) |
| 3 | SELECT shop_name, total_price, region |
The three columns projected |
| 4 | DISTRIBUTE BY region |
Rows distributed across reducers by region using hash partitioning |
| 5 | SORT BY total_price |
Rows sorted within each partition by total_price ascending |