如何在oracle中优化此sql?

如何在oracle中优化此sql?,sql,oracle,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 da

我有一个查询操作,如下所示,它来自单个表,但子查询太多。有人能帮上忙吗

 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
将t.order_no IN处替换为


仅就这一部分更换电话和日期:

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

如果要进行多个内部选择,则必须避免此情况,请改用此选项:

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

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

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')
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
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
下一步是通过删除最里面的子查询来消除表访问。只需选择2014年4月17日当天或之前的所有mtce_详细信息,并一次性统计当天和之前发生的事件数。现在,您可以在having子句中使用这些计算出的数字,如下所示:

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')
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
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子句一起使用。您的查询将更难阅读,但速度更快。这是留给读者的一个有趣的练习:-。因为我没有测试数据

有什么问题?太慢了?如果是这样,请发布执行计划。优化有时涉及将查询拆分为多个子查询,这些子查询可以并行执行,因此hving子查询本身并不是一件坏事。正如@Thilo所说:问题出在哪里?慢吗?没问题。我只是想知道是否有任何方法可以优化它,并使它更加友好。我觉得这太笨拙了。哦,不。一些订单记录了2014-04-17和其他时间。所以同一订单有多行?是的,我需要获取这些行,即使其指定日期不是2014-04-17,但至少有一行的订单号与之相同。日期在哪里,“yyyy-mm-dd”?这也选择了2014年4月17日没有出现的mcte_详细信息。他从mtce_详细信息中选择了3次,因此有一个条款规定date_-Apprimit@NightFox79语法错误,总数大于2,我认为countorder_no>2更好,谢谢大家:这通常都是胡说八道。证据:不要使用oracle版本8中2001年的文章。这不适用于现在,在10和11中,如果没有使用相同执行计划的数据,SQLFIDLE的性能会更好:与