![](/img/trans.png)
[英]How to optimize a SQL query that combines INNER JOINs, DISTINCT and WHERE?
[英]How can I write a SQL query that combines versions where nothing has changed?
我正在嘗試解決一個問題,每次更新項目時,我都有一個包含不同版本項目的表。
人 | 符合光盤 A | 符合光盤 B | 版本開始 | 版本結束 |
---|---|---|---|---|
鮑勃 | 是 | 是 | 2022-01-01 | 2022-01-04 |
鮑勃 | 是 | 否 | 2022-01-05 | 2022-01-13 |
鮑勃 | 否 | 否 | 2022-01-14 | 2022-01-22 |
鮑勃 | 是 | 否 | 2022-01-23 | 3000-12-31 |
對於這個問題,我不關心這個人是否有資格享受折扣 B,我只對折扣 A 感興趣。我想要的是提出一個查詢,每次該人的資格只返回一個新版本折扣 A 帶來變化。
我想要的是返回以下內容:
人 | 符合光盤 A | 版本開始 | 版本結束 |
---|---|---|---|
鮑勃 | 是 | 2022-01-01 | 2022-01-13 |
鮑勃 | 否 | 2022-01-14 | 2022-01-22 |
鮑勃 | 是 | 2022-01-23 | 3000-12-31 |
在此示例中,前兩行已合並,因為折扣 A 的資格值未更改。 在我正在處理的例子中,我們也可以在桌子上有很多不同的人。
我已經嘗試查看按 Elig 對光盤 A 進行分組是否可行,但隨后我將第 1、2 和 4 行全部組合在一起。
這可能與 SQL 相關嗎?
或者使用 MATCH_RECOGNIZE:
with data(Person, Elig_for_Disc_A, Elig_for_Disc_B, Version_Start, Version_End) as
(
select 'Bob', 'Y', 'Y', to_date('2022-01-01', 'yyyy-mm-dd'), to_date('2022-01-04', 'yyyy-mm-dd') from dual union all
select 'Bob', 'Y', 'N', to_date('2022-01-05', 'yyyy-mm-dd'), to_date('2022-01-13', 'yyyy-mm-dd') from dual union all
select 'Bob', 'N', 'N', to_date('2022-01-14', 'yyyy-mm-dd'), to_date('2022-01-22', 'yyyy-mm-dd') from dual union all
select 'Bob', 'Y', 'N', to_date('2022-01-23', 'yyyy-mm-dd'), to_date('3000-12-31', 'yyyy-mm-dd') from dual -- union all
)
select Person, Elig_for_Disc_A, Version_Start, Version_End
from data
match_recognize (
partition by person, Elig_for_Disc_A
order by Version_Start, Version_End
measures first(Version_Start) as Version_Start, max(Version_End) as Version_End
pattern( merged* strt )
define
merged as max(Version_End)+1 >= next(Version_Start)
)
order by person, Version_Start;
Bob Y 01/01/22 13/01/22
Bob N 14/01/22 22/01/22
Bob Y 23/01/22 31/12/00
以下是您可以在 Oracle 中使用的查詢示例:
SELECT person, Elig_for_Disc_A, Version_Start, Version_End
FROM (
SELECT person, Elig_for_Disc_A, Version_Start, Version_End,
LAG(Elig_for_Disc_A) OVER (PARTITION BY person ORDER BY Version_Start) as prev_Elig_Disc_A
FROM your_table
)
WHERE Elig_for_Disc_A != prev_Elig_Disc_A OR prev_Elig_Disc_A IS NULL
使用 LAG 或 LEAD 可以輕松查找更改。 真正的訣竅是重構您的開始/結束日期以涵蓋中間時間段。 嘗試這樣的事情(沒試過,可能需要調試):
SELECT person,
elig_for_disc_a,
new_version_start,
NVL(LEAD(new_version_start) OVER (PARTITION BY person ORDER BY version_start),old_version_end) new_version_end
FROM (SELECT person,
new_version_start,
MAX(version_end) old_version_end,
MAX(elig_for_disc_a) elig_for_disc_a
FROM (SELECT x.*,
MAX(start_of_elig_change) OVER (PARTITION BY person ORDER BY version_start ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) new_version_start
FROM (SELECT x.*,
DECODE(last_elig_a,elig_for_disc_a,NULL,version_start) start_of_elig_change
FROM (SELECT x.*,
LAG(elig_for_disc_a) OVER (PARTITION BY person ORDER BY version_start) last_elig_a
FROM your_table x) x) x) x
GROUP BY person,
new_version_start)
這是一個典型的間隙和孤島問題,您需要在同一分區內識別孤島(應為 go 的記錄)。 我們可以按照以下三個步驟找到島嶼之間的差距:
WITH cte AS (
SELECT tab.*,
CASE WHEN LAG(Version_End) OVER w = Version_Start -1
AND LAG(Elig_for_Disc_A) OVER w = Elig_for_Disc_A
THEN 0 ELSE 1
END AS change_part
FROM tab
WINDOW w AS (PARTITION BY Person ORDER BY Version_Start)
), cte2 AS (
SELECT cte.*,
SUM(change_part) OVER(PARTITION BY Person ORDER BY Version_Start) AS parts
FROM cte
)
SELECT Person,
Elig_for_Disc_A,
MIN(Version_Start) AS Version_Start,
MAX(Version_End) AS Version_End
FROM cte2
GROUP BY Person,
Elig_for_Disc_A,
Parts
Output:
人 | ELIG_FOR_DISC_A | VERSION_START | VERSION_END |
---|---|---|---|
鮑勃 | 是 | 22 年 1 月 1 日 | 22 年 1 月 13 日 |
鮑勃 | 否 | 22 年 1 月 14 日 | 22 年 1 月 22 日 |
鮑勃 | 是 | 22 年 1 月 23 日 | 00 年 12 月 31 日 |
在此處查看 Oracle 演示。
假設:您正在使用DATE
數據類型來存儲日期。
您可以使用 1 個案例語句來獲得正確的 version_end 日期和 rest 1 個帶有 LAG window function 的子查詢將起作用 -
SELECT PERSON, ELIG_FOR_DISC_A, ELIG_FOR_DISC_B, VERSION_START,
upd_version_end version_end
FROM (SELECT d.*, LEAD(Elig_for_Disc_A) OVER(PARTITION BY Person ORDER BY Version_Start) prev_Elig_Disc_A,
CASE WHEN Elig_for_Disc_A = LEAD(Elig_for_Disc_A) OVER(PARTITION BY Person ORDER BY Version_Start)
THEN LEAD(version_end) OVER(PARTITION BY Person ORDER BY Version_Start)
ELSE version_end
END UPD_VERSION_END
FROM DATA d
)
WHERE Elig_for_Disc_A != prev_Elig_Disc_A OR prev_Elig_Disc_A IS NULL;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.