Gần đây, tôi đã xem một ứng dụng tạo các truy vấn DB. Tôi hiểu rằng không có gì mới về điều đó, nhưng khi ứng dụng bắt đầu chạy chậm và tôi phải tìm ra lý do của việc chạy chậm, tôi đã rất ngạc nhiên khi tìm thấy những truy vấn này. Đây là những gì SQL Server đôi khi phải đối phó:
SELECT COUNT(DISTINCT "pr"."id") FROM ((((((((((((((((("SomeTable" "pr" LEFT OUTER JOIN "SomeTable1698" "uf_pr_id_698" ON "uf_pr_id_698"."request" = "pr"."id") LEFT OUTER JOIN "SomeTable1700" "ufref3737_i2" ON "ufref3737_i2"."request" = "pr"."id") LEFT OUTER JOIN "SomeTable1666" "x0" ON "x0"."request" = "ufref3737_i2"."f6_callerper") LEFT OUTER JOIN "SomeTable1666" "uf_ufref4646_i3_f58__666" ON "uf_ufref4646_i3_f58__666"."request" = "ufref3737_i2"."f58_") LEFT OUTER JOIN "SomeTable1694" "x1" ON "x1"."request" = "ufref3737_i2"."f38_servicep") LEFT OUTER JOIN "SomeTable3754" "ufref3754_i12" ON "pr"."id" = "ufref3754_i12"."request") LEFT OUTER JOIN "SomeTable1698" "uf_ufref3754_i12_reference_698" ON "uf_ufref3754_i12_reference_698"."request" = "ufref3754_i12"."reference") LEFT OUTER JOIN "SomeTable1698" "x2" ON "x2"."request" = "ufref3737_i2"."f34_parentse") LEFT OUTER JOIN "SomeTable4128" "ufref3779_4128_i14" ON "ufref3737_i2"."f34_parentse" = "ufref3779_4128_i14"."request") LEFT OUTER JOIN "SomeTable1859" "x3" ON "x3"."request" = "ufref3779_4128_i14"."reference") LEFT OUTER JOIN "SomeTable3758" "ufref3758_i15" ON "pr"."id" = "ufref3758_i15"."request") LEFT OUTER JOIN "SomeTable1698" "uf_ufref3758_i15_reference_698" ON "uf_ufref3758_i15_reference_698"."request" = "ufref3758_i15"."reference") LEFT OUTER JOIN "SomeTable3758" "ufref3758_i16" ON "pr"."id" = "ufref3758_i16"."request") LEFT OUTER JOIN "SomeTable4128" "ufref3758_4128_i16" ON "ufref3758_i16"."reference" = "ufref3758_4128_i16"."request") LEFT OUTER JOIN "SomeTable1859" "x4" ON "x4"."request" = "ufref3758_4128_i16"."reference") LEFT OUTER JOIN "SomeTable4128" "ufref4128_i17" ON "pr"."id" = "ufref4128_i17"."request") LEFT OUTER JOIN "SomeTable1859" "uf_ufref4128_i17_reference_859" ON "uf_ufref4128_i17_reference_859"."request" = "ufref4128_i17"."reference") LEFT OUTER JOIN "SomeTable1666" "uf_ufref4667_i25_f69__666" ON "uf_ufref4667_i25_f69__666"."request" = "uf_pr_id_698"."f69_" WHERE ("uf_pr_id_698"."f1_applicant" IN (248,169,180,201,203,205,209,215,223,357,371,379,3502,3503,3506,3514,3517,3531,3740,3741) OR "x0"."f24_useracco" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "uf_ufref4646_i3_f58__666"."f24_useracco" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "uf_ufref4667_i25_f69__666"."f24_useracco" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR ("uf_pr_id_698"."f10_status" Is Null OR "uf_pr_id_698"."f10_status" <> 111) AND "ufref3737_i2"."f96_" = 0 AND (("ufref3737_i2"."f17_source" Is Null OR "ufref3737_i2"."f17_source" <> 566425) AND ("ufref3737_i2"."f17_source" Is Null OR "ufref3737_i2"."f17_source" <> 566424) OR ("uf_pr_id_698"."f10_status" Is Null OR "uf_pr_id_698"."f10_status" <> 56) ) AND ("uf_pr_id_698"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "x1"."f19_restrict" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "uf_ufref3754_i12_reference_698"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "x2"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "x3"."f5_responsib" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "uf_ufref3758_i15_reference_698"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "x4"."f5_responsib" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) OR "uf_ufref4128_i17_reference_859"."f5_responsib" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)) AND ("uf_pr_id_698"."f12_responsi" Is Null OR "uf_pr_id_698"."f12_responsi" <> 579420) ) AND "pr"."area" IN (700) AND "pr"."area" IN (700) AND "pr"."deleted_by_user"=0 AND "pr"."temporary" = 0
Tên của các đối tượng đã được thay đổi.
Điều nổi bật nhất là cùng một bảng đã được sử dụng nhiều lần, và số lượng dấu ngoặc đơn đã khiến tôi phát điên. Tôi không phải là người duy nhất không thích mã này, SQL Server cũng không đánh giá cao nó và dành nhiều tài nguyên để xây dựng kế hoạch cho nó. Truy vấn có thể chạy trong 50-150 mili giây và quá trình tạo kế hoạch có thể mất đến 2,5 mili giây. Hôm nay, tôi sẽ không xem xét các cách để khắc phục sự cố, nhưng tôi sẽ nói một điều - trong trường hợp của tôi, không thể khắc phục việc tạo truy vấn trong ứng dụng.
Thay vào đó, tôi muốn phân tích lý do tại sao SQL Server xây dựng kế hoạch truy vấn lâu như vậy. Trong bất kỳ DBMS nào, kể cả SQL Sever, vấn đề tối ưu hóa chính là phương pháp nối các bảng với nhau. Bên cạnh phương thức nối, trình tự của các phép nối bảng cũng rất quan trọng.
Hãy nói về trình tự của các phép nối bảng một cách chi tiết. Điều rất quan trọng là phải hiểu rằng số lượng các phép nối bảng có thể tăng lên theo cấp số nhân, không phải tuyến tính. Ví dụ Fox, chỉ có 2 phương thức khả thi để ghép 2 bảng và con số có thể lên tới 12 phương thức cho 3 bảng. Các trình tự nối khác nhau có thể có chi phí truy vấn khác nhau và trình tối ưu hóa SQL Server phải chọn phương pháp tối ưu nhất. Nhưng khi số lượng bàn nhiều, nó sẽ trở thành một nhiệm vụ tiêu tốn nhiều tài nguyên. Nếu SQL Server bắt đầu xem xét tất cả các biến thể có thể có, thì truy vấn đó có thể không bao giờ được thực thi. Đó là lý do tại sao, SQL Server không bao giờ làm điều đó và luôn tìm kiếm một phương án khá tốt chứ không phải phương án tốt nhất. SQL Server luôn cố gắng đạt được sự thỏa hiệp giữa thời gian thực thi và chất lượng kế hoạch.
Đây là một ví dụ về sự phát triển theo cấp số nhân của các phương pháp nối. SQL Server có thể chọn nhiều phương thức nối khác nhau (sâu bên trái, sâu bên phải, cây rậm rạp). Về mặt trực quan, nó trông theo cách sau:
Bảng dưới đây cho thấy các phương pháp kết hợp có thể có khi số lượng bảng tăng lên:
Bạn có thể tự nhận các giá trị này:
Đối với sâu bên trái: 5! =5 x 4 x 3 x 2 x 1 =120
Đối với cây rậm rạp: (2n – 2)! / (N – 1)!
Kết luận :Đặc biệt chú ý đến số lượng THAM GIA và không cản trở trình tối ưu hóa. Nếu bạn không nhận được kết quả mong muốn trong truy vấn chứa nhiều JOIN, hãy ngắt nó thành nhiều truy vấn nhỏ và bạn sẽ ngạc nhiên với kết quả.
Tái bút Tất nhiên, chúng ta phải hiểu rằng bên cạnh việc xác định một chuỗi các phép nối bảng, trình tối ưu hóa truy vấn cũng phải chọn loại kết nối, phương thức truy cập dữ liệu (Quét, Tìm kiếm), v.v.
Sản phẩm hữu ích:
SQL Complete - viết, làm đẹp, cấu trúc lại mã của bạn một cách dễ dàng và tăng năng suất của bạn.