2013-06-14 54 views
17

Tôi có một bảng (MainTable) với hơn 600.000 bản ghi. Nó tham gia vào chính nó thông qua một bảng thứ 2 (JoinTable) trong một mối quan hệ kiểu phụ huynh/trẻ em:LEFT JOIN Đáng kể nhanh hơn INNER JOIN

SELECT Child.ID, Parent.ID 
FROM  MainTable 
AS  Child 
JOIN  JoinTable 
     ON Child.ID = JoinTable.ID 
JOIN  MainTable 
AS  Parent 
     ON Parent.ID = JoinTable.ParentID 
    AND Parent.SomeOtherData = Child.SomeOtherData 

Tôi biết rằng mọi kỷ lục con có một hồ sơ phụ huynh và các dữ liệu trong JoinTable được acurate.

Khi tôi chạy truy vấn này, phải mất vài phút để chạy. Tuy nhiên nếu tôi tham gia vào chuyên sử dụng một Left Join sau đó phải mất < 1 giây để chạy:

SELECT Child.ID, Parent.ID 
FROM  MainTable 
AS  Child 
JOIN  JoinTable 
     ON Child.ID = JoinTable.ID 
LEFT JOIN MainTable 
AS  Parent 
     ON Parent.ID = JoinTable.ParentID 
    AND Parent.SomeOtherData = Child.SomeOtherData 
WHERE ...[some info to make sure we don't select parent records in the child dataset]... 

Tôi hiểu sự khác biệt trong kết quả giữa một INNER JOINLEFT JOIN. Trong trường hợp này, nó sẽ trả về chính xác kết quả giống như mọi đứa trẻ có cha/mẹ. Nếu tôi cho phép cả hai truy vấn chạy, tôi có thể so sánh các tập dữ liệu và chúng giống hệt nhau.

Tại sao số LEFT JOIN chạy nhanh hơn rất nhiều so với INNER JOIN?


CẬP NHẬT Kiểm tra gói truy vấn và khi sử dụng tham gia bên trong, bắt đầu với tập dữ liệu Gốc. Khi thực hiện một phép nối trái, nó bắt đầu với tập dữ liệu con.

Các chỉ mục mà nó sử dụng đều giống nhau.

Tôi có thể buộc nó luôn bắt đầu với đứa trẻ không? Sử dụng một công việc tham gia trái, nó chỉ cảm thấy sai.


Câu hỏi tương tự đã được hỏi ở đây trước đây, nhưng dường như không có câu trả lời nào cho câu hỏi của tôi.

ví dụ: câu trả lời được chọn trong INNER JOIN vs LEFT JOIN performance in SQL Server cho biết rằng Gia nhập trái luôn luôn chậm hơn so với Gia nhập bên trong. Lập luận có ý nghĩa, nhưng nó không phải là những gì tôi nhìn thấy.

+1

Kiểm tra gói. – Blorgbeard

+0

@Blogbeard - xem cập nhật – Greg

Trả lời

12

Các Left tham gia có vẻ là nhanh hơn bởi vì SQL buộc phải làm nhỏ hơn chọn đầu tiên và sau đó tham gia vào bộ hồ sơ nhỏ hơn này. Vì lý do nào đó, người tối ưu không muốn làm điều này một cách tự nhiên.

3 cách để buộc các gia nhập xảy ra theo thứ tự đúng:

  1. Chọn tập hợp con đầu tiên của dữ liệu vào một bảng tạm thời (hoặc biến bảng) sau đó tham gia vào nó
  2. Sử dụng trái tham gia (và hãy nhớ rằng điều này có thể trả về dữ liệu khác nhau bởi vì đó là kết nối bên trái chứ không phải tham gia bên trong)
  3. sử dụng từ khóa ORDER ORDER. Lưu ý rằng nếu kích thước bảng hoặc lược đồ thay đổi thì kế hoạch truy vấn có thể không chính xác (xem https://dba.stackexchange.com/questions/45388/forcing-join-order)
+1

Tôi biết điều này là muộn, nhưng bạn có thể muốn đảm bảo rằng số liệu thống kê cơ sở dữ liệu của bạn được cập nhật. Nếu trình tối ưu hóa truy vấn không biết về kích thước bảng tương đối và phân phối giá trị trong các cột kết nối, nó có thể đưa ra quyết định kế hoạch truy vấn * xấu * (SQL Server có trình tối ưu hóa truy vấn tốt nhất liên tục của tất cả cơ sở dữ liệu tôi làm việc) . Đây là một mục blog về chủ đề: http://blogs.msdn.com/b/buckwoody/archive/2009/08/18/sql-server-best-practices-auto-create-and-auto-update-statistics- nên-be-on-nhất-of-the-time.aspx – Curt

+0

Cảm ơn ý tưởng. chúng tôi đã kiểm tra số liệu thống kê và họ đã được cập nhật – Greg

+0

Tôi biết muộn nhưng có thể giúp đỡ người khác. Điểm duy nhất tôi không đồng ý là đề xuất biến Table. Biến bảng luôn trả về một số hàng ước tính là 1 bất kể có bao nhiêu hàng trong bảng. Điều này có thể làm hỏng kế hoạch rất nhiều. Đọc http://blogs.msdn.com/b/psssql/archive/2014/08/11/if-you-have-queries-that-use-table-variables-sql-server-2012-sp2-can- help.aspx tuy nhiên, có một dấu vết trong 2012 SP2 có thể giúp –

2

Hãy thử cái này. Cùng một kết quả, cách tiếp cận khác nhau:

SELECT c.ID, p.ID 
FROM 
(SELECT Child.ID, JoinTable.ParentID 
FROM  MainTable 
AS  Child 
JOIN  JoinTable 
     ON Child.ID = JoinTable.ID) AS c 
INNER JOIN 
(SELECT Parent.ID, JoinTable.ID 
FROM  MainTable 
AS  Parent 
JOIN  JoinTable 
     ON Parent.ID = JoinTable.ParentID 
    AND Parent.SomeOtherData = Child.SomeOtherData) AS p 
ON c.ParentID = p.ID 

Nếu nó không giúp đỡ, sử dụng CTE:

;WITH cte AS 
(SELECT Child.ID, JoinTable.ParentID 
FROM  MainTable 
AS  Child 
JOIN  JoinTable 
     ON Child.ID = JoinTable.ID) 
SELECT cte.ID, Parent.ID 
FROM cte INNER JOIN 
MainTable 
AS  Parent 
     ON Parent.ID = cte.ParentID 
    AND Parent.SomeOtherData = cte.SomeOtherData 
+0

CTE dường như không giúp đỡ, nhưng buộc nó vào một biến bảng đã làm. Nếu không có câu trả lời nào khác xuất hiện thì tôi sẽ chạy với câu trả lời đó. – Greg