簡體   English   中英

帶 OR 子句的 LEFT JOIN 不帶 UNION

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM