我们如何更快地比较两个不同表之间的数据

时间:2019-08-13 13:38:54

标签: python pandas oracle list hdf5

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

当我尝试使用以下代码时,我遇到了一些性能问题(尽管在employee表中只有2条记录,但在Department表中却有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")

4 个答案:

答案 0 :(得分:3)

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

我敢肯定,大部分运行时间都花在了传输和转换数据上。从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语句。如果您的表有很大不同,解决方案将是 slower ,并且您需要处理数据以找到正确的块大小。

答案 1 :(得分:1)

您的代码似乎正在做比预期更多的工作。您的电话:

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这样的数据结构,甚至可能只是基本迭代

答案 2 :(得分:-1)

请考虑使用纯SQL解决方案,以避免多个Python循环。具体来说,请运行LEFT 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')

答案 3 :(得分:-1)

纯SQL。

select * from DeptResult
minus 
select * from EmployeeTable

union 

select * from EmployeeTable
minus 
select * from DeptResult