精通 SQL Select Into Insert:一篇搞懂資料表複製、遷移與效能優化

精通 SQL Select Into Insert:一篇搞懂資料表複製、遷移與效能優化

在資料庫管理與開發的日常工作中,我們經常需要複製、備份或遷移資料等相關信息。無論是為了建立一個用於測試的隔離環境、歸檔歷史數據,或是將資料從一個表格轉移到另一個,高效地複製資料都是一項核心技能。SQL 提供了兩種強大的語句來應對此類需求,也就是本文要探討的sql select into insert核心概念:SELECT INTO 和 INSERT INTO SELECT。

雖然這兩者都與資料複製有關,但它們的運作機制、使用情境及在不同資料庫系統(DBMS)中的支援程度卻有著天壤之別。誤用或混淆這兩者,輕則語法錯誤,重則可能導致非預期的資料操作。本文將深入探討這兩種陳述式的核心功能、細節特性、主要差異,並涵蓋它們在 SQL Server、MySQL、PostgreSQL 等主流資料庫中的具體實現與相容性問題,幫助您精準掌握 SQL 資料複製的藝術。

SELECT INTO:快速創建新表並填入資料

SELECT INTO陳述式的核心功能是:執行一個查詢,並將其結果集直接存入一個全新的資料表中。這個操作一步到位,從建立表格結構到插入資料一氣呵成。此sql select into語句非常方便。

核心要點: 目標資料表 (new_table) 在執行此select into from語句前必須不存在。如果該名稱的資料表已存在,系統將會報錯。

語法與參數 (以 Transact-SQL 為主)

SELECT column_name, column2, ...
INTO new_table -- 此處為新的資料表名稱
[ON filegroup]
FROM source_table -- 或是 from table1
WHERE condition;
  • new_table: 指定要建立的新資料表的資料表名稱。新資料表的表結構(名稱、資料類型、可否為空值)將完全由select陳述式的查詢結構推斷而來。
  • ON filegroup (SQL Server 特有): 允許您指定新資料表要建立在哪個檔案羣組上,提供更精細的儲存管理。

重要特性與限制 (SQL Server)

SELECT INTO 雖然方便,但在使用時必須瞭解其背後的特性與限制,這些細節往往決定了它是否適用於您的場景。

結構屬性的繼承

  1. 識別 (IDENTITY) 屬性:只有在特定條件下,來源欄位的 IDENTITY 屬性才會被繼承到新表中。如果 SELECT 語句包含了 JOIN、UNION、欄位被重複選取或包含在運算式中,IDENTITY 屬性將不會轉移,該欄位會變成一個普通的 NOT NULL 欄位。若需強制建立自訂的識別欄位,應在 SELECT 列表中使用 IDENTITY() 函數。
  2. 索引、約束與觸發程序:來源資料表上的主鍵(Primary Key)、外鍵(Foreign Key)、唯一約束(Unique Constraint)、索引(Index)和觸發程序(Trigger)完全不會被複製到新資料表中。您必須在 SELECT INTO 語句執行完畢後,手動為新資料表建立這些物件。
  3. 其他屬性:如 FILESTREAM 或疏鬆欄位(Sparse Column)等特殊屬性也不會被傳遞。

記錄行為與效能

SELECT INTO 的記錄行為與資料庫的復原模式密切相關。在簡單(Simple)或大量記錄(Bulk-Logged)復原模式下,此操作屬於最低限度記錄(Minimally Logged Operation),這代表它產生的交易日誌量非常小,執行效率極高。

相較於先 CREATE TABLE 再 INSERT,使用 SELECT INTO 在這兩種模式下通常會快得多。然而,在完整(Full)復原模式下,它仍然是完整記錄的操作。

資料庫系統的相容性差異

SELECT INTO 的行為在不同版本的資料庫系統中有著巨大的差異,這是開發者最容易混淆的地方。

資料庫系統 SELECT INTO 的行為 建議的替代方案
SQL Server 標準用法:建立一個新資料表並插入資料。 (無須替代,此為標準用法)
MySQL / MariaDB 不支援用於建立資料表。直接使用會導致語法錯誤。 CREATE TABLE new_table AS SELECT …;
PostgreSQL 支援(但具備兩面性):雖然支援此into from語法來建立新表,但這是「歷史用法」。官方更推薦使用 CREATE TABLE AS。因為在 PL/pgSQL(其程序化語言)中,SELECT INTO 用於將查詢結果指派給變數,容易造成混淆。 CREATE TABLE new_table AS SELECT …;
Oracle / IBM Db2 完全不同的意義:在這些系統的程序化語言(如 PL/SQL)中,SELECT INTO 用於將單一資料列的查詢結果存入宣告好的變數中,而不是用來建立資料表。 N/A (功能完全不同)

INSERT INTO SELECT:將資料新增至已存在的表格

相較於 SELECT INTO 的「創造性」,INSERT INTO SELECT語句則扮演著「填充者」的角色。它的核心功能是:執行一個查詢,並將其結果集插入到一個已經存在的資料表中。

核心要點: 目標表 (target_table) 在執行此insert into select from語句前必須已經存在。

語法

-- 插入特定欄位 (例如 insert into table2)
INSERT INTO target_table (column1, column2, ...)
SELECT source_column1, source_column2, ...
FROM source_table
WHERE condition;

