我想比较每个表中有两百万条记录的两个表,并从比较中获取匹配数据。
要从两个表中获取匹配数据,我们首先要比较table1
中的名称不应等于table2
中的名称。然后我们比较table1
中的城市应该等于table2
中的城市,然后最后我们将table1
中的date_of_birth与{1}中的date_of-birth范围进行比较{ {1}}。
table2
中的单行可以与Table 1
中的数据进行多次匹配。
此外,对于每个匹配,我需要一个唯一的记录ID,并且单个表1行的多个匹配数据必须具有相同的唯一记录ID。
我尝试使用Java代码和PL / SQL程序,但两者都耗费数小时,因为这需要将数百万数据与数百万数据进行比较。有没有更快的方法来进行匹配?
答案 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
的所有记录。
比较将如下进行:
从两个表中选择数据,按city
排序。
并行迭代两个结果集。
将一个结果集(left
)中的所有记录加载到下一个city
,即加载下一个块。将它们存储在TreeMap<LocalDate, List<Person>>
。
使用相同的right
迭代其他结果集(city
)中的所有记录,即处理该块。
对于right
中的每条记录,请致电subMap()
,在birthDate
的1年内查找记录,如下所示:
Collection<List<Person>> coll =
leftTree.subMap(right.birthDate.minusYears(1), true,
right.birthDate.plusYears(1), true)
.values();
迭代记录并跳过具有相同name
的记录。这些是与left
给定记录“匹配”的right
条记录。
如果需要,您可以展平并使用stream过滤名称:
List<Person> matches = coll.stream()
.flatMap(List::stream)
.filter(p -> ! p.name.equals(right.name))
.collect(Collectors.toList());
可选择用实际处理逻辑替换collect()
。
按照步骤4中所述处理块时,即当您看到下一个city
时,清除TreeMap
,然后从步骤3开始重复下一个块,即{{ 1}}。
这种逻辑的优点:
数据仅从数据库服务器发送一次,即从相对较慢的数据链路中消除由部分交叉连接引起的数据重复。
如果需要,这两个查询可以来自两个不同的数据库。
通过一次仅保留一个city
个查询的数据(city
的一块)来缩短内存占用量。
如果需要,匹配逻辑可以是多线程的,以获得额外的性能,例如
线程1将left
块加载到left
,并将其提供给线程2进行处理,而线程1开始加载下一个块。
线程2迭代TreeMap
并通过调用right
找到匹配的记录,迭代子图,将匹配的subMap()
和left
记录提供给线程3进行处理。
线程3处理匹配对。