【问题标题】:how can we compare data between two different tables much faster我们怎样才能更快地比较两个不同表之间的数据
【发布时间】:2019-12-20 02:01:53
【问题描述】:

我有来自两个不同数据库的两个不同表。我在两个表中都有大量数据,但我在两个表中都有相同的列。

当我尝试使用以下代码时,我遇到了一些性能问题(尽管它在员工表中只有 2 条记录,但在部门表中我有 100 000 条记录)比较需要 10 多分钟。

有什么办法可以降低性能并使其更快。

EmplTbl = cur.execute("select A , B , C from EmployeeTable where EmplName in ('A','B')") 
for line in EmplTbl:
    EmplData.append(line)

DeptTbl = cur.execute("select A , B , C from DeptTable") 

for line in DeptTbl:
    DeptData.append(line)

for Empl in EmplData:
    DeptResult = all(Empl in DeptData for elm in DeptData)
    if DeptResult:
        print("Yes")
    else:
        print("No")

【问题讨论】:

  • 两个问题:首先,您实际尝试实现什么?第二,数据库是 SQL 吗?如果是这样,为什么不使用连接?
  • 提供数据帧的 sn-p 和比文本说明更易于使用的预期输出
  • 只是好奇,DepData 是一个数据框吗?还是只是一个普通的列表?如果它是一个数据框(我怀疑),不建议像那样遍历 df。
  • @Derlin .. 非常感谢您的回复。数据库是 oracle,我正在尝试获取所有 3 列的两个表之间的完全匹配记录。两者都来自不同的数据库,这就是我不能使用联接的原因。
  • 如前所述,如果您正在寻找性能,我会尝试在数据库端实现结果,左连接、右连接、外连接等。

标签: python pandas oracle list hdf5


【解决方案1】:

如果表几乎相同,则比较数据块的哈希会更快,然后只比较有差异的块的所有数据。

