【问题标题】:Fastest way to compare millions of rows in one table with millions of rows in another [closed]将一个表中的数百万行与另一个表中的数百万行进行比较的最快方法[关闭]
【发布时间】:2017-04-14 06:14:34
【问题描述】:

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

为了从两个表中获取匹配数据,我们首先比较table1 中的名称不应等于table2 中的名称。然后我们比较table1 中的城市应该等于table2 中的城市,最后我们比较table1 中的date_of_birth 应该与table2 中的date_of-birth +-1 年范围内。

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

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

【问题讨论】:

  • 您能否分享表格的列以及您比较这些表格所依据的列。
  • 首先,由于您已经在 DB 中有数据,Java 肯定不是比较的正确解决方案。至于 DB,这样的任务需要的不仅仅是调整查询;数据存储、数据库和表定义、索引、硬件等起着更重要的作用。这是一个广泛的领域,您应该就此咨询您的 DBA。

标签: java database oracle performance optimization


【解决方案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

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中的城市 应该等于 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)
/

【讨论】:

  • 非常感谢您的帮助。为了从两个表中获取匹配数据,我们首先比较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年范围内的比较。
  • 我不同意“数据库在处理数据方面比 Java 好得多”。假设有足够的内存和昂贵的查询(因此数据加载时间无关紧要),Java 可以更快,因为它可以完成数据库知道的所有技巧,甚至更多。显然,它比编写 SQL 查询需要更多的工作和更多的技巧,所以我绝对同意你推荐 SQL。
  • @maaartinus - 如果有足够的 RAM,我可以将整个数据库读入 DB Buffer Cache。所以我不确定这不是一个有效的论点。而且我很想知道您想到的哪种查询在 SQL 中的计算成本比在 Java 中要高得多,相比之下,从数据库中传输数据的成本是微不足道的。显然有些数据类别不太适合 SQL - 图表是最好的例子 - 但我很小心地说“关系数据”:)
  • 当然,DB 也可以从足够的 RAM 中获利,但这不是我的意思(相反,DB 可以在较小的 RAM 下运行良好)。许多查询具有超线性复杂性,并且比顺序读取所有数据花费的时间更长(OP 的查询可能符合条件)。某些查询可能会从动态创建的索引或仅针对部分数据的索引中受益——这很容易在编程语言中完成,并且您的 DBMS 可能不支持。 +++ 邀请你chat.stackoverflow.com/rooms/141750/…
  • 再次感谢您的帮助,但我担心这对我有帮助吗?实际上,我们正在寻找与大表 1 的单行的大表 2 的多重匹配,即单个大表 1 行可以与大表 2 有多个匹配。匹配数据包含两个表中的列。
【解决方案2】:

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


更新

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&lt;LocalDate, List&lt;Person&gt;&gt; 中。

  4. 使用相同的city迭代来自另一个结果集(right)的所有记录,即处理块。

  5. 对于right 中的每条记录,通过调用subMap() 查找birthDate 1 年内的记录,如下所示:

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

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

      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 处理匹配对。

【讨论】:

  • 现在OP提供了一些细节我们可以看到比较涉及不等式和范围。它不适合逐行处理。
  • @APC 是的。为此,需要“块”处理。
  • 比一条七行 SQL 语句简单得多;)
  • @APC 猜猜这是对问题的解释,它只是说明根据上面列出的 3 个关键标准查找匹配项,但没有说明如何处理匹配的记录。我假设(是的,假设)需要匹配双方的记录,而您的 7 行 SQL 不提供。
  • 有太多的假设:这就是问题被关闭的原因。但它很容易交换表格以获得正面数据集。所以两个七行语句​:-D
猜你喜欢
  • 2015-06-09
  • 2011-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-26
  • 2018-02-15
  • 2018-01-01
相关资源
最近更新 更多