oracle sql查询带来结果很久

问题描述 投票:0回答:1

我在oracle sql中编写了一个查询,如下所示。但是sql查询带来的结果却是很长一段时间。例如,搜索客户时,4 分钟内就有结果。请帮我优化这个sql查询。 我尝试了多种方法来解决这个问题,但没有得到任何结果。 首先,我逐部分运行sql脚本。有些部分花了很多时间。但是,我不知道如何优化这些部分。作为第二种方法,我想使用 v$sql_monitor 表找出导致 sql 脚本无法得出结果的原因,但我无法得出结论。对我来说有趣的是,这个逻辑可以用更简单、更快的方式编写,同时保持相同的逻辑。老板给我的时间已经结束了。请帮助我。

WITH d_table AS (
    SELECT *
    FROM (
        SELECT *
        FROM (
            SELECT ROW_NUMBER() OVER (PARTITION BY customer_id, product ORDER BY min_fx_date ASC) AS rnk,
                   customer_id,
                   min_fx_date,
                   product,
                   balance_column
            FROM table_def
        )
        WHERE rnk = 1
    )
    WHERE min_fx_date > '01/01/2017'
),

tc_lo_product AS (
    SELECT a.customer_id, 
           a.currency, 
           b.product, 
           a.rep_date
    FROM table_port a
    LEFT JOIN table_product_names b
        ON a.ln_aim = b.product_name
),

if_lo AS (
    SELECT DISTINCT b.customer_id,
                    total_amount,
                    a.i_date,
                    x.product,
                    a.document_n,
                    a.pro,
                    b.close_date
    FROM table_full a
    LEFT JOIN (
        SELECT customer_id, 
               rep_date,
               FIRST_VALUE(close_date) OVER (PARTITION BY document_n ORDER BY rep_date DESC) AS close_date,
               currency,
               document_n
        FROM table_port
    ) b
        ON a.document_n = b.document_n
        AND a.i_date = LAST_DAY(b.rep_date)
    LEFT JOIN table_product_names x
        ON a.ln_aim = x.product_name
),

part3 AS (
    SELECT DISTINCT k.customer_id,
                    k.currency,
                    FIRST_VALUE(k.currency) OVER (PARTITION BY k.customer_id, k.product ORDER BY t.min_fx_date) AS main_currency,
                    t.min_fx_date,
                    k.product,
                    t.balance_column
    FROM d_table t
    LEFT JOIN tc_lo_product k
        ON t.customer_id = k.customer_id
        AND t.product = k.product
        AND k.rep_date = t.min_fx_date
),

kurs_cedvel AS (
    SELECT *
    FROM (
        SELECT c1.cur_short, 
               c1.k_v, 
               LAST_DAY(c1.p_date) AS rep_date, 
               ROW_NUMBER() OVER (PARTITION BY c1.cu_code, TO_CHAR(c1.p_date, 'MM-YYYY') ORDER BY c1.p_date DESC) AS rn
        FROM table_currency c1
        WHERE c1.kurs_type = 'CBAR'
    )
    WHERE rn = 1
),

in_rate_tab AS (
    SELECT *
    FROM (
        SELECT ROW_NUMBER() OVER (PARTITION BY customer_id, TRUNC(rep_date, 'MM') ORDER BY rep_date DESC, date_give DESC) AS rnk,
               rep_date,
               customer_id,
               interest_rate,
               date_give
        FROM table_port
        WHERE rep_date = LAST_DAY(rep_date)
          AND document_n NOT LIKE '%KOP%'
    )
    WHERE rnk = 1
),

