昨天我问了一个类似的问题,但我对自己想要的内容并不是很了解。这将更加清晰。
领先/滞后并没有让我得到我需要的东西。它很接近,但还不够。 将SQL Server 2014用于客户端,构建在SQL 2012上的实际服务器。
这是我的代码: 创建团队表
CREATE TABLE ##TeamTable
([UserID] varchar(50), [CurrentTeam] varchar(5), [ChangeDate] datetime)
;
INSERT INTO ##TeamTable
([UserID], [CurrentTeam], [ChangeDate])
VALUES
('User1', 'Team1', '6/1/2016'),
('User1', 'Team2', '9/1/2016'),
('User1', 'Team3', '12/1/2016'),
('User2', 'Team1', '4/1/2016'),
('User2', 'Team2', '10/1/2016'),
('User2', 'Team3', '11/1/2016');
现在创建数据表我需要加入
CREATE TABLE ##DataTable
([UserID] varchar(50), Month_sk datetime, Media varchar(50), NCO int)
INSERT INTO ##DataTable
([UserID] , Month_sk , Media , NCO )
VALUES
('User1', '2016-06-01 00:00:00', 'Fax', 100),
('User1', '2016-06-01 00:00:00', 'Voice', 120),
('User1', '2016-07-01 00:00:00', 'Voice', 90),
('User1', '2016-07-01 00:00:00', 'Email', 100),
('User1', '2016-08-01 00:00:00', 'Voice', 150),
('User1', '2016-08-01 00:00:00', 'Email', 100),
('User1', '2016-09-01 00:00:00', 'Voice', 100),
('User1', '2016-09-01 00:00:00', 'Email', 120),
('User1', '2016-10-01 00:00:00', 'Voice', 90),
('User1', '2016-10-01 00:00:00', 'Email', 100),
('User1', '2016-11-01 00:00:00', 'Voice', 150),
('User1', '2016-11-01 00:00:00', 'Email', 100),
('User1', '2016-12-01 00:00:00', 'Voice', 150),
('User1', '2016-12-01 00:00:00', 'Email', 100),
('User2', '2016-04-01 00:00:00', 'Fax', 100),
('User2', '2016-04-01 00:00:00', 'Voice', 120),
('User2', '2016-05-01 00:00:00', 'Fax', 100),
('User2', '2016-05-01 00:00:00', 'Voice', 120),
('User2', '2016-06-01 00:00:00', 'Fax', 100),
('User2', '2016-06-01 00:00:00', 'Voice', 120),
('User2', '2016-07-01 00:00:00', 'Voice', 90),
('User2', '2016-07-01 00:00:00', 'Email', 100),
('User2', '2016-08-01 00:00:00', 'Voice', 150),
('User2', '2016-08-01 00:00:00', 'Email', 100),
('User2', '2016-09-01 00:00:00', 'Voice', 100),
('User2', '2016-09-01 00:00:00', 'Email', 120),
('User2', '2016-10-01 00:00:00', 'Voice', 90),
('User2', '2016-10-01 00:00:00', 'Email', 100),
('User2', '2016-11-01 00:00:00', 'Voice', 150),
('User2', '2016-11-01 00:00:00', 'Email', 100),
('User2', '2016-12-01 00:00:00', 'Voice', 150),
('User2', '2016-12-01 00:00:00', 'Email', 100);
这是一个基本的选择,以显示最新情况:
SELECT b.UserID
,b.Media
,b.NCO
,Month_sk
,CurrentTeam
FROM ##DataTable b
LEFT OUTER JOIN ##TeamTable a on b.UserID = a.UserID and b.Month_sk = a.ChangeDate
order by UserID, Month_sk, media
这给了我一个如下所示的结果集:
我需要的是我有空的地方,它会吸引以前的团队名称,这不是空的。因此,在User1案例中,7月和8月的4个空值将说Team1,因为那是他最后一个团队。对于Team2之后的空值相同,那些应该是Team2。
领先/滞后已接近或我没有正确使用它。希望通过所有这些代码,这可以使某人的工作更轻松。
更新: 滞后/导致给出相同的结果。仍然需要空值来填写
SELECT b.UserID
,b.Media
,b.NCO
,Month_sk
,CurrentTeam
,LAG(CurrentTeam,1, currentteam) OVER(PARTITION BY a.userid, changedate ORDER BY ChangeDate) as Lag
FROM ##DataTable b
LEFT OUTER JOIN ##TeamTable a on b.UserID = a.UserID and b.Month_sk = a.ChangeDate
order by UserID, Month_sk, media
答案 0 :(得分:2)
(将更新说明移至结束)
我认为最简单的解决方案(概念上)是连接所有月份到month_sk
,然后过滤以仅获得最后一场比赛。这种“感觉”可能效率低下,因此您需要使用真实的数据量进行测试,如果出现问题,请寻找更好的方法。 (但“更好的东西”可能涉及改变物理数据模型......)
所以:
select userid, media, nco, month_sk, currentteam
from (SELECT b.UserID
, b.Media
, b.NCO
, Month_sk
, CurrentTeam
, rank() over(partition by b.userID
order by a.changeDate desc) n
FROM ##DataTable b
INNER JOIN ##TeamTable a
on b.UserID = a.UserID
and b.Month_sk >= a.ChangeDate
) x
where n = 1
order by UserID, Month_sk, media
请注意,在以前的版本中,我使用的是row_number() over()
而不是rank() over()
...您可以这样做,但如果您这样做,则必须在分区键中包含{{{ 1}}表在连接期间可能导致b
表中的行重复。使用a
可确保所有此类重复项共享其应有的排名。
更新 - 在我最初写这篇文章之后,我删除了它,因为我以为我误读了你的问题;但是当我写一个替代品时,我可能已经把它放在第一位了。所以这里有一个警告:
这假设您获得NULL值的唯一原因是外连接。如果“右手”表有一行并且其中只有一列的值为NULL,那么获取该列的先前值将需要进一步使用子查询或分析函数。但即使这样,领先/滞后也可能不起作用,因为它们是基于位置的。 (我认为使用LAST_VALUE的东西可能更合适,但除非需要,否则会留下详细信息。)
UPDATE 2 - 根据您对以下评论中的数据模型的描述,我正在更改查询以显示内部联接,因为它听起来会起作用(一旦您扩大了连接条件)并且应该更有效率。
更新3 - 我误读了您的示例数据并获得了用于计算rank
错误的分区表达式。假设n
表中的值是唯一的,则应该修复。如果不是它仍然可以修复但需要更多的诡计......
答案 1 :(得分:1)
您可以使用APPLY和这样的子查询执行此操作。
SELECT
userid,
media,
nco,
month_sk,
currentteam
FROM
##DataTable td
OUTER APPLY (
SELECT TOP (1)
CurrentTeam,
ChangeDate
FROM
##TeamTable tt
WHERE
tt.UserID = td.UserID
and tt.ChangeDate <= td.Month_sk
ORDER BY
tt.ChangeDate desc
) dataTableWithTeam
ORDER BY
td.UserID,
td.Month_sk,
td.media
答案 2 :(得分:0)
在这个版本中,我首先在CTE中确定适当的“链接”月份,然后将其用作最终连接中的查找。 (一旦我意识到Media
和NCO
在联接中没有真正的作用,它会变得容易得多。)
WITH cteDateLookup
as (
-- Get the ChangeDate for this User/Month
SELECT
b.UserID
,b.Month_sk
,max(a.ChangeDate) ChangeDate
from ##DataTable b
left outer join ##TeamTable a
on b.UserID = a.UserID
and b.Month_sk >= a.ChangeDate
group by
b.UserID
,b.Month_sk
)
-- Use the cte as a "lookup" for the appropriate date
SELECT
b.UserID
,b.Media
,b.NCO
,b.Month_sk
,a.CurrentTeam
from ##DataTable b
left outer join cteDateLookup cte
on cte.UserId = b.UserId
and b.Month_sk = cte.Month_sk
left outer join ##TeamTable a
on a.UserId = cte.UserId
and a.ChangeDate = cte.ChangeDate
order by
b.UserID
,b.Month_sk
,b.media