Các Store procedure hệ thống mà developer cần biết

Nếu bạn là developer thì chắc chắn rằng bạn phải biết SQLServer ! (Hiện tại SQLServer đã có đến phiên bản SQLServer 2008). Tuy nhiên ứng với phiên bản SQLServer nào Microsoft đều đưa ra các store procedure hệ thống (System stored procedures)

Bài viết này mình đề cập đến 5 store procedure hệ thống theo mình nghĩ là mỗi developer khi làm việc với SQLServer đều phải biết.
1. sp_help
a. Mục đích: Đây là store procedure hệ thống giúp bạn tra cứu nhanh về thông tin của các đối tượng (objects) có trong database. Như là: xem cấu trúc của 1 bảng, xem bảng này có phụ thuộc bảng nào, xem bảng này có Primary Key là Foregin Key của table nào, …..
b. Cú pháp
    * sp_help : để lấy tất cả các thông tin về tất cả các đối tượng trong Database
    * sp_help <tên của đối tượng> : để lấy tất cả các thông tin cụ thể của đối tượng (thông số tên đối tượng truyền vào) trong Database

c. Ví dụ
    * sp_help —> Khi đó bạn sẽ thấy giống màn hình sau:

    * sp_help ‘TB_TacGia’—> Khi cần lấy thông tin của table TB_TacGia trong Database: QuanLyThuVien.
      

2. sp_helptext
a. Mục đích: Đây là store procedure hệ thống giúp bạn tra cứu định nghĩa các đối tượng: store procedure hệ thống, store procedure do người dùng định nghĩa, hàm do người dùng định nghĩa, trigger, ….
b. Cú pháp
    * sp_helptext  <tên của đối tượng> : để lấy định nghĩa của đối tượng (thông số tên đối tượng truyền vào) trong Database
c. Ví dụ
    * sp_helptext ‘dbo.spTB_TacGia_TruyVan’: để lấy định nghĩa của store procedure do người dùng định nghĩa: spTB_TacGia_TruyVan trong Database: QuanLyThuVien. Khi đó bạn sẽ thấy màn hình như sau

3. sp_MSforeachtable
a. Mục đích: Đây là store procedure hệ thống giúp bạn duyệt qua tất cả các table trong Database. Khi đó bạn dùng kí tự ? để làm kí tự đại diện cho table. Ví dụ cơ bản nhất khi dùng store procedure hệ thống này là đếm số dòng của tất cả các bảng trong Database
b. Cú pháp
    *  sp_MSforeachtable <Câu lệnh SQL> —> Câu lệnh SQL bắt buộc phải chứ kí tự ? làm kí tự đại diện cho tên table
c. Ví dụ
    *  sp_MSforeachtable ‘SELECT ”?”, COUNT(*) FROM ?’ : Đếm số dòng của tất cả các table trong Database: QuanLyThuVien. Khi đó bạn sẽ thấy màn hình như sau

4. sp_depends
a. Mục đích: Đây là store procedure hệ thống giúp bạn liệt kê các Views, store procedure do người dùng định nghĩa, hàm do người dùng định nghĩa, trigger, … có phụ thuộc vào tên đối tượng truyền vào
b. Cú pháp
    *  sp_depends <tên của đối tượng>  –> tìm các đối tượng khác có phụ thuộc với tên đối tượng được của thông số truyền vào
c. Ví dụ
    *  sp_depends ‘Person.Address’  -> tìm các đối tượng khác phụ thuộc vào table: TB_TacGia trong Database: QuanLyThuVien. Khi đó ta sẽ thấy như màn hình sau

5. sp_spaceused
a. Mục đích: Đây là store procedure hệ thống giúp bạn lấy kích thước của Database hoặc kích thước của một đối tượng trong Database.
b. Cú pháp
    *  sp_spaceused —> lấy kích thước của Database (là Database mà ta đang thực thi store procedure hệ thống này)
    * sp_spaceused <tên của đối tượng> —> lấy kích thước của 1 đối tượng cụ thể được truyền vào từ thông số tên đối tượng