ana_cedvel AS (
    SELECT p.*, 
           o.sum_payed,
           CASE
               WHEN main_currency = 'AZN' THEN p.total_amount
               ELSE ROUND(p.total_amount / v.k_v, 2)
           END AS total_amount_c,
           CASE
               WHEN main_currency = 'AZN' THEN p.pro
               ELSE ROUND(p.pro / v.kurs_val, 2)
           END AS pro_c,
           CASE
               WHEN main_currency = o.valyuta THEN o.sum_payed
               ELSE ROUND(o.sum_payed / v.k_v, 2)
           END AS sum_payed_c
    FROM (
        SELECT k.customer_id,
               k.currency,
               k.main_currency,
               k.min_fx_date,
               k.product,
               k.balance_column,
               c.total_amount,
               c.i_date,
               c.document_n,
               c.pro,
               c.close_date
        FROM part3 k
        LEFT JOIN if_lo c
            ON k.customer_id = c.customer_id
            AND k.product = c.product
            AND c.i_date >= k.min_fx_date
    ) p
    LEFT JOIN kurs_cedvel v
        ON p.main_currency = v.cur_short 
        AND p.i_date = v.rep_date
    LEFT JOIN payments_table o
        ON p.document_n = o.document_n
        AND o.payment_date >= p.min_fx_date
        AND p.i_date = LAST_DAY(o.payment_date)
),

final_result AS (
    SELECT DISTINCT h.i_date,
                    h.customer_id,
                    h.main_currency,
                    h.product,
                    h.min_fx_date,
                    h.balance_column,
                    h.total_amount,
                    h.pro,
                    h.sum_payed,
                    h.interest_rate,
                    CASE
                        WHEN h.max_i_date = LAST_DAY(h.close_date)
                             AND h.i_date = h.max_i_date THEN h.close_date
                        ELSE NULL
                    END AS date_end
    FROM (
        SELECT DISTINCT i_date,
                        j.customer_id,
                        main_currency,
                        MAX(i_date) OVER (PARTITION BY j.customer_id, j.product) AS max_i_date,
                        j.document_n,
                        product,
                        min_fx_date,
                        balance_column,
                        p.interest_rate,
                        close_date,
                        SUM(total_amount_c) OVER (PARTITION BY i_date, j.customer_id, product) AS total_amount,
                        SUM(sum_payed_c) OVER (PARTITION BY i_date, j.customer_id, product) AS sum_payed,
                        SUM(pro) OVER (PARTITION BY i_date, j.customer_id, product) AS pro
        FROM ana_cedvel j
        LEFT JOIN in_rate_tab p
            ON j.customer_id = p.customer_id
            AND j.i_date = p.rep_date
    ) h
    WHERE h.customer_id = 85278945789
)

sql oracle-database oracle-sqldeveloper sql-execution-plan execution-time
1个回答
0
投票

重新格式化您的查询,以便每个子查询都表示为公共表表达式:

