1. 程式人生 > >SQL Server溫故系列(1):SQL 資料操作 CRUD 之增刪改合

SQL Server溫故系列(1):SQL 資料操作 CRUD 之增刪改合

  • 1、插入語句 INSERT INTO
    • 1.1、用 INSERT 插入單行資料
    • 1.2、用 INSERT 插入多行資料
    • 1.3、用 INSERT 插入子查詢結果行
    • 1.4、INSERT 小結及特殊欄位插入方法
  • 2、刪除語句 DELETE
    • 2.1、用 DELETE 刪除表中指定行
    • 2.2、用 TRUNCATE TABLE 高效清空表
  • 3、更新語句 UPDATE
  • 4、合併語句 MERGE
  • 5、用 TOP 子句限制受影響的行
  • 6、用 OUTPUT 子句返回受影響的資料
  • 7、本文小結

毋庸置疑,開發者最常用的資料庫技術就是 SQL 了,即便是 ORM 大行其道的今天也常常需要寫 SQL 語句。而 SQL 語句中最常用的就是增刪改查了,本系列就先對增刪改查語句來個系統的回顧吧!

1、插入語句 INSERT INTO

1.1、用 INSERT 插入單行資料

INSERT INTO 的作用是向表中新增新行,語法如下:

INSERT INTO table-name(column1,column2,...column-n) VALUES(value1,value2,...value-n);

譬如要向好學生表中新增 1 條資料,示例如下:

INSERT INTO T_GoodStudents(Name,Birthday) VALUES('李爾','1990-01-09'); -- 顯示指定要插入欄位

如果按表中的欄位順序給出全部欄位的值,那麼就不用顯示指定欄位了,示例如下:

INSERT INTO T_GoodStudents VALUES(1,'邱晨',1,'1990-09-01');

1.2、用 INSERT 插入多行資料

INSERT INTO 還可以一次向表中新增多條資料,如要一次性向學生表中新增 3 條資料,示例如下:

INSERT INTO T_GoodStudents(Name,Gender,Birthday) 
VALUES('張三',1,'1993-03-03'),('李四',1,'1994-04-04'),('王五',1,'1995-05-05');

注意:在插入全部欄位時,插入多行資料也可以像插入單行資料那樣省略欄位列表,但必須確保各行之間的資料個數相同、型別相容。

1.3、用 INSERT 插入子查詢結果行

向表中插入資料時,既可以通過 VALUES 子句顯示地列出插入值,也可以通過 SELECT 子句來獲得插入值。語法如下:

INSERT INTO target-table-name SELECT columns FROM source-table-name;

該語句的效果類似於把一張表的資料複製到另一張表,要複製的欄位和行都可以顯示的指定。當要將大量行從源表傳輸到目標表中時,該語句還能夠以最小日誌記錄的方式高效的完成。示例如下:

INSERT INTO T_GoodStudents SELECT Id,Name,Gender,Birthday FROM T_Students;   -- 完全複製(資料)
INSERT INTO T_GoodStudents(Name,Gender) SELECT Name,Gender FROM T_Students;  -- 指定部分欄位複製
INSERT INTO T_GoodStudents(Name) SELECT Name FROM T_Students WHERE Gender=1; -- 指定部分資料複製

如果目標表和源表的表結構相同,子查詢的欄位列表還可以用 * 來代替。在指定欄位複製時,無需表結構相同,只要對應欄位的資料型別相容即可,甚至還可以沒有源表,一個子查詢就夠了。示例如下:

INSERT INTO T_GoodStudents SELECT 999,'李敏',0,'1991-02-02'; -- 插入 1 條(來自子查詢的)資料

INSERT INTO T_GoodStudents(Id,Name,Birthday)
SELECT 11,'王陽','1991-03-02' UNION ALL
SELECT 12,'李玉','1991-07-02' UNION ALL
SELECT 13,'鄭爽','1991-02-02';                               -- 插入 3 條(來自子查詢的)資料

1.4、INSERT 小結及特殊欄位插入方法

在使用 INSERT INTO 語句向表中插入新行時,除了帶預設值和帶標識的欄位,其它必填的欄位都需要顯示的給出值,而非必填欄位不給值時 SQL Server 預設會給它一個 NULL 值,也可以顯示的給定一個 NULL 值。

1.4.1、將資料插入有預設值的欄位中 時,如果沒有為指定了預設值的欄位指定值,那麼新行的該欄位的值將會是預設值。假如要新增一行,有預設值的欄位就讓它為預設值,沒有預設值的欄位就讓它為 NULL,那麼就可以用如下語句:

INSERT INTO T_GoodStudents DEFAULT VALUES;