c. Ví dụ
    *  sp_spaceused —> lấy kích thước của Database: QuanLyThuVien . Khi đó bạn sẽ thấy màn hình như sau

sp_spaceused ‘Person.Address’ —> lấy kích thước của table: TB_TacGia trong Database: QuanLyThuVien Khi đó bạn sẽ thấy màn hình như sau

Và cuối cùng, bạn hãy thực hiện lệnh sau

sp_MSforeachtable ‘execute sp_spaceused @objname = ”?” ‘

Nó là sự kết hợp của 2 lệnh: sp_MSforeachtable, sp_spaceused mình vừa giới thiệu ở trên và cho biết nó làm mục đích gì ?

Di Chuyển TempDB Sang Đĩa Khác

Tempdb là một database hệ thống của SQL Server. Nó dùng để chứa các kết quả trung gian khi câu lệnh đang thực hiện, thực hiện sắp xếp (với câu lệnh có ORDER BY), chứa các bảng tạm, biến bảng…

Khi server bận bịu với nhiều yêu cầu truy vấn đồng thời thì kích thước của tempdb có thể trở nên rất lớn. Vị trí mặc định của các file database của tempdb là ở thư mục chứa các file chương trình SQL Server. Bạn nên chuyển chúng sang một ổ đĩa riêng để tránh hiện tượng các file này lớn lên chiếm hết không gian đĩa. Để chuyển các file sang vị trí mới bạn hãy làm theo các bước sau:
Bước 1: Kiểm tra tên file và vị trí hiện thời

USE TempDB
GO
EXEC SP_HELPFILE
GO

kết quả sẽ tương tự như sau:

name     fileid  filename
tempdev  1       C:\Program Files\Microsoft...\tempdb.mdf
templog  2       C:\Program Files\Microsoft...\tempdb.ldf

Tên mặc định cho database file là tempdev và cho log file là templog (đây là tên logic, không phải là tên file trên đĩa). Ghi lại thư mục hiện tại cho hai file này.
Bước 2: Chuyển các file này sang thư mục mới

USE master
GO
ALTER DATABASE tempdb MODIFY FILE
(NAME = tempdev, FILENAME = 'd:\data\tempdb.mdf')
GO
ALTER DATABASE tempdb MODIFY FILE
(NAME = templog, FILENAME = 'd:\log\templog.ldf')
GO
Bạn có thể chạy lại thủ tục sp_helpfile ở bước 1 để kiểm tra xem các file đã có vị trí mới.

Bước 3: Restart lại SQL Server, các file mới sẽ được tạo và sử dụng. Trở lại thư mục ở bước 1 và xóa hai file ở đó đi. Vì mỗi lần SQL Server khởi động nó sẽ tạo lại tempdb, nên bạn không cần bận tâm copy hai file từ thư mục cũ sang vị trí mới, chỉ việc xóa chúng đi để giải phóng chỗ.

Loại Bỏ Bản Ghi Trùng Trong Bảng

Khi làm việc với dữ liệu, có thể bạn gặp những tình huống trong đó, dữ liệu trong bảng có những bản ghi trùng nhau ở những trường đáng nhẽ ra không được trùng ...

Ví dụ bạn có 1 bảng chứa danh sách các địa chỉ email tên là DiaChiEmail gồm có các trường: ID, Email, và Ten; dữ liệu này được dùng cho công tác marketing (nôm na là spam ). Theo logic của dữ liệu thì trường Email xác định duy nhất một địa chỉ email, và bạn chỉ muốn gửi 1 email tới mỗi địa chỉ. Vì những lý do nào đó (có thể do lỗi nhập dữ liệu, hoặc khi import dữ liệu từ nhiều nguồn khác nhau…), bảng DiaChiEmail lại xuất hiện các bản ghi có trường Email giống nhau, mặc dù các trường còn lại thì khác nhau. Nay bạn muốn loại bỏ các bản ghi trùng và chỉ giữ lại một bản ghi duy nhất cho mỗi Email, và giả sử bạn muốn giữ lại bản ghi có trường ID lớn nhất (vì bản ghi này có dữ liệu mới nhất). Ta hãy xem xét cách làm như thế nào qua một ví dụ cụ thể:

