1. 程式人生 > >T-SQL基礎(五)之增刪改

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提供了兩種刪除資料的方式:DELETETRUNCATE,刪除操作也是非冪等的,與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)
);

insert_output

USE WJChi;

DELETE TOP (1) FROM dbo.UserInfo
-- 輸出被刪除的行
OUTPUT DELETED.NAME,DELETED.Age;

delete_output

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='雪飛鴻';

update_output

小結

增刪改相比於查詢較為簡單,通常語句本身也不會存在效能問題,一般所說的優化多指查詢效能優化。但,我們需要注意增刪改與查詢間排它鎖與共享鎖問題。

SQL Server Management外掛——SQL Prompt在執行DELETE、UPDATE但沒有帶WHERE子句的T-SQL時會彈出提示框,可以防止誤刪、誤更新操作。

推薦閱讀

T-SQL基礎(一)之簡單查詢

SQL Server中鎖與事務隔離級別

SELECT INTO 和 INSERT INTO SELECT 兩種表複製語句