[英]LEFT JOIN with OR clause without UNION
我知道這不應該發生在數據庫中,但它發生了,我們必須處理它。 如果新行不存在,我們需要根據另一個表中的值將它們插入到表中。 這很容易(只需執行 LEFT JOIN 並檢查第一個表中的 NULL 值)。 但是......連接不是很直接,我們需要使用 OR 而不是 AND 搜索 2 個條件的第一個表。 所以基本上,如果它在 2 個屬性中的任何一個上找到匹配項,我們認為第一個表中的相應行存在,我們不必插入新行。 如果這兩個屬性中的任何一個都沒有匹配項,那么我們將其視為新行。 我們可以在 LEFT JOIN 語句中使用 OR 條件,但據我了解,它會進行全表掃描,並且查詢需要很長時間才能完成,即使它產生了正確的結果。 我們也不能使用 UNION,因為它不會給我們想要的東西。 為簡單起見,請考慮以下場景(我們需要將數據插入到 tableA 中)。
If(OBJECT_ID('tempdb..#tableA') Is Not Null) Begin
Drop Table #tableA End
If(OBJECT_ID('tempdb..#tableB') Is Not Null) Begin
Drop Table #tableB End
create table #tableA ( email nvarchar(50), id int )
create table #tableB ( email nvarchar(50), id int )
insert into #tableA (email, id) values ('123@abc.com', 1), ('456@abc.com', 2), ('789@abc.com', 3), ('012@abc.com', 4)
insert into #tableB (email, id) values ('234@abc.com', 1), ('456@abc.com', 2), ('567@abc.com', 3), ('012@abc.com', 4), ('345@abc.com', 5)
--THIS QUERY IS CORRECTLY RETURNING 1 RECORD
select B.email, B.id
from #tableB B
left join #tableA A on A.email = B.email or B.id = A.id
where A.id is null
--THIS QUERY IS INCORRECTLY RETURNING 3 RECORDS SINCE THERE ARE ALREADY RECORDS WITH ID's 1 & 3 in tableA though the email addresses of these records don't match
select B.email, B.id
from #tableB B
left join #tableA A on A.email = B.email
where A.id is null
union
select B.email, B.id
from #tableB B
left join #tableA A on B.id = A.id
where A.id is null
If(OBJECT_ID('tempdb..#tableA') Is Not Null) Begin
Drop Table #tableA End
If(OBJECT_ID('tempdb..#tableB') Is Not Null) Begin
Drop Table #tableB End
第一個查詢工作正常,只返回 1 條記錄,但表大小只有幾條記錄,它在 1 秒內完成。 當 2 個表有數千條記錄時,查詢可能需要 10 分鍾才能完成。 第二個查詢當然會返回我們不想插入的記錄,因為我們認為它們存在。 有沒有辦法優化這個查詢,所以它需要一個可接受的時間來完成?
您正在使用反連接,這是另一種編寫直截了當的NOT EXISTS
的方式:
where not exists
(
select null
from #tableA A
where A.email = B.email or B.id = A.id
)
即,表 A 中不存在具有相同 email 或相同 ID 的行。 換句話說:不存在具有相同 email 的行並且不存在具有相同 id 的行。
where not exists (select null from #tableA A where A.email = B.email)
and not exists (select null from #tableA A where B.id = A.id)
使用適當的索引
on #tableA (id);
on #tableA (email);
這應該非常快。
很難調整你看不到的東西。 獲取數據的另一個選擇是:
SELECT B.email
, B.id
FROM #TableB B
EXCEPT
(
SELECT B.email
, B.id
FROM #tableB B
INNER JOIN #tableA A
ON A.email = B.email
UNION ALL
SELECT B.email
, B.id
FROM #tableB B
INNER JOIN #tableA A
ON B.id = A.id
)
這樣你就不必使用OR
,你可以使用INNER JOIN
而不是LEFT JOIN
並且你可以使用UNION ALL
而不是UNION
(盡管這個優勢很可能被EXCEPT
否定)。 所有這些都可能有助於您的表現。 當替換為EXISTS
時,連接可能會更有效。
您沒有提到這個問題是如何發生的(兩個表中的數據來自哪里,以及為什么它們不應該不同步),但最好從源頭修復它。
否,查詢正確返回 3 行
因為
select B.email, B.id
from #tableB B
left join #tableA A on A.email = B.email
where A.id is null
Allone 重新使用 3 行。
對於你的“問題”
select B.email, B.id
from #tableB B
left join #tableA A on A.email = B.email or B.id = A.id
where A.id is null
將 che3kc 為每一行,如果它是真的被包括在內
所以例如
('123@abc.com', 1) ('234@abc.com', 1)
由於ID相同,它將被加入
但是當您通過電子郵件加入時,條件為假,因此包含在結果集中
當您僅比較電子郵件或 ID 時,您只能使用 UNION 方法,但是這兩個查詢不等效
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.