我有来自两个不同数据库的两个不同表。我在两个表中都有大量数据,但是在两个表中确实有相同的列。
当我尝试使用以下代码时,我遇到了一些性能问题(尽管在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")
答案 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