ApsaraDB for SelectDB supports standard SQL syntax, including INSERT INTO statements for importing data into SelectDB tables. Use INSERT INTO...SELECT to run ETL on internal tables or sync data from external data lakes, and use INSERT INTO...VALUES for testing and validation only.
When to use INSERT INTO
INSERT INTO comes in two variants. Choose based on your scenario:
| Variant | Use when | Avoid when |
|---|---|---|
INSERT INTO...SELECT | Running ETL on internal tables, syncing from external data lakes via catalog | — |
INSERT INTO...VALUES | Testing and validation | Production environments or large data volumes |
INSERT INTO...VALUES has low write throughput. For production workloads with small but frequent writes, use Stream Load instead, which delivers significantly higher write performance.
How it works
Both variants are synchronous — the statement returns only after the import completes. Each import creates a transaction with a unique label. The result includes the label, transaction ID, and data visibility status.
Prerequisites
Before you begin, make sure you have:
An ApsaraDB for SelectDB instance
A destination table in SelectDB
Write permissions on the destination table
INSERT INTO...SELECT statement
Use this variant to run extract, transform, and load (ETL) operations on data already in SelectDB, or to sync data from external sources via a catalog.
Run ETL on an internal table
To transform data from one SelectDB table and write the results to another:
INSERT INTO bj_store_sales
SELECT id, total, user_id, sale_timestamp FROM store_sales WHERE region = "bj";This reads rows from store_sales where region = "bj" and writes them to bj_store_sales.
Sync data from a data lake
SelectDB catalogs let you map external data sources — including Hive, Iceberg, Hudi, Elasticsearch, and Java Database Connectivity (JDBC) sources — and query them with federated queries. Use a catalog to sync data from a data lake into a SelectDB table.
The following example syncs data from a Hive source into SelectDB.
Connect to your SelectDB instance. For details, see Connect to an ApsaraDB for SelectDB instance by using a MySQL client.
Create a catalog to integrate the Hive data source. For details, see Hive data source.
(Optional) Create a destination database. Skip this step if the database already exists.
CREATE DATABASE hive_db;Switch to the destination database.
USE hive_db;Create a destination table. If the table already exists, verify that its column types match the source table. For the type mapping reference, see Column data type mappings.
CREATE TABLE test_Hive2SelectDB ( id int, name varchar(50), age int ) DISTRIBUTED BY HASH(id) BUCKETS 4 PROPERTIES("replication_num" = "1");(Optional) Preview the table before importing.
SELECT * FROM test_Hive2SelectDB;
Run the INSERT INTO...SELECT statement to sync data. Assign a unique label to the import job with
WITH LABEL.INSERT INTO test_Hive2SelectDB WITH LABEL test_label SELECT * FROM hive_catalog.testdb.hive_t;Query the destination table to verify the data. The destination table data is shown on the left and the source data on the right.

