2015-09-06 21 views
0

所以我一直有這個問題一段時間了,我需要連接兩個表,但我沒有唯一的標識符,所以我必須使用ID和日期範圍。如果兩個日期範圍重疊,我需要加入第一個值。我需要成爲一個外連接。我現在的代碼在理論上是正確的,但它不適合這種情況(我不能讓這個連接成爲外連接,因爲我這樣得到一個錯誤)。SQL如何在不使用嵌套子查詢的情況下外連接第一個匹配值

邏輯如下:員工可以是外籍員工。如果他是外籍人士,我需要顯示所有專欄。但棘手的部分 - 如果在分配期間(在effective_start_date和effective_end_date之間)他在2個不同的國家,我只需要顯示關於最後一趟的信息。 最後一部分(關於只加入MAX(valid_to需要調整)) - 這樣,有唯一的assign_id,effetive_start_date,effective_end date(所以這個表正確地加入到其他表)組合。我知道這可以修復一個單一的連接,我只是不知道如何(我可以做到這一點,而不使用任何嵌套的子查詢)。

請給我提供ORACLE語法,是的,我知道有人說這已經過時了,但是這段代碼絕對需要現在編寫的語法+它對我來說更容易理解。

with assignments (assignment_id, person_id, effective_start_date, effective_end_date) as (
    select 456, 123, date '2015-01-01', date '2015-03-15' from dual union all 
    select 456, 123, date '2015-03-16', date '4712-12-31' from dual union all 
    select 975, 123, date '2015-03-16', date '4712-12-31' from dual union all 
    select 674, 145, date '2015-03-16', date '4712-12-31' from dual 
), 


expatriates (person_id, home_country, host_country, date_from, date_to, valid_from, valid_to) 
as 
(
    select 123, 'TEST2', 'TEST2', date '2015-01-01', date '2015-03-15', date '2015-01-01', date '2015-03-15' from dual union all 
    select 123, 'TEST1', 'TEST1', date '2015-04-16', date '2016-06-15', date '2015-04-16', date '2016-06-15' from dual union all 
    select 123, 'TEST', 'TEST', date '2015-03-16', date '2016-04-15', date '2015-03-16', date '2015-04-15' from dual 

) 

select 
a.assignment_id, 
a.person_id, 
a.effective_start_date, 
a.effective_end_date, 
subq.home_country, 
subq.host_country, 
subq.date_from, 
subq.date_to 
from assignments a, expatriates subq 
where 
a.person_id=subq.person_id(+) 
and subq.valid_from(+) <= a.effective_end_date 
and subq.valid_to(+) >= a.effective_start_date 
and subq.date_from(+) = 
(
    select 
    max(date_from) 
    from expatriates sq2 
    where 
     sq2.person_id = a.person_id and 
     sq2.valid_from <= a.effective_end_date and 
     sq2.valid_to >= a.effective_start_date 
) 
+3

使用正確的'left join'運算符也是「Oracle語法」 - 甚至Oracle建議停止使用舊的(+)'運算符。 –

+1

你是說你提出的查詢給出了正確的結果,但是你想要一個在WHERE子句中不使用子查詢的替代方案嗎? –

+0

您的查詢只選擇完全屬於任務期限的外派日期範圍。您是否也需要考慮範圍重疊的情況,但分配範圍並未完全包含外派範圍? –

回答

0

我想用開始查詢您關注的是,它採用了相關子查詢,而相對於不相關的一個性能問題確實是。查詢確實可以被構造來避免相關的子查詢,但是不能更一般地避免用於標識與每個分配相關聯的哪個外派行是要報告的行的單獨查詢。以下版本非常高效,因爲最頂層的查詢只是從CTE結果中過濾不需要的行。

這個單獨的查詢可以被構造爲內嵌視圖或公共表格表達式。堅持這個問題的字母,我使用下面的CTE來避免查詢嵌套,即使在內聯視圖中有一個完全等效的表達式。它使用標準的外連接語法。它還使用窗口功能版本MAX(),這需要相當新的Oracle。

with 
-- other CTEs ... , 
all_rows (assignment_id, person_id, effective_start_date, effective_end_date, 
    home_country, host_country, date_from, date_to, max_from) as 
(
    select 
    a.assignment_id, 
    a.person_id, 
    a.effective_start_date, 
    a.effective_end_date, 
    subq.home_country, 
    subq.host_country, 
    subq.date_from, 
    subq.date_to, 
    max(subq.date_from) over (partition by a.assignment_id, a.person_id) 
    from 
    assignments a 
    left outer join expatriates subq 
     ON a.person_id = subq.person_id 
     and subq.valid_from <= a.effective_end_date 
     and subq.valid_to >= a.effective_start_date 
) 
select * 
from all_rows 
where date_from = max_from or max_from is null 
; 

當你在問題中提供的熱膨脹係數相結合,該查詢產生三個結果行,每行的assignment_idperson_id一個獨特的組合。

要處理,其中外籍範圍重疊的分配範圍,但在它沒有完全包含的情況下,你可能想改變連接條件......

 ON a.person_id = subq.person_id 
     AND (subq.valid_from BETWEEN a.effective_start_date AND a.effective_end_date 
      OR a.effective_start_date BETWEEN subq.valid_from AND subq.valid_to) 

,覆蓋了所有的情況下兩個範圍重疊。

相關問題