Phi*_*lᵀᴹ 12 sql-server sql-server-2008-r2 gaps-and-islands
鉴于以下数据:
create table #histories
(
username varchar(10),
account varchar(10),
assigned date
);
insert into #histories
values
('PHIL','ACCOUNT1','2017-01-04'),
('PETER','ACCOUNT1','2017-01-15'),
('DAVE','ACCOUNT1','2017-03-04'),
('ANDY','ACCOUNT1','2017-05-06'),
('DAVE','ACCOUNT1','2017-05-07'),
('FRED','ACCOUNT1','2017-05-08'),
('JAMES','ACCOUNT1','2017-08-05'),
('DAVE','ACCOUNT2','2017-01-02'),
('PHIL','ACCOUNT2','2017-01-18'),
('JOSH','ACCOUNT2','2017-04-08'),
('JAMES','ACCOUNT2','2017-04-09'),
('DAVE','ACCOUNT2','2017-05-06'),
('PHIL','ACCOUNT2','2017-05-07') ;
Run Code Online (Sandbox Code Playgroud)
...代表给定用户分配给帐户的时间。
我希望在每个月的最后一天确定谁拥有给定的帐户(分配的日期是帐户转移所有权的日期),并填充任何缺失的月末(可能是从dates我可用的方便表格中创建的,带有有用的列DateKey,Date和LastDayOfMonth, [@AaronBertrand 提供]) 1 .
期望的结果是:
PETER, ACCOUNT1, 2017-01-31
PETER, ACCOUNT1, 2017-02-28
DAVE, ACCOUNT1, 2017-03-31
DAVE, ACCOUNT1, 2017-04-30
FRED, ACCOUNT1, 2017-05-31
FRED, ACCOUNT1, 2017-06-30
FRED, ACCOUNT1, 2017-07-31
JAMES, ACCOUNT1, 2017-08-31
PHIL, ACCOUNT2, 2017-01-31
PHIL, ACCOUNT2, 2017-02-28
PHIL, ACCOUNT2, 2017-03-31
JAMES, ACCOUNT2, 2017-04-30
PHIL, ACCOUNT2, 2017-05-31
Run Code Online (Sandbox Code Playgroud)
使用窗口函数执行此操作的初始部分是微不足道的,它添加了我正在努力解决的“缺失”行。
解决此问题的一种方法是执行以下操作:
LEAD在 SQL Server 2008 上模拟。您可以APPLY为此使用或 suquery。我稍微修改了您的测试数据以使结果具有确定性。还添加了一个索引:
create table #histories
(
username varchar(10),
account varchar(10),
assigned date
);
insert into #histories
values
('PHIL','ACCOUNT1','2017-01-04'),
('PETER','ACCOUNT1','2017-01-15'),
('DAVE','ACCOUNT1','2017-03-04'),
('ANDY','ACCOUNT1','2017-05-06'),
('DAVE','ACCOUNT1','2017-05-07'),
('FRED','ACCOUNT1','2017-05-08'),
('JAMES','ACCOUNT1','2017-08-05'),
('DAVE','ACCOUNT2','2017-01-02'),
('PHIL','ACCOUNT2','2017-01-18'),
('JOSH','ACCOUNT2','2017-04-08'), -- changed this date to have deterministic results
('JAMES','ACCOUNT2','2017-04-09'),
('DAVE','ACCOUNT2','2017-05-06'),
('PHIL','ACCOUNT2','2017-05-07') ;
-- make life easy
create index gotta_go_fast ON #histories (account, assigned);
Run Code Online (Sandbox Code Playgroud)
这是有史以来最懒惰的日期维度表:
create table #date_dim_months_only (
month_date date,
primary key (month_date)
);
-- put 2500 month ends into table
INSERT INTO #date_dim_months_only WITH (TABLOCK)
SELECT DATEADD(DAY, -1, DATEADD(MONTH, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), '20000101'))
FROM master..spt_values;
Run Code Online (Sandbox Code Playgroud)
对于第 1 步,有很多方法可以模拟LEAD. 这是一种方法:
SELECT
h1.username
, h1.account
, h1.assigned
, next_date.assigned
FROM #histories h1
OUTER APPLY (
SELECT TOP 1 h2.assigned
FROM #histories h2
WHERE h1.account = h2.account
AND h1.assigned < h2.assigned
ORDER BY h2.assigned ASC
) next_date;
Run Code Online (Sandbox Code Playgroud)
对于第 2 步,我们需要将 NULL 值更改为其他值。您想包括每个帐户的最后一个月,因此在开始日期上加上一个月就足够了:
ISNULL(next_date.assigned, DATEADD(MONTH, 1, h1.assigned))
Run Code Online (Sandbox Code Playgroud)
对于第 3 步,我们可以连接到日期维度表。维度表中的列正是结果集所需的列:
INNER JOIN #date_dim_months_only dd ON
dd.month_date >= h1.assigned AND
dd.month_date < ISNULL(next_date.assigned, DATEADD(MONTH, 1, h1.assigned))
Run Code Online (Sandbox Code Playgroud)
当我把它们放在一起时,我不喜欢我得到的查询。组合OUTER APPLY和时,连接顺序可能会出现问题INNER JOIN。为了获得我想要的连接顺序,我用子查询重写了它:
SELECT
hist.username
, hist.account
, dd.month_date
FROM
(
SELECT
h1.username
, h1.account
, h1.assigned
, ISNULL(
(SELECT TOP 1 h2.assigned
FROM #histories h2
WHERE h1.account = h2.account
AND h1.assigned < h2.assigned
ORDER BY h2.assigned ASC
)
, DATEADD(MONTH, 1, h1.assigned)
) next_assigned
FROM #histories h1
) hist
INNER JOIN #date_dim_months_only dd ON
dd.month_date >= hist.assigned AND
dd.month_date < hist.next_assigned;
Run Code Online (Sandbox Code Playgroud)
我不知道你有多少数据,所以这对你来说可能无关紧要。但该计划看起来像我想要的那样:
结果与您的相符:
????????????????????????????????????
? username ? account ? month_date ?
????????????????????????????????????
? PETER ? ACCOUNT1 ? 2017-01-31 ?
? PETER ? ACCOUNT1 ? 2017-02-28 ?
? DAVE ? ACCOUNT1 ? 2017-03-31 ?
? DAVE ? ACCOUNT1 ? 2017-04-30 ?
? FRED ? ACCOUNT1 ? 2017-05-31 ?
? FRED ? ACCOUNT1 ? 2017-06-30 ?
? FRED ? ACCOUNT1 ? 2017-07-31 ?
? JAMES ? ACCOUNT1 ? 2017-08-31 ?
? PHIL ? ACCOUNT2 ? 2017-01-31 ?
? PHIL ? ACCOUNT2 ? 2017-02-28 ?
? PHIL ? ACCOUNT2 ? 2017-03-31 ?
? JAMES ? ACCOUNT2 ? 2017-04-30 ?
? PHIL ? ACCOUNT2 ? 2017-05-31 ?
????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2652 次 |
| 最近记录: |