我有一个如下的查询操作,它来自单个表,但是子查询太多。有人可以对它进行优化吗?
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
答案 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')