条件连接性能优化-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(其他筛选条件)
      合并到您的联接中。您能否解释一下为什么这是一个更好的解决方案?我想说,通常最好使用“确定”联接而不是“条件”联接,这样优化器就可以制定更准确的计划,而不必猜测。话虽如此,哟