WITH 
tbl_def as (
  SELECT 
    ROW_NUMBER() OVER (PARTITION BY customer_id, product ORDER BY min_fx_date ASC) AS rnk
  , customer_id
  , min_fx_date
  , product
  , balance_column
  FROM table_def
),
tbl_defb as (
  SELECT *
  FROM tbl_def
  WHERE rnk = 1
),
d_table AS (
  SELECT *
  FROM tbl_defb
  WHERE min_fx_date > '01/01/2017'
),
tc_lo_product AS (
  SELECT 
    a.customer_id
  , a.currency
  , b.product
  , a.rep_date
  FROM table_port a
  LEFT JOIN table_product_names b ON a.ln_aim = b.product_name
),
tbl_port as (
  SELECT 
    customer_id
  , rep_date
  , FIRST_VALUE(close_date) OVER (PARTITION BY document_n ORDER BY rep_date DESC) AS close_date
  , currency
  , document_n
  FROM table_port
),
if_lo AS (
  SELECT DISTINCT 
    b.customer_id
  , total_amount
  , a.i_date
  , x.product
  , a.document_n
  , a.pro
  , b.close_date
  FROM        table_full a
    LEFT JOIN tbl_port b ON a.document_n = b.document_n
                        AND a.i_date = LAST_DAY(b.rep_date)
    LEFT JOIN table_product_names x ON a.ln_aim = x.product_name
),
part3 AS (
  SELECT DISTINCT 
    k.customer_id
  , k.currency
  , FIRST_VALUE(k.currency) OVER (PARTITION BY k.customer_id, k.product ORDER BY t.min_fx_date) AS main_currency
  , t.min_fx_date
  , k.product
  , t.balance_column
  FROM        d_table t
    LEFT JOIN tc_lo_product k ON t.customer_id = k.customer_id
                             AND t.product = k.product
                             AND k.rep_date = t.min_fx_date
),
cbar as (
  SELECT 
    c1.cur_short
  , c1.k_v
  , LAST_DAY(c1.p_date) AS rep_date
  , ROW_NUMBER() OVER (PARTITION BY c1.cu_code, TO_CHAR(c1.p_date, 'MM-YYYY') ORDER BY c1.p_date DESC) AS rn
  FROM table_currency c1
  WHERE c1.kurs_type = 'CBAR'
),
kurs_cedvel AS (
  SELECT *
  FROM cbar
  WHERE rn = 1
),
kop as (
  SELECT 
    ROW_NUMBER() OVER (PARTITION BY customer_id, TRUNC(rep_date, 'MM') ORDER BY rep_date DESC, date_give DESC) AS rnk
  , rep_date
  , customer_id
  , interest_rate
  , date_give
  FROM table_port
  WHERE rep_date = LAST_DAY(rep_date)
    AND document_n NOT LIKE '%KOP%'
),
in_rate_tab AS (
  SELECT *
  FROM kop
  WHERE rnk = 1
),
part3b as (
  SELECT 
    k.customer_id
  , k.currency
  , k.main_currency
  , k.min_fx_date
  , k.product
  , k.balance_column
  , c.total_amount
  , c.i_date
  , c.document_n
  , c.pro
  , c.close_date
  FROM        part3 k
    LEFT JOIN if_lo c ON k.customer_id = c.customer_id
                     AND k.product = c.product
                     AND c.i_date >= k.min_fx_date
),
ana_cedvel AS (
  SELECT 
    p.*
  , o.sum_payed
  , CASE
      WHEN main_currency = 'AZN' THEN p.total_amount
      ELSE ROUND(p.total_amount / v.k_v, 2)
    END AS total_amount_c
  , CASE
      WHEN main_currency = 'AZN' THEN p.pro
      ELSE ROUND(p.pro / v.kurs_val, 2)
    END AS pro_c
  , CASE
      WHEN main_currency = o.valyuta THEN o.sum_payed
      ELSE ROUND(o.sum_payed / v.k_v, 2)
    END AS sum_payed_c
  FROM        part3b p
    LEFT JOIN kurs_cedvel v ON p.main_currency = v.cur_short 
                           AND p.i_date = v.rep_date
    LEFT JOIN payments_table o ON p.document_n = o.document_n
                              AND o.payment_date >= p.min_fx_date
                              AND p.i_date = LAST_DAY(o.payment_date)
),
almost_there as (
  SELECT DISTINCT 
    i_date
  , j.customer_id
  , main_currency
  , MAX(i_date) OVER (PARTITION BY j.customer_id, j.product) AS max_i_date
  , j.document_n
  , product
  , min_fx_date
  , balance_column
  , p.interest_rate
  , close_date
  , SUM(total_amount_c) OVER (PARTITION BY i_date, j.customer_id, product) AS total_amount
  , SUM(sum_payed_c) OVER (PARTITION BY i_date, j.customer_id, product) AS sum_payed
  , SUM(pro) OVER (PARTITION BY i_date, j.customer_id, product) AS pro
  FROM        ana_cedvel j
    LEFT JOIN in_rate_tab p ON j.customer_id = p.customer_id
                           AND j.i_date = p.rep_date
),
final_result AS (
  SELECT DISTINCT 
    h.i_date
  , h.customer_id
  , h.main_currency
  , h.product
  , h.min_fx_date
  , h.balance_column
  , h.total_amount
  , h.pro
  , h.sum_payed
  , h.interest_rate
  , CASE
      WHEN h.max_i_date = LAST_DAY(h.close_date)
       AND h.i_date = h.max_i_date 
        THEN h.close_date
      ELSE NULL
    END AS date_end
  FROM almost_there h
  WHERE h.customer_id = 85278945789
)

