Ето един подход за групиране на последователни записи с едно и също ниво, като същевременно се откриват промени.
Идеята е да се присъедините самостоятелно към таблицата и да свържете всеки запис със следващия запис, който има различно ниво. Това се прави с помощта на NOT EXISTS
условие със свързана подзаявка.
LEFT JOIN
е необходимо, за да се избегне филтрирането на последния запис (който притежава текущия слой), който все още няма следващ запис:за този запис използваме COALESCE()
за да зададете крайна дата по подразбиране.
SELECT
c1.day day_from,
COALESCE(c2.day, TO_DATE('2199-12-31', 'yyyy-mm-dd')) day_to,
c1.Vendor_ID,
c1.Customer_ID,
c1.rank
FROM customer_records c1
LEFT JOIN customer_records c2
ON c2.Vendor_ID = c1.Vendor_ID
AND c2.Customer_ID = c1.Customer_ID
AND c2.rank <> c1.rank
AND c2.DAY > c1.DAY
AND NOT EXISTS (
SELECT 1
FROM customer_records c3
WHERE
c3.Vendor_ID = c1.Vendor_ID
AND c3.Customer_ID = c1.Customer_ID
AND c3.rank <> c1.rank
AND c3.DAY > c1.DAY
AND c3.DAY < c2.DAY
)
Това връща:
DAY_FROM | DAY_TO | Vendor_ID | Customer_ID | rank
:-------- | :-------- | ------------------: | ----------: | -----------------:
24-SEP-14 | 22-OCT-14 | 71047795 | 476095 | 3103
01-OCT-14 | 22-OCT-14 | 71047795 | 476095 | 3103
08-OCT-14 | 22-OCT-14 | 71047795 | 476095 | 3103
15-OCT-14 | 22-OCT-14 | 71047795 | 476095 | 3103
22-OCT-14 | 12-NOV-15 | 71047795 | 476095 | 3102
29-OCT-14 | 12-NOV-15 | 71047795 | 476095 | 3102
05-NOV-15 | 12-NOV-15 | 71047795 | 476095 | 3102
12-NOV-15 | 31-DEC-99 | 71047795 | 476095 | 3103
Сега можем да групираме набора от записи по ниво и крайна дата, за да генерираме очакваните резултати. ROW_NUMBER()
може да ви даде номера на версията. Също така е лесно да проверите кой запис е текущият, както е обяснено по-горе.
SELECT
ROW_NUMBER() OVER(ORDER BY c2.day) version,
DECODE(c2.day, NULL, 'Y') current_flag,
MIN(c1.day) day_from,
COALESCE(c2.day, TO_DATE('2199-12-31', 'yyyy-mm-dd')) day_to,
c1.Vendor_ID,
c1.Customer_ID,
c1.rank
FROM customer_records c1
LEFT JOIN customer_records c2
ON c2.Vendor_ID = c1.Vendor_ID
AND c2.Customer_ID = c1.Customer_ID
AND c2.rank <> c1.rank
AND c2.DAY > c1.DAY
AND NOT EXISTS (
SELECT 1
FROM customer_records c3
WHERE
c3.Vendor_Id = c1.Vendor_Id
AND c3.Customer_ID = c1.Customer_ID
AND c3.rank <> c1.rank
AND c3.DAY > c1.DAY
AND c3.DAY < c2.DAY
)
GROUP BY
c1.Vendor_Id,
c1.Customer_ID,
c1.rank,
c2.day
ORDER BY
day_from
Резултати:
VERSION | CURRENT_FLAG | DAY_FROM | DAY_TO | Vendor_ID | Customer_ID | rank ------: | :----------- | :-------- | :-------- | ------------------: | ----------: | -----------------: 1 | N | 24-SEP-14 | 22-OCT-14 | 71047795 | 476095 | 3103 2 | N | 22-OCT-14 | 12-NOV-15 | 71047795 | 476095 | 3102 3 | Y | 12-NOV-15 | 31-DEC-99 | 71047795 | 476095 | 3103
В Oracle можете да превърнете всеки избор в заявка за сливане, като използвате синтаксиса MERGE
. Можете да съпоставите във всички колони, очакван current_flag
и day_to
и ги актуализирайте, ако вече съществува запис; в противен случай просто поставете нов.
MERGE INTO dimensions dim
USING (
-- above query goes here --
) cust
ON dim.DAY_FROM = cust.DAY_FROM
AND dim.vendor_id = cust.vendor_id
AND dim.Customer_ID = cust.Customer_ID
AND dim.rank = cust.rank
WHEN MATCHED THEN UPDATE SET
dim.DAY_TO = cust.DAY_TO,
dim.CURRENT_FLAG = cust.CURRENT_FLAG
WHEN NOT MATCHED THEN
INSERT (
dim.DAY_FROM,
dim.VERSION,
dim.CURRENT_FLAG,
dim.DAY_FROM,
dim.DAY_TO,
dim.vendor_id,
dim.customer_id,
dim.rank
) VALUES (
cust.DAY_FROM,
cust.VERSION,
cust.CURRENT_FLAG,
cust.DAY_FROM,
cust.DAY_TO,
cust.vendor_id,
cust.Customer_ID,
cust.rank
)