比较一个表中数百万行和另一个表中数百万行的最快方法

时间:2017-04-14 06:14:34

标签: java database oracle performance optimization

我想比较每个表中有两百万条记录的两个表,并从比较中获取匹配数据。

要从两个表中获取匹配数据,我们首先要比较table1中的名称不应等于table2中的名称。然后我们比较table1中的城市应该等于table2中的城市,然后最后我们将table1中的date_of_birth与{1}中的date_of-birth范围进行比较{ {1}}。

table2中的单行可以与Table 1中的数据进行多次匹配。 此外,对于每个匹配,我需要一个唯一的记录ID,并且单个表1行的多个匹配数据必须具有相同的唯一记录ID。

我尝试使用Java代码和PL / SQL程序,但两者都耗费数小时,因为这需要将数百万数据与数百万数据进行比较。有没有更快的方法来进行匹配?

2 个答案:

答案 0 :(得分:2)

  

“我尝试使用java通过jdbc连接从列表中的两个表中存储数据,然后用另一个列表迭代一个列表。但它非常慢,需要花费很多时间才能完成,甚至很多时候都会超时。” / p>

祝贺。这是通往启蒙之路的第一步。数据库在处理数据方面要比Java好得多。 Java是一种优秀的通用编程语言,但数据库针对关系数据处理进行了优化:它们只需更快,更少的CPU,更少的内存和更少的网络流量。

  

“我也为同样创建了一个sql程序,它有些更快   比java程序,但仍然花了很多时间(几个小时)来   完成“。

你正处于启蒙的第二步:逐行处理(即程序迭代)很慢。 SQL是一种基于集合的范例。设置处理要快得多。

为了提供具体的建议,我们需要一些关于你真正做的事情的细节,但作为一个例子,这个查询会给你两个表中这些列的匹配集:

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

MINUS运算符会为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中比较city   应该等于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)
/

答案 1 :(得分:1)

从两个表中选择数据,按键字段排序,然后并行迭代并进行比较。比较时间应该很快,因此总运行时间应该仅略大于每个有序查询的运行时间总和。

<强>更新

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. 的内存中
  5. 使用相同的right迭代其他结果集(city)中的所有记录,即处理该块。

  6. 对于right中的每条记录,请致电subMap(),在birthDate的1年内查找记录,如下所示:

    Collection<List<Person>> coll =
            leftTree.subMap(right.birthDate.minusYears(1), true,
                            right.birthDate.plusYears(1), true)
                    .values();
    
  7. 迭代记录并跳过具有相同name的记录。这些是与left给定记录“匹配”的right条记录。

    • 如果需要,您可以展平并使用stream过滤名称:

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

      可选择用实际处理逻辑替换collect()

  8. 按照步骤4中所述处理块时,即当您看到下一个city时,清除TreeMap,然后从步骤3开始重复下一个块,即{{ 1}}。

  9. 这种逻辑的优点:

    • 数据仅从数据库服务器发送一次,即从相对较慢的数据链路中消除由部分交叉连接引起的数据重复。

    • 如果需要,这两个查询可以来自两个不同的数据库。

    • 通过一次仅保留一个city个查询的数据(city的一块)来缩短内存占用量。

    • 如果需要,匹配逻辑可以是多线程的,以获得额外的性能,例如

      1. 线程1将left块加载到left,并将其提供给线程2进行处理,而线程1开始加载下一个块。

      2. 线程2迭代TreeMap并通过调用right找到匹配的记录,迭代子图,将匹配的subMap()left记录提供给线程3进行处理。

      3. 线程3处理匹配对。