【问题标题】:Optimize query with TO CHAR function使用 TO CHAR 函数优化查询
【发布时间】:2016-10-25 22:19:11
【问题描述】:

我有一个相当大的 SELECT 查询,但它通常只运行大约 1.5/2 分钟,但在添加另一块时,我必须在左外连接上使用 to_char 函数来解决“无效数字”错误.

现在,我的查询需要 10 多分钟,并且在 SSRS 中已超时。 to_char 有更好的替代品吗?我在这个子句中使用它:

left outer join ifsapp.customer_order_line col on col.order_no = fa.order_ref_1 and col.line_no = fa.order_ref_2 and col.rel_no = fa.order_ref_3 and to_char(col.line_item_no) = fa.order_ref_4

line_item_no 字段是“数字”类型,order_ref_4 字段是“varchar2(20)”类型。

我可以使用强制转换或转换来提高效率吗?

【问题讨论】:

    标签: sql oracle reporting-services oracle11g ssrs-2008


    【解决方案1】:

    尝试使用字符串格式为表添加索引:

    CREATE INDEX char_ind
     ON ifsapp.customer_order_line (TO_CHAR(line_item_no));
    

    【讨论】:

    • 我不熟悉这样做...我将这部分代码放在哪里?在加入?或其他地方。
    • 不,它是一个 DDL 命令。只需在查询之外执行一次,表格将永久拥有此索引@DominicG。
    • 好的。对不起,如果这是一个愚蠢的问题,但我可以在 SSRS 中这样做吗?
    • 我认为是的,但我不知道 ssrs。在执行选择查询时尝试以 aame 执行它
    • 不幸的是我做不到。该死。如果在 SSRS 中有办法做到这一点,我会很高兴。
    【解决方案2】:

    我会建议你在开始报告之前修复字段 order_ref_4。如果您创建索引(如果您有授权),您应该在 (order_no,line_no,col.rel_no,to_char(col.line_item_no) ) 上进行。请参阅解释计划以找到确切的问题。 regexp_like + to_number 也不会更快,尤其是当 fa 表很大并且 to_number 将无法在 fa 表上使用索引时。

    最好有一致的数据。标记为错误或修复 order_ref_4 中的错误编号。你可以找到这样的错误

    select distinct fa.order_ref_4 from fa
    where  TRANSLATE(fa.order_ref_4,'0123456789',' ')  is not null
    

    如果只有一种情况不是样本“*”的数字,请使用此

    and (fa.order_ref_4 <> '*' and col.line_item_no = fa.order_ref_4) 
    

    试试这个

    and (TRANSLATE(fa.order_ref_4,'0123456789',' ')  is null and col.line_item_no = fa.order_ref_4) 
    

    第二个将再次导致无法使用 fa 上的索引。

    【讨论】:

    • 我可以给你建议,当你告诉我错误出现在 order_ref_4 中时
    • 如果我尝试将 fa.order_ref_4 转换为数字,则会收到无效数字错误。如果我尝试将 col.line_item_no 转换为字符,则会超时。如果我都不转换,我会得到无效的号码。
    • 您是否有权更新 order_ref_4 列? fa 表有多大?
    • 如果该列总是应该包含数字,那么它应该是一个数字列。如果它是 varchar2,因为它预期有不同类型的数据,那么 (a) 这是一个糟糕的设计选择,并且 (b) 你不能“修复”它。
    • @AlexPoole 你没有自己的答案吗?你对 OP 的数据库了解多少?
    【解决方案3】:

    使用to_char() 会导致所有line_item_no 值(或至少与其他过滤器匹配的行中的所有值,具体取决于优化器选择的计划)转换为字符串,然后才能与order_ref_4 进行比较.除了为了进行转换而增加少量开销外,更严重的是,它会阻止使用该列上的任何索引。执行时间的差异表明正在发生的事情 - 您可以查看旧查询和新查询的执行计划,以了解其他可能发生的变化。

    您可以在比较的右侧检查order_ref_4 值是否可以转换为数字,这将允许line_item_no 上的任何索引仍然可以使用:

       and col.line_item_no =
         case when regexp_like(fa.order_ref_4, '^\d+$')
           then to_number(fa.order_ref_4) end
    

    如果它有任何非数字字符,那么它将尝试匹配 null - 当然,这不会与 = 匹配,因为 null 不能与相等性进行比较。这也只适用于正整数(因为减号或小数字符与\d 不匹配),但我想行项目编号是一个整数。

    如果regexp_like() 太慢,如果您有很多数据,则可能是这样,您可以改用translate() - 与Motor's answer中的想法相同,但仍保护to_number()

       and col.line_item_no =
         case when translate(fa.order_ref_4, '0123456789', ' ') is null
           then to_number(fa.order_ref_4) end
    

    在通用文本字段中存储不同类型的数据会引发此类问题。您确实应该将数据存储在正确类型的列中 - 数字作为数字,日期作为日期等。

    【讨论】:

    • 不幸的是,我无法将 fa.order_ref_4 转换为数字。我收到无效号码错误。
    • @DominicG。 - 你在case表达式中得到了那个?我了解您在尝试 to_char() 之前从(隐式)转换中得到了这一点;本案应该避免这种情况。 Oracle 会短路,因此它不会尝试 to_number(),除非模式匹配,即它是全数字。
    • 现在测试 case 表达式——抱歉,最初只尝试将 order_ref_4 转换为数字。已经跑了 7 分钟,不幸的是还在数。
    • 如果有很多数据,那么正则表达式可能太慢了;您可以使用 translate() 代替 Motortor 显示的。但实际上,您需要查看执行计划以了解它在做什么。
    猜你喜欢
    • 1970-01-01
    • 2012-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-14
    相关资源
    最近更新 更多