首先,我想说我是stackoverflow社区的新手,而SQL本身是新手,所以请原谅我,如果我对问题的格式不正确或没有清楚说明我的要求。
我正在尝试在Oracle中实现2型SCD。源表(customer_records
)的结构如下。
CREATE TABLE customer_records(
day date,
snapshot_day_dw_id number,
vend_loy_acct_dw_id number,
hub_cust_id number,
cur_loy_tier_dw_id number
);
INSERT INTO customer_records
(day,snapshot_day_dw_id,vend_loy_acct_dw_id,hub_cust_id,cur_loy_tier_dw_id)
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);
上表每周更新一次,我已经提取了由vend_loy_acct_dw_id
和hub_cust_id
代表的特定客户的记录。这样每个客户将拥有唯一的vend_loy_acct_dw_id
和hub_cust_id
。我正在尝试跟踪客户的层(cur_loy_tier_dw_id
)中的更改。可能会发生这样的情况,即客户的等级可能会保持几个星期不变,而我们仅愿意跟踪客户等级发生变化的时间。
所需的输出(尺寸表)如下所示:
SK Version Date_From Date_To Vend_Loy_Acct_Dw_Id Hub_Cust_Id Cur_Cust_Tier_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'。
我希望能够使用合并做到这一点。
答案 0 :(得分: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.VEND_LOY_ACCT_DW_ID,
c1.HUB_CUST_ID,
c1.CUR_LOY_TIER_DW_ID
FROM customer_records c1
LEFT JOIN customer_records c2
ON c2.VEND_LOY_ACCT_DW_ID = c1.VEND_LOY_ACCT_DW_ID
AND c2.HUB_CUST_ID = c1.HUB_CUST_ID
AND c2.CUR_LOY_TIER_DW_ID <> c1.CUR_LOY_TIER_DW_ID
AND c2.DAY > c1.DAY
AND NOT EXISTS (
SELECT 1
FROM customer_records c3
WHERE
c3.VEND_LOY_ACCT_DW_ID = c1.VEND_LOY_ACCT_DW_ID
AND c3.HUB_CUST_ID = c1.HUB_CUST_ID
AND c3.CUR_LOY_TIER_DW_ID <> c1.CUR_LOY_TIER_DW_ID
AND c3.DAY > c1.DAY
AND c3.DAY < c2.DAY
)
这将返回:
DAY_FROM | DAY_TO | VEND_LOY_ACCT_DW_ID | HUB_CUST_ID | CUR_LOY_TIER_DW_ID
:-------- | :-------- | ------------------: | ----------: | -----------------:
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.VEND_LOY_ACCT_DW_ID,
c1.HUB_CUST_ID,
c1.CUR_LOY_TIER_DW_ID
FROM customer_records c1
LEFT JOIN customer_records c2
ON c2.VEND_LOY_ACCT_DW_ID = c1.VEND_LOY_ACCT_DW_ID
AND c2.HUB_CUST_ID = c1.HUB_CUST_ID
AND c2.CUR_LOY_TIER_DW_ID <> c1.CUR_LOY_TIER_DW_ID
AND c2.DAY > c1.DAY
AND NOT EXISTS (
SELECT 1
FROM customer_records c3
WHERE
c3.VEND_LOY_ACCT_DW_ID = c1.VEND_LOY_ACCT_DW_ID
AND c3.HUB_CUST_ID = c1.HUB_CUST_ID
AND c3.CUR_LOY_TIER_DW_ID <> c1.CUR_LOY_TIER_DW_ID
AND c3.DAY > c1.DAY
AND c3.DAY < c2.DAY
)
GROUP BY
c1.VEND_LOY_ACCT_DW_ID,
c1.HUB_CUST_ID,
c1.CUR_LOY_TIER_DW_ID,
c2.day
ORDER BY
day_from
结果:
VERSION | CURRENT_FLAG | DAY_FROM | DAY_TO | VEND_LOY_ACCT_DW_ID | HUB_CUST_ID | CUR_LOY_TIER_DW_ID ------: | :----------- | :-------- | :-------- | ------------------: | ----------: | -----------------: 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中,您可以使用the MERGE syntax将任何选择转换为合并查询。您可以在所有预期的current_flag
和day_to
列上进行匹配,如果记录已经存在,则可以更新这些列;否则,只需插入一个新的。
MERGE INTO dimensions dim
USING (
-- above query goes here --
) cust
ON dim.DAY_FROM = cust.DAY_FROM
AND dim.VEND_LOY_ACCT_DW_ID = cust.VEND_LOY_ACCT_DW_ID
AND dim.HUB_CUST_ID = cust.HUB_CUST_ID
AND dim.CUR_LOY_TIER_DW_ID = cust.CUR_LOY_TIER_DW_ID
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.VEND_LOY_ACCT_DW_ID,
dim.HUB_CUST_ID,
dim.CUR_LOY_TIER_DW_ID
) VALUES (
cust.DAY_FROM,
cust.VERSION,
cust.CURRENT_FLAG,
cust.DAY_FROM,
cust.DAY_TO,
cust.VEND_LOY_ACCT_DW_ID,
cust.HUB_CUST_ID,
cust.CUR_LOY_TIER_DW_ID
)
答案 1 :(得分:0)
我希望能够使用合并做到这一点。
MERGE不会为您这样做。 MERGE基本上是一个case语句:对于USING子查询中的每个记录,我们可以插入上匹配的记录或更新匹配的记录。问题是,当现有客户的层更改时,您需要为两个维记录执行DML:
current_flag
='N',将day_to
设置为systimestamp
(或其他)。因此,您需要有一个过程-可能是PL / SQL过程-执行UPDATE语句以关闭过期的当前记录,然后执行INSERT添加新的当前记录。
我认为,子查询可能不是最佳途径。
您将自己描述为对SQL相对较新的人,因此您可以为此担心,但不必担心。避免过早优化。做最简单的操作即可根据需要进行调整。子查询应该是tp识别需要更新的当前记录的最有效方法。如果我们编写明智的SQL,则Oracle数据库是主力军,可以处理大量负载。
对于您而言,这意味着:
customer_records.snapshot_day_dw_id
,并且仅对具有更高snapshot_day_dw_id
的记录应用更改(或者可能不行,我想您的过程是这样)。