使用 TO CHAR 函数优化查询
Optimize query with TO CHAR function
我有一个相当大的 SELECT 查询,但它 运行 通常只有大约 1.5/2 分钟,但在添加另一块后,我不得不使用 to_char左外连接上的函数以解决 "invalid number" 错误。
现在,我的查询需要 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 字段是 "number" 类型,order_ref_4 字段是 "varchar2(20)" 类型。
我可以使用转换或转换来提高效率吗?
尝试在 table 添加一个字符串格式的索引:
CREATE INDEX char_ind
ON ifsapp.customer_order_line (TO_CHAR(line_item_no));
使用 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()
代替 - 与 Mottor 的回答相同的想法,但仍然保护 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
在通用文本字段中存储不同类型的数据会引发这类问题。您确实应该将数据存储在正确类型的列中——数字作为数字,日期作为日期,等等。
我建议您在开始报告之前修复字段 order_ref_4。如果您创建索引(如果您有资助),您应该在 (order_no,line_no,col.rel_no,to_char(col.line_item_no) 上进行。请参阅解释计划以找到确切的问题。 regexp_like + to_number 也不会更快,尤其是当 fa table 很大并且 to_number 将无法在 fa table 上使用索引时。
最好有一致的数据。标记为错误或修复 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 上的索引。
我有一个相当大的 SELECT 查询,但它 运行 通常只有大约 1.5/2 分钟,但在添加另一块后,我不得不使用 to_char左外连接上的函数以解决 "invalid number" 错误。
现在,我的查询需要 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 字段是 "number" 类型,order_ref_4 字段是 "varchar2(20)" 类型。
我可以使用转换或转换来提高效率吗?
尝试在 table 添加一个字符串格式的索引:
CREATE INDEX char_ind
ON ifsapp.customer_order_line (TO_CHAR(line_item_no));
使用 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()
代替 - 与 Mottor 的回答相同的想法,但仍然保护 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
在通用文本字段中存储不同类型的数据会引发这类问题。您确实应该将数据存储在正确类型的列中——数字作为数字,日期作为日期,等等。
我建议您在开始报告之前修复字段 order_ref_4。如果您创建索引(如果您有资助),您应该在 (order_no,line_no,col.rel_no,to_char(col.line_item_no) 上进行。请参阅解释计划以找到确切的问题。 regexp_like + to_number 也不会更快,尤其是当 fa table 很大并且 to_number 将无法在 fa table 上使用索引时。
最好有一致的数据。标记为错误或修复 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 上的索引。