2012-10-19 56 views
1

好的,所以我正在研究这个(相当老的)项目,它使用了大量针对Oracle数据库的查询。我最近偶然发现了这个宝石,大约需要6-7小时小时才能运行并返回〜1400行。问题中的表格/视图包含〜200'000行。我觉得这感觉好像比合理的时间稍长一点,所以我开始仔细观察它。现在我不能,出于安全/特殊原因,共享准确的查询,但这应该显示查询确实较一般什么:验证Oracle查询的优化

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和网络,以及当它涉及到数据库的专家很远,但我重写,如下所示:

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

为避免自己加入'some_view',可能值得考虑[分析函数](http://docs.oracle.com/cd/E11882_01/server.112/e26088/functions004.htm)。但你应该看看解释计划,看看它花费的时间。 –

回答

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 
    ); 

如果我重写,以避免本通过使用analytic function,而不是-join上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没有时间组件,您可能不需要。

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