验证Oracle查询的优化

时间:2012-10-19 10:23:51

标签: sql oracle

好的,所以我正在研究这个(相当古老的)工作项目,该项目使用大量查询来处理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小时。它目前正在运行,以便我可以进行某种分析以查看新查询是否有效,但我想我也会在这里询问是否有任何明显错误的事情我已经做过了?

编辑还删除了最外层的查询,这似乎没有达到任何目的,除了可能使查询看起来更酷..或者其他东西..我不知道..

1 个答案:

答案 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没有时间成分,则可能不需要。

但当然,检查一下你确实得到了相同的结果。