簡體   English   中英

我如何編寫一個 SQL 查詢來組合沒有任何變化的版本?

[英]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 的記錄)。 我們可以按照以下三個步驟找到島嶼之間的差距:

  • 對於每個人,當之前的 Version_End = Version_Start - 1 天和之前的 Elig_for_Disc_A 與當前值相同時,不標記分區更改
  • 計算標志上的運行總和,以制作新分區
  • 在每個新分區上聚合 MIN(Version_Start)、MAX(Version_End)。
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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM