2010-05-21 35 views
7

Tôi nhận được các kết quả khác nhau dựa trên điều kiện bộ lọc trong truy vấn dựa trên nơi tôi đặt điều kiện bộ lọc. Câu hỏi của tôi là:Tiêu chuẩn SQL về tham gia bên ngoài trái và điều kiện ở đâu

  • Có sự khác biệt về kỹ thuật giữa các truy vấn này không?
  • Có bất kỳ điều gì trong tiêu chuẩn SQL giải thích các tập bản ghi khác nhau từ các truy vấn không?

Với kịch bản đơn giản:

--Table: Parent Columns: ID, Name, Description 
--Table: Child Columns: ID, ParentID, Name, Description 

--Query 1 
SELECT p.ID, p.Name, p.Description, c.ID, c.Name, c.Description 
FROM Parent p 
    LEFT OUTER JOIN Child c ON (p.ID = c.ParentID) 
WHERE c.ID IS NULL OR c.Description = 'FilterCondition' 

--Query 2 
SELECT p.ID, p.Name, p.Description, c.ID, c.Name, c.Description 
FROM Parent p 
    LEFT OUTER JOIN Child c 
    ON (p.ID = c.ParentID AND c.Description = 'FilterCondition') 

tôi cho rằng các truy vấn sẽ trả lại resultsets cùng và tôi đã rất ngạc nhiên khi họ không. Tôi đang sử dụng MS SQL2005 và trong các truy vấn thực tế, truy vấn 1 trả về ~ 700 hàng và truy vấn 2 trả về ~ 1100 hàng và tôi không thể phát hiện mẫu mà hàng được trả lại và hàng nào bị loại trừ. Vẫn còn nhiều hàng trong truy vấn 1 với hàng con với dữ liệu và dữ liệu NULL. Tôi thích kiểu truy vấn 2 (và tôi nghĩ nó tối ưu hơn), nhưng tôi nghĩ các truy vấn sẽ trả lại kết quả tương tự.

Edit/Tóm tắt:

Đã có một số câu trả lời lớn được cung cấp ở đây. Tôi đã có một thời gian khó khăn để lựa chọn ai để trao giải đáp. Tôi quyết định đi với mdma vì đó là câu trả lời đầu tiên và là câu trả lời rõ ràng nhất. Dựa trên câu trả lời cung cấp, đây là tóm tắt của tôi:

kết quả có thể xảy ra:

  • Một: Phụ huynh không có con
  • B: Cha mẹ với con cái
  • | -> B1: Phụ huynh với trẻ em nơi không có trẻ em nào phù hợp với bộ lọc
  • \ -> B2: Cha mẹ có trẻ em từ 1 trở lên phù hợp với bộ lọc

kết quả truy vấn:

  • Query 1 lợi nhuận (A, B2)
  • Query 2 lợi nhuận (A, B1, B2)

Query 2 luôn trả về cha mẹ vì sự tham gia bên trái. Trong truy vấn 1, mệnh đề WHERE được thực hiện sau khi kết nối bên trái, vì vậy cha mẹ có con mà không có con nào phù hợp với bộ lọc bị loại trừ (trường hợp B1).

Lưu ý: chỉ thông tin cha mẹ được trả lại trong trường hợp B1 và ​​trong trường hợp B2 chỉ thông tin cha/con phù hợp với bộ lọc được trả về.

HLGEM cung cấp một liên kết tốt:

http://wiki.lessthandot.com/index.php/WHERE_conditions_on_a_LEFT_JOIN

Trả lời

9

Truy vấn đầu tiên sẽ trở lại trường hợp phụ huynh không có trẻ em hoặc nơi một số các trẻ em phù hợp với điều kiện lọc.Specificaly, trường hợp cha mẹ có một con, nhưng nó không khớp với điều kiện lọc sẽ bị bỏ qua.

Truy vấn thứ hai sẽ trả lại hàng cho tất cả phụ huynh. Nếu không có sự phù hợp về điều kiện lọc, một NULL sẽ được trả về cho tất cả các cột của c. Đây là lý do tại sao bạn nhận được nhiều hàng hơn trong truy vấn 2 - cha mẹ có con không phù hợp với điều kiện bộ lọc là đầu ra với giá trị con NULL, trong đó truy vấn đầu tiên chúng được lọc ra.

10

Có, có sự khác biệt rất lớn. Khi bạn đặt bộ lọc trong mệnh đề ON trên LEFT JOIN, bộ lọc được áp dụng trước kết quả được nối với bảng ngoài. Khi bạn áp dụng một bộ lọc trong mệnh đề WHERE, nó sẽ xảy ra sau khi LEFT JOIN được áp dụng.

Tóm lại, truy vấn đầu tiên sẽ loại trừ các hàng có hàng con nhưng mô tả con không bằng điều kiện bộ lọc, trong khi truy vấn thứ hai sẽ luôn trả về hàng cho phụ huynh.

+0

Cảm ơn. Tôi hiểu sự khác biệt về hiệu suất, nhưng tôi tò mò hơn về các kết quả khác nhau. – Ryan

