在使用INSERT語句的時候,一般都是使用它向數據庫中一條條的插入數據,比如:
INSERT INTO MyTable(FId,FName,FAge)VALUES(1,"John",20)
但是有時我們可能需要將數據批量插入表中,比如創建一個和T_ReaderFavorite表結構完全相同的表T_ReaderFavorite2,然后將T_ReaderFavorite 中的輸入復制插入到T_ReaderFavorite2表。
首先我們創建T_ReaderFavorite2 表:
MYSQL,MSSQLServer,DB2:
CREATE TABLE T_ReaderFavorite2 (FCategoryId INT,FReaderId INT) Oracle: CREATE TABLE T_ReaderFavorite2 (FCategoryId NUMBER (10),FReaderId NUMBER (10))
按照普通的實現思路,我們需要編寫如下的宿主語言代碼:
rs = ExecuteQuery("SELECT * from T_ReaderFavorite"); while(rs.next()) { int categoryId = rs.get("FCategoryId"); int readerId = rs.get("FReaderId"); Execute("INSERT INTo T_ReaderFavorite2(FCategoryId,FReaderId) VALUES(?,?)", categoryId, readerId); }
在宿主語言中逐條讀取T_ReaderFavorite表中的記錄,然后將它們逐條插入T_ReaderFavorite2表中。這樣的處理方式能夠正確的完成要求的功能,而且由於目前T_ReaderFavorite 表中的數據量非常少,所以這樣處理速度上也沒有什么影響。但是如果T_ReaderFavorite 表中有大量的數據的話,由於每插入一條數據都要執行兩次數據庫操作,所以這種處理方式效率非常低,因此必須采用其他的處理方式。
除了INSERT……VALUES……這種用法外,INSERT語句還支持另外一種語法,那就是INSERT……SELECT……,采用這種使用方式可以將SELECT語句返回的結果集直接插入到目標表中,因為這一切都是都數據庫內部完成的,所以效率非常高。下面看一下使用INSERT……SELECT……來實現將T_ReaderFavorite中的輸入復制插入到T_ReaderFavorite2表,SQL語句如下:
INSERT INTO T_ReaderFavorite2(FCategoryId,FReaderId) SELECT FCategoryId,FReaderId FROM T_ReaderFavorite
這里使用SELECT FCategoryId,FReaderId FROM T_ReaderFavorite將T_ReaderFavorite表中的數據讀出,然后使用INSERT INTO T_ReaderFavorite2(FCategoryId,FReaderId)將檢索結果插入到T_ReaderFavorite2 表中,注意上下的列順序必須是一一對應的。
執行完畢我們后我們查看T_ReaderFavorite2 表中的內容:
FCategoryId FReaderId
1 1
5 2
2 3
3 4
5 5
1 6
1 7
4 8
6 9
5 10
2 11
2 12
1 12
3 1
1 3
4 4
可以看到T_ReaderFavorite表中的數據已經被正確的復制到T_ReaderFavorite2 表中了。
使用INSERT……SELECT……不僅能夠實現簡單的將一個表中的數據導出到另外一個表中的功能,還能在將輸入插入目標表之前對數據進行處理,比如下面的SQL 語句用於將T_ReaderFavorite表中的數據復制到T_ReaderFavorite2 表中,但是如果T_ReaderFavorite表中的FReaderId 列的值大於10,則將FReaderId 的值減去FCategoryId 的值后再復制到T_ReaderFavorite2 表中。編寫如下的SQL語句:
INSERT INTO T_ReaderFavorite2(FCategoryId,FReaderId) SELECT FCategoryId,(CASE WHEN FReaderId<=10 THEN FReaderId ELSE FReaderId- FCategoryId END) FROM T_ReaderFavorite
這里在SELECT 語句中使用CASE 函數來實現對數據插入前的處理。執行完畢我們后我們查看T_ReaderFavorite2 表中的內容:
FCategoryId FReaderId
1 1
5 2
2 3
3 4
5 5
1 6
1 7
4 8
6 9
5 10
2 9
2 10
1 11
3 1
1 3
4 4
使用這種插入前的數據處理可以完成諸如“將數據從A表導出到B表,並且將B表的主鍵全部加上bak前綴”、“將A公司的所有員工插入到我們的會員表,自動導入所有的客戶信息,並且為其自動生成會員編號”等復雜的任務。
因為可以在插入目標表前可以對數據進行處理,所以INSERT……SELECT……語句不局限於同結構表間的數據插入,也可以實現異構表見輸入的插入。假設要將所有會員愛好的圖書統一增加“小說”,也就是為T_Reader 表中的每個讀者都在T_ReaderFavorite表中創建一條FCategoryId等於1 的記錄,實現SQL語句如下:
INSERT INTO T_ReaderFavorite(FCategoryId,FReaderId) SELECT 1,FId FROM T_Reader
SELECT語句從T_Reader表中檢索所有的讀者信息,並且將第一列設定為固定值1,而將第二列設定為讀者的主鍵,執行完畢查看T_ReaderFavorite表中的內容:
FCategoryId FReaderId
1 1
5 2
2 3
3 4
5 5
1 6
1 7
4 8
6 9
5 10
2 11
2 12
1 12
3 1
1 3
4 4
1 1
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
1 11
1 12
數據被正確的插入了,但是仔細查看T_ReaderFavorite表中的內容,不難發現其中有重復的數據,比如FCategoryId等於1、FReaderId等於1的記錄出現了兩次,這就出現了數據冗余,必須修改SQL語句防止這種情況的發生。也就是在將輸入插入T_ReaderFavorite表之前要檢查T_ReaderFavorite表中是否已經存在了同樣的數據。
在繼續進行之前請首先清空T_ReaderFavorite 表中的數據,然后執行開頭的T_ReaderFavorite表數據的初始化代碼。
首先需要在SELECT語句中添加WHERE子句,這個WHERE子句檢查每個讀者是否有Story的愛好,即是否存在圖書分類主鍵為1的的愛好,只有沒有的時候才返回記錄:
SELECT 1,FId FROM T_Reader WHERE NOT EXISTS ( SELECT * FROM T_ReaderFavorite WHERE T_ReaderFavorite. FCategoryId=1 AND T_ReaderFavorite. FReaderId= T_Reader.FId )
執行這個子查詢片段,執行完畢我們就能在輸出結果中看到下面的執行結果:
FId
1 2
1 4
1 5
1 8
1 9
1 10
1 11
可以看到檢索出的確實是還沒有以Story做為愛好的讀者,所以編寫下面的SQL語句來執行數據的插入:
INSERT INTO T_ReaderFavorite(FCategoryId,FReaderId) SELECT 1,FId FROM T_Reader WHERE NOT EXISTS ( SELECT * FROM T_ReaderFavorite WHERE T_ReaderFavorite. FCategoryId=1 AND T_ReaderFavorite. FReaderId= T_Reader.FId )
執行完畢查看T_ReaderFavorite表中的內容:
FCategoryId FReaderId
1 1
5 2
2 3
3 4
5 5
1 6
1 7
4 8
6 9
5 10
2 11
2 12
1 12
3 1
1 3
4 4
1 2
1 4
1 5
1 8
1 9
1 10
1 11
可以看到表中的數據沒有重復的了,即使重復執行這個SQL 語句也都不會添加新的記錄,因為這個SQL語句已經對數據的重復做了檢查。