[英]How can I make this Oracle SQL query More efficient and less redundant to avoid a “snapshot” error on large datasets?
我正在尝试提高查询大型数据集的技能。
我想要在特定日期范围内使用“ xyz”支付码付款,其中“ id1”不为0且不为null,并且如果对特定“ id1”的付款超过13次,这应该是我查询的结果。
我感觉好像在重复查询两次付款表,但是我不确定如何在一次查询中获取ID1计数和具有特定支付码的付款。
如果我在大量数据上运行它,它将非常慢,并且无法完成。 我收到“快照”错误。
在下面的联接/查询中正确指示了所有表/数据关系,但是如何使它更优雅,更高效?
SELECT
emp.emp_fname first_name,
emp.emp_lname last_name,
pmt.id1,
pmt.id2,
pmt.pay_date,
pmt.pay_key,
pmt.check_num,
case.file_num
FROM
/*query to collect all valid id1s that occur more than 13 times */
(
SELECT
id1
FROM
(
SELECT
id1,
COUNT(*) AS cnt
FROM
payment pmt
WHERE
pay_key IN (
SELECT DISTINCT
pay_key
FROM
allotment
WHERE
pay_code = 'xyz'
)
AND
id1 <> 0
AND
id1 IS NOT NULL
AND
trunc(pmt.pay_date) BETWEEN TO_DATE('01-JUL-17') AND TO_DATE('01-OCT-17')
AND
upper(TRIM(deleted) ) = 'N'
GROUP BY
id1
)
WHERE
cnt > 13
) ptxids
/*join these to full-payment records with matchinig id1s during this time*/
INNER JOIN (
SELECT
*
FROM
payment
WHERE
upper(TRIM(deleted) ) = 'N'
AND
id1 <> 0
AND
id1 IS NOT NULL
AND
trunc(payment.pay_date) BETWEEN TO_DATE('01-JUL-17') AND TO_DATE('01-OCT-17')
) pmt ON pmt.id1 = ptxids.id1
/*join on allotments where pay_key matches and allotment pay_code is xyz*/
INNER JOIN allotment alt ON
upper(TRIM(alt.deleted) ) = 'N'
AND
alt.pay_code = 'xyz'
AND
alt.pay_key = pmt.pay_key
/*join on case to return the requested file-num*/
INNER JOIN case ON case.event_num = alt.event_num
/*join on emp to return payee / employee name */
INNER JOIN emp ON emp.event_num = case.event_num;
一些初步想法
1.)在payment.pay_date上删除trunc,因为这将禁用此列上的索引使用。 如果此列上没有索引,请添加一个。
trunc(payment.pay_date) BETWEEN TO_DATE('01-JUL-17') AND TO_DATE('01-OCT-17')
做了
payment.pay_date BETWEEN TO_DATE('01-JUL-17') AND TO_DATE('01-OCT-17')+(86399/86400)
2.)删除IN内部查询中的与众不同
SELECT /*DISTINCT*/ pay_key FROM allotment WHERE pay_code = 'xyz'
确保在pay_code上也有一个索引。 实际上,任何直接指向pay_code ='xyz'的引用都应确保该列上有一个索引。
无需详细介绍,就可以在具有高选择性(即小于行的10%)的任何列上添加索引。 将这些列合并到同一表的同一索引中。
删除列上的任何函数,因为这会使索引无用。
3)在查询的这一部分中添加一个having子句,以停止返回您只想过滤掉的数据
SELECT id1
FROM payment pmt
WHERE pay_key IN (SELECT pay_key
FROM allotment
WHERE pay_code = 'xyz'
)
AND id1 <> 0
AND id1 IS NOT NULL
AND pmt.pay_date BETWEEN TO_DATE('01-JUL-17') AND
TO_DATE('01-OCT-17')+(86399/86400)
AND upper(TRIM(deleted) ) = 'N'
GROUP BY id1
having count(*)>13
此联接:
FROM
/*query to collect all valid id1s that occur more than 13 times */
(
SELECT
id1
FROM
(
SELECT
id1,
COUNT(*) AS cnt
FROM
payment pmt
WHERE
pay_key IN (
SELECT DISTINCT
pay_key
FROM
allotment
WHERE
pay_code = 'xyz'
)
AND
id1 <> 0
AND
id1 IS NOT NULL
AND
trunc(pmt.pay_date) BETWEEN TO_DATE('01-JUL-17') AND TO_DATE('01-OCT-17')
AND
upper(TRIM(deleted) ) = 'N'
GROUP BY
id1
)
WHERE
cnt > 13
) ptxids
/*join these to full-payment records with matchinig id1s during this time*/
INNER JOIN (
SELECT
*
FROM
payment
WHERE
upper(TRIM(deleted) ) = 'N'
AND
id1 <> 0
AND
id1 IS NOT NULL
AND
trunc(payment.pay_date) BETWEEN TO_DATE('01-JUL-17') AND TO_DATE('01-OCT-17')
) pmt ON pmt.id1 = ptxids.id1
可以通过count(*) over
分析函数使用count(*) over
来消除。 仅一个子查询:
FROM
/*query to collect all valid id1s that occur more than 13 times */
(
SELECT pmt.*,
COUNT(*) Over( Partition by id1) AS cnt
FROM
payment pmt
WHERE
pay_key IN (
SELECT
pay_key
FROM
allotment
WHERE
pay_code = 'xyz'
)
AND
id1 <> 0
AND
id1 IS NOT NULL
AND pmt.pay_date >= TO_DATE('01-JUL-17')
AND pmt.pay_date < TO_DATE('01-OCT-17')+1
AND
upper(TRIM(deleted) ) = 'N'
) xx
WHERE
xx.cnt > 13
一些小的变化:
DISTINCT
trunc(pmt.pay_date)
因为它阻止RDBMS在此列上使用索引 您可以尝试以下选择:
SELECT *
FROM (
SELECT
COUNT(pmt.id1) OVER(PARTITION BY pmt.id1) cnt,
emp.emp_fname first_name,
emp.emp_lname last_name,
pmt.id1,
pmt.id2,
pmt.pay_date,
pmt.pay_key,
pmt.check_num,
case.file_num
FROM payment pmt
INNER JOIN allotment alt ON alt.pay_key = pmt.pay_key
AND UPPER(TRIM(alt.deleted) ) = 'N'
AND alt.pay_code = 'xyz'
INNER JOIN case ON case.event_num = alt.event_num
INNER JOIN emp ON emp.event_num = case.event_num
WHERE pmt.id1 <> 0
AND
pmt.id1 IS NOT NULL
AND
TRUNC(pmt.pay_date) BETWEEN TO_DATE('01-JUL-17') AND TO_DATE('01-OCT-17')
AND
UPPER(TRIM(pmt.deleted)) = 'N'
) A
WHERE A.cnt > 13;
如果在pay_date和pmt.deleted列上存在索引,则where子句中的TRUNC(pmt.pay_date)和UPPER(TRIM(pmt.deleted))不使用索引。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.