SQL INSERT INTO SELECT combines the power of INSERT and SELECT to move or copy data in bulk between tables or across databases. Instead of supplying literal VALUES, the statement takes the result set produced by a SELECT query and appends it to a target table. The column order or explicit column list must align between the target table and the SELECT output, both in number and compatible data types. Because the SELECT can include joins, aggregations, filters, or even subqueries, this construct is ideal for transforming data while loading it. Most engines treat the entire operation as a single transaction, rolling back on error unless autocommit is on. Advanced dialects add clauses like ON CONFLICT, IGNORE, or RETURNING, but the core behavior remains consistent: evaluate the SELECT, then insert its rows. Be mindful of constraints (primary keys, foreign keys, uniques) that may reject rows, and remember that triggers or default values on the target table still fire for each inserted row.
target_table
(identifier) - Table that will receive the new rows.column_list
(list) - Optional ordered list of columns in the target table that will be populated.select_query
(query) - Any valid SELECT statement whose result set matches the column list or table structure.INSERT VALUES, SELECT, CREATE TABLE AS SELECT (CTAS), MERGE, UPSERT, COPY, ON CONFLICT
SQL-92 Standard
INSERT VALUES inserts explicit literal rows one at a time. INSERT SELECT pulls rows from a query, making it suitable for large or dynamic datasets.
Only if the SELECT output columns are not in the same order or you want to omit certain target columns that have defaults. Providing an explicit list is safer.
Disable indexes or constraints temporarily, batch the inserts inside transactions, or use engine-specific bulk-loading features such as PostgreSQL's COPY or SQL Server's TABLOCK hint.
The statement completes successfully and inserts nothing. The row count returned is 0.