1.4.2、將資料插入到標識列中 時,無論是指定插入欄位還是不指定插入欄位,都無需考慮標識列,因為 SQL Server 的關係引擎會根據標識增量和標識種子自動為標識列賦值。如果需要為標識列指定值,就需要先把 IDENTITY_INSERT 開啟,然後才能插入,示例如下:

SET IDENTITY_INSERT T_Students ON;                  -- 當前會話有效,別的會話不受影響
INSERT INTO T_Students(Id,Name) VALUES(-1,'李哈哈'); -- Id 欄位為標識列

注意1:必須在 INTO 子句中顯示列出標識列,否則即便在 VALUES 子句中提供所有欄位的值也還是會報錯。

注意2:如果想在當前會話中繼續像預設情況那樣忽略標識列,就需要把 IDENTITY_INSERT 關掉,示例如下:

SET IDENTITY_INSERT T_Students OFF;

2、刪除語句 DELETE

2.1、用 DELETE 刪除表中指定行

DELETE 語句用於從表中刪除現有行,語法如下:

DELETE FROM table-name WHERE delete-conditions;

WHERE 子句的作用在於確定刪除哪些行,示例如下:

DELETE FROM T_GoodStudents WHERE Id >= 20;                             -- 刪除 Id 大於等於 20 的資料
DELETE FROM T_GoodStudents WHERE Id NOT IN(SELECT Id FROM T_Students); -- 刪除 Id 不在學生表中的資料

注意:在 PL/SQL 中可以方便的給要刪資料的表取個別名,以便限定 WHERE 子句中的欄位,但在 T-SQL 中卻不能直接給 DELETE 語句中要刪資料的表取別名。如果想要限定刪除條件中的欄位,可以用如下兩種寫法:

DELETE FROM T_Students WHERE T_Students.Id = 4;     -- 直接用表名來限定(條件欄位少時比較方便)
DELETE T_Students FROM T_Students t WHERE t.Id = 5; -- 在 DELETE 子句中加上表名(條件欄位多時更方便)

理論上 DELETE 語句是可以不帶 WHERE 子句的,但這個操作很危險,因為它意味著刪除表中所有行。

2.2、用 TRUNCATE TABLE 高效清空表

TRUNCATE TABLE 用於刪除表中的所有行,如果表中有標識列,標識列會重新開始計數,相當於清空了整個表。語法如下:

TRUNCATE TABLE table-name;

如要清空好學生表,示例如下:

TRUNCATE TABLE T_GoodStudents;

注意:儘管不帶 WHERE 條件的 DELETE 語句就可以刪除表中所有資料,但 TRUNCATE TABLE 比 DELETE 的速度更快,使用的系統資源和事務日誌資源也更少。

3、更新語句 UPDATE

UPDATE 語句用於更新指定表中的現有資料,語法如下:

UPDATE table-name 
SET column1 = value1,column2 = value2,...column-n = value-n 
WHERE update-conditions;

WHERE 子句用於限定哪些行需要被更新,如果不帶 WHERE 子句就會更新所有行,當然這很危險,一般也沒有這種需求。可以一次更新一個欄位,也可以一次更新多個欄位,欄位的值可以顯示給出,也可以是個表示式,表示式中還可以引用表中的欄位。示例如下:

UPDATE T_GoodStudents SET Name = '王娜' WHERE Id = 7;            -- 更新一個欄位的值
UPDATE T_GoodStudents SET Name = '徐莉',Gender = 0 WHERE Id = 7; -- 更新多個欄位的值
UPDATE T_GoodStudents SET Birthday = GETDATE()-10 WHERE Id = 7;  -- 用表示式給欄位賦值
UPDATE T_GoodStudents SET Birthday = Birthday-10 WHERE Id = 7;   -- 在表示式中引用欄位
UPDATE T_GoodStudents SET Name += '學生' WHERE Id > 3;           -- 在姓名後面加上"學生"

3.1、SET 子句內包含子查詢時,示例如下(把班級名更新到學生備註中):

UPDATE T_Students SET Remark = (SELECT t.Name FROM T_Classes t WHERE t.Id = ClassId);

注意1:上例中沒有 WHERE 子句,這意味著(不論學生表中的 ClassId 是否在班級表中出現過)都會更新整個學生表,ClassId 未在班級表中出現過的學生備註會被更新為 NULL。儘管看似簡單,但筆者就曾在職場中多次遇到工作數年的技術人員因忽略這點而誤改了資料。

注意2,如果恰好兩個表中的關聯欄位名相同,大概率上會出問題或報錯,為了穩妥起見需要限定一下欄位。在 Oracle 中可以方便的通過表別名來限定,然而 SQL Server 卻不支援給 UPDATE 語句的 UPDATE 子句中的表取別名,但可以直接通過表名來限定欄位。示例如下:

UPDATE T_GoodStudents 
SET Name = (SELECT t.Name FROM T_Students t WHERE t.Id = T_GoodStudents.Id) 
WHERE T_GoodStudents.Id IN(SELECT Id FROM T_Students); -- 將學生表的姓名同步到好學生表

3.2、WHERE 子句內含子查詢時,示例如下(將單科考試 3 次不及格的寫入到學生備註中):

UPDATE T_Students SET Remark = '單科3次不及格' 
WHERE Id IN(
    SELECT t.StudentId 
    FROM T_ExamResults t 
    WHERE t.Scores < 60 
    GROUP BY t.StudentId,t.CourseId HAVING COUNT(1) >= 3
);

3.3、帶 FROM 子句的 UPDATE 語句,示例如下(把所有學生最近一次考試的總成績更新到學生備註中):

UPDATE T_Students SET Remark = t2.SumScore
FROM T_Students t1 
JOIN(
    SELECT t.StudentId,SUM(t.Scores) SumScore 
    FROM T_ExamResults t 
    WHERE t.Counts = (SELECT MAX(Counts) FROM T_ExamResults) 
    GROUP BY t.StudentId
) t2 
ON t1.Id=t2.StudentId;

如果只需要更新部分學生,比如僅更新 1 班的學生,就可以在 ON 後面直接加AND t1.ClassId=1,或者在整個語句後面加WHERE t1.ClassId=1。有意思的是,這種 UPDATE 語句即便沒有 WHERE 條件,也不會對未在 FROM 子句中限定的行產生影響。

4、合併語句 MERGE

相比較 INSERT、DELETE、UPDATE 和 SELECT 來說,MERGE 出現的要晚一些,但也有十多年了,各大 SQL 資料庫在 21 世紀頭幾年陸續提供了對 MERGE 的支援。簡單來說,MERGE 語句就是對增刪改查的“合併”,使得可以在一個語句內根據查詢的匹配情況來決定是否要增、刪或改某些資料,而不必再寫冗長的邏輯判斷和事物處理了。語法如下:

MERGE target-table-name
USING source-table-expressions ON merge-search-conditions
WHEN MATCHED AND clause-search-conditions THEN merge-matched
WHEN NOT MATCHED AND clause-search-conditions THEN merge-not-matched;

使用 MERGE 在單個語句中對錶執行 INSERT 或 UPDATE 操作,示例如下:

MERGE T_Students AS target 
USING(SELECT '朱丹丹',0) AS source (Name,Gender) ON(target.Name = source.Name)
WHEN MATCHED THEN 
    UPDATE SET Gender = source.Gender 
WHEN NOT MATCHED THEN 
    INSERT(Name,Gender) VALUES(source.Name,source.Gender);

使用 MERGE 在單個語句中對錶執行 INSERT、DELETE 或 UPDATE 操作,示例如下:

MERGE T_Students AS target 
USING(SELECT '劉天寶',1,'1990-09-09') AS source (Name,Gender,Birthday) 
ON(target.Name = source.Name)
WHEN MATCHED AND target.Birthday < source.Birthday THEN 
    DELETE 
WHEN MATCHED THEN 
    UPDATE SET target.Gender = source.Gender,target.Birthday = source.Birthday
WHEN NOT MATCHED THEN
    INSERT(Name,Gender,Birthday) VALUES(source.Name,source.Gender,source.Birthday);

5、用 TOP 引數限制受影響的行

熟悉 SQL Server 的開發者估計都知道 TOP 引數可以用來限制查詢語句的返回行數,但其實 TOP 引數不僅可以限制 SELECT 的結果集,還以限制受 INSERT、DELETE 或 UPDATE 影響的行。

5.1、帶 TOP 引數的 INSERT 語句,示例如下(隨機將 3 個女學生新增到好學生表):

INSERT TOP(3) INTO T_GoodStudents 
SELECT t.Id,t.Name,t.Gender,t.Birthday FROM T_Students t WHERE t.Gender = 0;

如果想要按某種特定的順序插入資料,譬如要把年齡最大的 3 個學生新增到好學生表,示例如下:

INSERT INTO T_GoodStudents 
SELECT TOP(3) t.Id,t.Name,t.Gender,t.Birthday FROM T_Students t ORDER BY t.Birthday;

5.2、帶 TOP 引數的 DELETE 語句,示例如下(隨機刪除 3 個女學生):

DELETE TOP(3) FROM T_GoodStudents WHERE Gender = 0;

如果想要按某種特定的順序刪除資料,譬如要刪除年齡最大的 3 個學生的資訊,示例如下:

DELETE FROM T_GoodStudents 
WHERE Id IN(SELECT TOP(3) t.Id FROM T_GoodStudents t ORDER BY t.Id DESC);

5.3、帶 TOP 引數的 UPDATE 語句,示例如下(隨機將 3 個男學生的性別更新為 0):

UPDATE TOP(3) T_Students SET Gender = 0 WHERE Gender = 1;

如果想要按某種特定的順序更新資料,譬如要將年齡最大的 3 個男學生的性別更新為 0,示例如下:

UPDATE T_GoodStudents SET Gender = 0 
FROM(SELECT TOP(3) t1.Id FROM T_GoodStudents t1 ORDER BY t1.Id DESC) t2 
WHERE T_GoodStudents.Id = t2.Id;

6、用 OUTPUT 子句返回受影響的資料

試想一下,如果需要在插入的一條資料的同時返回這條資料,或者在刪除一條資料的同時備份這條資料,我們當然可以用多條簡單語句來共同完成,並且通過事務來確保操作的原子性。但其實這類需求可以通過 OUTPUT 子句來更好的完成,而且一個語句就能搞定,不必加事務,因為它本身就具備原子性。

在使用 OUTPUT 返回資料時,需要藉助 INSERTED 或 DELETED 來引用欄位值。INSERTED 用來引用插入操作或更新操作新增的值,DELETED 用來引用刪除操作或更新操作刪除的值。在 INSERT 語句中不能訪問 DELETED,在 DELETE 語句中不能訪問 INSERTED,在 UPDATE 語句中兩個都能訪問。示例如下:

INSERT T_GoodStudents OUTPUT inserted.* VALUES(7,'高鵬',1,'1979-11-11'); -- 插入 1 條資訊並輸出
DELETE TOP(1) FROM T_GoodStudents OUTPUT deleted.Id,deleted.Name;       -- 刪除 1 條資訊並輸出

UPDATE TOP(2) T_GoodStudents SET Gender = 1 
OUTPUT deleted.Name,inserted.Name,deleted.Gender,inserted.Gender;       -- 更新 2 條資訊並輸出

還可以結合 INTO 把 OUTPUT 返回的資料插入到另一張表中,示例如下:

INSERT T_GoodStudents OUTPUT inserted.* INTO T_GoodStudents VALUES(9,'黃強',1,'1999-11-11');
DELETE TOP(1) FROM T_GoodStudents OUTPUT deleted.* INTO T_GoodStudents;
UPDATE TOP(2) T_GoodStudents SET Gender = 1 OUTPUT deleted.* INTO T_GoodStudents;

7、本文小結

本文主要講述了 T-SQL 語句中的 INSERT、DELETE、UPDATE 和 MERGE 共 4 個 DML 語句及其子句,以及一個 DDL 語句 TRUNCATE TABLE,而且這幾個語句都是實際開發中特別常用的語句。

在 Oracle 中總是給表取別名是個很好的習慣,但 SQL Server 的增刪改語句均不支援對目標表取別名,只有合併語句和查詢語句支援別名。不過 SQL Server 中的所有 DML 語句都支援用表名來限定欄位名。

有些讀者可能會有疑問“為什麼 SQL Server 管理工具生成的語句總是要給物件名前後加上中括號?”。儘管不好看,但的確有道理,因為它可以防止使用者自定義名稱跟系統關鍵字衝突。譬如你要用 USER 做表名或欄位名,就得用中括號包裹一下。另外,如果想用某些特殊符號來命名也需要用中括號包裹,但一般不建議這麼做,太變態了!

如果你不幸遇到頭尾帶空格的物件名,你會發現只寫空格以外的名稱部分是訪問不到該物件的,這種情況也可以用中括號來解決。如果你有修改許可權的話建議還是把空格刪掉吧,太噁心了!假如學生表前後有空格,查詢示例如下:

SELECT * FROM [ T_Students ];

本文參考連結:

  • 1、SQL Server 2016 INSERT
  • 2、SQL Server 2016 DELETE
  • 3、SQL Server 2016 TRUNCATE TABLE
  • 4、SQL Server 2016 UPDATE
  • 5、SQL Server 2016 MERGE
  • 6、SQL Server 2016 TOP
  • 7、SQL Server 2016 OUTPUT

本文連結:http://www.cnblogs.com/hanzongze/p/tsql-crud.html
版權宣告:本文為部落格園博主 韓宗澤 原創,作者保留署名權!歡迎通過轉載、演繹或其它傳播方式來使用本文,但必須在明顯位置給出作者署名和本文連結!個人部落格,能力有限,若有不當之處,敬請批評指正,謝謝!