Hàm COALESCE trong SQL thú vị như thế nào?
Điều đó đủ thú vị để trở nên quan trọng đối với tôi. Và tôi sẽ rất vui khi thuê được một anh chàng mới không có thói quen xấu là bỏ qua mục tiêu THAN. Điều đó bao gồm các biểu thức và hàm khác để xử lý các tình huống tương tự.
Hôm nay, bạn sẽ tìm thấy câu trả lời cho năm câu hỏi được hỏi nhiều nhất về biểu thức SQL COALESCE. Một trong những điều này đang được tranh luận nhiều lần.
Chúng ta bắt đầu chứ?
Việc sử dụng hàm COALESCE trong SQL là gì?
Nó có thể được trả lời bằng 2 từ: xử lý nulls .
Null là khoảng trống của các giá trị. Nói cách khác, không rõ. Nó khác với một chuỗi rỗng hoặc một số không. Xử lý null yêu cầu sử dụng các biểu thức và hàm. Một trong số đó là THAN.
Để hiểu ý tôi, hãy xem các câu dưới đây:
DECLARE @number INT
SELECT @number + 33587
Nó sẽ chạy tốt chứ? Nó sẽ.
Có vấn đề gì không? Không, hiện tại.
Nhưng các câu lệnh sẽ dẫn đến NULL vì thêm null vào một số bằng NULL.
Nếu tất cả các truy vấn của bạn chỉ rơi vào mức này, bạn có thể ngừng đọc. Nhưng tất nhiên, họ không. Chúng tôi được trả nhiều tiền hơn là sản xuất loại mã này.
Bây giờ, hãy thêm một chút ‘thảm họa’:
DECLARE @number INT
SET @number = @number + 33587
UPDATE myTable
set col1 = @number -- Surprise! col1 is NOT NULLABLE
where ID = 1
PRINT 'Success!'
Việc thực thi đoạn mã trên sẽ ở cuối khi nó đạt đến câu lệnh UPDATE. Nó sẽ không in "Thành công!" Vì bạn không thể đặt giá trị null trên cột không thể null. Câu lệnh này có nói rõ lý do tại sao chúng ta cần xử lý null không?
Hãy thay đổi mã để thêm một mạng lưới an toàn:
DECLARE @number INT
SET @number = COALESCE(@number,0) + 33587 -- our safety net. Thanks to COALESCE.
UPDATE myTable
set col1 = @number -- Disaster averted!
where ID = 1
PRINT 'Success!'
COALESCE sẽ thay đổi giá trị null thành 0 và tổng sẽ không rỗng.
Bài học rút ra là, COALESCE là một trong những mạng lưới an toàn chống lại sự vô hiệu. Thậm chí tốt hơn, việc xử lý null đúng cách trong mã SQL của bạn làm giảm đau đầu và cho phép bạn về nhà sớm. Đó là điều chắc chắn.
Giờ thì bạn đã hiểu lý do tại sao tôi muốn trong nhóm của mình có người siêng năng xử lý null.
Các ví dụ thực tế hơn trong việc sử dụng SQL COALESCE
Hãy tham khảo các ví dụ thực tế hơn.
Giả sử bạn sống ở một vùng mà một số người có tên đệm, nhưng những người khác thì không. Làm thế nào bạn sẽ tạo ra tên đầy đủ từ họ, tên đệm và họ mà không rơi vào bẫy trống?
Dưới đây là một giải pháp khả thi sử dụng COALESCE:
USE AdventureWorks
GO
SELECT
p.LastName + ', ' + p.FirstName + ' ' + COALESCE(p.MiddleName + ' ','') AS FullName
FROM Person.Person p
Một ví dụ khác:giả sử bạn là nhân viên trong một công ty nơi tổng lương được tính khác nhau cho mỗi nhân viên. Đối với một số người trong số họ, có giá theo giờ. Những người khác được trả theo tỷ giá hàng tuần hoặc hàng tháng.
Dưới đây là mẫu bảng cùng với giải pháp truy vấn sử dụng COALESCE:
-- STEP 1: Create the table
CREATE TABLE EmployeeWages (
employee_id INT PRIMARY KEY,
hourly_rate SMALLMONEY,
weekly_rate SMALLMONEY,
monthly_rate MONEY,
CHECK(
hourly_rate IS NOT NULL OR
weekly_rate IS NOT NULL OR
monthly_rate IS NOT NULL)
);
-- STEP 2: Insert data
INSERT INTO
EmployeeWages(
employee_id,
hourly_rate,
weekly_rate,
monthly_rate
)
VALUES
(1,60, NULL,NULL),
(2,40, NULL,NULL),
(3,NULL, 1000,NULL),
(4,NULL, NULL,7000),
(5,NULL, NULL,5000);
-- STEP 3: Query the monthly salary.
SELECT
employee_id,
COALESCE(
hourly_rate*22.00*8.00,
weekly_rate*4.00,
monthly_rate
) AS monthly_salary
FROM
EmployeeWages;
Bảng chứa các chế độ trả lương khác nhau cho mỗi ID nhân viên. Truy vấn yêu cầu bạn xuất ra mức lương hàng tháng cho tất cả.
Đây là nơi COALESCE sẽ tỏa sáng:nó chấp nhận một danh sách các giá trị và có thể có bất kỳ số lượng mục nào. COALESCE sẽ chọn cái đầu tiên không rỗng:
Gọn gàng phải không?
COALESCE hoạt động như thế nào trong SQL?
Định nghĩa của COALESCE là một biểu thức trả về giá trị khác rỗng đầu tiên từ danh sách các giá trị. Cú pháp COALESCE là:
COALESCE (biểu thức [,… n])
Ví dụ trước đây của chúng tôi với các chế độ trả lương khác nhau minh họa điều này.
What’s Under the Hood
Nói tóm lại, hàm COALESCE trong SQL là một biểu thức bọc đường cho biểu thức CASE dài hơn nhiều. Nó giúp loại bỏ nhu cầu gõ một CASE tương đương, lâu hơn (và mệt mỏi, đối với những người đánh máy lười biếng như tôi). Kết quả sẽ giống nhau.
Chúng ta có thể chứng minh nó? Đúng! Đối với một, Microsoft thừa nhận điều đó.
Nhưng tốt cho chúng tôi, Microsoft đã đưa nó vào Kế hoạch Thực thi. Do đó, chúng tôi biết điều gì đang xảy ra.
Hãy thử nó bằng cách sử dụng một ví dụ trước với tiền lương. Nhưng trước khi bạn chạy lại truy vấn bên dưới, hãy bật Bao gồm kế hoạch thực thi thực tế hoặc chỉ cần nhấn CTRL-M .
SELECT
employee_id,
COALESCE(
hourly_rate*22.00*8.00,
weekly_rate*4.00,
monthly_rate
) AS monthly_salary
FROM
EmployeeWages;
Nhấp vào Kế hoạch thực thi tab trong kết quả. Trông có vẻ đơn giản, nhưng viên ngọc ẩn của chúng tôi nằm trong Compute Scalar nút. Khi di chuột qua nó, bạn thấy một biểu thức có tên Expr1002 (Hình 2). Nó có thể là gì?
Hãy cùng tìm hiểu sâu hơn. Nhấp chuột phải vào nó và chọn Hiển thị XML kế hoạch thực thi . Một cửa sổ mới sẽ xuất hiện. Hãy xem Hình 3 bên dưới:
Có câu lệnh CASE của bạn. Dưới đây là toàn bộ nội dung được định dạng và thụt lề để dễ đọc:
CASE WHEN CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)
*(22.00)*(8.00) IS NOT NULL
THEN CONVERT_IMPLICIT(numeric(23,8),CONVERT_IMPLICIT(numeric(10,4),
[TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)*(22.00)*(8.00),0)
ELSE CASE WHEN
CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)
*(4.00) IS NOT NULL
THEN CONVERT_IMPLICIT(numeric(23,8),CONVERT_IMPLICIT(numeric(10,4),
[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*(4.00),0)
ELSE CONVERT_IMPLICIT(numeric(23,8),[TestDatabase].[dbo].[EmployeeWages].[monthly_rate],0)
END
END
Đó là khá lâu so với
COALESCE(
hourly_rate * 22.00 * 8.00,
weekly_rate * 4.00,
monthly_rate
)
Đó là những gì SQL Server đã làm với truy vấn của chúng tôi với COALESCE. Tất cả là để nhận giá trị đầu tiên không phải là giá trị rỗng trong danh sách các giá trị.
Nó có thể ngắn hơn không?
Tôi biết bạn đang nghĩ gì. Nếu SQL Server làm điều đó trong quá trình xử lý truy vấn, COALESCE phải chậm. Chưa kể đến sự xuất hiện nhiều lần của CONVERT_IMPLICIT. Bạn có muốn sử dụng các lựa chọn thay thế không?
Đối với một, bạn có thể tự mình nhập câu lệnh CASE ngắn hơn. Hoặc, bạn có thể sử dụng ISNULL. Thêm về điều đó sau. Nói về sự chậm chạp, tôi đã đề cập đến điều đó trước khi bài đăng này kết thúc.
Sự khác biệt giữa COALESCE và ISNULL trong SQL là gì?
Một trong những lựa chọn thay thế cho THAN là ISNULL. Sử dụng COALESCE với 2 giá trị làm cho nó tương tự như ISNULL. Ít nhất, kết quả trông giống nhau. Tuy nhiên, vẫn có những khác biệt đáng chú ý. Bạn có thể sử dụng nó như một hướng dẫn để quyết định xem bạn sẽ sử dụng COALESCE hay ISNULL.
(1) ISNULL chấp nhận 2 đối số. COALESCE chấp nhận một danh sách các lập luận
Đó là sự khác biệt rõ ràng nhất. Từ cú pháp, chắc chắn là khác.
ISNULL (check_expression, Replace_value)
COALESCE (biểu thức [,… n])
Khi bạn sử dụng cả hai với 2 đối số, kết quả là như nhau. 2 câu lệnh dưới đây sẽ dẫn đến kết quả là 1:
SELECT ISNULL(NULL, 1)
SELECT COALESCE(NULL, 1)
Mặc dù kết quả giống nhau nhưng chúng có ý nghĩa khác nhau:
- ISNULL (NULL, 1) trả về 1 vì đối số đầu tiên là NULL.
- COALESCE (NULL, 1) trả về 1 vì 1 là giá trị không rỗng đầu tiên trong danh sách .
(2) COALESCE là Chuẩn SQL-92
Đúng rồi. Bạn có thể kiểm tra nó. Ví dụ:nếu bạn muốn chuyển mã SQL của mình sang MySQL từ SQL Server, COALESCE sẽ hoạt động tương tự. Xem Hình 4 và so sánh kết quả từ Hình 1:
Tuy nhiên, việc sử dụng ISNULL trong MySQL sẽ gây ra lỗi nếu bạn sử dụng cú pháp của SQL Server.
ví dụ:Chạy phần sau trong SQL Server Management Studio và MySQL Workbench:
SELECT ISNULL(null,1)
Chuyện gì đã xảy ra thế? Trong SQL Server, đầu ra là 1. Nhưng trong MySQL, đầu ra là lỗi:
06:36:52 CHỌN ISNULL (null, 1) Mã lỗi:1582. Đếm tham số không chính xác trong lệnh gọi hàm gốc ‘ISNULL’
Vấn đề là, ISNULL trong MySQL chấp nhận 1 đối số và trả về 1 nếu đối số là null. Nếu không, nó trả về 0.
(3) Vượt qua 2 Null trong COALESCE gây ra lỗi. Nó ổn với ISNULL
Điều này sẽ gây ra lỗi:
SELECT COALESCE(NULL, NULL)
Lỗi là:‘Ít nhất một trong các đối số của COALESCE phải là một biểu thức không phải là hằng số NULL.’
Điều này sẽ ổn thôi:
SELECT ISNULL(NULL, NULL)
Thay đổi mã COALESCE thành mã tương tự bên dưới sẽ không gây ra lỗi:
DECLARE @value INT = NULL
SELECT COALESCE(@value,null)
Điều đó xảy ra vì @value không phải là hằng số rỗng.
(4) SQL COALESCE được chuyển đổi thành CASE. ISNULL Lưu trữ ISNULL
Chúng tôi đã thấy điều này trong ‘COALESCE hoạt động như thế nào trong SQL?’ tiết diện. Sau đây, hãy xem xét một ví dụ khác:
SELECT
P.LastName + ', ' + P.FirstName + ' ' + COALESCE(P.MiddleName + ' ','') AS FullName
FROM Person.Person p
Kiểm tra XML kế hoạch thực thi cho toán tử vô hướng tiết lộ chuyển đổi thành CASE:
[AdventureWorks].[Person].[Person].[LastName] as [p].[LastName]+N', '
+[AdventureWorks].[Person].[Person].[FirstName] as [p].[FirstName]+N' '
+CASE WHEN ([AdventureWorks].[Person].[Person].[MiddleName] as [p].[MiddleName]+N' ') IS NOT NULL
THEN [AdventureWorks].[Person].[Person].[MiddleName] as [p].[MiddleName]+N' '
ELSE N''
END
Bây giờ, hãy chạy một truy vấn tương đương bằng ISNULL:
SELECT
P.LastName + ', ' + P.FirstName + ' ' + ISNULL(P.MiddleName + ' ','') AS FullName
FROM Person.Person p
Sau đó, kiểm tra XML kế hoạch thực thi cho toán tử vô hướng:
[AdventureWorks].[Person].[Person].[LastName] as [p].[LastName]+N', '
+[AdventureWorks].[Person].[Person].[FirstName] as [p].[FirstName]+N' '
+isnull([AdventureWorks].[Person].[Person].[MiddleName] as [p].[MiddleName]+N' ',N'')
ISNULL vẫn là ISNULL.
(5) Loại Dữ liệu của Biểu thức Kết quả là Khác nhau
Việc xác định kiểu dữ liệu của biểu thức kết quả cũng khác nhau giữa COALESCE và ISNULL:
- ISNULL sử dụng kiểu dữ liệu của tham số đầu tiên.
- COALESCE trả về kiểu dữ liệu của giá trị có mức độ ưu tiên cao nhất.
Để biết danh sách ưu tiên kiểu dữ liệu, hãy xem liên kết này.
Hãy lấy một ví dụ:
SELECT
employee_id
,COALESCE(CAST(weekly_rate * 4 AS MONEY),0.0000) AS monthly_rate
FROM EmployeeWages
Sau đó, kiểm tra chuyển đổi thành CASE trong XML kế hoạch thực thi :
CASE WHEN CONVERT(money,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate]
*CONVERT_IMPLICIT(smallmoney,[@1],0),0) IS NOT NULL
THEN CONVERT_IMPLICIT(numeric(19,4), CONVERT(money,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate]
*CONVERT_IMPLICIT(smallmoney,[@1],0),0),0)
ELSE (0.0000)
END
Trong biểu thức CASE ở trên, kiểu dữ liệu của kết quả sẽ là số (19,4).
Tại sao? Nó có mức độ ưu tiên cao hơn tiền và smallmoney ngay cả khi bạn ĐĂNG nó thành tiền . Tại sao lại là số chứ không phải tiền ? Vì hằng số 0,0000.
Trong trường hợp bạn đang thắc mắc @ 1 là gì, thì XML kế hoạch thực thi có câu trả lời. Nó là số không đổi 4.
<ParameterList>
<ColumnReference Column="@1" ParameterDataType="int" ParameterCompiledValue="(4)"
ParameterRuntimeValue="(4)" />
</ParameterList>
Hãy thử nó với ISNULL:
SELECT
employee_id
,ISNULL(CAST(weekly_rate * 4 AS MONEY),0.0000) AS monthly_rate
FROM EmployeeWages
Một lần nữa, hãy tìm kiếm Toán tử vô hướng ‘S Chuỗi vô hướng :
ISNULL(CONVERT(MONEY,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate]*($4.0000),0),($0.0000))
Cuối cùng, kiểu dữ liệu của biểu thức kết quả sẽ là tiền . Đây là kiểu dữ liệu của đối số đầu tiên.
Cách vượt qua ưu tiên dữ liệu
Bạn có thể ưu tiên dữ liệu ‘outsmart’ bằng cách thêm một vài thay đổi vào mã của mình. Trước đó, kết quả có số loại dữ liệu. Nếu bạn muốn kết quả là tiền loại dữ liệu và loại bỏ CONVERT_IMPLICIT, hãy làm như sau:
SELECT
employee_id
,COALESCE(CAST(weekly_rate AS MONEY) * ($4.0000),($0.0000)) AS monthly_rate
FROM EmployeeWages
Bạn có nhận thấy các hằng số ($ 4.000) và ($ 0,0000) không? Đó là tiền các hằng số. Điều gì xảy ra tiếp theo sẽ xuất hiện trong XML kế hoạch thực thi ‘S Chuỗi vô hướng :
CASE WHEN CONVERT(money,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*($4.0000) IS NOT NULL
THEN CONVERT(money,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*($4.0000)
ELSE ($0.0000)
END
Điều đó tốt hơn nhiều. Nó ngắn hơn và CONVERT_IMPLICIT đã biến mất. Và loại dữ liệu kết quả là tiền .
(6) Tính NULL của Biểu thức Kết quả là Khác nhau
ISNULL (NULL, 1) và COALESCE (NULL, 1) có kết quả tương tự, nhưng giá trị nullability của chúng khác nhau. COALESCE là vô hiệu. ISNULL không. Bạn có thể thấy điều này khi sử dụng nó trên các cột được tính toán.
Hãy tham khảo một ví dụ. Câu lệnh dưới đây sẽ gây ra lỗi vì KHÓA CHÍNH không thể chấp nhận các giá trị NULL. Đồng thời, tính vô hiệu của biểu thức COALESCE cho column2 đánh giá là NULL.
CREATE TABLE NullabilityDemo
(
column1 INTEGER NULL,
column2 AS COALESCE(column1, 0) PRIMARY KEY,
column3 AS ISNULL(column1, 0)
);
Câu lệnh này thành công vì tính vô hiệu của hàm ISNULL đánh giá AS NOT NULL.
CREATE TABLE NullabilityDemo
(
column1 INTEGER NULL,
column2 AS COALESCE(column1, 0),
column3 AS ISNULL(column1, 0) PRIMARY KEY
);
(7) Đối số Bên trái của ISNULL được đánh giá một lần. Ngược lại với COALESCE
Hãy xem xét lại ví dụ trước:
SELECT
employee_id,
COALESCE(
hourly_rate*22.00*8.00,
weekly_rate*4.00,
monthly_rate
) AS monthly_salary
FROM
EmployeeWages;
Sau đó, kiểm tra Chuỗi vô hướng cho điều này:
CASE WHEN CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)
*(22.00)*(8.00) IS NOT NULL
THEN CONVERT_IMPLICIT(numeric(23,8),CONVERT_IMPLICIT(numeric(10,4),
[TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)
*(22.00)*(8.00),0)
ELSE CASE WHEN CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)
*(4.00) IS NOT NULL
THEN CONVERT_IMPLICIT(numeric(23,8),CONVERT_IMPLICIT(numeric(10,4),
[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*(4.00),0)
ELSE CONVERT_IMPLICIT(numeric(23,8),[TestDatabase].[dbo].[EmployeeWages].[monthly_rate],0)
END
END
Khi hàm COALESCE trong SQL được chuyển đổi thành CASE, mỗi biểu thức được đánh giá hai lần (ngoại trừ biểu thức cuối cùng). Như bạn có thể thấy ở trên, hourly_rate * 22,00 * 8,00 xuất hiện hai lần. Điều tương tự với week_rate * 4.00 . Biểu thức cuối cùng, month_rate , đã xuất hiện một lần.
Vì COALESCE sẽ đánh giá các biểu thức hai lần, nên có thể có một hình phạt về hiệu suất. Tìm hiểu thêm về điều này sau.
Tuy nhiên, hãy kiểm tra ISNULL tương đương:
SELECT
employee_id,
ISNULL(hourly_rate * 22.00 * 8.00,ISNULL(weekly_rate * 4.00,monthly_rate)) AS
monthly_salary
FROM EmployeeWages
Sau đó, chúng tôi kiểm tra Chuỗi vô hướng trong XML kế hoạch thực thi :
isnull(CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)*(22.00)*(8.00),
CONVERT_IMPLICIT(numeric(19,8),
isnull(CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*(4.00),
CONVERT_IMPLICIT(numeric(14,6),[TestDatabase].[dbo].[EmployeeWages].[monthly_rate],0)),0))
Như bạn có thể thấy ở trên, hourly_rate , week_rate và month_rate chỉ xuất hiện một lần. Vì vậy, bạn không cần phải lo lắng về việc các biểu thức được đánh giá hai lần với ISNULL.
Chúng ta có thể sử dụng COALESCE trong Mệnh đề WHERE không?
Điều chắc chắn. Không có cách nào tốt hơn là đưa ra một ví dụ để chứng minh điều này.
-- Query all the names in Person table with no middle name
USE AdventureWorks
GO
SELECT
p.LastName
,p.FirstName
FROM person.Person p
WHERE COALESCE(p.MiddleName,'') = ''
COALESCE sẽ trả về một chuỗi trống nếu MiddleName là NULL. Tất nhiên, có một cách ngắn hơn để tạo ra kết quả. Nhưng điều này cho thấy COALESCE hoạt động trong mệnh đề WHERE.
Cái nào nhanh hơn:COALESCE hay ISNULL?
Cuối cùng, chúng ta đã đến một chủ đề nóng:Hiệu suất!
Bạn sẽ nhận được nhiều trang với các bài kiểm tra và so sánh, nhưng sẽ có một cuộc chiến giữa những người ủng hộ COALESCE và ISNULL trong các nhận xét. Chúng tôi sẽ loại bỏ khói bụi của những cuộc chiến tranh đó.
Cái nào nhanh hơn:COALESCE hay ISNULL? Hãy để tôi bắt đầu bằng cách nói rằng câu trả lời là:
(trống lăn)
NÓ PHỤ THUỘC!
(há hốc mồm)
Thất vọng? Tôi sẽ giải thích điều đó trong giây lát.
Đầu tiên, tôi đồng ý rằng dường như cả hai đều có sự khác biệt về hiệu suất nếu bạn sử dụng thời gian đã trôi qua làm chỉ số của mình. Một số người đã hỗ trợ thực tế này khi SQL Server dịch câu lệnh COALESCE thành CASE. Trong khi đó, ISNULL vẫn giữ nguyên.
Những người khác có thể lập luận khác nhau vì kết quả khác nhau. Ngoài ra, đối với họ, chuyển đổi COALESCE thành CASE nhanh hơn chúng ta chớp mắt. Cũng giống như những gì được chỉ ra trong chủ đề này, sự khác biệt về hiệu suất "là rất nhỏ". Tôi đồng ý. Đây là một bài báo khác nói rằng sự khác biệt "là nhỏ".
Tuy nhiên, đây là một vấn đề lớn:chúng ta có thể tin tưởng một khoảng thời gian rất nhỏ đã trôi qua làm thước đo không? Điều thực sự quan trọng là truy vấn có bao nhiêu lượt đọc logic. Đó là những gì chúng tôi sẽ chỉ ra trong các ví dụ tiếp theo của chúng tôi.
(Xem bài viết khác của tôi về các lần đọc logic và lý do tại sao yếu tố này làm chậm các truy vấn của bạn.)
Ví dụ 1
Hãy xem xét cùng một ví dụ và so sánh các lần đọc và kế hoạch thực hiện hợp lý của chúng. Trước khi chạy, hãy đảm bảo IO STATISTICS IO đang BẬT và Bao gồm kế hoạch thực thi thực tế được bật.
SET STATISTICS IO ON
SELECT
P.LastName + ', ' + P.FirstName + ' ' + COALESCE(P.MiddleName + ' ','') AS FullName
FROM Person.Person p
SELECT
P.LastName + ', ' + P.FirstName + ' ' + ISNULL(P.MiddleName + ' ','') AS FullName
FROM Person.Person p
Đây là sự thật:
Đọc logic cho cả hai truy vấn là như nhau. Cả hai đều là 107 * 8KB trang. Tất nhiên, nếu chúng ta có một triệu bản ghi, số lần đọc logic sẽ tăng lên. Nhưng số lần đọc logic cho cả hai truy vấn sẽ bằng nhau. Đó là trường hợp ngay cả khi COALESCE được chuyển đổi thành CASE:
Hãy kiểm tra kế hoạch thực hiện. Đây là cách chúng tôi sẽ làm điều đó:
- Thực thi câu lệnh SELECT đầu tiên với biểu thức COALESCE.
- Nhấp vào Kế hoạch Thực thi tab trong kết quả. Nhấp chuột phải vào nó và chọn Lưu kế hoạch thực thi dưới dạng . Và đặt tên tệp là plan1.sqlplan .
- Thực thi câu lệnh SELECT thứ 2 với hàm ISNULL.
- Nhấp vào Kế hoạch Thực thi tab trong kết quả.
- Nhấp chuột phải vào nó và chọn So sánh sơ đồ trình chiếu .
- Chọn tệp plan1.sqlplan . Một cửa sổ mới sẽ xuất hiện.
Đó là cách chúng tôi sẽ kiểm tra kế hoạch thực thi cho tất cả các ví dụ.
Quay lại ví dụ đầu tiên của chúng tôi, xem Hình 6 để xem so sánh kế hoạch thực thi:
Bạn có nhận thấy những điểm quan trọng này trong Hình 6 không?
- Phần bóng mờ của 2 kế hoạch (Quét chỉ mục) có nghĩa là Máy chủ SQL đã sử dụng các hoạt động giống nhau cho 2 truy vấn.
- QueryPlanHash đối với 2 gói là 0x27CEB4CCE12DA5E7, nghĩa là gói này giống nhau cho cả hai.
- Chênh lệch vài mili giây cho thời gian đã trôi qua là không đáng kể.
Bài học rút ra
Nó giống như so sánh táo với táo, nhưng khác loại. Một là táo Fuji từ Nhật Bản, một là táo đỏ từ New York. Tuy nhiên, cả hai đều là những quả táo.
Tương tự, SQL Server yêu cầu cùng một tài nguyên và kế hoạch thực thi đã chọn cho cả hai truy vấn. Sự khác biệt duy nhất là sử dụng COALESCE hoặc ISNULL. Sự khác biệt nhỏ, vì kết quả cuối cùng là giống nhau.
Ví dụ 2
Sự khác biệt lớn xuất hiện khi bạn sử dụng truy vấn con làm đối số cho cả COALESCE và ISNULL:
USE AdventureWorks
GO
SELECT COALESCE(
(SELECT
SUM(th.ActualCost)
FROM Production.TransactionHistory th
WHERE th.ProductID = 967)
,0)
SELECT ISNULL(
(SELECT
SUM(th.ActualCost)
FROM Production.TransactionHistory th
WHERE th.ProductID = 967)
,0)
Đoạn mã trên sẽ có cùng kết quả, nhưng bên trong rất khác.
Hãy bắt đầu với các bài đọc hợp lý:
Câu lệnh SELECT với biểu thức COALESCE có gấp đôi số lần đọc logic của câu lệnh đã sử dụng ISNULL.
Nhưng tại sao lại tăng gấp đôi số lần đọc hợp lý? So sánh kế hoạch thực hiện sẽ tiết lộ nhiều hơn:
Hình 8 giải thích lý do tại sao các lần đọc logic lại tăng gấp đôi bằng cách sử dụng COALESCE. Xem 2 nút Tổng hợp Luồng trong sơ đồ dưới cùng:chúng là bản sao. Câu hỏi tiếp theo là, tại sao nó lại bị trùng lặp?
Hãy nhớ lại điểm liên quan đến thời điểm COALESCE được chuyển đổi thành CASE. Bao nhiêu lần các biểu thức được đánh giá trong các đối số? TWICE!
Vì vậy, truy vấn con được đánh giá hai lần. Nó hiển thị trong kế hoạch thực thi với các nút trùng lặp.
Điều này cũng giải thích việc đọc lôgic kép bằng cách sử dụng COALESCE so với ISNULL. Nếu bạn định xem xét XML kế hoạch thực thi của truy vấn với COALESCE, thì nó sẽ khá dài. Nhưng nó tiết lộ rằng truy vấn con sẽ được thực thi hai lần.
Giờ thì sao? Chúng ta có thể thông minh hơn điều này không? Tất nhiên rồi! Nếu bạn đã từng đối mặt với bất cứ điều gì như thế này, điều mà tôi tin là rất hiếm, thì cách khắc phục có thể là chia rẽ và chinh phục. Hoặc, sử dụng ISNULL nếu đó chỉ là một truy vấn con.
Cách tránh đánh giá hai lần biểu thức truy vấn con
Dưới đây là cách tránh đánh giá hai lần truy vấn con:
- Khai báo một biến và gán kết quả của truy vấn con cho nó.
- Sau đó, chuyển biến làm đối số cho COALESCE.
- Lặp lại các bước tương tự tùy thuộc vào số lượng truy vấn phụ.
Như tôi đã nói, nó sẽ hiếm khi xảy ra, nhưng nếu nó xảy ra, bạn biết phải làm gì bây giờ.
Một vài từ về mức độ cô lập
Đánh giá truy vấn con hai lần có thể gây ra một vấn đề khác. Tùy thuộc vào mức độ cô lập của truy vấn của bạn, kết quả của lần đánh giá đầu tiên có thể khác với lần đánh giá thứ hai trong môi trường nhiều người dùng. Thật là điên rồ.
Để đảm bảo kết quả trả về ổn định, bạn có thể thử sử dụng SNAPSHOT ISOLATION. Ngoài ra, bạn có thể sử dụng ISNULL. Hoặc, nó có thể là một cách tiếp cận chia để trị, như đã chỉ ra ở trên.
Bài học rút ra
Vậy, bản chất là gì?
- Luôn kiểm tra các lần đọc hợp lý. Nó quan trọng hơn thời gian đã trôi qua. Sử dụng thời gian đã trôi qua và lấy đi sự tỉnh táo của bạn. Máy của bạn và máy chủ sản xuất sẽ luôn có các kết quả khác nhau. Hãy cho bản thân nghỉ ngơi.
- Luôn kiểm tra kế hoạch thực hiện để bạn biết điều gì đang diễn ra.
- Lưu ý rằng khi COALESCE được dịch sang CASE, các biểu thức được đánh giá hai lần. Khi đó, một truy vấn con hoặc một thứ gì đó tương tự có thể là một vấn đề. Gán kết quả truy vấn con cho một biến trước khi sử dụng nó trong COALESCE có thể là một giải pháp.
- Điều gì nhanh hơn phụ thuộc vào kết quả đọc hợp lý và kế hoạch thực hiện. Không có quy tắc chung nào để xác định xem COALESCE hay ISNULL nhanh hơn. Nếu không, Microsoft có thể thông báo về điều đó, như họ đã làm trong HierarchyID và SQL Graph.
Cuối cùng, nó thực sự phụ thuộc.
Kết luận
Tôi hy vọng bạn đã có một giá trị đọc bài viết này. Dưới đây là những điểm chúng ta đã thảo luận:
- COALESCE là một trong những cách để xử lý null. Đó là một mạng lưới an toàn để tránh lỗi trong mã.
- Nó chấp nhận danh sách đối số và trả về giá trị khác rỗng đầu tiên.
- Nó được chuyển đổi thành biểu thức CASE trong quá trình xử lý truy vấn, nhưng nó không làm chậm truy vấn.
- Mặc dù có một số điểm tương đồng với ISNULL, nhưng có 7 điểm khác biệt đáng chú ý đối với ISNULL.
- Nó có thể được sử dụng với mệnh đề WHERE.
- Cuối cùng, nó không nhanh hơn hoặc chậm hơn ISNULL.
Nếu bạn thích bài đăng này, các nút mạng xã hội đang chờ bạn nhấp chuột. Chia sẻ là quan tâm.
Xin cảm ơn.
Đọc thêm
Xử lý các giá trị NULL một cách hiệu quả với Hàm COALESCE trong SQL cho người mới bắt đầu
Cách sử dụng thực tế của Hàm COALESCE trong SQL