-- tạo bảng
CREATE TABLE dbo.DiaChiEmail(
ID INT IDENTITY PRIMARY KEY,
Email VARCHAR(100),
Ten NVARCHAR(100)
)
GO
-- insert một vài bản ghi
INSERT dbo.DiaChiEmail(
Email,
Ten
)
SELECT 'abc@gmail.com', 'abc' UNION ALL -- #1
SELECT 'def@yahoo.com', 'def' UNION ALL -- #2
SELECT 'ghi@hotmail.com', 'ghi' UNION ALL -- #3
SELECT 'jkl@ymail.com', 'jkl' UNION ALL -- #4
SELECT 'abc@gmail.com', 'abc 2' UNION ALL -- #5, trùng với bản ghi #1
SELECT 'abc@gmail.com', 'abc 3' UNION ALL -- #6, trùng với bản ghi #1
SELECT 'ghi@hotmail.com', 'ghi 2' -- #7, trùng với bản ghi #3

Như vậy địa chỉ email abc@gmail xuất hiện 3 lần với tên khác nhau; còn ghi@hotmail.com xuất hiện 2 lần. Vì ta chỉ muốn giữ lại bản ghi sau cùng cho mỗi địa chỉ email, kết quả mà ta mong đợi từ quá trình loại bỏ bản ghi trùng sẽ là:

‘def@yahoo.com’, ‘def’, ’2010/01/13′ — bản ghi #2
‘jkl@ymail.com’, ‘jkl’, ’2010/01/14′ — bản ghi 4
‘abc@gmail.com’, ‘abc 3′, ’2010/01/15′ — bản ghi #6
‘ghi@hotmail.com’, ‘ghi 2′, ’2010/01/16′ — bản ghi #7

Câu lệnh sau giúp ta làm điều đó:

DELETE a
FROM DiaChiEmail a
JOIN
(
SELECT Email, MAX(ID) AS ID
FROM DiaChiEmail
GROUP BY Email
HAVING COUNT(*)>1
) b
ON a.Email = b.Email
AND a.ID < b.ID
Giải thích: Câu lệnh con từ dòng 5-8 lấy ra các bản ghi mới nhất cho mỗi email bị trùng. Mệnh đề HAVING COUNT(*)>1 đảm bảo các bản ghi có email xuất hiện một lần không bị động đến. Ở ví dụ trên, câu lệnh con sẽ chứa các bản ghi #6 và #7. Sau đó lệnh DELETE xóa đi trong bảng chính các bản ghi “cũ” hơn (có ID nhỏ hơn giá trị lớn nhất) cho các email bị trùng. Đó là các bản ghi #1, #5 và #3.

Trong trường hợp bảng DiaChiEmail có rất nhiều bản ghi và số bản ghi bị trùng cũng rất lớn, bạn có thể đưa kết quả của câu lệnh con ở trên vào một bảng tạm, sau đó tạo index trên trường Email của bảng tạm để hỗ trợ câu lệnh DELETE:
CREATE TABLE #BanGhiTrung(
Email VARCHAR(100) PRIMARY KEY,
ID INT
)
GO
INSERT #BanGhiTrung(Email, ID)
SELECT Email, MAX(ID) AS ID
FROM dbo.DiaChiEmail
GROUP BY Email
HAVING COUNT(*) > 1
GO
DELETE a
FROM DiaChiEmail a
JOIN #BanGhiTrung b
ON a.Email = b.Email
AND a.ID < b.ID
Bạn Red Devilic cung cấp một cách làm dùng Common Table Expression kết hợp với hàm ROW_NUMBER(), là những tính năng mới trong version 2005 trở lên:
WITH [TBL_DUPLICATE] AS
(
SELECT
  STT = ROW_NUMBER() OVER (PARTITION BY Email ORDER BY ID DESC), Email
FROM DiaChiEmail
)
DELETE FROM [TBL_DUPLICATE] WHERE STT > 1
Giải thích: Đoạn lệnh từ dòng 3-5 trả về 1 Common Table Expression gồm có Email và STT cho mỗi Email; bắt đầu từ 1 và tăng dần cho mỗi Email bị trùng (“ORDER BY ID DESC”) – chức năng của PARTITION gần giống như GROUP BY. Các bản ghi không bị trùng thì tất nhiên chỉ xuất hiện 1 lần và có STT=1. Sau đó lệnh DELETE áp dụng trên Common Table Expression để xóa đi các bản ghi có STT>1, tức là chỉ giữ lại Email có ID lớn nhất. Các Email không bị trùng vì thế không bị ảnh hưởng. Lệnh DELETE này có tác dụng trên bảng và do đó các bản ghi có Email bị trùng bị xóa khỏi bảng.

 