select *
from final_result

最明显的性能改进来自于减少每一步处理的行数。 在此过程中尽早应用

customer_id
过滤器即可做到这一点。 事实证明,您可以在
tc_lo_product
查询中一直执行此操作。

但是还有更多...

您只需要一个特定的

customer_id
,因此您对流程中此时为 NULL 的任何
customer_id
不感兴趣。 但是当我回溯时,
customer_id
过滤器将
part3
跟踪为
k.customer_id
。 所以有效地,在
part3
你想要的是...

FROM        t
  LEFT JOIN k on t.customer_id = k.customer_id
WHERE k.customer_id

这就是我所说的

LEFT INNER JOIN
(虚构的构造)。 换句话说,您已经声明您想要一个
LEFT OUTER JOIN
(如果不存在匹配行,巫师将从右侧返回 NULL),但然后在“WHERE”子句中包含右侧的某些内容,强制连接成为
 INNER JOIN

查询中可能会有更多错误,但是在花费大约 10 分钟之后......以下可能会提供一些性能改进。 但您可能还需要考虑逻辑中的其他因素。 例如,

table_port
table_product_names
都至少被调用两次。 如果逻辑确实如此,那么在此过程中使用一些临时表可能会有所帮助。

WITH 
tbl_def as (
  SELECT 
    ROW_NUMBER() OVER (PARTITION BY customer_id, product ORDER BY min_fx_date ASC) AS rnk
  , customer_id
  , min_fx_date
  , product
  , balance_column
  FROM table_def
),
tbl_defb as (
  SELECT *
  FROM tbl_def
  WHERE rnk = 1
),
d_table AS (
  SELECT *
  FROM tbl_defb
  WHERE min_fx_date > '01/01/2017'
),
tc_lo_product AS (
  SELECT 
    a.customer_id
  , a.currency
  , b.product
  , a.rep_date
  FROM        table_port a
    LEFT JOIN table_product_names b ON a.ln_aim = b.product_name
  WHERE a.customer_id = 85278945789
),
tbl_port as (
  SELECT 
    customer_id
  , rep_date
  , FIRST_VALUE(close_date) OVER (PARTITION BY document_n ORDER BY rep_date DESC) AS close_date
  , currency
  , document_n
  FROM table_port
),
if_lo AS (
  SELECT DISTINCT 
    b.customer_id
  , total_amount
  , a.i_date
  , x.product
  , a.document_n
  , a.pro
  , b.close_date
  FROM        table_full a
    LEFT JOIN tbl_port b ON a.document_n = b.document_n
                        AND a.i_date = LAST_DAY(b.rep_date)
    LEFT JOIN table_product_names x ON a.ln_aim = x.product_name
),
part3 AS (
  SELECT DISTINCT 
    k.customer_id
  , k.currency
  , FIRST_VALUE(k.currency) OVER (PARTITION BY k.customer_id, k.product ORDER BY t.min_fx_date) AS main_currency
  , t.min_fx_date
  , k.product
  , t.balance_column
  FROM        d_table t
    --LEFT JOIN 
    INNER JOIN tc_lo_product k ON t.customer_id = k.customer_id
                              AND t.product = k.product
                              AND k.rep_date = t.min_fx_date
  --WHERE k.customer_id = 85278945789
),
cbar as (
  SELECT 
    c1.cur_short
  , c1.k_v
  , LAST_DAY(c1.p_date) AS rep_date
  , ROW_NUMBER() OVER (PARTITION BY c1.cu_code, TO_CHAR(c1.p_date, 'MM-YYYY') ORDER BY c1.p_date DESC) AS rn
  FROM table_currency c1
  WHERE c1.kurs_type = 'CBAR'
),
kurs_cedvel AS (
  SELECT *
  FROM cbar
  WHERE rn = 1
),
kop as (
  SELECT 
    ROW_NUMBER() OVER (PARTITION BY customer_id, TRUNC(rep_date, 'MM') ORDER BY rep_date DESC, date_give DESC) AS rnk
  , rep_date
  , customer_id
  , interest_rate
  , date_give
  FROM table_port
  WHERE rep_date = LAST_DAY(rep_date)
    AND document_n NOT LIKE '%KOP%'
),
in_rate_tab AS (
  SELECT *
  FROM kop
  WHERE rnk = 1
),
part3b as (
  SELECT 
    k.customer_id
  , k.currency
  , k.main_currency
  , k.min_fx_date
  , k.product
  , k.balance_column
  , c.total_amount
  , c.i_date
  , c.document_n
  , c.pro
  , c.close_date
  FROM        part3 k
    LEFT JOIN if_lo c ON k.customer_id = c.customer_id
                     AND k.product = c.product
                     AND c.i_date >= k.min_fx_date
  --WHERE k.customer_id = 85278945789
),
ana_cedvel AS (
  SELECT 
    p.*
  , o.sum_payed
  , CASE
      WHEN main_currency = 'AZN' THEN p.total_amount
      ELSE ROUND(p.total_amount / v.k_v, 2)
    END AS total_amount_c
  , CASE
      WHEN main_currency = 'AZN' THEN p.pro
      ELSE ROUND(p.pro / v.kurs_val, 2)
    END AS pro_c
  , CASE
      WHEN main_currency = o.valyuta THEN o.sum_payed
      ELSE ROUND(o.sum_payed / v.k_v, 2)
    END AS sum_payed_c
  FROM        part3b p
    LEFT JOIN kurs_cedvel v ON p.main_currency = v.cur_short 
                           AND p.i_date = v.rep_date
    LEFT JOIN payments_table o ON p.document_n = o.document_n
                              AND o.payment_date >= p.min_fx_date
                              AND p.i_date = LAST_DAY(o.payment_date)
  --WHERE p.customer_id = 85278945789
),
almost_there as (
  SELECT DISTINCT 
    i_date
  , j.customer_id
  , main_currency
  , MAX(i_date) OVER (PARTITION BY j.customer_id, j.product) AS max_i_date
  , j.document_n
  , product
  , min_fx_date
  , balance_column
  , p.interest_rate
  , close_date
  , SUM(total_amount_c) OVER (PARTITION BY i_date, j.customer_id, product) AS total_amount
  , SUM(sum_payed_c) OVER (PARTITION BY i_date, j.customer_id, product) AS sum_payed
  , SUM(pro) OVER (PARTITION BY i_date, j.customer_id, product) AS pro
  FROM        ana_cedvel j
    LEFT JOIN in_rate_tab p ON j.customer_id = p.customer_id
                           AND j.i_date = p.rep_date
  --WHERE j.customer_id = 85278945789
),
final_result AS (
  SELECT DISTINCT 
    h.i_date
  , h.customer_id
  , h.main_currency
  , h.product
  , h.min_fx_date
  , h.balance_column
  , h.total_amount
  , h.pro
  , h.sum_payed
  , h.interest_rate
  , CASE
      WHEN h.max_i_date = LAST_DAY(h.close_date)
       AND h.i_date = h.max_i_date 
        THEN h.close_date
      ELSE NULL
    END AS date_end
  FROM almost_there h
  --WHERE h.customer_id = 85278945789
)

select *
from final_result
© www.soinside.com 2019 - 2024. All rights reserved.