Реализация SCD типа 2 в Oracle - PullRequest
0 голосов
/ 16 февраля 2019

Во-первых, я хотел бы сказать, что я новичок в сообществе stackoverflow и относительно плохо знаком с самим SQL, поэтому прошу прощения, если я неправильно отформатировал свой вопрос или не сформулировал свои требования четко.

Я пытаюсь внедрить SCD типа 2 в Oracle.Структура исходной таблицы (customer_records) приведена ниже.

CREATE TABLE customer_records(
    day date,
    snapshot_day number,
    vendor_id number,
    customer_id number,
    rank number
);

INSERT INTO customer_records 
(day,snapshot_day,vendor_id,customer_id,rank)
VALUES
(9/24/2014,6266,71047795,476095,3103),
(10/1/2014,6273,71047795,476095,3103),
(10/8/2014,6280,71047795,476095,3103),
(10/15/2014,6287,71047795,476095,3103),
(10/22/2014,6291,71047795,476095,3102),
(10/29/2014,6330,71047795,476095,3102),
(11/05/2015,6351,71047795,476095,3102),
(11/12/2015,6440,71047795,476095,3103);

Таблица выше обновляется еженедельно, и я собрал записи для конкретного клиента, представленные vendor_id и customer_id.Так что каждый клиент будет иметь уникальные vendor_id и customer_id.Я пытаюсь отслеживать изменения на уровне (rank) клиента.Может так случиться, что уровень клиента может оставаться неизменным в течение нескольких недель, и мы готовы отслеживать только изменения уровня клиента.

Желаемый результат (таблица измерений) будет выглядеть примерно так:

SK  Version   Date_From    Date_To    Vendor_id   Customer_Id  Rank_Id

1     1       9/24/2014    10/22/2014    71047795            476095       3103
2     2       10/22/2014   11/05/2015    71047795            476095       3102
3     3       11/05/2015   12/31/2199    71047795            476095       3103

Таким образом, чтобы при изменении уровня клиента мы отслеживали его в новой таблице.Кроме того, желая включить current_flag = 'Y' для самого текущего уровня.

Я хочу быть в состоянии сделать это с помощью слияния.

Ответы [ 2 ]

0 голосов
/ 17 февраля 2019

Я хочу сделать это с помощью слияния.

MERGE не сделает это за вас.MERGE - это, в основном, оператор case: для каждой записи в подзапросе USING мы можем вставлять сопоставленные записи или обновлять сопоставленные записи.Суть в том, что при изменении уровня существующего клиента необходимо выполнить DML для двух записей измерений:

  • обновить предыдущую текущую запись - установить current_flag = 'N',установите day_to в systimestamp (или как угодно).
  • вставьте новую текущую запись.

Таким образом, вам нужен процесс - возможно, процедура PL / SQL - который выполняетоператор UPDATE для закрытия текущих записей с истекшим сроком действия, за которыми следует INSERT для добавления новых текущих записей.

Подзапрос может быть не лучшим маршрутом, как я считаю.

Вы описываете себя как относительно нового для SQL, поэтому вы можете беспокоиться об этом, но не волнуйтесь.Избегайте преждевременной оптимизации.Сделайте самое простое, что может сработать, и настройте его по мере необходимости.Подзапрос должен быть наиболее эффективным способом определения текущих записей, которые необходимо обновить.Базы данных Oracle являются рабочими лошадками и могут справляться с существенными нагрузками, при условии, что мы пишем разумный SQL.

В вашем случае это означает:

  • использование операций над множествами (т.е. не построчно) дляОБНОВЛЕНИЕ и ВСТАВКА.
  • убедитесь, что вы работаете с наименьшим необходимым набором записей.Применяйте изменения только к записям в базовой таблице, которые изменились с момента последнего обновления измерения.В вашем случае вам нужно отслеживать customer_records.snapshot_day и применять изменения только для записей, которые имеют более высокий snapshot_day (или, может быть, нет, я предполагаю, что в вашем процессе).
  • правильно проиндексировать вашу таблицу измерений, чтобыон эффективно применяет подзапрос.
0 голосов
/ 17 февраля 2019

Здесь представлен подход к группированию последовательных записей, имеющих один и тот же уровень, при обнаружении изменений.

Идея состоит в том, чтобы самостоятельно присоединиться к таблице и связать каждую запись со следующей записью, имеющей другой уровень,Это делается с использованием условия 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
    )
...