1

首先,我想说我是 stackoverflow 社区的新手,并且对 SQL 本身相对较新,所以如果我没有正确格式化我的问题或没有清楚地说明我的要求,请原谅我。

我正在尝试在 Oracle 中实现类型 2 SCD。源表 ( 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_idcustomer_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'。

我希望能够使用合并来做到这一点。

4

2 回答 2

1

这是一种在检测更改时对具有相同层的连续记录进行分组的方法。

这个想法是自连接表,并将每条记录与具有不同层的下一条记录相关联。这是使用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

结果 :

版本 | CURRENT_FLAG | DAY_FROM | DAY_TO | 供应商 ID | 客户ID | 秩
------: | :----------- | :-------- | :-------- | ------------------: | ----------: | -----------------:
      1 | N | 24-SEP-14 | 14 年 10 月 22 日 | 71047795 | 476095 | 3103
      2 | N | 14 年 10 月 22 日 | 15 年 11 月 12 日 | 71047795 | 476095 | 3102
      3 | 是 | 15 年 11 月 12 日 | 99 年 12 月 31 日 | 71047795 | 476095 | 3103

在 Oracle 中,您可以使用MERGE 语法将任何选择转换为合并查询。您可以匹配所有预期的列current_flagday_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
    )
于 2019-02-16T23:41:59.553 回答
0

我希望能够使用合并来做到这一点。

MERGE 不会为你做这件事。MERGE 基本上是一个 case 语句:对于 USING 子查询中的每条记录,我们可以插入匹配的记录或更新匹配的记录。问题是,当现有客户的等级发生变化时,您需要对二维记录执行DML

  • 更新之前的当前记录 - set current_flag= 'N',设置day_tosystimestamp(或其他)。
  • 插入新的当前记录。

所以你需要一个进程——可能是一个 PL/SQL 过程——它执行一个 UPDATE 语句来关闭过期的当前记录,然后是一个 INSERT 来添加新的当前记录。

子查询可能不是我认为的最佳途径。

您将自己描述为对 SQL 比较陌生,因此您可能会担心这一点,但不必担心。避免过早优化。做最简单的事情,并根据需要对其进行调整。子查询应该是识别您需要更新的当前记录的最有效方式。如果我们编写合理的 SQL,Oracle 数据库是主力,可以处理大量负载。

在您的情况下,这意味着:

  • 对 UPDATE 和 INSERT 使用集合操作(​​即不是逐行)。
  • 确保您使用最少的必要记录集。仅对自上次刷新维度后已更改的基表中的记录应用更改。在您的情况下,您需要跟踪customer_records.snapshot_day并仅对具有更高记录的记录应用更改snapshot_day(或者可能不是,我猜测您的过程)。
  • 正确索引您的维度表,以便有效地应用子查询。
于 2019-02-17T14:44:33.030 回答