-- 插入所有欄位 (前提是來源與目標結構完全匹配)
INSERT INTO target_table
SELECT *
FROM source_table
WHERE condition;

用法與彈性

INSERT INTO SELECT 的標準化程度非常高,幾乎所有主流的 SQL 資料庫都支援此語法,使其成為資料遷移的通用選擇。

  • 欄位對應:SELECT 列表中的欄位數量、順序和資料類型必須與 INSERT INTO 中指定的目標欄位相容。
  • 高度靈活:您可以從多個來源表 JOIN 查詢數據,也可以在 SELECT 列表中包含計算值或常數,然後將它們插入到目標表的對應欄位中。例如,從舊的customers資料表(from table_name_old)中,篩選出特定country的客戶信息,插入到新表中。
  • 保留目標表結構:此操作只會新增資料列,不會對目標表的既有結構(如索引、約束、觸發程序)產生任何改變。當然,插入的表資料仍需滿足目標表上所有約束(如主鍵唯一性、NOT NULL 限制等)。

實用技巧與語法比較

如何只複製表結構,不含資料?

這是一個常見需求,特別是在建立測試環境時。

SQL Server / PostgreSQL 方法:

sql — 使用一個永遠為假的條件 SELECT * INTO new_table FROM old_table WHERE 1 = 0;

MySQL / MariaDB 方法:

sql
— 方法一:使用 LIKE 關鍵字,最為直接
CREATE TABLE new_table LIKE old_table; — 方法二:同樣使用永遠為假的條件
CREATE TABLE new_table AS SELECT * FROM old_table WHERE 1 = 0;

如何複製表結構與表資料?

若目標是複製整個表的表結構與表資料,且是在 MySQL 或 PostgreSQL 環境,可使用 CREATE TABLE AS。若目標是將資料插入已存在的表,則可使用 insert into table_name_new select * from table_name_old;。

SELECT INTO vs. INSERT INTO SELECT 快速比較

特性 SELECT INTO INSERT INTO SELECT
主要目的 根據查詢結果建立新表並填入資料 將查詢結果插入已存在的表格
目標表格要求 目標表必須不存在 目標表必須已存在
結構轉移 不轉移索引、約束、觸發程序等 不影響目標表的既有結構
主要支援系統 SQL Server (標準用法) SQL Server, MySQL, PostgreSQL, Oracle 等 (通用)
效能考量 在特定模式下(SQL Server)為最低限度記錄,非常高效 一般為完整記錄的DML操作
標準化程度 低,各系統差異大,易混淆 高,是 SQL 標準的一部分

常見問題 (FAQ)

Q1: SELECT INTO 和 INSERT INTO SELECT 最大的不同是什麼?

A: 最根本的區別在於對目標表的要求:SELECT INTO 用於建立一個新的資料表,因此目標表必須不存在;而 INSERT INTO SELECT 用於向一個已存在的資料表新增資料。

Q2: 為什麼我的 SELECT INTO 語句在 MySQL 中會執行失敗?

A: 因為 MySQL 不支援使用 SELECT INTO 來建立資料表。在 MySQL 中,您應該使用 CREATE TABLE new_table AS SELECT …; 語句來達成相同的目的。

Q3: 如何只複製一個資料表的表結構,而不要裡面的資料?

A: 您可以使用幾種方法:在 MySQL 中,最推薦的是 CREATE TABLE newtable LIKE oldtable;。在 SQL Server 或 PostgreSQL 中,您可以使用 SELECT * INTO newtable FROM oldtable WHERE 1=0; 的技巧。

Q4: 使用 SELECT INTO 建立新資料表時,主鍵 (Primary Key) 和索引 (Index) 會一起複製過去嗎?

A: 不會。SELECT INTO 只會建立基本的資料表結構與資料,來源資料表上的所有索引、主鍵、外鍵、約束和觸發程序都不會被複製。您必須在資料表建立後手動添加它們。

Q5: SELECT INTO 在不同資料庫中的意思真的完全不同嗎?

A: 是的。這個語句的多義性是常見的混淆來源。在 SQL Server 中,它用於建立資料表。但在 Oracle、IBM Db2 以及 PostgreSQL 的 PL/pgSQL 程序化語言中,它的作用是將一個sql select查詢的單行結果指派給程式中的變數,與建立資料表無關。

總結

SELECT INTO 和 INSERT INTO SELECT 是 SQL 資料庫中功能強大但定位清晰的工具。簡單來說,它們的抉擇點在於「目標資料表是否存在」:

  • 當您需要根據查詢結果快速建立一個全新的、無索引約束的資料表時,SELECT INTO (在 SQL Server 中) 是最簡潔高效的選擇。但在其他資料庫中,應使用 CREATE TABLE AS SELECT 來達到相同目的。
  • 當您需要將資料新增到一個已經定義好的、結構完整的現有資料表時,INSERT INTO SELECT 是您不二的、跨平臺的標準選擇。

理解它們的核心差異與各資料庫的相容性,是避免語法錯誤和確保程式碼可移植性的關鍵。在撰寫跨資料庫的應用程式時,優先選用標準化程度更高的 INSERT INTO SELECT 和 CREATE TABLE AS 會是更穩健的策略。

資料來源

返回頂端