2017-02-18 81 views
0

數據樣本將記錄:甲骨文 - 基於重疊範圍

id lowerlimt upperlimit 
    1 5   10 ---Master Record 
    2 8   12 
    3 3   8 
    4 8   9 
    5 11   15 

在上表中,我們假設紀錄id=1作爲主記錄。我想將其他記錄與同一個表中的第一條記錄進行比較,如果它與範圍重疊並分配一個標記,則拆分每條記錄。如果它重疊分配'Y',否則'N'。 如果重疊將記錄部分分成兩部分,一部分重疊範圍,另一部分重疊範圍。

id lowerlimt upperlimit flag 
2 8   10   y 
2 10   12   n 
3 3   5   n 
3 5   8   y 
4 8   9   y 
5 11   15   n 
+0

所以主記錄將始終具有ID = 1,它會一直存在?然後:一個時間間隔可能需要分成三次(例如2到15的時間間隔)。對?如果一個記錄只是「觸及」主記錄,比如2到5,那麼它是如何處理的?標誌'n',結果是單行,還是標誌'y',我們顯示從5到5的記錄? – mathguy

+0

此外,是原始記錄的旗幟,還是最終的碎片?該文本說「分配標誌的記錄」(似乎暗示原始記錄),但輸出顯示分配給每一塊的標誌。 – mathguy

+0

@mathguy是的..如果忘記提及從2到15間隔的測試用例。同樣在目標表中,我們可以避免主記錄 –

回答

1

我增加了更多的「測試數據」進行測試和說明。主要計算是將輸入範圍分解成三部分(其中一些無意義,並在最後階段被淘汰 - 當輸入範圍嚴格與主記錄重疊時,最多達到三個輸入範圍方向)。

爲了提高效率,最好每個輸入行只訪問一次。因此,我並不是使用union all(最簡單的路線),而是同時創建所有三個子範圍和相應的標誌,結果有9列,而不是三個子範圍和標誌。然後我使用unpivot將它們放入不同的行中。

with 
     test_data (id, lowerlimit, upperlimit) as (
     select 1, 5, 10 from dual union all ---Master Record 
     select 2, 8, 12 from dual union all 
     select 3, 3, 8 from dual union all 
     select 4, 8, 9 from dual union all 
     select 5, 11, 15 from dual union all 
     select 6, 2, 5 from dual union all 
     select 7, 1, 14 from dual 
    ) 
-- end of test data (not part of the solution) 
-- SQL query begins BELOW THIS LINE (use your actual table name) 
select id, lowerlimit, upperlimit, flag 
from (
    select id, 
        t.lowerlimit  as x1, least(t.upperlimit, m.ll) as y1, 'n' as f1, 
      greatest(t.lowerlimit, m.ll) as x2, least(t.upperlimit, m.ul) as y2, 'y' as f2, 
      greatest(t.lowerlimit, m.ul) as x3,  t.upperlimit  as y3, 'n' as f3 
    from test_data t cross join 
      (select lowerlimit ll, upperlimit ul 
      from test_data 
      where id = 1 
     ) m 
    where t.id != 1 
    ) 
unpivot ((lowerlimit, upperlimit, flag) 
         for (x, y, f) in ((x1, y1, f1), (x2, y2, f2), (x3, y3, f3))) 
where lowerlimit < upperlimit 
order by id, lowerlimit -- if needed 
; 

輸出

ID LOWERLIMIT UPPERLIMIT FLAG 
-- ---------- ---------- ---- 
2   8   10 y 
2   10   12 n 
3   3   5 n 
3   5   8 y 
4   8   9 y 
5   11   15 n 
6   2   5 n 
7   1   5 n 
7   5   10 y 
7   10   14 n 

10 rows selected. 
+0

你可以放置交叉連接部分嗎? –

+1

交叉連接(對於子查詢的單行結果)就是我可以訪問'id = 1'的值。它類似於程序編程語言中變量的使用。如果我需要從表中提取一些數據 - 例如一行中的值 - 我將編寫一個子查詢來選擇該行,並對其進行交叉連接,以便可以將這些值用作「變量」。順便說一句,這是一個非常常見的用法 - 我沒有發明任何東西(也沒有什麼不尋常的)。 – mathguy

+0

但是,使用unpivot實現此邏輯的方式是非常普通的。真棒! –

0

的一種方法是「之前」這一分裂成三個重疊的條件下,基本上,「中」,和「之後」。由於需要多個不同每個現有行,你可以使用union all做到這一點:

select t.id, t.lowerlimit, 
     least(tm.lowerlimit, t.upperlimit) as upperlimit, 
     'n' as overlaps 
from t join 
    t tm 
    on t.id <> 1 and tm.id = 1 and 
     t.lowerlimit < tm.lowerlimit 
union all 
select t.id, 
     greatest(t.lowerlimit, tm.lowerlimit), 
     least(t.upperlimit, tm.upperlimit), 'y' as overlaps 
from t join 
    t tm 
    on t.id <> 1 and tm.id = 1 and 
     t.lowerlimit <= tm.lowerlimit and 
     t.upperlimit >= tm.upperlimit 
union all 
select t.id, greatest(tm.upperlimit, t.upperlimit), 
     t.upperlimit, 'n' 
from t join 
    t tm 
    on t.id <> 1 and tm.id = 1 and 
     t.upperlimit > tm.upperlimit; 
+0

除了缺少'tm.id'中的'id'(你在三個地方有'tm = 1',但'tm'是一個表別名],邏輯不起作用。你可以自己測試一下,在我的答案中使用CTE(我把它命名爲'test_data'而不是't',所以你必須先改變它。例如,對於輸入「id = 2」,它會產生一個單獨的間隔,從12到12. – mathguy

+0

@TomJMuthirenthi - 「正在工作」是什麼意思?它仍然會產生與前面指出的'id = 2'相同的錯誤結果,並且它仍不會爲'id = 4'產生任何**輸出。正如我所建議的那樣,戈登並沒有測試他的答案。 – mathguy

+0

upperlimit =下限值記錄在查詢運行之後。使用@mathguy的測試數據,我可以看到查詢失敗的情況下爲id(2,5,7) –