优化Oracle Join查询(同一个表上的多个连接)

时间:2017-12-13 17:53:30

标签: oracle query-optimization

我的Oracle数据库中有三个表:

表1:包含A公司的员工和电话号码:

EmployeeName, WorkPhone, MobilePhone, PersonalPhone,OtherPhone
Adam,1234,1111,0987,NULL
Catherine,2345,5432,NULL,NULL
Tom, 4567,7654,0101,0002

表2:包含B公司的员工和电话号码:

EmployeeName, WorkPhone, MobilePhone, PersonalPhone, OtherPhone
David,8888,9999,0000,1245
Sam,4321,5432,NULL,NULL
Clara,4567,7654,0101,NULL

表3:包含电话号码,其中电话号码可以记录在第1列或第2列或两者中:

PhoneNumber1, PhoneNumber2
1234,NULL
7654,7575
0000,1111
1234,4321
NULL,1234
5432,1234

现在,我想将表3 中的电话号码加入其各自的员工,以及了解该员工的工作地点(公司A或B)。挑战在于我们总共有8个匹配的" 表2 的可能性和表2 的可能性(表A / B中的每列可以加入表3 中的第1列或第2列。

数据集很大。 (表1中有20M行,表2中有大约2M行)。现在暂时不使用Table2,只关注将Table1连接到Table3。

如果我执行以下操作,查询非常慢(我想它会在某些时候耗尽临时表空间):

SELECT * FROM Table3 t3
LEFT JOIN Table1 t1
ON (PhoneNumber1 in (WorkPhone, MobilePhone, PersonalPhone, OtherPhone)
   OR PhoneNumber2 in (WorkPhone, MobilePhone, PersonalPhone, OtherPhone))

如果我执行以下操作,查询将耗尽临时表空间(我不允许增加它)

SELECT * FROM Table3
LEFT JOIN Table1 t1_1
PhoneNumber1 = t1_1.WorkPhone
LEFT JOIN Table1 t1_2
PhoneNumber1 = t1_1.MobilePhone
LEFT JOIN Table1 t1_3
PhoneNumber1 = t1_1.PersonalPhone
...etc

我们如何优化此查询?

1 个答案:

答案 0 :(得分:1)

您可以将其中一个或两个表都取消,以便为每个(非空)电话号码获取一行,然后进行简单的连接:

with cte1 as (
  select * from (
    select 'A' as company, t.* from table1 t
    union all
    select 'B' as company, t.* from table2 t
  )
  unpivot (phone for type in (workphone as 'Work', mobilephone as 'Mobile',
    personalphone as 'Personal', otherphone as 'Other'))
),
cte2 as (
  select distinct phone from table3
  unpivot (phone for type in (phonenumber1 as 'Phone1', phonenumber2 as 'Phone2'))
)
select cte1.*
from cte2
join cte1 on cte1.phone = cte2.phone;

C EMPLOYEEN TYPE     PHON
- --------- -------- ----
A Adam      Work     1234
A Adam      Mobile   1111
A Catherine Mobile   5432
A Tom       Mobile   7654
B David     Personal 0000
B Sam       Work     4321
B Sam       Mobile   5432
B Clara     Mobile   7654

8 rows selected. 

第一个CTE首先将表1和表2联合在一起,同时添加一个伪列,指示数据来自哪个表;然后取消结果,这样每个人每个电话号码就可以获得一行:

...
select * from cte1;

C EMPLOYEEN TYPE     PHON
- --------- -------- ----
A Adam      Work     1234
A Adam      Mobile   1111
A Adam      Personal 0987
A Catherine Work     2345
A Catherine Mobile   5432
...
B Clara     Personal 0101

18 rows selected. 

你也可以先将每张桌子都展开,然后将它们结合在一起,可能值得尝试两种方式。

第二个CTE将表3删除,因此您可以在任一列中为每个非空电话号码获取一行:

...
select * from cte2;

PHON
----
7654
7575
0000
4321
1234
5432
1111

7 rows selected. 

当然,这是在少量虚拟数据上;它可能会在你真正的大桌子上表现得更糟......而且我已经对你最终想要的东西做了一些假设。

另一种方法可能是将表3的值转换为单个列,您可以手动执行而不是显式地取消隐藏,然后将多个查询与前两个表中的每一个联合在一起:

with cte as (
  select phone from (
    select phonenumber1 as phone from table3
    union
    select phonenumber2 as phone from table3
  )
  where phone is not null
)
select 'A' as customer, employeename, 'Work' as type, workphone as phone
from table1 where workphone in (select phone from cte)
union all
select 'A', employeename, 'Mobile', mobilephone
from table1 where mobilephone in (select phone from cte)
union all
select 'A', employeename, 'Personal', mobilephone
from table1 where personalphone in (select phone from cte)
union all
select 'A', employeename, 'Other', mobilephone
from table1 where otherphone in (select phone from cte)
union all 
select 'B', employeename, 'Work', workphone
from table2 where workphone in (select phone from cte)
union all
select 'B', employeename, 'Mobile', mobilephone
from table2 where mobilephone in (select phone from cte)
union all
select 'B', employeename, 'Personal', personalphone
from table2 where personalphone in (select phone from cte)
union all
select 'B', employeename, 'Other', otherphone
from table2 where otherphone in (select phone from cte)
/

C EMPLOYEEN TYPE     PHON
- --------- -------- ----
A Adam      Work     1234
A Adam      Mobile   1111
A Catherine Mobile   5432
A Tom       Mobile   7654
B Sam       Work     4321
B Sam       Mobile   5432
B Clara     Mobile   7654
B David     Personal 0000

8 rows selected. 

我个人觉得难以阅读和维护,但它的表现也可能有很大不同。