有人可以帮我调整这个查询吗?在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
通常是查询编写错误的指示符。规范化数据库不包含重复数据,那么必须使用 DISTINCT
删除的重复数据突然从何而来?通常是您自己的查询产生这些。首先避免产生重复项,这样以后就不需要 DISTINCT
。
在您的情况下,您正在子查询a
中加入表notification
,但您没有在该子查询中使用它的行;您只能从 notification_master_id
中进行选择。
毕竟,您想要获取通知主控,获取其最新的相关通知(首先获取其 ID,然后选择行)。您不需要数百个子查询来实现此目的。
一些旁注:
- 要从
template_classification
获取描述,您需要再次加入通知表,但这不是必需的。
子查询中的 ORDER BY
(ORDER BY nm.id DESC
) 是多余的,因为子查询结果是按照标准 SQL 未排序的。 (Oracle 有时会违反此标准,以便对结果应用ROWNUM
,但您在查询中没有使用ROWNUM
。)- 遗憾的是,您将
created_at
存储为数字,而不是DATE
或TIMESTAMP
。这迫使你进行计算。不过,我认为这不会对您的查询产生很大影响,因为您在OR
条件中使用它。 CURRENT_DATE
为您获取客户日期。这是很少需要的,因为您从数据库中选择数据,这当然不应该与某些客户端的日期相关,而应该与它自己的日期SYSDATE
相关。
如果我没记错的话,您的查询可以缩短为:
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;
如上所述,您可能需要将 CURRENT_DATE
替换为 SYSDATE
。
我推荐使用以下索引进行查询:
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);
关于分页的最后一点:为了跳过 n 行来获取下一个 n 行,必须对所有数据执行整个查询,然后对所有结果行进行排序,最后仅选取其中的 n 行。通常最好记住上次获取的 ID,然后在下次执行时仅选择具有更高 ID 的行。
关于sql - Oracle SQL 查询的性能调优,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61053212/