好的,所以我正在研究这个(相当古老的)工作项目,该项目使用大量查询来处理Oracle数据库。我最近偶然发现了这个宝石,它需要大约6-7个小时来运行并返回~1400行。有问题的表/视图包含~200'000行。我觉得这感觉好像花了一点时间而不是看似合理,所以我开始仔细研究它。现在,出于安全/专有原因,我不能共享确切的查询,但这应该以更一般的术语显示查询的作用:
SELECT
some_field,
some_other_field
FROM (
SELECT
*
FROM
some_view a
WHERE
some_criteria AND
a.client_no || ':' || a.engagement_no || ':' || a.registered_date = (
SELECT
b.client_no || ':' || b.engagement_no || ':' || MAX(b.registered_date)
FROM
some_view b
JOIN some_engagement_view e
ON e.client_no = b.client_no AND e.engagement_no = b.engagement_no
JOIN some_client_view c
ON c.client_no = b.client_no
WHERE
some_other_criteria AND
b.client_no = a.client_no AND
b.engagement_no = a.engagement_no
GROUP BY
b.client_no,
b.engagement_no
)
);
基本上我应该做的是,从some_view(其中包含客户/约定的评估)获取最新评估,以获取每个独特的客户/参与。
这两个联接是为了确保客户端和参与存在于另一个系统中,在此系统中完成评估之后,主要处理它们。
注意它如何连接两个数字和一个日期,然后将其与子查询进行比较? “有趣”的设计选择。所以我认为,如果用适当的比较替换连接,至少可以获得某种性能提升。请注意我主要开发.NET和Web,并且在数据库方面远非专家,但我重写如下:
SELECT
some_field,
some_other_filed
FROM
some_view a
WHERE
some_criteria AND
(a.client_no, a.engagement_no, a.registered_date) = (
SELECT
b.client_no,
b.engagement_no,
MAX(b.registered_date)
FROM
some_view b
JOIN some_engagement_view e
ON e.client_no = b.client_no AND e.engagement_no = b.engagement_no
JOIN some_client_view c
ON c.client_no = b.client_no
WHERE
some_other_criteria AND
b.client_no = a.client_no AND
b.engagement_no = a.engagement_no
GROUP BY
b.client_no,
b.engagement_no
)
);
现在如果我用COUNT(1)
替换第一个选择中的字段,我得到两个查询的行数完全相同,这是一个好的开始。新查询以与计数一样快的速度提取数据,< 10秒旧查询在大约20秒内得到计数,正如我之前提到的,数据需要接近6-7小时。它目前正在运行,以便我可以进行某种分析以查看新查询是否有效,但我想我也会在这里询问是否有任何明显错误的事情我已经做过了?
编辑还删除了最外层的查询,这似乎没有达到任何目的,除了可能使查询看起来更酷..或者其他东西..我不知道..
答案 0 :(得分:1)
扩展我的评论...如果我尝试使用内置视图复制您的查询结构,它也会运行很长时间。例如,为每个所有者获取最近创建的表(纯粹用于演示目的,可以更简单地完成),这样需要几分钟,使用任一版本:
SELECT
owner,
object_name
FROM
all_objects a
WHERE
(a.owner, a.object_type, TRUNC(a.created)) = (
SELECT
b.owner, b.object_type, TRUNC(MAX(b.created))
FROM
all_objects b
JOIN all_tables e
ON e.owner = b.owner and e.table_name = b.object_name
JOIN all_users c
ON c.username = b.owner
WHERE
b.owner = a.owner AND
b.object_type = a.object_type
GROUP BY
b.owner,
b.object_type
);
如果我重写一下以避免all_objects
上的自我加入(相当于您示例中的some_view
),而是使用analytic function代替:
SELECT
owner,
object_name
FROM (
SELECT
a.owner,
a.object_name,
row_number() over (partition by a.owner, a.object_type
order by a.created desc) as rn
FROM
all_objects a
JOIN all_tables e
ON e.owner = a.owner and e.table_name = a.object_name
JOIN all_users c
ON c.username = a.owner
)
WHERE
rn = 1;
......然后需要几秒钟。
现在,在这种情况下,我没有得到完全相同的输出,因为我有多个对象同时创建(在created
范围内的同一秒内)。
我当然不知道registered_date
中存储的值的精确度。因此,您可能需要查看不同的函数,可能是rank
而不是row_number
,或者在必要时调整处理关系的顺序。
rank() over (partition by a.owner, a.object_type
order by trunc(a.created) desc) as rn
...
WHERE
rn = 1;
给了我相同的结果(好吧,差不多;加入all_tables
也是在扭曲事情,因为我似乎在all_objects
中列出的表格不在all_tables
中,但这是一个副作用)。或者max
也可以起作用:
max(created) over (partition by a.owner, a.object_type) as mx
...
WHERE
TRUNC(created) = TRUNC(mx)
在我使用trunc
同时获取所有内容的两个中;如果您的registered_date
没有时间成分,则可能不需要。
但当然,检查一下你确实得到了相同的结果。