条件连接性能优化-Oracle SQL
我需要查看数据库中的一个重要对象,它可以是我的小型建筑公司处理的任何类型的交易。然后,根据交易类型,计算到期日和承诺日期。有十五种不同的交易类型,但我主要关注四种或五种:条件连接性能优化-Oracle SQL,sql,oracle,performance,Sql,Oracle,Performance,我需要查看数据库中的一个重要对象,它可以是我的小型建筑公司处理的任何类型的交易。然后,根据交易类型,计算到期日和承诺日期。有十五种不同的交易类型,但我主要关注四种或五种: SELECT datatable.ID_Number, datatable.Object_Type, CASE WHEN Object_Type = 'AA' THEN (SELECT PO_DUE_DATE FROM tblPO WHERE datatable.ID_Number =
SELECT
datatable.ID_Number,
datatable.Object_Type,
CASE
WHEN Object_Type = 'AA' THEN (SELECT PO_DUE_DATE FROM tblPO WHERE datatable.ID_Number = tblPO.PO_ID)
WHEN Object_Type = 'AB' THEN (SELECT PROD_DUE_DATE FROM tblPROD WHERE datatable.ID_Number = tblPROD.PROD_ID)
WHEN Object_Type = 'AC' THEN (SELECT PLAN_DUE_DATE FROM tblPLAN WHERE datatable.ID_Number = tblPLAN.PLAN_ID)
WHEN Object_Type = 'BN' THEN (SELECT NEED_DUE_DATE FROM tblPURCHASE WHERE datatable.ID_Number = tblPURCHASE.PURCHASE_ID)
ELSE TO_DATE(NULL) END AS Object_Due_Date,
CASE
WHEN Object_Type = 'AA' THEN (SELECT PO_PROM_DATE FROM tblPO WHERE datatable.ID_Number = tblPO.PO_ID)
WHEN Object_Type = 'AB' THEN (SELECT PROD_PROM_DATE FROM tblPROD WHERE datatable.ID_Number = tblPROD.PROD_ID)
WHEN Object_Type = 'AC' THEN (SELECT PLAN_PROM_DATE FROM tblPLAN WHERE datatable.ID_Number = tblPLAN.PLAN_ID)
WHEN Object_Type = 'BN' THEN (SELECT NEED_PROM_DATE FROM tblPURCHASE WHERE datatable.ID_Number = tblPURCHASE.PURCHASE_ID)
ELSE TO_DATE(NULL) END AS Object_Promised_Date
FROM
datatable
WHERE
( other filtering criteria )
这给了我一个输出,比如:
| ID_Number | Object_Type | Object_Due_Date | Object_Promised_Date |
|:---------:|:-----------:|:---------------:|:--------------------:|
| 1 | AA | 11/26/2018 | 10/18/2018 |
| 2 | AB | 5/12/2018 | 3/31/2018 |
| 3 | AA | 6/15/2018 | 9/18/2018 |
| 4 | AA | 1/24/2018 | 10/2/2018 |
| 5 | ZZ | 10/27/2018 | 6/11/2018 |
| 7 | BN | 1/23/2018 | 7/2/2018 |
| 8 | AC | 4/3/2018 | 8/3/2018 |
| 9 | BN | 12/1/2018 | 8/16/2018 |
| 10 | BN | 1/10/2018 | 10/6/2018 |
而且它工作得很漂亮!问题是,datatable
大约有2000万条记录,而且这些日期可能会更改,因此我需要每隔一段时间(每周一到两次)刷新报告。运行和更新需要8-9个小时,因为对于每个记录,我都有条件地加入另一个表
如何提高此查询的运行时效率?我知道我可以保留表的连接,但我不知道如何用日期填充单个列的值,这取决于对象类型
,而不是为类型
类型等设置n列
SELECT
datatable.ID_Number,
datatable.Object_Type,
tblPO.PO_DUE_DATE Object_Due_Date,
tblPO.PO_PROM_DATE Object_Promised_Date
FROM
datatable
join tblPO on datatable.ID_Number = tblPO.PO_ID
WHERE
Object_Type = 'AA'
( other filtering criteria )
union all
SELECT
datatable.ID_Number,
datatable.Object_Type,
tblPROD.PROD_DUE_DATE,
tblPROD.PROD_PROM_DATE
FROM
datatable
join tblPLAN on datatable.ID_Number = tblPLAN.PLAN_ID
WHERE
Object_Type = 'AB'
( other filtering criteria )
union all
as early with tables tblPLAN and tblPURCHASE
试试这个
SELECT
datatable.ID_Number,
datatable.Object_Type,
tblPO.PO_DUE_DATE Object_Due_Date,
tblPO.PO_PROM_DATE Object_Promised_Date
FROM
datatable
join tblPO on datatable.ID_Number = tblPO.PO_ID
WHERE
Object_Type = 'AA'
( other filtering criteria )
union all
SELECT
datatable.ID_Number,
datatable.Object_Type,
tblPROD.PROD_DUE_DATE,
tblPROD.PROD_PROM_DATE
FROM
datatable
join tblPLAN on datatable.ID_Number = tblPLAN.PLAN_ID
WHERE
Object_Type = 'AB'
( other filtering criteria )
union all
as early with tables tblPLAN and tblPURCHASE
类似于针头的答案,但我喜欢将来自不同来源的类似数据合并到一个视图中。在本例中,我使用的是内联视图,但是如果您打算在很多地方使用它,那么创建一个实际视图来显示不同事务类型的到期日期和承诺日期可能是值得的
SELECT
datatable.ID_Number,
datatable.Object_Type,
type_lookups.Object_Due_Date,
type_lookups.Object_Promised_Date
FROM
datatable
LEFT JOIN (
select 'AA' as Object_Type,
PO_DUE_DATE as Object_Due_Date,
PO_PROM_DATE as Object_Promised_Date,
PO_ID as ID
from tblPO
union all
select 'AB' as Object_Type,
PROD_DUE_DATE as Object_Due_Date,
PROD_PROM_DATE as Object_Promised_Date,
PROD_ID as ID
from tblPROD
union all
select 'AC' as Object_Type,
PLAN_DUE_DATE as Object_Due_Date,
PLAN_PROM_DATE as Object_Promised_Date,
PLAN_ID as ID
from tblPLAN
union all
select 'BN' as Object_Type,
NEED_DUE_DATE as Object_Due_Date,
NEED_PROM_DATE as Object_Promised_Date,
PURCHASE_ID as ID
from tblPURCHASE
) type_lookups
ON type_lookups.object_type = datatable.Object_Type
AND type_lookups.ID = datatable.ID_Number
WHERE
( other filtering criteria )
类似于针头的答案,但我喜欢将来自不同来源的类似数据合并到一个视图中。在本例中,我使用的是内联视图,但是如果您打算在很多地方使用它,那么创建一个实际视图来显示不同事务类型的到期日期和承诺日期可能是值得的
SELECT
datatable.ID_Number,
datatable.Object_Type,
type_lookups.Object_Due_Date,
type_lookups.Object_Promised_Date
FROM
datatable
LEFT JOIN (
select 'AA' as Object_Type,
PO_DUE_DATE as Object_Due_Date,
PO_PROM_DATE as Object_Promised_Date,
PO_ID as ID
from tblPO
union all
select 'AB' as Object_Type,
PROD_DUE_DATE as Object_Due_Date,
PROD_PROM_DATE as Object_Promised_Date,
PROD_ID as ID
from tblPROD
union all
select 'AC' as Object_Type,
PLAN_DUE_DATE as Object_Due_Date,
PLAN_PROM_DATE as Object_Promised_Date,
PLAN_ID as ID
from tblPLAN
union all
select 'BN' as Object_Type,
NEED_DUE_DATE as Object_Due_Date,
NEED_PROM_DATE as Object_Promised_Date,
PURCHASE_ID as ID
from tblPURCHASE
) type_lookups
ON type_lookups.object_type = datatable.Object_Type
AND type_lookups.ID = datatable.ID_Number
WHERE
( other filtering criteria )
像这样的怎么样
SELECT
dt.ID_Number,
dt.Object_Type,
CASE
WHEN Object_Type = 'AA' THEN po.PO_DUE_DATE
WHEN Object_Type = 'AB' THEN pd.PROD_DUE_DATE
WHEN Object_Type = 'AC' THEN pl.PLAN_DUE_DATE
WHEN Object_Type = 'BN' THEN pc.NEED_DUE_DATE
ELSE TO_DATE(NULL) END AS Object_Due_Date,
CASE
WHEN Object_Type = 'AA' THEN po.PO_PROM_DATE
WHEN Object_Type = 'AB' THEN pd.PROD_PROM_DATE
WHEN Object_Type = 'AC' THEN pl.PLAN_PROM_DATE
WHEN Object_Type = 'BN' THEN pc.NEED_PROM_DATE
ELSE TO_DATE(NULL) END AS Object_Promised_Date
FROM
datatable dt
LEFT JOIN tblPO po ON dt.ID_Number = po.PO_ID
LEFT JOIN tblPROD pd ON dt.ID_Number = pd.PROD_ID
LEFT JOIN tbdPLAN pl ON dt.ID_Number = pl.PLAN_ID
LEFT JOIN tblPURCHASE pc ON dt.ID_Number = pc.PURCHASE_ID
WHERE
( other filtering criteria )
我刚刚用左连接替换了条件选择
。您可以运行一个EXPLAIN PLAN
,对比原始查询,看看这是否改变了什么
其他想法
- 如果尚未在
datatable.ID\u Number
上定义索引,则应在其上创建一个索引,因为在所有选择中都可以访问该索引
- 如果其他
JOIN
列(PO\u ID、PROD\u ID,…)尚未编制索引,则可能在这些列上创建索引
- 如果您知道行始终存在于
左联接
表中,请将联接更改为内部联接
…这样可以加快联接速度
像这样的东西怎么样
SELECT
dt.ID_Number,
dt.Object_Type,
CASE
WHEN Object_Type = 'AA' THEN po.PO_DUE_DATE
WHEN Object_Type = 'AB' THEN pd.PROD_DUE_DATE
WHEN Object_Type = 'AC' THEN pl.PLAN_DUE_DATE
WHEN Object_Type = 'BN' THEN pc.NEED_DUE_DATE
ELSE TO_DATE(NULL) END AS Object_Due_Date,
CASE
WHEN Object_Type = 'AA' THEN po.PO_PROM_DATE
WHEN Object_Type = 'AB' THEN pd.PROD_PROM_DATE
WHEN Object_Type = 'AC' THEN pl.PLAN_PROM_DATE
WHEN Object_Type = 'BN' THEN pc.NEED_PROM_DATE
ELSE TO_DATE(NULL) END AS Object_Promised_Date
FROM
datatable dt
LEFT JOIN tblPO po ON dt.ID_Number = po.PO_ID
LEFT JOIN tblPROD pd ON dt.ID_Number = pd.PROD_ID
LEFT JOIN tbdPLAN pl ON dt.ID_Number = pl.PLAN_ID
LEFT JOIN tblPURCHASE pc ON dt.ID_Number = pc.PURCHASE_ID
WHERE
( other filtering criteria )
我刚刚用左连接替换了条件选择
。您可以运行一个EXPLAIN PLAN
,对比原始查询,看看这是否改变了什么
其他想法
- 如果尚未在
datatable.ID\u Number
上定义索引,则应在其上创建一个索引,因为在所有选择中都可以访问该索引
- 如果其他
JOIN
列(PO\u ID、PROD\u ID,…)尚未编制索引,则可能在这些列上创建索引
- 如果您知道行始终存在于
左联接
表中,请将联接更改为内部联接
…这样可以加快联接速度
您也可以尝试以下方法:
SELECT
datatable.ID_Number,
datatable.Object_Type,
COALESCE (tblPO.PO_DUE_DATE, tblPROD.PROD_DUE_DATE, tblPLAN.PLAN_DUE_DATE,
tblPURCHASE.NEED_DUE_DATE) AS Object_Due_Date,
COALESCE (tblPO.PO_PROM_DATE, tblPROD.PROD_PROM_DATE, tblPLAN.PLAN_PROM_DATE,
NEED_PROM_DATE.NEED_DUE_DATE) AS Object_Promised_Date
FROM
datatable
LEFT JOIN tblPO
ON datatable.ID_Number = tblPO.PO_ID
AND datatable.Object_Type = 'AA'
LEFT JOIN tblPROD
ON datatable.ID_Number = tblPROD.PROD_ID
AND datatable.Object_Type = 'AB'
LEFT JOIN tblPLAN
ON datatable.ID_Number = tblPLAN.PLAN_ID
AND datatable.Object_Type = 'AC'
LEFT JOIN tblPURCHASE
ON datatable.ID_Number = tblPURCHASE.PURCHASE_ID
AND datatable.Object_Type = 'BN'
WHERE
( other filtering criteria )
您也可以尝试以下方法:
SELECT
datatable.ID_Number,
datatable.Object_Type,
COALESCE (tblPO.PO_DUE_DATE, tblPROD.PROD_DUE_DATE, tblPLAN.PLAN_DUE_DATE,
tblPURCHASE.NEED_DUE_DATE) AS Object_Due_Date,
COALESCE (tblPO.PO_PROM_DATE, tblPROD.PROD_PROM_DATE, tblPLAN.PLAN_PROM_DATE,
NEED_PROM_DATE.NEED_DUE_DATE) AS Object_Promised_Date
FROM
datatable
LEFT JOIN tblPO
ON datatable.ID_Number = tblPO.PO_ID
AND datatable.Object_Type = 'AA'
LEFT JOIN tblPROD
ON datatable.ID_Number = tblPROD.PROD_ID
AND datatable.Object_Type = 'AB'
LEFT JOIN tblPLAN
ON datatable.ID_Number = tblPLAN.PLAN_ID
AND datatable.Object_Type = 'AC'
LEFT JOIN tblPURCHASE
ON datatable.ID_Number = tblPURCHASE.PURCHASE_ID
AND datatable.Object_Type = 'BN'
WHERE
( other filtering criteria )
LEFT JOIN PO_DUE_DATE FROM tblPO
你说的FROM
是什么意思通常我在列名上使用JOIN then这个词,这是一个错误修复的好方法。奇怪的是,COALESCE
似乎给我带来了一些非常奇怪的结果,可能是因为字段不是NULL
而是空的,充满了空格或垃圾字节数据。LEFT JOIN PO\u due\u DATE FROM tblPO
你说的FROM
是什么意思通常我在列名上使用单词JOIN then,这是一个错误-fixedTony,好办法。奇怪的是,COALESCE
似乎给我带来了一些非常奇怪的结果,可能是因为字段不是NULL
,而是空的,充满了空格或垃圾字节数据。你能解释一下为什么这比我当前的方法好吗?标量子查询(你当前的方法)每行调用一次;如果您的唯一值数量较少,则结果缓存会有所帮助,但通常如果您的查询返回大量行,我认为您应该尝试将逻辑移动到联接中。您能否解释一下为什么这优于我当前的方法?标量子查询(您当前的方法)每行调用一次;如果您的唯一值数量较少,则结果缓存会有所帮助,但通常,如果您的查询返回大量行,我认为您应该尝试将逻辑移动到联接中。您能否解释为什么这是一个更好的解决方案?我想说,通常最好使用“确定”联接而不是“条件”联接加入,这样优化器就可以制定更准确的计划,而无需猜测。话虽如此……你是最好的裁判:)试着运行两个查询和一个解释计划
,看看是否有任何区别/改进。出于好奇,你能测试一下吗?是否有任何性能改进?我在速度方面进行了测试,速度快了很多(9小时->4小时),但我没有使用EXPLAIN PLAN
,因为我不知道有多甜!!只需将文本解释计划
放在您的查询前面(即解释计划选择dt.ID\U编号…
并运行它。这将为您提供优化器的查询计划。您可以以相同的方式将其与旧的查询计划进行比较,并查看它的不同之处。也许试着摆弄一下索引,看看你是否能更快地得到它!或者,您也可以将一些WHERE(其他筛选条件)
合并到您的联接中。您能否解释一下为什么这是一个更好的解决方案?我想说,通常最好使用“确定”联接而不是“条件”联接,这样优化器就可以制定更准确的计划,而不必猜测。话虽如此,哟