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

Hiệu suất của các biến bảng trong SQL Server

Trong bài viết này, chúng ta sẽ đề cập đến chủ đề về hiệu suất của các biến bảng. Trong SQL Server, chúng ta có thể tạo các biến hoạt động dưới dạng bảng hoàn chỉnh. Có lẽ, các cơ sở dữ liệu khác có cùng khả năng, tuy nhiên, tôi chỉ sử dụng các biến như vậy trong MS SQL Server.

Do đó, bạn có thể viết như sau:

declare @t as table (int value)

Ở đây, chúng tôi khai báo biến @t dưới dạng một bảng sẽ chứa một cột Giá trị duy nhất của kiểu Số nguyên. Có thể tạo các bảng phức tạp hơn, tuy nhiên, trong ví dụ của chúng tôi, một cột là đủ để khám phá tối ưu hóa.

Bây giờ, chúng ta có thể sử dụng biến này trong các truy vấn của mình. Chúng tôi có thể thêm nhiều dữ liệu vào nó và thực hiện truy xuất dữ liệu từ biến này:

insert into @t
select UserID
from User
or
select * from @t

Tôi nhận thấy rằng các biến bảng được sử dụng khi cần tìm nạp dữ liệu cho một lựa chọn lớn. Ví dụ:có một truy vấn trong mã trả về người dùng của trang web. Bây giờ, bạn thu thập ID của tất cả người dùng, thêm họ vào biến bảng và có thể tìm kiếm địa chỉ cho những người dùng này. Có lẽ, ai đó có thể hỏi tại sao chúng tôi không thực hiện một truy vấn trên cơ sở dữ liệu và nhận được mọi thứ ngay lập tức? Tôi có một ví dụ đơn giản.

Giả sử rằng người dùng đến từ dịch vụ Web, trong khi địa chỉ của họ được lưu trữ trong cơ sở dữ liệu của bạn. Trong trường hợp này, không có lối thoát. Chúng tôi nhận được một loạt các ID người dùng từ dịch vụ và để tránh truy vấn cơ sở dữ liệu, ai đó quyết định rằng việc thêm tất cả các ID vào tham số truy vấn dưới dạng một biến bảng sẽ dễ dàng hơn và truy vấn sẽ trông gọn gàng:

select *
from @t as users 
   join Address a on a.UserID = users.UserID
os

Tất cả điều này hoạt động chính xác. Trong mã C #, bạn có thể nhanh chóng kết hợp kết quả của cả hai mảng dữ liệu thành một đối tượng bằng cách sử dụng LINQ. Tuy nhiên, hiệu suất của truy vấn có thể bị ảnh hưởng.

Thực tế là các biến bảng không được thiết kế để xử lý khối lượng lớn dữ liệu. Nếu tôi không nhầm, trình tối ưu hóa truy vấn sẽ luôn sử dụng phương pháp thực thi LOOP. Do đó, đối với mỗi ID từ @t, một tìm kiếm trong bảng Địa chỉ sẽ xảy ra. Nếu có 1000 bản ghi trong @t, máy chủ sẽ quét Địa chỉ 1000 lần.

Về mặt thực thi, do số lần quét quá lớn, máy chủ chỉ đơn giản là cố gắng tìm kiếm dữ liệu.

Sẽ hiệu quả hơn nhiều khi quét toàn bộ bảng Địa chỉ và tìm tất cả người dùng cùng một lúc. Phương pháp này được gọi là MERGE. Tuy nhiên, SQL Server chọn nó khi có nhiều dữ liệu được sắp xếp. Trong trường hợp này, trình tối ưu hóa không biết số lượng và dữ liệu nào sẽ được thêm vào biến và liệu có sắp xếp hay không vì biến như vậy không bao gồm chỉ mục.

Nếu có ít dữ liệu trong biến bảng và bạn không chèn hàng nghìn hàng vào đó thì mọi thứ vẫn ổn. Tuy nhiên, nếu bạn muốn sử dụng các biến như vậy và thêm một lượng lớn dữ liệu vào chúng, bạn phải tiếp tục đọc.

