T-SQL基礎(五)之增刪改
在前面的文章中對T-SQL的查詢做了基本總結,接下來我們看下SQL中的另外一個常用操作——資料的修改。
INSERT
INSERT
向資料表中插入資料的基本語句,句式:
INSERT INTO table_name(field1[,field2]...)
VALUES(value1[,value2]...);
T-SQL對VALUES
進行了增強,可以一條語句插入多條資料:
INSERT INTTO table_name(field1[,field2]...)
VALUES(value1[,value2]...)[,(value3,[value4]...)];
示例如下:
USE WJChi; -- 插入一條資料 INSERT INTO dbo.UserInfo(Id,Name,Age,IdentifyId,UAddressId) VALUES (NEWID(),N'xfh',26,NEWID(),NEWID()); -- 插入多條資料 INSERT INTO dbo.UserInfo(Id,Name,Age,IdentifyId,UAddressId) VALUES (NEWID(),N'xfh',26,NEWID(),NEWID()), (NEWID(),N'雪飛鴻',28,NEWID(),NEWID());
BULK INSERT
該語句用於將檔案中的資料插入到一個現有表中,句式:
BULK INSERT target_table FROM `data_file_path`
WITH
(
DATAFILETYPE = '',
FIELDTERMINATOR = '',
ROWTERMINATOR = ''
)
INSERT...SELECT...
該語句向已存在的目標表中插入有SELECT查詢返回的結果集,句式:
INSERT INTO target_table(field1[,field2]) SELECT column1[,column2] FROM raw_table WHERE...
SELECT...INTO...
SELECT...INTO...
不是標準SQL,該語句會建立新表並將查詢結果集資料插入到表中,句式:
SELECT field1[,field2]
INTO target_table
FROM raw_table;
目標表的結構和資料是基於源表的,但,不會從源表複製:約束、索引、觸發器和許可權。
INSERT...EXEC...
該語句可將儲存過程或動態SQL處理的結果集插入到目標表中,句式:
INSERT INTO target_table(field1[,field2])
EXEC stored_procedure;
UPDATE
UPDATE
UPDATE是標準SQL語句,用於更行表中的行,句式:
UPDATE target_table
SET field1[,field2]
WHERE ...
UPDATE操作不是冪等的,我們可以藉助事務來防止誤操作:
BEGIN TRAN
UPDATE ...
ROLLBACK -- or COMMIT
SQL中有all-at-once operations(同時操作)的概念,即出現在同一邏輯處理階段的所有表示式在同一時間進行邏輯計算。基於同時操作的概念,注意以下語句的運算結果:
交換兩個欄位的值
UPDATE dbo.T1
SET col1 = col2, col2 = col1;
如果欄位col1=100,col2=200
UPDATE dbo.T1
SET col1 = col1 + 10,col2 = col1 + 10;
執行以上語句後,col1和col2的值均為110。
基於聯接的UPDATE
基於聯接的UPDATE操作是非標準的SQL。
UPDATE OD
SET discount += 0.5
FROM dbo.OrderDetails AS OD
JOIN dbo.Orders AS O
ON OD.orderid = O.orderid
WHERE O.custid = 1;
對上面SQL的理解,應按照SQL的執行順序來,即:先FROM,後WHERE,最後再看UPDATE語句。該語句用於更新OD表中存在於查詢結果集(FROM語句)中的資料。
DELETE
T-SQL提供了兩種刪除資料的方式:DELETE
與TRUNCATE
,刪除操作也是非冪等的,與UPDATE一樣,我們可以藉助事務防止誤操作。
DELETE
DELETE FROM target_table WHERE...
基於聯接的DELETE
基於聯接的DELETE操作是非標準的SQL。
示例如下:
DELETE FROM Sales.SalesPersonQuotaHistory
FROM Sales.SalesPersonQuotaHistory AS spqh
INNER JOIN Sales.SalesPerson AS sp
ON spqh.BusinessEntityID = sp.BusinessEntityID
WHERE sp.SalesYTD > 2500000.00;
上述語句換一種寫法,如下:
DELETE spqh
FROM
Sales.SalesPersonQuotaHistory AS spqh
INNER JOIN Sales.SalesPerson AS sp
ON spqh.BusinessEntityID = sp.BusinessEntityID
WHERE sp.SalesYTD > 2500000.00;
上面兩條SQL的理解與UPDATE...FROM...
類似:先看FROM,其次是WHERE,最後是DELETE。該語句用於刪除spqh表中存在於查詢結果集(FROM語句)中的資料。
刪除符合條件的部分資料,如前20行:
DELETE TOP (20)
FROM Purchasing.PurchaseOrderDetail
WHERE DueDate < '20020701';
TRUNCATE
TRUNCATE TABLE target_table;
TRUNCATE
會刪除表中的所有資料並重置表結構,相當於刪掉表然後重建。與DELETE
相比,TRUNCATE
使用最小日誌記錄方式而非完全日誌記錄方式,所以在效能上會有優勢。
但,當目標表被外來鍵約束引用時,即使引用表(父表)為空甚至外來鍵被禁用,都不允許使用TRUNCATE
操作。我們可以通過建立一個虛擬表,帶有指向生產表的外來鍵(甚至可以禁止外來鍵以防影響效能),依此來避免TRUNCATE
誤操作。
MERGE
MERGE是標準的SQL語句,T-SQL對其進行了一定的擴充套件。MERGE語句實現的功能通常可以轉換為幾個其他DML語句(INSERT、UPDATE、DELETE)的組合,使用MERGE語句可以使程式碼更為簡潔。
MERGE target_table
USING raw_table
ON...
WHEN MATCHED THEN
// do something
WHEN NOT MATCHED THEN
// do something
;
注意,必須以分號來結束MERGE語句。示例程式碼如下:
MERGE INTO dbo.Customers AS CT
USING dbo.CustomersStage AS CS
ON CT.Custid = CS.Custid
-- 源表中的資料與目標表相匹配
WHEN MATCHED THEN
UPDATE SET CT.CompanyName = CS.CompanyName
-- 源表中的資料與目標表不匹配
WHEN NOT MATCHED THEN
INSERT(Custid,CompanyName) VALUES(CS.Custid,CS.CompanyName)
-- 目標表中的資料不被源表匹配
WHEN NOT MATCHED BY SOURCE THEN
DELETE;
以上各個分支子句,如:WHEN MATCHED THEN
可以附帶額外的判斷條件,即WHEN MATCHED AND... THEN
。詳細內容,可參考MERGE (Transact-SQL)。
通過表表達式修改資料
對較為複雜的語句進行故障排除時,首先看哪些行會被修改,而不是實際的修改它們。選擇之一是將程式碼修改為SELECT語句,並在排除程式碼故障後,將其改回UPDATE語句。
可以使用表表達式來簡化上述解決問題的思路,示例如下:
WITH Temp AS
(
SELECT custid,OD.orderid,discount,discount+1 AS newDiscount
FROM dbo.OrderDetails AS OD
JOIN dbo.Orders AS O
ON OD.orderid = O.orderid
WHERE O.cusstid = 1
)
UPDATE Temp SET discount = newDiscount;
或者:
UPDATE Temp SET discount = newDiscount FROM
(
SELECT custid,OD.orderid,discount,discount+1 AS newDiscount
FROM dbo.OrderDetails AS OD
JOIN dbo.Orders AS O
ON OD.orderid = O.orderid
WHERE O.cusstid = 1
) AS Temp;
:bulb:表表達式不僅可用於SELECT,也可用於其它DML語句(INSERT、UPDATE、DELETE和MERGE)
TOP & OFFSET-FETCH
與SELECT語句一樣,T-SQL也支援在INSERT、UPDATE、DELETE、MERGE語句中直接使用TOP選項用於修改部分資料。但,與SELECT不同的是,DML中使用TOP時無法使用ORDER BY子句,所以無法保證能按預期修改資料。但可以使用表表達式來避免這個問題:
WITH Temp AS
(
SELECT TOP(50) * FROM dbo.Orders ORDER BY orderid DESC
)
UPDATE Temp SET freight += 10.00;
此外,可以使用OFFSET-FETCH來替代TOP:
WITH Temp AS
(
SELECT * FROM dbo.Orders ORDER BY orderid DESC
OFFSET 0 ROWS FETCH FIRST 50 ROWS ONLY;
)
UPDATE Temp SET freight += 10.00;
OUTPUT
顧名思義,OUTPUT用於輸出,句式:
INSERT[DELETE|UPDATE|MERGE]
OUTPUT
-- 輸出修改前的資料
DELETED
-- 輸出修改後的資料
INSERTED
WHERE...
示例程式碼如下:
USE WJChi;
INSERT INTO dbo.UAddress
(
Id,
ShortAddress,
LongAddress
)
OUTPUT Inserted.Id,Inserted.ShortAddress,Inserted.LongAddress
VALUES
( NEWID(), -- Id - uniqueidentifier
N'臨時地址', -- ShortAddress - nvarchar(50)
N'上海市,臨時地址' -- LongAddress - nvarchar(200)
);
USE WJChi;
DELETE TOP (1) FROM dbo.UserInfo
-- 輸出被刪除的行
OUTPUT DELETED.NAME,DELETED.Age;
USE WJChi;
UPDATE dbo.UserInfo SET Age=30
-- 輸出修改前的行
OUTPUT Deleted.Name AS OLD_AGE,Deleted.Age AS OLD_AGE,
-- 輸出修改後的行
INSERTED.NAME AS NEW_NAME,INSERTED.Age AS NEW_AGE
WHERE Name='雪飛鴻';
小結
增刪改相比於查詢較為簡單,通常語句本身也不會存在效能問題,一般所說的優化多指查詢效能優化。但,我們需要注意增刪改與查詢間排它鎖與共享鎖問題。
SQL Server Management外掛——SQL Prompt在執行DELETE、UPDATE
但沒有帶WHERE
子句的T-SQL時會彈出提示框,可以防止誤刪、誤更新操作。