Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Kiểm tra xem cột không phải LOB có cần được cập nhật hay không

Đôi khi tôi thấy mọi người cố gắng "tối ưu hóa" các câu lệnh cập nhật của họ để tránh ghi cùng một giá trị vào một cột cụ thể. Tôi luôn hiểu rằng nếu bạn định cập nhật một hàng, giả sử tất cả các giá trị đều nằm trong hàng, thì chi phí khóa hàng sẽ cao hơn nhiều so với chi phí gia tăng của việc cập nhật một, hai hoặc tất cả các cột trong hàng đó .

Vì vậy, tôi đã tạo một bảng đơn giản để kiểm tra điều này:

CREATE TABLE dbo.whatever
(
  ID INT IDENTITY(1,1) PRIMARY KEY,
  v1 NVARCHAR(50) NOT NULL,
  v2 NVARCHAR(50) NOT NULL,
  v3 NVARCHAR(50) NOT NULL,
  v4 NVARCHAR(50) NOT NULL,
  v5 NVARCHAR(50) NOT NULL,
  v6 NVARCHAR(50) NOT NULL
);

Sau đó, tôi tạo một thủ tục được lưu trữ để điền vào bảng 50.000 hàng với nhiều chuỗi nhỏ khác nhau:

CREATE PROCEDURE dbo.clean
AS
BEGIN
  SET NOCOUNT ON;
 
  TRUNCATE TABLE dbo.whatever;
 
  ;WITH x(d) AS
  (
    SELECT d FROM
    (
      VALUES (N'abc'),(N'def'),(N'ghi'),
             (N'jkl'),(N'mno'),(N'pqr')
    ) AS y(d)
  )
  INSERT dbo.whatever(v1, v2, v3, v4, v5, v6)
  SELECT TOP (50000) x1.d, x2.d, x3.d, x4.d, x5.d, x6.d
   FROM x AS x1, x AS x2, x AS x3, x AS x4,
        x AS x5, x AS x6, x AS x7;
END
GO

Sau đó, tôi đã viết các câu lệnh cập nhật được xây dựng theo hai cách mà bạn có thể "tránh" việc ghi vào một cột cụ thể, với sự gán biến này:

DECLARE
  @v1 NVARCHAR(50) = N'abc',
  @v2 NVARCHAR(50) = N'def',
  @v3 NVARCHAR(50) = N'ghi',
  @v4 NVARCHAR(50) = N'jkl',
  @v5 NVARCHAR(50) = N'mno',
  @v6 NVARCHAR(50) = N'pqr';

Đầu tiên bằng cách sử dụng biểu thức CASE để kiểm tra xem giá trị trong cột có giống với giá trị trong biến không:

UPDATE dbo.whatever SET
  v1 = CASE WHEN v1 <> @v1 THEN @v1 ELSE v1 END,
  v2 = CASE WHEN v2 <> @v2 THEN @v2 ELSE v2 END,
  v3 = CASE WHEN v3 <> @v3 THEN @v3 ELSE v3 END,
  v4 = CASE WHEN v4 <> @v4 THEN @v4 ELSE v4 END,
  v5 = CASE WHEN v5 <> @v5 THEN @v5 ELSE v5 END,
  v6 = CASE WHEN v6 <> @v6 THEN @v6 ELSE v6 END
WHERE
(
     v1 <> @v1 OR v2 <> @v2 OR v3 <> @v3 
  OR v4 <> @v4 OR v5 <> @v5 OR v6 <> @v6
);

Và thứ hai bằng cách đưa ra một CẬP NHẬT độc lập cho mỗi cột (mỗi cột chỉ nhắm mục tiêu các hàng mà trên thực tế, giá trị đó đã thay đổi):

UPDATE dbo.whatever SET v1 = @v1 WHERE v1 <> @v1;
UPDATE dbo.whatever SET v2 = @v2 WHERE v2 <> @v2;
UPDATE dbo.whatever SET v3 = @v3 WHERE v3 <> @v3;
UPDATE dbo.whatever SET v4 = @v4 WHERE v4 <> @v4;
UPDATE dbo.whatever SET v5 = @v5 WHERE v5 <> @v5;
UPDATE dbo.whatever SET v6 = @v6 WHERE v6 <> @v6;

Sau đó, tôi sẽ so sánh điều này với cách hầu hết chúng ta sẽ làm điều này ngày nay:chỉ cần CẬP NHẬT tất cả các cột mà không cần quan tâm đó có phải là giá trị đã có từ trước cho cột cụ thể đó không:

UPDATE dbo.whatever SET
  v1 = @v1, v2 = @v2, v3 = @v3,
  v4 = @v4, v5 = @v5, v6 = @v6
WHERE
(
     v1 <> @v1 OR v2 <> @v2 OR v3 <> @v3 
  OR v4 <> @v4 OR v5 <> @v5 OR v6 <> @v6
);

(Tất cả đều giả định rằng các cột và tham số / biến không phải là NULL - chúng sẽ cần sử dụng COALESCE để tính đến việc so sánh các NULL ở hai bên nếu trường hợp đó xảy ra. Chúng cũng giả định rằng bạn sẽ có thêm mệnh đề WHERE để nhắm mục tiêu các hàng cụ thể - trong ví dụ này, bạn có thể chạy các truy vấn đầu tiên và thứ ba mà không có mệnh đề WHERE bao gồm tất cả và xem các kết quả gần như giống hệt nhau. Tôi giữ điều này đơn giản cho ngắn gọn.)

Sau đó, tôi muốn xem điều gì xảy ra trong ba trường hợp này khi bất kỳ giá trị nào có thể bị thay đổi, khi nào các giá trị cụ thể có thể bị thay đổi, khi nào không giá trị nào được thay đổi và khi nào tất cả các giá trị sẽ được thay đổi. Tôi có thể ảnh hưởng đến điều này bằng cách thay đổi quy trình được lưu trữ để chèn các hằng số vào các cột cụ thể hoặc bằng cách thay đổi cách chỉ định các biến.

-- to show when any value might change in a row, the procedure uses the full cross join:
 
  SELECT TOP (50000) x1.d, x2.d, x3.d, x4.d, x5.d, x6.d
 
-- to show when particular values will change on many rows, we can hard-code constants:
 
  -- two values exempt:
  SELECT TOP (50000) N'abc', N'def', x3.d, x4.d, x5.d, x6.d
 
  -- four values exempt:
  SELECT TOP (50000) N'abc', N'def', N'ghi', N'jkl', x5.d, x6.d
 
-- to show when no values will change, we hard-code all six values:
 
  SELECT TOP (50000) N'abc', N'def', N'ghi', N'jkl', N'mno', N'pqr'
 
-- and to show when all values will change, a different variable assignment would take place:
 
DECLARE
  @v1 NVARCHAR(50) = N'zzz',
  @v2 NVARCHAR(50) = N'zzz',
  @v3 NVARCHAR(50) = N'zzz',
  @v4 NVARCHAR(50) = N'zzz',
  @v5 NVARCHAR(50) = N'zzz',
  @v6 NVARCHAR(50) = N'zzz';

Kết quả

Sau khi chạy các bài kiểm tra này, "cập nhật mù" đã chiến thắng trong mọi tình huống đơn lẻ. Bây giờ, bạn đang nghĩ, một vài trăm mili giây là gì? Suy rộng ra. Nếu bạn đang thực hiện nhiều cập nhật trong hệ thống của mình, điều này thực sự có thể bắt đầu gây tốn kém.

Kết quả chi tiết trong Plan Explorer:Mọi thay đổi | 2 giá trị được miễn | 4 giá trị được miễn | Tất cả các giá trị được miễn | Tất cả đều thay đổi

Dựa trên phản hồi từ Roji, tôi cũng quyết định kiểm tra điều này với một vài chỉ mục:

CREATE INDEX x1 ON dbo.whatever(v1);
CREATE INDEX x2 ON dbo.whatever(v2);
CREATE INDEX x3 ON dbo.whatever(v3) INCLUDE(v4,v5,v6);

Thời lượng đã tăng lên đáng kể với các chỉ số này:

Kết quả chi tiết trong Plan Explorer:Mọi thay đổi | 2 giá trị được miễn | 4 giá trị được miễn | Tất cả các giá trị được miễn | Tất cả đều thay đổi

Kết luận

Từ bài kiểm tra này, đối với tôi, có vẻ như việc kiểm tra xem một giá trị có nên được cập nhật hay không thường không đáng giá. Nếu câu lệnh UPDATE của bạn ảnh hưởng đến nhiều cột, thì hầu như luôn rẻ hơn cho bạn khi quét tất cả các cột mà bất kỳ giá trị nào có thể đã thay đổi thay vì kiểm tra từng cột riêng lẻ. Trong một bài đăng trong tương lai, tôi sẽ điều tra xem liệu kịch bản này có song song với các cột LOB hay không.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Các phương pháp tốt nhất cho tổng số chạy được nhóm lại

  2. Cách cắt chuỗi trong SQL

  3. Cách sử dụng COUNT trong SQL?

  4. Cách nhóm theo hai cột trong SQL

  5. CTE có thể hỗ trợ như thế nào trong việc viết các truy vấn phức tạp, mạnh mẽ:Quan điểm về hiệu suất