简体   繁体   English

Oracle SQL查询性能调优

[英]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:一些旁注:

  • To get the description from 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 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.) (Oracle 有时会违反此标准,以便在结果上应用ROWNUM ,但您没有在查询中使用ROWNUM 。)
  • It's a pity that you store created_at not as a DATE or TIMESTAMP , but as a number.遗憾的是,您将created_at存储为不是DATETIMESTAMP ,而是存储为数字。 This forces you to calculate.这迫使你计算。 I don't think this has a great impact on your query, though, because you are using it in an 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 .这是很少需要的,因为您来自数据库的 select 数据当然不应该与某些客户的日期相关,而是与它自己的日期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.

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