【问题标题】:Slow query when using datediff function in Oracle在 Oracle 中使用 datediff 函数时查询慢
【发布时间】:2020-01-29 13:52:54
【问题描述】:

作为我的标题,我有以下代码:

 SELECT
        *
    FROM
        de.Department
    WHERE
        de.flag = 1
        AND   de.DepartmentNum IN (10,4)
        AND   de.status IN (0,-1,100)
        AND   datediff('dd',de.datequit,'30-SEP-19') > 9

datediff 函数使我的查询运行非常慢(11 条记录需要 16 秒),而且成本也非常高(~43k)。

这是我的 datediff 函数代码

create or replace FUNCTION       DATEDIFF 
(
  P_TYPE_DATE IN VARCHAR2 
, P_START_DATE IN TIMESTAMP 
, P_END_DATE IN TIMESTAMP 
) RETURN NUMBER AS 
  v_Result NUMBER := -1;
BEGIN

  IF P_TYPE_DATE IS NOT NULL AND P_START_DATE IS NOT NULL AND P_END_DATE IS NOT NULL THEN
    CASE UPPER(P_TYPE_DATE) 
        WHEN 'DD' THEN RETURN ROUND(TRUNC(P_END_DATE,'DD') - TRUNC(P_START_DATE,'DD'),0);
        WHEN 'HH' THEN RETURN ROUND((TRUNC(P_END_DATE,'HH') - TRUNC(P_START_DATE,'HH')) * 24,0);
        WHEN 'MI' THEN RETURN ROUND((TRUNC(P_END_DATE,'MI') - TRUNC(P_START_DATE,'MI')) * 24 * 60,0);
        WHEN 'SS' THEN RETURN ROUND((TRUNC(P_END_DATE,'MI') - TRUNC(P_START_DATE,'MI')) * 24 * 60 * 60 + extract(second from (P_END_DATE - P_START_DATE)),0);
        ELSE RETURN NULL;
    END CASE;
  END IF;
  RETURN NULL;
  EXCEPTION
    WHEN OTHERS THEN
       raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END DATEDIFF;

我使用 SELECT * 是因为我想在 Department 表中获取几乎列,所以如果我 SELECT 一些我需要的列,它就不会再改变了。

我可以重写以提高性能和成本吗?

谢谢!

【问题讨论】:

  • 我假设您错误地标记了它并且实际上正在使用 SQL Server。它是一个已知问题。此页面上列出了一些替代方案。 sqltouch.blogspot.com/2013/11/…
  • Oracle has no datediff() 您的查询将根本不会运行 - 并且会因错误而失败。
  • @a_horse_with_no_name 我在 SQL 中创建了一个名为 datediff 的函数作为 datediff 函数,先生
  • @thewindlove123 那么你的问题应该是“为什么我的 DATEDIFF 函数这么慢”并贴出 DATEDIFF 函数代码
  • @CathalMF 我添加了代码,感谢您的建议

标签: sql oracle plsql oracle12c datediff


【解决方案1】:

我在 SQL 中创建了一个名为 datediff 的函数作为 datediff 函数,先生

不要使用自定义函数,因为它们会阻止 Oracle 在列上使用索引;而是将列与静态值进行比较:

SELECT *
FROM   Department
WHERE  flag = 1
AND    DepartmentNum IN (10,4)
AND    status IN (0,-1,100)
AND    datequit > DATE '2019-09-30' + INTERVAL '9' DAY

AND    datequit > DATE '2019-09-30' + NUMTODSINTERVAL( 9, 'DAY' )

AND    datequit > DATE '2019-09-30' + 9

这是我的 datediff 函数代码

...
WHEN 'DD' THEN RETURN ROUND(TRUNC(P_END_DATE,'DD') - TRUNC(P_START_DATE,'DD'),0);
...

如果您想与使用 TRUNC 进行等效比较以忽略时间分量,则从使用大于比较更改为使用大于或等于并添加一个时间单位(在您的示例中为天) 到预期的差异。例如:

SELECT *
FROM   Department
WHERE  flag = 1
AND    DepartmentNum IN (10,4)
AND    status IN (0,-1,100)
AND    datequit >= DATE '2019-09-30' + INTERVAL '10' DAY

【讨论】:

  • 3 个案例返回“未选择行”(如果我使用 datediff,则超过 8k5 行)
  • @thewindlove123 我没有足够的上下文来理解该评论的含义。更少的“没有选择行”是好事还是坏事?如果这是一个问题,那么您需要 edit 您的问题包括证明问题的测试数据以及您对该测试数据的预期输出。
  • 为什么我的声明是 '30-SEP-19' - datequit > 9 时大于或等于?如果我选择小于或等于,而不是大于先生,它是正确的。但是,成本包括从 43k 到超过 100k
  • 如果您不是 TRUNCating 时间戳,那么您将有小数天数,因此您需要从 integer_difference > 9 更改为 decimal_difference >= 10。如果您将其保留为 decimal_difference > 9,那么它还将匹配 9.00001 到 9.99999 天的差异,这不是您之前结果中包含的内容。
  • @thewindlove123 成本并不总是最佳指标,因为 Oracle 不会确定用户定义函数的总成本。 [1] 声明“评估用户定义函数的成本取决于算法及其参数的统计属性。存储可用作所有函数参数的所有可能的列组合的统计信息是不切实际的。 "如果您想要准确的 UDF 成本,那么您需要查看可扩展的优化器功能并生成您自己的统计信息
猜你喜欢
  • 2013-05-05
  • 1970-01-01
  • 2012-12-24
  • 1970-01-01
  • 2015-04-08
  • 2018-10-05
  • 2020-03-23
  • 2020-09-22
  • 2015-10-04
相关资源
最近更新 更多