Vì sao nên tránh viết SQL code trong ứng dụng

Trong ứng dụng khi cần tương tác với database, có lẽ một cách làm rất phổ biến là tạo lập một chuỗi chứa lệnh SQL, ghép các giá trị  nhập vào của người dùng thành một lệnh SQL hoàn chỉnh, rồi thực hiện chuỗi lệnh SQL đó.

Như ví dụ dưới đây:

string cmdStr = "INSERT INTO Customer(Name, Address, Email, Phone) VALUES('" + txtName.Text + "', '" + txtEmail.Text + "','" + txtPhone.Text + "')";
conn.Open();
SqlCommand cmd = new SqlCommand(cmdStr, conn);
cmd.ExecuteNonQuery();
Cách làm này có ưu điểm tiện lợi, giúp quá trình phát triển code nhanh (không phải chuyển qua lại giữa Visual Studio và Management Studio). Tuy nhiên nó tiềm ẩn rất nhiều vấn đề (cách làm tối ưu là viết một thủ tục trong database rồi từ ứng dụng gọi thủ tục này và truyền các tham số cho nó):

1. An ninh: việc viết lệnh SQL thẳng trong ứng dụng như vậy sẽ tạo ra lỗ hổng SQL injection, tức là hacker có thể khéo léo nhập thẳng vào trường text một chuỗi có chứa đoạn lệnh SQL và để cho database sẽ thực hiện đoạn lệnh đó. Ví dụ, khi hacker nhập vào trường txtPhone giá trị:
123');delete from order--
thì đoạn lệnh mà ứng dụng gửi cho SQL Server sẽ là:
INSERT INTO Customer(Name, Address, Email, Phone) Values('','','123');delete from order--')
Chú ý là hacker cố tình đưa hai dấu gạch ngang "--" vào cuối để biến toàn bộ đoạn ký tự phía sau thành đoạn giải thích. Kết quả là SQL Server sẽ thực hiện hai lệnh, "INSERT INTO Customer…" và "delete from order", và bảng order bị xóa sạch. Tương tự hacker có thể đưa vào các lệnh khác như "drop table order" để xóa hẳn bảng khỏi database, hoặc "select * from user" để lấy hết thông tin tài khoản của người dùng.

Nếu dùng thủ tục thì vấn đề hoàn toàn bị hóa giải, vì toàn bộ giá trị của trường text sẽ được lưu vào cột tương ứng trong bảng bên trong database. Khi đó chỉ có một lệnh INSERT được thực hiện và giá trị của Phone của bản ghi mới sẽ là "123');delete from order--"
 
