【问题标题】:Optimise Oracle Join query (multiple joins on the same table)优化Oracle Join查询(同一张表的多个join)
【发布时间】:2017-12-13 17:53:30
【问题描述】:

我的 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)。挑战在于,Table 2 共有 8 种“匹配”可能性,Table 2 共有 8 种“匹配”可能性(表 A/B 中的每一列都可以连接到任一列 1或 表 3 中的 column2。

数据集很大。 (表 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

我们如何优化这个查询?

【问题讨论】:

  • 您在寻找样本数据的什么输出?您的第一个查询从两个表中获取所有列;第二个从 table1 的多个实例的所有投影中获取所有列,这可能不是您想要的。您实际上需要检索哪些列(来自任一表),如果一个联系人的两个号码匹配,您希望看到什么?
  • 查询解释计划向您展示了什么?在寻找优化时从那里开始。

标签: oracle query-optimization


【解决方案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. 

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

【讨论】:

  • 魔术。 Unpivoting 大大提高了查询的速度。谢谢!
猜你喜欢
  • 2022-01-20
  • 2013-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多