1

好的,所以我正在研究这个(相当老的)项目,它使用大量查询来访问 Oracle 数据库。我最近偶然发现了这个 gem,它需要大约 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
    )
);

现在,如果我用 a 替换第一个 select 中的字段COUNT(1),我会得到两个查询完全相同的行数,所以这是一个好的开始。新查询获取数据的速度与计数一样快,< 10 秒。旧查询在约 20 秒内获得计数,正如我之前提到的,数据需要接近 6-7 小时。它目前正在运行,以便我可以进行某种分析以查看新查询是否有效,但我想我也会在这里问一下,看看我是否有任何明显的错误?

编辑还删除了最外面的查询,它似乎没有实现任何目的,除了可能使查询看起来更酷..或其他什么..我不知道..

4

1 回答 1

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

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如果您没有时间组件,您可能不需要。

但是,当然,请检查您是否确实得到了相同的结果。

于 2012-10-19T12:27:56.780 回答