2. Hiệu năng (performance): cách làm trên sẽ dẫn đến mỗi lần thực hiện SQL Server sẽ biên dịch lại câu lệnh. Khi SQL Server nhận được một câu lệnh, nó sẽ kiểm tra xem câu lệnh này đã có kế hoạch thực thi lưu trong cache hay chưa. Nó băm (hash) câu lệnh để chuyển thành một con số và đối chiếu với bảng băm trong cache, nếu tìm thấy có nghĩa là câu lệnh này đã thực hiện trước đó rồi và SQL Server dùng luôn kế hoạch thực thi đã có sẵn. Nếu không tìm thấy có nghĩa đây là câu lệnh mới và SQL Server sẽ biên dịch, tạo kế hoạch thực thi, lưu vào cache, và thực hiện câu lệnh. Vì với hàm băm, câu lệnh chỉ cần khác đi một chút (chỉ cần thêm một dấu cách) là đã cho số băm khác nhau, nên với mỗi giá trị người dùng nhập vào sẽ tạo thành một câu lệnh mới và SQL Server lại phải trải qua các bước biên dịch, tạo kế hoạch thực thi, lưu vào cache trước khi tiến hành thực hiện câu lệnh.

Trong nhiều trường hợp, chi phí cho các bước kể trên có thể rất lớn. Ví d�� với một câu lệnh mất 100 mili giây để biên dịch trong khi cũng mất 100 mili giây để thực hiện, thì chi phí để thực hiện câu lệnh này bị tăng gấp đôi. Một hậu quả khác là mỗi lần biên dịch thì kế hoạch thực thi mới sẽ chiếm chỗ trong cache, trong khi kích thước của cache chỉ có hạn. Đến một lúc kế hoạch thực thi của các câu lệnh khác sẽ bị loại khỏi cache để giải phóng chỗ, và đến khi các câu lệnh khác đó được thực hiện thì lại cần biên dịch lại. Như vậy hiệu năng của toàn bộ hệ thống bị giảm.

Khi dùng thủ tục thì tình huống sẽ thay đổi hẳn, vì SQL Server chỉ băm câu lệnh gọi đến thủ tục mà bỏ qua các tham số, cho nên khi đã thực hiện EXEC Proc1 @param=1 thì đến khi gặp EXEC Proc1 @param=2 SQL Server không cần biên dịch lại thủ tục nữa.
 
3. Bảo mật: Khi ta viết thẳng lệnh SQL vào ứng dụng như trên thì user được dùng để kết nối vào database (trong connection string) cần phải có quyền INSERT vào bảng. Thông thường các ứng dụng có đủ các thao tác đọc/ghi/xóa vào database, cho nên user trên cũng đòi hỏi đủ các quyền SELECT/INSERT/UPDATE/DELETE vào các bảng trong database. Khi hacker chiếm được quyền truy nhập vào database với user trên, hắn ta có thể mặc sức tung hoành làm bất cứ điều gì hắn muốn trong database.

Khi dùng thủ tục thì user không cần bất cứ quyền nào trực tiếp trên bảng, user chỉ cần quyền thực thi thủ tục và khi chạy thì thủ tục thực hiện các thao tác trên bảng cho user. Ta có thể dỡ bỏ hết các quyền của user trên tất cả các bảng và chỉ cấp quyền thực thi trên các thủ tục cần thiết. Ta cũng có thể đồng thời dỡ bỏ quyền truy nhập vào các bảng hệ thống, cho nên nếu hacker có truy nhập được vào database thông qua user trên, hắn ta sẽ mù tịt không biết database có các bảng nào để mà phá.

Nếu hắn truy nhập được vào mã nguồn của ứng dụng thì có thể biết được tên của các thủ tục được dùng trong ứng dụng và chạy các thủ tục này, nhưng mức độ phá hoại của hacker bị khống chế ở mức thấp hơn nhiều so với khi user có đủ các quyền.


 4. Bảo trì: thường có những đoạn lệnh SQL được dùng lại ở một vài nơi khác nhau trong ứng dụng, hoặc thậm chí ở các ứng dụng khác nhau truy nhập chung vào một database. Khi viết thẳng SQL code trong ứng dụng ta sẽ phải viết lại đoạn lệnh trên ở tất cả những nơi nó được dùng. Điều này đã phạm vào lỗi "lặp lại code" (duplication of code) trong phát triển phần mềm. Khi cần phải sửa lại câu lệnh SQL trên (vì lỗi hoặc cần viết theo cách tối ưu hơn, hoặc cấu trúc database thay đổi dẫn đến cần viết lại) ta sẽ phải tìm đến tất cả các nơi có dùng câu lệnh SQL đó để sửa.