我敢打赌,大部分运行时间都花在了传输和转换数据上。从 Employee 表中读取 100,000 行在数据库中可能只需要几秒钟。使用函数DBMS_SQLHASH.GETHASH,Oracle 可以快速为大量数据生成哈希。 (您可能需要让 DBA 运行 grant execute on sys.dbms_sqlhash to your_user;

例如,想象一下这两个表(实际上它们要大得多,并且位于不同的数据库中):

create table EmployeeTable1 as
select 1 a, 2 b, 3 c, 'abcdefg' EmplName from dual union all
select 1 a, 2 b, 3 c, 'bcdefg'  EmplName from dual union all
select 1 a, 2 b, 3 c, 'cdefg'   EmplName from dual;

create table EmployeeTable2 as
select 1 a, 2 b, 3 c, 'abcdefg' EmplName from dual union all
select 1 a, 2 b, 3 c, 'bcdefg'  EmplName from dual union all
select 9 a, 9 b, 9 c, 'cdefg'   EmplName from dual;

为员工姓名的每个首字母生成一个哈希值。

--Table 1 hashes:
select 'a', dbms_sqlhash.gethash('select a,b,c,EmplName from EmployeeTable1 where EmplName like ''a%'' order by 1,2,3', 3) from dual union all
select 'b', dbms_sqlhash.gethash('select a,b,c,EmplName from EmployeeTable1 where EmplName like ''b%'' order by 1,2,3', 3) from dual union all
select 'c', dbms_sqlhash.gethash('select a,b,c,EmplName from EmployeeTable1 where EmplName like ''c%'' order by 1,2,3', 3) from dual;

a   923920839BFE25A44303718523CBFE1CEBB11053
b   355CB0FFAEBB60ECE2E81F3C9502F2F58A23F8BC
c   F2D94D7CC0C82329E576CD867CDC52D933C37C2C <-- DIFFERENT


--Table 2 hashes:
select 'a', dbms_sqlhash.gethash('select a,b,c,EmplName from EmployeeTable2 where EmplName like ''a%'' order by 1,2,3', 3) from dual union all
select 'b', dbms_sqlhash.gethash('select a,b,c,EmplName from EmployeeTable2 where EmplName like ''b%'' order by 1,2,3', 3) from dual union all
select 'c', dbms_sqlhash.gethash('select a,b,c,EmplName from EmployeeTable2 where EmplName like ''c%'' order by 1,2,3', 3) from dual;

a   923920839BFE25A44303718523CBFE1CEBB11053
b   355CB0FFAEBB60ECE2E81F3C9502F2F58A23F8BC
c   6B7B1D374568B353E9A37EB35B4508B6AE665F8A <-- DIFFERENT

Python程序只需比较哈希值,就能很快发现“a”和“b”是相同的,区别在于以“c”开头的员工。然后程序只需比较所有细节以获得较小的结果集。

不幸的是,这个解决方案需要更多的编码,因为您必须在 Python 中构建循环,并构建多个 SQL 语句。如果您的表完全不同,解决方案将更慢,并且您需要处理数据以找到正确的块大小。

【讨论】:

  • 在您看来,将 ORDER BY 子句添加到 SELECT ... FROM EmployeeTable1 语句中是否明智? (即到 dbms_sqlhash.gethash() 的“sqltext”参数)?
  • @stefan 你说得对,谢谢你的提醒。 ORDER BY 是必要的,否则结果将不确定。我以前被那个烫伤过,我应该记得的。
  • @JonHeller。非常感谢您的回复.. 我们是否有可能在不依赖 DB 的情况下在 python 本身中创建哈希值。 ?
  • @Arya 在 Python 中创建哈希不会有帮助,因为您仍然必须将所有数据传输到程序。哈希的意义在于显着减少网络流量和开销,因此需要在源头生成。
【解决方案2】:

您的代码所做的工作似乎比您预期的要多得多。你的线路:

DeptResult = all(Empl in DeptData for elm in DeptData)

隐含地在做:

DeptResult = True
for elem in DeptData:
  for tmp in DeptData:
     DeptResult = DeptResult and Empl == tmp

即当您只需要一个时,您将在 DeptData 上进行两次嵌套传递,因此需要 len(DeptData) ** 2 操作。这意味着您正在尝试进行 1e10 比较,并且确实需要很长时间才能完成

我将其改写为:

cur.execute("select A , B , C from DeptTable")
dept_entries = set(cur)

cur.execute("select A , B , C from EmployeeTable where EmplName in ('A','B')") 
for empl in cur:
  if empl in dept_entries:
    print(empl, 'Yes')
  else:
    print(empl, 'No')

请注意,Python 数据库连接器通常不会从它们的 execute 方法返回结果,您应该调用它们的 fetch* 方法之一或在游标上迭代。我不使用 Oracle,但other posts 建议他们应该遵循标准并且您的代码已损坏

DeptTable 放入set 意味着查找现在是O(1),因此empl in dept_entries 非常便宜

注意:可能值得阅读一些关于 tuple 相等在 Python 中如何工作的教程,以及像 set 这样的数据结构,甚至可能只是基本的迭代

【讨论】:

  • 。嗨,Sam,非常感谢您的回复。我尝试了这个解决方案,但仍然需要更多时间。虽然在“Empl”中我有 2 条记录,并且它们与部门表匹配,但要花费大量时间来提供输出。
【解决方案3】:

纯 sql。

select * from DeptResult
minus 
select * from EmployeeTable

union 

select * from EmployeeTable
minus 
select * from DeptResult

【讨论】:

  • 我试图用 Python 而不是 sql 实现
【解决方案4】:

考虑使用纯 SQL 解决方案来避免多个 Python 循环。具体来说,运行 aLEFT JOIN 并测试所有三列中的 NULL 匹配:

select e.A as emp_A, e.B as emp_B, e.C as emp_C, 
       d.A as dept_A, d.B as dept_B, d.C as dept_C,
       case
           when not (d.A is null or d.B is null or d.C is null) then 'Yes'
           else 'No'
       end as DeptResult
from EmployeeTable e
left join DeptTable d 
    on e.A = d.A and e.B = d.B and e.C = d.C
where e.EmplName in ('A', 'B')

【讨论】:

  • 我正在寻找在 python 中而不是在 SQL 中实现的解决方案。
  • 好奇为什么?再一次,这个解决方案是 一个 SQL 调用避免 ALL 循环。否则,请考虑矢量化pandas 解决方案(执行相同的合并和 if/then 逻辑)。您的上述 cmets 似乎对 SQL 开放。我可以删除这个。但顺便说一句,我很好奇 - 这个查询有效吗?
猜你喜欢
  • 2020-04-06
  • 1970-01-01
  • 2015-11-22
  • 2019-12-16
  • 1970-01-01
  • 2017-06-19
  • 2011-07-20
  • 2023-03-05
  • 2013-02-28
相关资源
最近更新 更多