2017-04-14 103 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