按性能问题排序的 Oracle 查询
Posted
技术标签:
【中文标题】按性能问题排序的 Oracle 查询【英文标题】:Oracle query with order by perfomance issue 【发布时间】:2017-07-12 21:40:11 【问题描述】:我的查询很复杂:
select * from (
select * from tbl_user ...
where ...
and date_created between :date_from and :today
...
order by date_created desc
) where rownum <=50;
由于 where 子句,当前查询速度足够快(仅在今天前 3 个月,date_from = 今天 - 90 天)。
我必须删除此子句,但它会导致性能下降。 如果先通过`
计算date_from会怎样SELECT MIN(date_created) where...
然后将此值插入到主查询中?数据集将是相同的。它会提高性能吗?是否有意义? 任何人都可以对优化有任何假设吗?
【问题讨论】:
威尔,目前你的问题很难理解。请编辑您的帖子以更好地说明您的问题并包含您的架构和完整查询。如果您不提供此类内容,我们将无法完全帮助您优化您的解决方案。 索引了哪些列,我们在这里处理了多少行?您是否还检查了解释计划和/或自动跟踪以获取有关执行计划的信息? 限制子句(仅查找最近的 50 行)非常有用 - 即使您包括所有数据,而不仅仅是最近三个月,您也不会看到性能大幅下降。这是因为优化器看到了外部查询where
子句并且它没有对内部查询中的所有行进行排序——它只查找date_created
的最高50 个值。你有date_created
的索引吗?这应该很有帮助。
对 date_created 子集的评论:看执行计划,因为写的 SQL 需要隐式类型转换;如果 oracle 对列执行此操作,那么即使它有索引,也不会使用它。要绕过隐式类型转换,请在输入字符串周围显式使用 to_date 函数。
【参考方案1】:
使用order by
操作当然会导致查询需要更长的时间才能返回。话虽如此,在数据库中排序几乎总是比在应用程序逻辑中排序更快。
如果没有完整的查询和架构信息,很难真正优化,但我会尝试对我来说最明显的东西。
转换为 Rank()
如果您使用带窗口的rank()
函数,您的查询可能会更有效。我还将它转换为使用common table expression(又名CTE)。这不会提高性能,但会更容易阅读。
with cte as (
select
*
, rank() over (
partition by
-- insert what fields differentiate your rows here
-- unlike a group by clause, this doesn't need to be
-- every field
order by
date_created desc
)
from
tbl_user
...
where
...
and date_created between :date_from and :today
)
select
*
from
cte
where
rk <= 50
索引
-
如果
date_created
未编入索引,则可能应该编入索引。
查看您的自动跟踪结果。找出哪些过滤器的成本最高。这些可能是未编入索引的,也许应该是。
如果您发布您的架构,我很乐意提出更好的建议。
【讨论】:
为什么rank()
会使查询更快?我不相信——你有这方面的文件或理论论证吗? rank()
中的 ORDER BY
子句与 OP 原始查询中的单独 ORDER BY
子句的作用相同。在这两种情况下,无论是使用单独的order by
子句还是rank()
,限制性where
子句(rownum <= 50
或rnk <= 50
)都将允许优化器重写,以便仅识别“第一”50 行,而不是订购整套。
此外,如果可以打成平手,正确使用的函数是row_number()
,而不是rank()
。
对 date_created 子集的评论:看执行计划,因为写的 SQL 需要隐式类型转换;如果 oracle 在列上执行此操作,那么即使它有索引,也不会被使用。要绕过隐式类型转换,请在输入字符串周围显式使用 to_date 函数。
@RogerCornejo 不。假设将日期传递给绑定参数就可以了。
@Jacobm001 我并不是说优化器会使用隐式类型转换,而是我建议检查(而不是假设)它。来自执行计划(或 SQL 调优顾问)的详细信息应该告诉您哪些谓词用于访问(即正在使用的索引)或过滤器(只是数据子集)。以上是关于按性能问题排序的 Oracle 查询的主要内容,如果未能解决你的问题,请参考以下文章