Script mode (Script Mode SQL) enables you to compile and submit multiple SQL statements as a single unit. This process generates one execution plan, queues the script once, and runs all statements in a single job. This approach is ideal for ETL data pipelines, periodic batch processing, and query orchestration scenarios requiring multiple coordinated statements. Script mode supports two execution modes: normal mode, which provides atomic execution by default, and step-by-step execution mode, which runs statements serially.
-
Script mode does not support cost estimation. Actual fees are based on your billing details.
-
A single script can reference up to 10,000 tables. Each reference is counted individually, including repeated references to the same table and tables referenced in view definitions.
-
If the data preparation for multiple data sources finishes at significantly different times, for example, one at 01:00 and another at 07:00, do not use a table variable to combine them into a single Script Mode SQL job.
Use cases
-
Rewrite complex statements: Break down deeply nested subqueries into a sequence of readable table variable assignments.
-
Build multi-statement pipelines: Combine logically related statements into a single job to reduce queuing and scheduling overhead. In normal mode, all statements execute atomically. In step-by-step execution mode, statements run serially, which is ideal for read-after-write scenarios or migrations from other platforms. For more information, see Execution modes.
Syntax
-
Statement types: Script mode supports SET statements, some DDL statements, and DML statements. Result-displaying statements such as DESC and SHOW are not supported.
-
Statement order: A script must follow a fixed
SET→DDL→DMLorder. Each section can contain zero or more statements, but you cannot interleave statements from different sections.
-- 1. SET
SET odps.sql.type.system.odps2=true;
[SET odps.stage.reducer.num=xxx;]
[SET odps.sql.step.script.mode=true;] -- Enable step-by-step execution mode
[...]
-- 2. DDL
CREATE TABLE table1 xxx;
[CREATE TEMPORARY TABLE table2 xxx;] -- Create a temporary table
[...]
-- 3. DML&DQL
@var1 := SELECT [ALL | DISTINCT] select_expr, select_expr, ...
FROM table3
[WHERE where_condition];
@var2 := SELECT [ALL | DISTINCT] select_expr, select_expr, ...
FROM table4
[WHERE where_condition];
@var3 := SELECT [ALL | DISTINCT] var1.select_expr, var2.select_expr, ...
FROM @var1 JOIN @var2 ON ...;
INSERT OVERWRITE|INTO TABLE [PARTITION (partcol1=val1, partcol2=val2 ...)]
SELECT [ALL | DISTINCT] select_expr, select_expr, ...
FROM @var3;
[@var4 := SELECT [ALL | DISTINCT] var1.select_expr, var1.select_expr, ... FROM @var1
UNION ALL | UNION
SELECT [ALL | DISTINCT] var2.select_expr, var2.select_expr, ... FROM @var2;
CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name
AS
SELECT [ALL | DISTINCT] select_expr, select_expr, ...
FROM @var4;]
[...]
Execution modes
Script mode supports two execution modes.
|
Behavior |
Normal mode (default) |
Step-by-step execution mode |
|
How to enable |
Default. No configuration is needed. |
Add |
|
Compilation |
All DML statements are compiled into a single execution plan. |
Each DML statement is compiled into a separate execution plan. Note: Very complex scripts, such as those with thousands of lines or numerous operations, may exceed compilation memory limits. |
|
Execution |
All statements are executed atomically as a single job that starts only after all input data is ready. If any statement fails, the entire script fails and all operations are rolled back. |
DML statements are executed serially. If a statement fails, previously executed statements are not rolled back, and you must retry the script from the beginning. We recommend that you split large scripts into smaller ones. |
|
Read-after-write |
Not supported. An error occurs if you write to and then read from the same table in a script. |
Supported, but not for transactional or partitioned tables. |
|
Temporary table |
Not supported. |
Supported. |
DDL syntax
Create a temporary table
In script mode, you can create a temporary table to cache intermediate results for reuse within the same script. This operation requires a read-after-write pattern and is supported only in step-by-step execution mode.
Syntax
CREATE TEMPORARY TABLE <table_name> (
<col_name> <data_type>, ...
)
[LIFECYCLE <days>]
[AS <select_statement>];
Parameters
|
Parameter |
Description |
|
|
Accessible only within the current script. |
|
|
Optional. The number of days to retain the table before it is automatically deleted. Default: 1. |
|
|
Optional. Populates the table with data from a SELECT statement when created. |
Limitations
-
Temporary tables require the step-by-step execution mode. Add
SET odps.sql.step.script.mode=true;to the SET section of your script. -
A temporary table is accessible only within the script where it was created.
-
Temporary tables cannot be partitioned tables or transactional tables.
-
To explicitly drop a temporary table, the
DROP TABLEstatement must be the last statement in the script. -
Avoid creating more than 20 temporary tables in a single script.
Create a standard table
Script mode supports creating standard tables. For syntax details, see CREATE TABLE.
DML and DQL syntax
Statement limitations
-
Result-displaying statements: A script can contain only one statement that produces screen output, such as a standalone
SELECTstatement. Including more than one causes an error. Avoid using these types of statements in scripts. -
CREATE TABLE AS: You can use this statement only once per script, and it must be the last executable statement. We recommend creating the table first and then inserting data.
-
Mixed write modes: You cannot use both OVERWRITE and INTO on the same table within the same script. DML operations on transactional tables and standard tables cannot be mixed.
-
read-after-write: In normal mode, writing to and then reading from a table in the same script causes an error. To resolve this issue, either use a table variable or switch to step-by-step execution mode. The step-by-step execution mode supports the read-after-write operation, but not for transactional tables or partitioned tables. For a detailed example, see Example 3: Read-after-write in normal mode.
Variables
Syntax
-- Declare a variable by using @. Supported types include TABLE and any MaxCompute data type.
@var1 <type>
-- Assign a value by using :=
@var1 := <select_statement>
Usage notes
-
You cannot assign a table-type variable to a variable with a specified data type. For example, the following syntax is not allowed:
@a TABLE (name STRING); @a := SELECT 'tom'; @b STRING; @b := SELECT * FROM @a; -
Variables can store constant values. You can convert a variable's value to a scalar by using
SELECT * FROM @var. A constant can also be stored in a single-row table, as shown in the following example. For more information about the conversion syntax, see subquery.@a := SELECT 10; -- Assign the constant 10 @b := SELECT key, value + (SELECT * FROM @a) FROM t2; -- Use @a as a scalar SELECT * FROM @b;
IF statements
In script mode, use IF statements to control the execution flow based on conditions.
Syntax
-- Single branch
IF (condition) BEGIN
statements
END
-- Multi-branch
IF (condition) BEGIN
statements
END ELSE IF (condition2) BEGIN
statements
END ELSE BEGIN
statements
END
Usage notes
-
If a branch contains only a single statement, the
BEGINandENDkeywords are optional, similar to{ }in Java. -
DDL statements such as
CREATE TABLE,ALTER TABLE, andTRUNCATE TABLEare not supported within an IF branch. -
The condition supports two types:
-
Boolean expression: The branch is determined at compile time.
-
Boolean scalar subquery: The branch is determined at run time. MaxCompute may submit multiple jobs.
-
Example 1: In an IF statement, the condition is an expression of the BOOLEAN type. This type of IF ELSE statement can determine which branch to execute at compile time, as shown in the following example:
@date := '20190101';
@row TABLE(id STRING);
IF (CAST(@date AS BIGINT) % 2 == 0) BEGIN
@row := SELECT id FROM src1;
END ELSE BEGIN
@row := SELECT id FROM src2;
END
INSERT OVERWRITE TABLE dest SELECT * FROM @row;
Example 2: The condition in the IF statement is a Boolean scalar subquery. For this type of IF ELSE statement, the execution branch is determined at run time, which requires MaxCompute to submit multiple jobs:
@i BIGINT;
@t TABLE(id BIGINT, value BIGINT);
IF ((SELECT COUNT(*) FROM src WHERE a = '5') > 1) BEGIN
@i := 1;
@t := SELECT @i, @i*2;
END ELSE BEGIN
@i := 2;
@t := SELECT @i, @i*2;
END
SELECT id, value FROM @t;
Examples
Example 1: Basic example
The following script joins and unions data from three source tables, and then inserts the results into two destination tables. All statements are compiled into a single DAG and executed atomically.
CREATE TABLE IF NOT EXISTS dest(key STRING, value BIGINT) PARTITIONED BY (d STRING);
CREATE TABLE IF NOT EXISTS dest2(key STRING, value BIGINT) PARTITIONED BY (d STRING);
@a := SELECT * FROM src WHERE value > 0;
@b := SELECT * FROM src2 WHERE key IS NOT NULL;
@c := SELECT * FROM src3 WHERE value IS NOT NULL;
@d := SELECT a.key, b.value FROM @a LEFT OUTER JOIN @b ON a.key = b.key AND b.value > 0;
@e := SELECT a.key, c.value FROM @a INNER JOIN @c ON a.key = c.key;
@f := SELECT * FROM @d UNION SELECT * FROM @e UNION SELECT * FROM @a;
INSERT OVERWRITE TABLE dest PARTITION (d='20171111') SELECT * FROM @f;
@g := SELECT e.key, c.value FROM @e JOIN @c ON e.key = c.key;
INSERT OVERWRITE TABLE dest2 PARTITION (d='20171111') SELECT * FROM @g;
Example 2: Read-after-write in step-by-step mode
The following script enables the step-by-step execution mode to perform a read-after-write operation in the same script.
SET odps.sql.step.script.mode=true;
DROP TABLE IF EXISTS foo_t1;
DROP TABLE IF EXISTS foo_t2;
CREATE TABLE foo_t1(a STRING) LIFECYCLE 1;
CREATE TABLE foo_t2(a STRING) LIFECYCLE 1;
@x := SELECT 'hello, world' AS a;
INSERT OVERWRITE TABLE foo_t1 SELECT * FROM @x;
INSERT INTO foo_t2 SELECT * FROM foo_t1 UNION ALL SELECT * FROM foo_t1;
SELECT * FROM foo_t2;
Example 3: Read-after-write in normal mode
In normal mode, writing to and then reading from the same table in a script causes an error. This example demonstrates the error and two possible solutions.
Data preparation
CREATE TABLE src(key BIGINT, value BIGINT) LIFECYCLE 1;
CREATE TABLE src2(key BIGINT, value BIGINT) LIFECYCLE 1;
INSERT INTO src VALUES(1, 2), (3, 3);
Error example (read-after-write in normal mode)
INSERT OVERWRITE TABLE src2 SELECT * FROM src WHERE key > 0;
@a := SELECT * FROM src2; -- Error: src2 is written to and then read from in the same script.
SELECT * FROM @a;
Solution 1: Enable step-by-step execution mode
SET odps.sql.step.script.mode=true;
INSERT OVERWRITE TABLE src2 SELECT * FROM src WHERE key > 0;
@a := SELECT * FROM src2;
SELECT * FROM @a;
Solution 2: Rewrite the SQL to use a table variable
@a := SELECT * FROM src WHERE key > 0;
INSERT OVERWRITE TABLE src2 SELECT * FROM @a;
SELECT * FROM @a;
Example 4: Create and delete a temporary table
The following script creates a temporary table to cache the intermediate results of a JOIN, reads from it twice, and then drops it.
-- Enable step-by-step execution mode (required for temporary tables).
SET odps.sql.step.script.mode=true;
DROP TABLE IF EXISTS foo_t1;
CREATE TABLE foo_t1(a BIGINT, b BIGINT);
-- Create a temporary table by using a JOIN.
CREATE TEMPORARY TABLE t AS
SELECT t1.a AS a, t2.d AS b FROM
(SELECT 1 a, 2 b) t1
JOIN
(SELECT 1 c, 10 d) t2
ON t1.a = t2.c;
INSERT INTO foo_t1 SELECT * FROM t UNION ALL SELECT * FROM t;
SELECT * FROM foo_t1;
-- Drop the temporary table (must be the last statement in the script).
DROP TABLE t;
Submit scripts
Script mode is supported in MaxCompute Studio, the MaxCompute client (odpscmd), DataWorks, and the Java and Python SDKs.
MaxCompute client (odpscmd)
Use odpscmd v0.27 or later. After you install the MaxCompute client, submit a script using the -s flag:
Create a script file, such as myscript.sql. Then, run the following command from your command-line interface to run odpscmd. For more information about how to run the MaxCompute client from the command line, see Run the MaxCompute client.
..\bin>odpscmd -s myscript.sql
Script mode and table variables are not supported in the odpscmd interactive shell. The -s flag is a command-line option for odpscmd, similar to -f and -e. It is not a command in the interactive environment.
DataWorks
In DataWorks, use an ODPS Script node to write and run scripts. You can then view the execution plan and results by using the Logview URL provided in the output.
In the upper-left corner, click + Create > Create Node. Under the MaxCompute category, select ODPS Script.
After you write the script, click the Run icon on the toolbar to execute it. The output log provides a Logview URL to check the execution plan and final results.
SDKs
You can run a SQL script directly using the Java SDK or Python SDK. For more information, see the Java SDK and Python SDK introductions.
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.aliyun.odps.Instance;
import com.aliyun.odps.Odps;
import com.aliyun.odps.OdpsException;
import com.aliyun.odps.account.Account;
import com.aliyun.odps.account.AliyunAccount;
import com.aliyun.odps.data.Record;
import com.aliyun.odps.task.SQLTask;
public class SdkTest {
public static void main(String[] args) throws OdpsException {
// Your Alibaba Cloud AccessKey pair grants full access to your account and carries high risks.
// We strongly recommend that you create and use a RAM user for API calls and daily operations.
// To create a RAM user, log on to the RAM console.
// This example shows how to use environment variables to store your credentials.
// For security, never hard-code your AccessKey pair into your code.
Account account = new AliyunAccount(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"), System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
Odps odps = new Odps(account);
odps.setDefaultProject("your project_name");
odps.setEndpoint("your end_point");
String sqlScript = "@a := SELECT * FROM jdbc_test;\n"
+ "SELECT * FROM @a;";
// Required: Enable script mode.
Map<String, String> hints = new HashMap<>();
hints.put("odps.sql.submit.mode", "script");
Instance instance = SQLTask.run(odps, "your project_name", sqlScript, hints, null);
instance.waitForSuccess();
List<Record> recordList = SQLTask.getResult(instance);
for (Record record : recordList) {
System.out.println(record.get(0));
System.out.println(record.get(1));
}
}
}import os
from odps import ODPS
# Your Alibaba Cloud AccessKey pair grants full access to your account and carries high risks.
# We strongly recommend that you create and use a RAM user for API calls and daily operations.
# To create a RAM user, log on to the RAM console.
# This example shows how to use environment variables to store your credentials.
# For security, never hard-code your AccessKey pair into your code.
o = ODPS(
os.environ["ALIBABA_CLOUD_ACCESS_KEY_ID"],
os.environ["ALIBABA_CLOUD_ACCESS_KEY_SECRET"],
"your project_name",
"your end_point"
)
sql_script = """
@a := SELECT * FROM jdbc_test;
SELECT * FROM @a;
"""
# Required: Enable script mode.
hints = {"odps.sql.submit.mode", "script"}
instance = o.execute_sql(sql_script, hints=hints)
with instance.open_reader() as reader:
for rec in reader:
print(rec[0], rec[1])MaxCompute Studio
Before you run a script in MaxCompute Studio, complete the following prerequisites:
After you compile and run the script, MaxCompute Studio displays the execution plan. Although the script contains multiple statements, the plan renders as a single DAG.