Với thủ tục thì ta chỉ cần sửa ở một nơi và nó có tác dụng cho toàn ứng dụng. Giống như .net tách mã chương trình ra khỏi mã html, dùng thủ tục cũng tách mã SQL ra khỏi mã chương trình. Lúc đó thủ tục có chức năng như là cổng truy nhập, hay API, mà qua đó ứng dụng giao tiếp với database. Nó tạo thành một lớp ngăn cách giữa ứng dụng và database, và che dấu toàn bộ cấu trúc database khỏi ứng dụng. Ứng dụng không cần biết database gồm có những bảng gì, mỗi bảng có các cột nào, hay các bảng quan hệ với nhau ra sao. Vì thế ta có thể dễ dàng thay đổi cấu trúc của database khi cần và sửa lại các thủ tục có liên quan mà không ảnh hưởng gì đến ứng dụng.
 
Kết luận: Việc viết mã SQL trong ứng dụng có rất nhiều vấn đề như đã chỉ ra. Với những ứng dụng nhỏ hoặc ứn dụng có những đặc thù nhất định, những vấn đề trên có thể không bộc lộ hết ra, nhưng khi ta quyết định chọn phương pháp này thì cũng cần ý thức được những hệ quả có thể xảy ra của nó. Có những trường hợp ta không muốn lưu mã nguồn trong database, ví dụ để ứng dụng có thể dễ dàng chuyển đổi giữa các hệ CSDL khác nhau. Khi đó ta bắt buộc phải viết SQL code trong ứng dụng, nhưng để giảm nhẹ các vấn đề của nó, ta nên dùng chuỗi SQL có tham số (dùng cmd.Parameters.Add) để tránh được lỗi SQL injection và biên dịch lại câu lệnh (điểm 1 và 2 ở trên). Còn khi ta có lựa chọn dùng thủ tục thì nên áp dụng phương pháp này. Đây là phương pháp mà Microsoft khuyến cáo khi viết ứng dụng trên môi trường phát triển của họ.

So sánh hai cách xóa dữ liệu DELETE và TRUNCATE

SQL Server cung cấp 2 phương pháp để xóa dữ liệu, DELETE và TRUNCATE. Cú pháp của hai lệnh này như sau:

DELETE

DELETE dbo.Tblxxx WHERE...

hoặc

DELETE a FROM dbo.Tblxxx a WHERE...

Khi cần xóa dữ liệu với điều kiện liên quan đến bảng khác:

DELETE a
FROM dbo.Tblxxx a
JOIN dbo.Tblyyy b ON a.Col1 = b.Col1

hoặc:

DELETE a
FROM dbo.Tblxxx a
WHERE EXISTS(SELECT 1 FROM dbo.Tblyyy b WHERE a.Col1 = b.Col1)

TRUNCATE không có tùy biến nào

TRUNCATE TABLE dbo.Tblxxx

Tuy cùng để xóa dữ liệu, nhưng hai lệnh này có những khác nhau cơ bản:

+ DELETE cung cấp các lựa chọn để xóa những dòng dữ liệu thỏa mãn các điều kiện nhất định, như WHERE hoặc JOIN với các bảng khác.
+ TRUNCATE không có lựa chọn nào, mà luôn cắt bỏ toàn bộ dữ liệu của bảng. Nói cách khác, ta không thể TRUNCATE 1 nửa hay 1 phần của bảng.

+ DELETE hỗ trợ transaction. Khi lệnh DELETE nằm trong 1 transaction và trong một tình huống nào đó transaction được ROLLBACK thì các bản ghi bị xóa bởi lệnh DELETE sẽ trở lại bảng không có gì suy xuyển.
+ TRUNCATE thì ngược lại, không hỗ trợ transaction. Một khi đã thực hiện thì không thể lấy lại dữ liệu được nữa.