Ngay cả khi bạn thay thế biến bảng bằng SQL, nó sẽ tăng tốc đáng kể hiệu suất truy vấn:

select *
from (
 Select 10377 as UserID
 Union all
 Select 73736
 Union all
 Select 7474748
 ….
  ) as users 
   join Address a on a.UserID = users.UserID

Có thể có hàng nghìn câu lệnh SELECT như vậy và văn bản truy vấn sẽ rất lớn, nhưng nó sẽ được thực thi nhanh hơn hàng nghìn lần đối với một lượng lớn dữ liệu vì SQL Server có thể chọn một kế hoạch thực thi hiệu quả.

Truy vấn này trông không tuyệt. Tuy nhiên, kế hoạch thực thi của nó không thể được lưu vào bộ nhớ đệm vì chỉ thay đổi một ID cũng sẽ thay đổi toàn bộ văn bản truy vấn và không thể sử dụng các tham số.

Tôi nghĩ rằng Microsoft không mong đợi người dùng sử dụng các biến dạng bảng theo cách này, nhưng có một cách giải quyết tốt.

Có một số cách để giải quyết vấn đề này. Tuy nhiên, theo tôi, hiệu quả nhất về mặt hiệu suất là thêm TÙY CHỌN (RECOMPILE) vào cuối truy vấn:

select *
from @t as users 
   join Address a on a.UserID = users.UserID
OPTION (RECOMPILE)

Tùy chọn này được thêm một lần vào cuối truy vấn sau ngay cả ORDER BY. Mục đích của tùy chọn này là làm cho SQL Server biên dịch lại truy vấn sau mỗi lần thực thi.

Nếu chúng tôi đo lường hiệu suất truy vấn sau đó, rất có thể thời gian để thực hiện tìm kiếm sẽ giảm xuống. Với dữ liệu lớn, việc cải thiện hiệu suất có thể đáng kể, từ hàng chục phút đến vài giây. Bây giờ, máy chủ biên dịch mã của nó trước khi chạy mỗi truy vấn và không sử dụng kế hoạch thực thi từ bộ nhớ cache, nhưng tạo một kế hoạch mới, tùy thuộc vào lượng dữ liệu trong biến và điều này thường giúp ích rất nhiều.

Hạn chế là kế hoạch thực thi không được lưu trữ và máy chủ phải biên dịch truy vấn và tìm kiếm một kế hoạch thực thi hiệu quả mỗi lần. Tuy nhiên, tôi chưa thấy các truy vấn trong đó quá trình này mất hơn 100 mili giây.

Sử dụng các biến bảng có phải là một ý tưởng tồi không? Không có nó không phải là. Chỉ cần nhớ rằng chúng không được tạo cho dữ liệu lớn. Đôi khi, tốt hơn là tạo một bảng tạm thời, nếu có nhiều dữ liệu và chèn dữ liệu vào bảng này, hoặc thậm chí tạo một chỉ mục ngay lập tức. Tôi đã phải làm điều này với các báo cáo, mặc dù chỉ một lần. Hồi đó, tôi đã giảm thời gian tạo một báo cáo từ 3 giờ xuống còn 20 phút.

Tôi thích sử dụng một truy vấn lớn thay vì chia nó thành nhiều truy vấn và lưu trữ kết quả trong các biến. Cho phép SQL Server điều chỉnh hiệu suất của một truy vấn lớn và nó sẽ không làm bạn thất vọng. Xin lưu ý rằng bạn chỉ nên sử dụng các biến bảng trong những trường hợp cực đoan khi bạn thực sự thấy lợi ích của chú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. DATETIME2FROMPARTS () Ví dụ trong SQL Server (T-SQL)

  2. Truy vấn SQL chậm trong ứng dụng .NET nhưng ngay lập tức trong SQL Server Management Studio

  3. Tại sao UDF lại chậm hơn nhiều so với truy vấn con?

  4. Làm cách nào để tạo một bước trong SQL Server Agent Job sẽ chạy gói SSIS của tôi?

  5. Cải tiến hiệu suất &khả năng quản lý ẩn trong SQL Server 2012/2014