INSERT INTO...VALUES statement
Use this variant only for testing and validation, not in production. Send insert requests via a SQL client or a JDBC application.
First, create a destination table:
CREATE TABLE test_table
(
id int,
name varchar(50),
age int
)
DISTRIBUTED BY HASH(id) BUCKETS 4
PROPERTIES("replication_num" = "1");Using a SQL client
Wrap multiple INSERT INTO statements in a transaction to batch them into a single import:
BEGIN;
INSERT INTO test_table VALUES (1, 'Zhang San', 32),(2, 'Li Si', 45),(3, 'Zhao Liu', 23);
INSERT INTO test_table VALUES (4, 'Wang Yi', 32),(5, 'Zhao Er', 45),(6, 'Li Er', 23);
INSERT INTO test_table VALUES (7, 'Li Yi', 32),(8, 'Wang San', 45),(9, 'Zhao Si', 23);
COMMIT;Using a JDBC application
The following example batches multiple INSERT INTO statements in a single transaction using JDBC. Replace the placeholder values with your own.
public static void main(String[] args) throws Exception {
// Number of INSERT statements per batch
int insertNum = 10;
// Number of rows per INSERT statement
int batchSize = 10000;
// Replace <host> and <port> with your VPC (virtual private cloud) endpoint values.
// Find these on the Instance Details page under Network Information.
String URL = "jdbc:mysql://<host>:<port>/test_db?useLocalSessionState=true";
Connection connection = DriverManager.getConnection(URL, "admin", "<password>");
Statement statement = connection.createStatement();
statement.execute("BEGIN;");
for (int num = 0; num < insertNum; num++) {
StringBuilder sql = new StringBuilder();
sql.append("INSERT INTO test_table VALUES ");
for (int i = 0; i < batchSize; i++) {
if (i > 0) {
sql.append(",");
}
// Replace with your actual field values
sql.append("(1, 'Zhang San', 32)");
}
statement.addBatch(sql.toString());
}
statement.addBatch("COMMIT;");
statement.executeBatch();
statement.close();
connection.close();
}Understand the import result
INSERT INTO is synchronous — check the return value to determine the outcome.
Successful import with no rows
If the SELECT clause returns no rows, SelectDB returns:
INSERT INTO tbl1 SELECT * FROM empty_tbl;
Query OK, 0 rows affected (0.02 sec)Query OK means the statement ran without error. 0 rows affected means no data was imported.
Successful import with rows
INSERT INTO tbl1 SELECT * FROM tbl2;
Query OK, 4 rows affected (0.38 sec)
{'label':'insert_8510c568-9eda-****-9e36-6adc7d35291c', 'status':'visible', 'txnId':'4005'}The JSON response includes:
| Field | Description |
|---|---|
label | The import job identifier — either the value you specified with WITH LABEL or an auto-generated one. Unique within a database. |
status | Data visibility. visible means the data is queryable. committed means the data is written but not yet visible. |
txnId | The transaction ID for this import. |
err | Any unexpected errors. |
If status is committed, the data will eventually become visible. To check:
SHOW TRANSACTION WHERE id=4005;If TransactionStatus shows visible, the data is queryable.
Successful import with filtered rows
If some rows were filtered out, the result shows a warning count:
Query OK, 2 rows affected, 2 warnings (0.31 sec)
{'label':'insert_f0747f0e-7a35-****-affa-13a235f4020d', 'status':'visible', 'txnId':'4005'}To inspect filtered rows, find the label in the SHOW LOAD output:
SHOW LOAD WHERE label="insert_f0747f0e-7a35-****-affa-13a235f4020d";Then query the error details with the URL from the output:
SHOW LOAD WARNINGS ON "<error-url>";Failed import
If the import fails, no data is written and SelectDB returns an error:
INSERT INTO tbl1 SELECT * FROM tbl2 WHERE k1 = "a";
ERROR 1064 (HY000): all partitions have no load data. url: http://10.74.167.16:8042/api/_load_error_log?file=__shard_2/error_log_insert_stmt_ba8bb9e158e4879-ae8de8507c0bf8a2_ba8bb9e158e4879_ae8de8507c0bf8a2Retrieve detailed error information using the URL in the error message:
SHOW LOAD WARNINGS ON "<error-url>";Configuration reference
Session variables
| Variable | Default | Description |
|---|---|---|
query_timeout | 300s (5 min) | Timeout for the INSERT INTO operation. If the import does not finish within this period, SelectDB cancels it. |
enable_insert_strict | true | When true, the import fails if any rows are filtered out. When false, filtered rows are silently ignored. |
enable_unique_key_partial_update | false | When true, enables partial column updates on Unique Key model tables using Merge on Write (MOW). |
Partial column updates
By default, INSERT INTO writes full rows. To update only specific columns on a Unique Key model table that uses Merge on Write (MOW):
SET enable_unique_key_partial_update = true;This variable only applies to tables using the Unique Key model with Merge on Write (MOW) mode.
If both
enable_unique_key_partial_updateandenable_insert_strictaretrue, INSERT INTO can only update existing rows. If a key does not exist in the table, an error is returned.To both update existing columns and insert new rows, set
enable_unique_key_partial_update = trueandenable_insert_strict = false. For details, see Configure variables.
For a full list of variables, see Variable management.
Best practices
Avoid high-frequency small writes. Frequent small inserts degrade performance and can cause deadlocks on tables. Keep write frequency on a single table above 10 seconds per write, and batch multiple rows into a single INSERT INTO statement.
Batch size for INSERT INTO...VALUES. Batch between 1,000 and 1,000,000 rows per statement for optimal performance.
Use Stream Load for production ingestion. For production environments and large data volumes, use Stream Load rather than INSERT INTO...VALUES.
Assign labels for traceability. Use WITH LABEL to assign meaningful labels to import jobs. This makes it easier to query job status and debug errors. If you want to use common table expressions (CTEs) to define subqueries in an INSERT INTO statement, you must specify WITH LABEL and column.
Filtering threshold. INSERT INTO does not support the max_filter_ratio parameter. By default, all error rows are ignored (equivalent to max_filter_ratio = 1). To enforce zero tolerance for data errors, set enable_insert_strict = true.
FAQ
Why does the `get table cloud commit lock timeout` error appear during import?
This happens when writes to the same table are too frequent, causing lock contention. Reduce write frequency so that each table receives writes no more than once every 5 seconds, and consolidate multiple small inserts into fewer, larger batches.
What's next
Stream Load — high-throughput ingestion for production use cases
Data lakehouse — integrate external data sources with SelectDB
Variable management — configure session variables for INSERT INTO behavior