如何在oracle中优化这个sql?

时间:2014-04-23 07:05:13

标签: sql oracle

我有一个如下的查询操作,它来自单个表,但是子查询太多。有人可以对它进行优化吗?

 SELECT t.order_no         ,
  user_assign             ,
  t.busi_code             ,
  t.inst_addr4 AS district,
  t.inst_addr3 AS estate  
 ,listagg(to_char(date_appoint,'yyyy-mm-dd'),',') within group(order by date_appoint asc) as "日期排序"
   FROM mtce_detail t
  WHERE order_no IN
  (SELECT order_no
     FROM
    (SELECT m.order_no,
      user_assign    ,
      COUNT(order_no) AS total
       FROM mtce_detail m
      WHERE order_no IN
      (SELECT order_no
         FROM mtce_detail
        WHERE date_appoint = to_date('2014-04-17', 'yyyy-mm-dd')
      )
    AND date_appoint <= to_date('2014-04-17', 'yyyy-mm-dd')
   GROUP BY order_no,
      user_assign
    )
    WHERE total > 2
  )
  group by t.order_no,t.user_assign,t.busi_code,t.inst_addr4,t.inst_addr3

4 个答案:

答案 0 :(得分:1)

您可以采取的第一步是删除带有“total&gt; 2”的子查询,并将其替换为更优雅的HAVING子句,如下所示:

SELECT t.order_no
     , user_assign
     , t.busi_code
     , t.inst_addr4 AS district
     , t.inst_addr3 AS estate  
     , listagg(to_char(date_appoint,'yyyy-mm-dd'),',') within group(order by date_appoint asc) as "something_chinese"
  FROM mtce_detail t
 WHERE order_no IN
       ( SELECT m.order_no
           FROM mtce_detail m
          WHERE order_no IN
                ( SELECT order_no
                    FROM mtce_detail
                   WHERE date_appoint = to_date('2014-04-17', 'yyyy-mm-dd')
                )
            AND date_appoint <= to_date('2014-04-17', 'yyyy-mm-dd')
          GROUP BY order_no
              , user_assign
         having count(order_no) > 2
       )
 group by t.order_no
     , t.user_assign
     , t.busi_code
     , t.inst_addr4
     , t.inst_addr3

下一步是通过删除最里面的子查询来消除表访问。只需在2014年4月17日或之前选择所有mtce_details,并一次性计算当天及之前的出现次数。现在,您可以在having子句中使用这些计算出的数字,如下所示:

SELECT t.order_no
     , user_assign
     , t.busi_code
     , t.inst_addr4 AS district
     , t.inst_addr3 AS estate  
     , listagg(to_char(date_appoint,'yyyy-mm-dd'),',') within group (order by date_appoint asc) as "something_chinese"
  FROM mtce_detail t
 WHERE order_no IN
       ( SELECT m.order_no
           FROM mtce_detail m
          WHERE date_appoint <= date '2014-04-17'
          GROUP BY order_no
              , user_assign
         having count(order_no) > 2
            and count(case date_appoint when date '2014-04-17' then 1 end) >= 1
       )
 group by t.order_no
     , t.user_assign
     , t.busi_code
     , t.inst_addr4
     , t.inst_addr3

性能应略有改进,但您的查询仍包含同一个表的两个表访问。如果需要最后一个表访问,则可以将分析函数与partition子句一起使用。您的查询将更难阅读,但速度更快。这对读者来说是一个有趣的练习:-)。 (因为我没有testdata)

答案 1 :(得分:0)

WHERE t.order_no IN替换为

JOIN
(subselect) sub ON t.order_no =sub.order_no

替换来电AND date_appoint <= to_date('2014-04-17', 'yyyy-mm-dd') 使用AND date_appoint <= :date_parameter来避免每行的to_date()转换。

WHERE

中使用的列添加索引

答案 2 :(得分:0)

你做了许多内部选择,你必须避免这种情况,改为使用它:

SELECT t.order_no         ,
  user_assign             ,
  t.busi_code             ,
  t.inst_addr4 AS district,
  t.inst_addr3 AS estate  
 ,listagg(to_char(date_appoint,'yyyy-mm-dd'),',') within group(order by date_appoint asc) as "日期排序"
   FROM mtce_detail t
  WHERE exists
  (SELECT m.order_no,
      user_assign    ,
      COUNT(order_no) AS total
       FROM mtce_detail m
      WHERE date_appoint <= to_date('2014-04-17', 'yyyy-mm-dd')
        AND m.order_no = t.order_no
   GROUP BY order_no, user_assign
   HAVING count(order_no) > 2
  )
  group by t.order_no,t.user_assign,t.busi_code,t.inst_addr4,t.inst_addr3

答案 3 :(得分:-1)

关于这一部分:

SELECT m.order_no,
      user_assign    ,
      COUNT(order_no) AS total
       FROM mtce_detail m
      WHERE order_no IN
      (SELECT order_no
         FROM mtce_detail
        WHERE date_appoint = to_date('2014-04-17', 'yyyy-mm-dd')
      )

为什么不这样写:

SELECT m.order_no,
      user_assign    ,
      COUNT(order_no) AS total
       FROM mtce_detail m
      WHERE date_appoint = to_date('2014-04-17', 'yyyy-mm-dd')