2017-04-14 107 views
1

我想比較每個表中有兩百萬條記錄的表,並從比較中獲取匹配數據。將一個表中數百萬行與另一個表中數百萬行進行比較的最快方法

要從兩個表中獲得匹配數據,我們首先比較table1中的名稱不應等於table2中的名稱。然後我們在table1之間比較的城市應該等於table2的城市,然後最後我們比較table1中的date_of_birth應該在table2的出生日期的+ 1年範圍內。

Table 1中的單個行可以與Table 2中的數據有多個匹配。 同樣對於每場比賽,我需要唯一的記錄ID,並且單個表1行的多個匹配數據必須具有相同的唯一記錄ID。

我嘗試過使用Java代碼和PL/SQL過程,但都需要花費數小時,因爲這涉及到數百萬數據與數百萬數據的比較。有沒有更快的方法來做這種匹配?

+1

你能分享表和列的列,你在哪些基礎上比較這些表。 – Usama

+1

首先,因爲您已經擁有數據庫中的數據,所以Java肯定不是比較的正確解決方案。至於DB,這樣的任務不僅需要調整查詢,數據存儲,數據庫和表格定義,索引,硬件等都扮演着更重要的角色。這是一個廣泛的領域,您應該與您的DBA討論。 –

回答

1

從兩個表中選擇數據,按關鍵字段排序,然後並行迭代並進行比較。比較時間應該很快,所以總運行時間應該僅比每個有序查詢的運行時間總和略多。


UPDATE

New information示出的部分的數據的期望交叉聯接:

left.name <> right.name 
left.city = right.city 
abs(left.birthDate - right.birthDate) <= 1 year 

因此,考慮存在一個相等測試,則可以處理該數據在大塊中,塊都是相同的所有記錄city

比較如將進展如下:

  1. 兩個表,由city排序選擇數據。

  2. 並行處理兩個結果集。

  3. 從一個結果集(left)加載下一個city的所有記錄,即加載下一個塊。將它們存儲在內存中的TreeMap<LocalDate, List<Person>>

  4. 使用相同的city迭代來自其他結果集(right)的所有記錄,即處理該塊。

  5. 對於每個記錄在right,通過調用subMap(),類似這樣的發現1歲birthDate內記錄:

    Collection<List<Person>> coll = 
         leftTree.subMap(right.birthDate.minusYears(1), true, 
             right.birthDate.plusYears(1), true) 
           .values(); 
    
  6. 迭代記錄,並跳過具有相同name記錄。這些是left記錄,其與記錄的right「匹配」。

    • 如果需要,可以壓平這一點,使用流中過濾的名稱:

      List<Person> matches = coll.stream() 
           .flatMap(List::stream) 
           .filter(p -> ! p.name.equals(right.name)) 
           .collect(Collectors.toList()); 
      

      任選地與實際的處理邏輯替換collect()

  7. 完成時,如步驟4,即所描述的處理塊,當你看到下一city,清除TreeMap,並從步驟3重複對下一個塊,又名city

優點這個邏輯:

  • 數據只從數據庫服務器發送一次,即造成數據的重複的部分橫加入從相對較慢的數據鏈路消除。

  • 如果需要,兩個查詢可以來自兩個不同的數據庫。

  • 通過一次只保留其中一個查詢的一個city的數據(塊大小爲left),可以減少內存佔用量。

  • 如果需要,匹配邏輯可以是多線程的,以獲得額外的性能,例如,

    1. 線程1負載left塊爲TreeMap,並賦予它線程2的處理,而線程1開始加載下一個塊。

    2. 線程2迭代right,並發現通過調用subMap(),迭代子圖,給匹配leftright記錄到線程3用於處理匹配的記錄。

    3. 線程3處理匹配對。

+0

現在OP已經提供了一些細節,我們可以看到比較涉及不等式和範圍。這不適合逐行處理。 – APC

+0

@APC真。爲此,需要「塊」處理。 – Andreas

+0

比單個七行SQL語句簡單得多;) – APC

1

「我試圖用java通過通過JDBC連接列表中存儲兩個表中的數據,然後遍歷一個列表與其他。但它是非常緩慢的,花了很多時間來完成,甚至很多時候都有超時的例外。「

恭喜。這是通往啓蒙之路的第一步。數據庫比Java更好地處理數據。 Java是一種很好的通用編程語言,但是數據庫針對關係數據處理進行了優化:它們只是以更快的速度,更少的CPU,更少的內存和更少的網絡流量進行操作。

「我還創建了一個SQL程序一樣,它是一些比Java程序快了什麼,但 還是花了很多時間(幾個小時) 完成。「

你在第二步的邊緣以啓示:一行一行地處理(即程序迭代)是緩慢的SQL是基於集合的模式設置處理的速度要快得多

要給予具體的建議,我們需要你的做法其實一些細節,但作爲一個例子該查詢會給你設定的這些列匹配的兩個表中:

select col1, col2, col3 
from huge_table_1 
INTERSECT 
select col1, col2, col3 
from huge_table_2 

減號來會給你的huge_table_1中的行不在huge_table_2中。翻轉表來獲得正面設置。

select col1, col2, col3 
from huge_table_1 
MINUS 
select col1, col2, col3 
from huge_table_2 

擁抱歡樂套裝!


「我們首先在比較中huge_table_1名字不應該是平等的 在huge_table_2的名字,然後我們在huge_table_1 比較城市應該等於城市huge_table_2然後最後我們 比較在huge_table_1中的date_of_birth應該在+ 1年內 在huge_table_2中的date_of-birth的範圍「

嗯。從不平等開始往往是不好的,特別是在大型表格中。很可能你會有許多不匹配的名字和那些匹配的標準。但你可以嘗試這樣的事情:

select * from huge_table_1 ht1 
where exists 
     (select null from huge_table_2 ht2 
     where ht2.city = ht1.city 
     and ht1.date_of birth between add_months(ht2.date_of birth, -12) 
            and add_months(ht2.date_of birth, 12) 
     and ht2.name != ht1.name) 
/
+0

非常感謝您的幫助。爲了從兩個表中獲得匹配數據,我們首先比較huge_table_1中的名稱不應該等於huge_table_2中的名稱。然後我們在huge_table_1中比較城市應該等於huge_table_2中的城市,然後最後我們在huge_table_1中比較date_of_birth應該與在huge_table_2中的date_of-birth的+ 1年範圍中。 –

+0

我不同意「數據庫比Java更好地處理數據」。假設有足夠的內存和昂貴的查詢(以便數據加載時間無關緊要),Java可以做得更快,因爲它可以完成數據庫知道的所有技巧,甚至更多。顯然,它需要比編寫SQL查詢更多的工作和更多的技能,所以我絕對同意你推薦SQL。 – maaartinus

+0

@maaartinus - 給出足夠的RAM我可以將整個數據庫讀入DB緩衝區緩存。所以我不確定這不是一個有效的論點。而且我很想知道你在想什麼樣的查詢在SQL中比在Java中在計算上花費太多,因此將數據從數據庫中傳輸出去的代價相比之下微不足道。很明顯,有一些數據類型不適合SQL - 圖形是最好的例子 - 但我小心地說「關係數據」:) – APC