+ DELETE khi thực hiện bao gồm quá trình tìm các bản ghi thỏa mãn điều kiện của câu lệnh, và xóa các bản ghi này. Việc tìm các bản ghi cần xóa được thực hiện giống hệt như một câu lệnh SELECT, cũng tối ưu hóa, lựa chọn giữa các phương án thực hiện khác nhau và chọn ra phương án tối ưu (dựa vào index, statistics…).
+ TRUNCATE thì chỉ có một phương án thực hiện duy nhất, đó là cắt bỏ tất cả các dòng dữ liệu của bảng.

+ Với DELETE, các bản ghi bị xóa sẽ được kiểm tra xem có vi phạm ràng buộc FOREIGN KEY không. Ví dụ ta có hai bảng MAT_HANG và BAN_HANG là quan hệ 1-n thông qua MA_MH; nếu MA_MH=1 đã có giao dịch, nghĩa là bảng BAN_HANG đã có bản ghi với MA_MH=1, thì khi DELETE bản ghi với MA_MH=1 từ bảng MAT_HANG (bảng cha) SQL SERVER sẽ báo lỗi và không cho xóa.
Nếu trước đó, khi ta định nghĩa ràng buộc FOREIGN KEY mà có lựa chọn CASCADE DELETE, thì thay vì báo lỗi SQL Server sẽ đồng thời xóa hết các bản ghi trong cả bảng BAN_HANG với MA_MH=1.
+ TRUNCATE thì không có những đoạn kiểm tra dài dòng như thế. Nếu bảng có ràng buộc FOREIGN KEY, SQL Server sẽ báo lỗi và không cho thực hiện (nhớ là lựa chọn CASCADE DELETE trong khai báo FOREIGN KEY chỉ ảnh hưởng đến lệnh DELETE chứ không tác dụng đối với TRUNCATE).

+ Vì DELETE hỗ trợ transaction và dùng transaction log, nó có thể dùng với bảng nằm trong một replication hoặc database có dùng log shipping.
+ TRUNCATE thì vì không ghi gì vào transaction log nên khi gặp một trong các tình huống trên sẽ bị từ chối ngay.

+ Với DELETE, nếu bảng có index thì các index cũng sẽ được cập nhật để xóa đi các node tương ứng với các bản ghi bị xóa.
+ TRUNCATE thì rất đơn giản, các index của bảng cũng bị cắt cụt theo.

+ DELETE không ảnh hưởng đến giá trị IDENTITY. Nếu bảng có 100 bản ghi và cột IDENTITY có giá trị từ 1-100; nay ta DELETE bản ghi có cột IDENTITY=100 rồi INSERT một bản ghi mới; bản ghi mới sẽ có cột IDENTITY=101.
+ TRUNCATE luôn đặt lại IDENTITY trở về 1. Bản ghi đầu tiên được INSERT sau khi TRUNCATE sẽ có cột IDENTITY=1.

+ DELETE thực ra chỉ đánh dấu xóa các bản ghi chứ ngay sau đó dữ liệu của các bản ghi bị xóa vẫn nằm nguyên tại chỗ. Dần dần khi ta INSERT thêm dữ liệu vào bảng thì các bản ghi mới sẽ ghi đè lên các vùng lưu trữ đó. Ta có thể kiểm tra để thấy kích thước bảng không thay đổi ngay cả sau khi chạy DELETE FROM TblName (xóa hết các bản ghi).
+ TRUNCATE thì xóa hết dữ liệu đồng thời giải phóng vùng lưu trữ giành cho bảng, trả lại cho SQL Server. Ta có thể so sánh DELETE như là xóa file, còn TRUNCATE thì như format lại ổ cứng.

Vì những lý do trên, DELETE luôn luôn chậm hơn TRUNCATE. Càng có nhiều bản ghi DELETE càng chậm, còn TRUNCATE thì không phụ thuộc vào lượng dữ liệu. DELETE có phạm vi ứng dụng rộng hơn; còn TRUNCATE thì chỉ dùng được mỗi một việc, nhưng nó lại làm rất nhanh. Vì vậy, hãy nhớ dùng TRUNCATE khi có thể được.