+0

@Ryan - Đó không phải là về hiệu suất. Trường hợp lọc được áp dụng có thể làm cho tất cả sự khác biệt về kết quả thích hợp. – Thomas

+0

OK. Cảm ơn. – Ryan

0

tôi nhận thấy vài sự khác biệt mà có thể làm cho kết quả vary.In truy vấn đầu tiên, bạn có LEFT OUTER JOIN Child c ON (p.ID = c.ParentID) và sau đó trong truy vấn thứ hai bạn có LEFT OUTER JOIN Child c ON (p.ID = c.ParentID AND c.Description = 'FilterCondition') và điều này làm cho các truy vấn thứ hai trả lại tất cả các bậc cha mẹ với con cái thỏa mãn điều kiện của bạn, nơi là điều kiện đầu tiên cũng sẽ trả lại cha mẹ không có con. Cũng xem xét ưu tiên của điều kiện tham gia và điều kiện.

1

cha mẹ chỉ có con với description != 'FilterCondition' sẽ không xuất hiện trong truy vấn 1 vì mệnh đề WHERE được đánh giá sau khi các hàng được nối.

1

Truy vấn đầu tiên trả về ít hàng hơn vì nó chỉ trả về các hàng không có con hoặc có con khớp với điều kiện bộ lọc.

Các mệnh đề WHERE không bao gồm phần còn lại (những người DO có con nhưng không phù hợp với điều kiện lọc.)

Truy vấn 2 cho thấy tất cả ba điều kiện nêu trên.

2

Đối với recordset này:

parent 

id 
1 

child 

id parent filter 
1  1  OtherCondition 
2  1  OtherCondition 

, truy vấn đầu tiên sẽ trở lại 0 hồ sơ, trong khi cái thứ hai sẽ trở lại 1 kỷ lục:

WITH parent (id) AS 
     (
     SELECT 1 
     ), 
     child (id, parent, condition) AS 
     (
     SELECT 1, 1, 'OtherCondition' 
     UNION ALL 
     SELECT 2, 1, 'OtherCondition' 
     ) 
SELECT * 
FROM parent 
LEFT JOIN 
     child 
ON  child.parent = parent.id 

/* The children are found, so no fake NULL records returned */ 

1 1 1 OtherCondition 
1 2 1 OtherCondition 

Bây giờ thêm WHERE khoản:

WITH parent (id) AS 
     (
     SELECT 1 
     ), 
     child (id, parent, condition) AS 
     (
     SELECT 1, 1, 'OtherCondition' 
     UNION ALL 
     SELECT 2, 1, 'OtherCondition' 
     ) 
SELECT * 
FROM parent 
LEFT JOIN 
     child 
ON  child.parent = parent.id  
WHERE child.id IS NULL OR child.condition = 'FilterCondition' 
Điều khoản

WHERE lọc các bản ghi được trả lại ở bước trước và không có bản ghi nào khớp với điều kiện.

Trong khi điều này một:

WITH parent (id) AS 
     (
     SELECT 1 
     ), 
     child (id, parent, condition) AS 
     (
     SELECT 1, 1, 'OtherCondition' 
     UNION ALL 
     SELECT 2, 1, 'OtherCondition' 
     ) 
SELECT * 
FROM parent 
LEFT JOIN 
     child 
ON  child.parent = parent.id  
     AND child.condition = 'FilterCondition' 

1 NULL NULL NULL 

trả về một kỷ lục giả duy nhất.

+0

Ví dụ chi tiết tuyệt vời. Rất đẹp. Tôi không biết nếu tôi sẽ gọi nó là một hồ sơ 'giả' mặc dù vì nó là một tham gia trái và bạn sẽ mong đợi các cột bảng bên ngoài được NULL ở lần. Tôi có thể nói truy vấn 2 luôn trả về một phụ huynh trong khi truy vấn 1 trả về các bậc cha mẹ hoặc không có con hoặc cha mẹ có con phù hợp rõ ràng với điều kiện bộ lọc. Rất cám ơn ví dụ. – Ryan

3

Đưa điều kiện trong mệnh đề where chuyển nó đến một bên tham gia (trừ khi bạn đang sử dụng một cái gì đó nơi nơi id là null mang đến cho bạn không ghi inthe bảng) Xem này cho một lời giải thích đầy đủ hơn:

http://wiki.lessthandot.com/index.php/WHERE_conditions_on_a_LEFT_JOIN

+0

Tôi thích liên kết. Tốt đẹp. – Ryan

+1

+1, mặc dù điều này tất nhiên phụ thuộc vào điều kiện. Nói, đặt một điều kiện như 'id IS NULL' chuyển đổi một' LEFT JOIN' thành 'NOT EXISTS' (và hầu hết các công cụ thậm chí tối ưu hóa nó một cách chính xác). Bất kỳ sự bình đẳng hoặc bất bình đẳng nào sẽ chuyển đổi nó thành một sự tham gia bên trong. – Quassnoi

Các vấn đề liên quan