我在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
)
重新格式化您的查询,以便每个子查询都表示为公共表表达式:
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