[英]Performance tuning of Oracle SQL query
Can someone help me out to tune this query?有人可以帮我调整这个查询吗? It's taking 1 minute time to return the data in sqldeveloper.
在 sqldeveloper 中返回数据需要 1 分钟的时间。
SELECT
masterid, notification_id, notification_list, typeid,
subject, created_at, created_by, approver, sequence_no,
productid, statusid, updated_by, updated_at, product_list,
notification_status, template, notification_type, classification
FROM
(
SELECT
masterid, notification_id, notification_list, typeid, subject,
approver, created_at, created_by, sequence_no, productid,
statusid, updated_by, updated_at, product_list, notification_status,
template, notification_type, classification,
ROW_NUMBER() OVER(ORDER BY masterid DESC)AS r
FROM
(
SELECT DISTINCT
a.masterid AS masterid,
a.maxid AS notification_id,
notification_list,
typeid,
noti.subject AS subject,
noti.approver AS approver,
noti.created_at AS created_at,
noti.created_by AS created_by,
noti.sequence_no AS sequence_no,
a.productid AS productid,
a.statusid AS statusid,
noti.updated_by AS updated_by,
noti.updated_at AS updated_at,
(
SELECT LISTAGG(p.name,',') WITHIN GROUP(ORDER BY p.id) AS list_noti
FROM product p
INNER JOIN notification_product np ON np.product_id = p.id
WHERE notification_id = a.maxid
) AS product_list,
(
SELECT description
FROM notification_status
WHERE id = a.statusid
) AS notification_status,
(
SELECT name
FROM template
WHERE id = a.templateid
) AS template,
(
SELECT description
FROM notification_type
WHERE id = a.typeid
) AS notification_type,
(
SELECT tc.description
FROM template_classification tc
INNER JOIN notification nt ON tc.id = nt.classification_id
WHERE nt.id = a.maxid
) AS classification
FROM
(
SELECT
nm.id AS masterid,
nm.product_id AS productid,
nm.notification_status_id AS statusid,
nm.template_id AS templateid,
nm.notification_type_id AS typeid,
(
SELECT MAX(id)
FROM notification
WHERE notification_master_id = nm.id
) AS maxid,
(
SELECT LISTAGG(n.id,',') WITHIN GROUP(ORDER BY nf.id) AS list_noti
FROM notification n
WHERE notification_master_id = nm.id
) AS notification_list
FROM notification_master nm
INNER JOIN notification nf ON nm.id = nf.notification_master_id
WHERE nm.disable = 'N'
ORDER BY nm.id DESC
) a
INNER JOIN notification noti
ON a.maxid = noti.id
AND
(
(
(
TO_DATE('01-jan-1970','dd-MM-YYYY') +
numtodsinterval(created_at / 1000,'SECOND')
) <
(current_date + INTERVAL '-21' DAY)
)
OR (typeid exists(2,4) AND statusid = 4)
)
)
)
WHERE r BETWEEN 11 AND 20
DISTINCT
is very often an indicator for a badly written query. DISTINCT
通常是一个写得不好的查询的指标。 A normalized database doesn't contain duplicate data, so where do the duplicates suddenly come from that you must remove with DISTINCT
?规范化数据库不包含重复数据,那么重复数据突然来自哪里,您必须使用
DISTINCT
删除? Very often it is your own query producing these.很多时候是你自己的查询产生这些。 Avoid producing duplicates in the first place, so you don't need
DISTINCT
later.首先避免产生重复,所以你以后不需要
DISTINCT
。
In your case you are joining with the table notification
in your subquery a
, but you are not using its rows in that subquery;在您的情况下,您在子查询
a
中加入表notification
,但您没有在该子查询中使用它的行; you only select from notification_master_id
.您只有来自
notification_master_id
的 select 。
After all, you want to get notification masters, get their latest related notification (by getting its ID first and then select the row).毕竟,您想获得通知大师,获得他们最新的相关通知(首先获得其 ID,然后是 select 行)。 You don't need hundreds of subqueries to achieve this.
您不需要数百个子查询来实现这一点。
Some side notes:一些旁注:
template_classification
you are joining again with the notification table, which is not necessary.template_classification
获取描述,您将再次加入通知表,这不是必需的。ORDER BY
in a subquery ( ORDER BY nm.id DESC
) is superfluous, because subquery results are per standard SQL unsorted.ORDER BY
( ORDER BY nm.id DESC
) 是多余的,因为子查询结果按照标准 SQL 未排序。 (Oracle violates this standard sometimes in order to apply ROWNUM
on the result, but you are not using ROWNUM
in your query.) ROWNUM
,但您没有在查询中使用ROWNUM
。)created_at
not as a DATE
or TIMESTAMP
, but as a number.created_at
存储为不是DATE
或TIMESTAMP
,而是存储为数字。 This forces you to calculate.OR
condition.OR
条件下使用它。CURRENT_DATE
gets you the client date. CURRENT_DATE
为您提供客户日期。 This is rarely wanted, as you select data from the database, which should of course not relate to some client's date, but to its own date SYSDATE
.SYSDATE
相关。 If I am not mistaken, your query can be shortened to:如果我没记错的话,您的查询可以缩短为:
SELECT
nm.id AS masterid,
nf.id AS notification_id,
nfagg.notification_list AS notification_list,
nm.notification_type_id AS typeid,
nf.subject AS subject,
nf.approver AS approver,
nf.created_at AS created_at,
nf.created_by AS created_by,
nf.sequence_no AS sequence_no,
nm.product_id AS productid,
nm.notification_status_id AS statusid,
nf.updated_by AS updated_by,
nf.updated_at AS updated_at,
(
SELECT LISTAGG(p.name, ',') WITHIN GROUP (ORDER BY p.id)
FROM product p
INNER JOIN notification_product np ON np.product_id = p.id
WHERE np.notification_id = nf.id
) AS product_list,
(
SELECT description
FROM notification_status
WHERE id = nm.notification_status_id
) AS notification_status,
(
SELECT name
FROM template
WHERE id = nm.template_id
) AS template,
(
SELECT description
FROM notification_type
WHERE id = nm.notification_type_id
) AS notification_type,
(
SELECT description
FROM template_classification
WHERE id = nf.classification_id
) AS classification
FROM notification_master nm
INNER JOIN
(
SELECT
notification_master_id,
MAX(id) AS maxid,
LISTAGG(id,',') WITHIN GROUP (ORDER BY id) AS notification_list
FROM notification
GROUP BY notification_master_id
) nfagg ON nfagg.notification_master_id = nm.id
INNER JOIN notification nf
ON nf.id = nfagg.maxid
AND
(
(
DATE '1970-01-01' + NUMTODSINTERVAL(nf.created_at / 1000, 'SECOND')
< CURRENT_DATE + INTERVAL '-21' DAY
)
OR (nm.notification_type_id IN (2,4) AND nm.notification_status_id = 4)
)
WHERE nm.disable = 'N'
ORDER BY nm.id DESC
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
As mentioned, you may want to replace CURRENT_DATE
with SYSDATE
.如前所述,您可能希望将
CURRENT_DATE
替换为SYSDATE
。
I recommend the following indexes for the query:我为查询推荐以下索引:
CREATE INDEX idx1 ON notification_master (disable, id, notification_status_id, notification_type_id);
CREATE INDEX idx2 ON notification (notification_master_id, id, created_at);
A last remark on paging: In order to skip n rows to get the next n, the whole query must get executed for all data and then all result rows be sorted only to pick n of them at last.关于分页的最后一点说明:为了跳过 n 行以获取下一个 n,必须对所有数据执行整个查询,然后对所有结果行进行排序,最后只选择其中的 n 个。 It is usually better to remember the last fetched ID and then only select rows with a higher ID in the next execution.
通常最好记住最后获取的 ID,然后在下次执行时只记住具有更高 ID 的 select 行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.