繁体   English   中英

如何使此Oracle SQL查询更高效,更少冗余,以避免在大型数据集上出现“快照”错误?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM