我有以下表结构如下:(请忽略blackbox。在Excel中格式化问题)
alt text http://img525.imageshack.us/img525/5788/beforeznj.jpg
我需要做的是使用TSQL将SessionGUID数据转换为以下内容: alt text http://img4.imageshack.us/img4/4553/afterjer.jpg
这是我网站的用户会话表,我们搞砸了SessionGUID,因为它在每个页面加载时生成一个NEWID()。所以现在我们需要通过计时将会话分组(假设如果用户在最后一次页面加载后30分钟没有触发页面加载会话中断),则使用创建的第一个SessionGUID。
为了进一步解释这个简短的例子,用户(00000000-0000-0000-0000-000000000000)在09/01/2009访问了我的网站两次。一个在13:37,另一个在14:46。用户(A107EF1E-00A2-4515-A120-984086BC8368)。请注意,实际表包含需要更新的数百万行。 :(
更新 每个会话的登录页面/起始URL并不总是“主页”,它可以是网站上的任何页面。情节变浓了..
UPDATE2 测试数据
CREATE TABLE Sessions (IPAddress VARCHAR(15), UserGUID UNIQUEIDENTIFIER, DATE DATETIME, URL VARCHAR(200), SessionGUID UNIQUEIDENTIFIER)
INSERT INTO Sessions (
IPAddress,
UserGUID,
DATE,
URL,
SessionGUID
)
VALUES ( '192.168.0.1', '00000000-0000-0000-0000-000000000000', '09/01/2009 13:37:34', 'homepage', '2B3A80B1-A247-4BB5-81BB-B54DED0C9C6A')
VALUES ( '192.168.0.1', '00000000-0000-0000-0000-000000000000', '09/01/2009 13:37:36', 'page1', '7FB10E12-5EB9-428C-BE3E-57818DEF8512')
VALUES ( '192.168.0.1', '00000000-0000-0000-0000-000000000000', '09/01/2009 13:37:41', 'page2', 'D12C3539-1239-447E-8BD8-DBA6B7087ADE')
VALUES ( '192.168.0.1', '00000000-0000-0000-0000-000000000000', '09/01/2009 14:56:00', 'homepage', '4FE36C46-640B-464F-8118-AFFE477347A1')
VALUES ( '192.168.0.1', '00000000-0000-0000-0000-000000000000', '09/01/2009 14:56:10', 'page2', 'FF9BF55B-3630-4D05-AB57-1B6ECAB96657')
VALUES ( '192.168.0.1', '00000000-0000-0000-0000-000000000000', '09/01/2009 14:56:18', 'page4', '863D3424-9788-481A-8440-09313ED4F8FE')
VALUES ( '192.168.0.1', '00000000-0000-0000-0000-000000000000', '09/01/2009 14:56:19', 'page3', '105D7FE5-C731-4EB6-B287-720127AAF0A3')
VALUES ( '192.168.0.1', '00000000-0000-0000-0000-000000000000', '09/01/2009 15:00:35', 'page5', '296479D0-3848-4189-94E2-41906BAE580D')
VALUES ( '192.168.0.1', '00000000-0000-0000-0000-000000000000', '09/01/2009 15:00:36', 'page7', 'E3FFEBC6-C11E-4DF4-81FA-B42F1BF7AFD3')
VALUES ( '212.1.1.0', 'A107EF1E-00A2-4515-A120-984086BC8368', '12/01/2009 18:30:22', 'homepage', '1F918AB3-34E1-4343-8462-FA56423B921D')
VALUES ( '212.1.1.0', 'A107EF1E-00A2-4515-A120-984086BC8368', '12/01/2009 18:34:26', 'page1', '801C3DC8-F0F3-4B9C-BD53-BCCBE784CFAE')
VALUES ( '212.1.1.0', 'A107EF1E-00A2-4515-A120-984086BC8368', '12/01/2009 18:38:17', 'page2', 'A9A5C2BD-31B9-4A9B-A8BC-88C460F17282')
VALUES ( '212.1.1.0', 'A107EF1E-00A2-4515-A120-984086BC8368', '15/01/2009 11:42:27', 'page3', 'B29CE754-C7A3-40E8-8CB0-216A3E852762')
VALUES ( '212.1.1.0', 'A107EF1E-00A2-4515-A120-984086BC8368', '15/01/2009 11:42:32', 'page4', 'E291C4B9-A422-4A76-A550-F65C208DD886')
VALUES ( '212.1.1.0', 'A107EF1E-00A2-4515-A120-984086BC8368', '15/01/2009 11:44:51', 'page6', '63D4A636-8336-44E7-8C97-9CD65D21359E')
VALUES ( '212.1.1.0', 'A107EF1E-00A2-4515-A120-984086BC8368', '15/01/2009 11:44:55', 'page2', '7BB814CD-C9B3-4CAF-A45C-4405DC0B07D2')
VALUES ( '212.1.1.0', 'A107EF1E-00A2-4515-A120-984086BC8368', '15/01/2009 11:48:35', 'page4', 'B6DCEC1E-C262-425D-8E46-8F4B47F2921A')
感谢。
不满的DBA新秀。
答案 0 :(得分:1)
根据您评论中的其他数据,我必须得出结论,表格中没有足够的信息可靠地进行此更新。您可以进行更多分析并找到可以分组的可接受的时间跨度值,并使用Charles Bretana的方法进行一次性修复,但是您将自己进行分析并且您将无法进行分析指望那个数字或任何数字继续长期工作。
答案 1 :(得分:1)
编辑我有很多语法错误,因为我没有测试。 (注意自己,不要在没有测试代码的情况下提交答案。)最重要的是,一旦我修改了语法,并且反对测试数据,我发现我的答案是错误的。在30分钟之前测试存在一行的子查询完全是假的。
更正版本:
; with EliminateTies (UserGuid, SessionGuid, "Date") as
(select UserGuid
, cast(min(cast(SessionGuid as varbinary)) as uniqueidentifier)
, "Date"
from Sessions
group by UserGuid, "Date")
, SessionBoundaries (UserGuid, SessionGuid, StartDateTime, SessionNumber) as
(select UserGuid, SessionGuid, "Date"
, row_number() over
(partition by UserGuid
order by "Date") as SessionNumber
from (select UserGuid, SessionGuid, "Date" from EliminateTies
union all
-- Add a set of records at the end of time, to bound the last
-- of each users sessions
select distinct UserGuid
, null as SessionGuid
, cast('9999-12-31' as datetime) as "Date"
from Sessions) ET_Out
where not exists (select *
from EliminateTies ET_In
where ET_Out.UserGuid = ET_In.UserGuid
and ET_Out.SessionGuid <> ET_In.SessionGuid
and (dateadd(minute, -30, ET_Out."Date") < ET_In."Date"
and ET_In."Date" <= ET_Out."Date")))
Update MT
set SessionGuid = LowBound.SessionGuid
from SessionBoundaries LowBound
inner join SessionBoundaries HighBound
on LowBound.UserGuid = HighBound.UserGuid
and LowBound.SessionNumber = HighBound.SessionNumber - 1
inner join Sessions S
on S.UserGuid = LowBound.UserGuid
and LowBound.StartDateTime <= S."Date"
and S."Date" < HighBound.StartDateTime
编辑2添加说明
; with EliminateTies ...
定义名为EliminateTies的关系。这是为了处理三元组(UserGuid, SessionGuid, "Date")
可能包含给定(UserGuid, "Date")
的重复项的可能性。 SQL Server日期时间的分辨率为1/300秒,因此重复不太可能,但并非不可能。
(UserGuid, "Date")
对group by UserGuid, "Date"
包含一行。 SessionGuid
从该对可用的SessionGuid
组中任意挑选min()
。正如评论中所指出的那样,MIN(GUID)
是不允许的,因此转换为VARBINARY
,找到MIN()
,然后转换回UNIQUEIDENTIFIER
。 with ...
之前的语句的情况下,它不会尝试重新训练自己以结束所有语句用分号语句,我训练自己用分号开始所有with ...
。分号实际上终止了之前的任何陈述。 , SessionBoundaries
定义了另一种关系。此关系具有新会话开始的所有点,基于在最近30分钟内Sessions中没有条目时给定UserGuid会话开始的规则。
from (select ... union all ...) ET_Out
为每个UserGuid添加一个额外的行,其日期位于“结束时间”。这是因为我们想要在高边界和低边界之间对行进行分组,并且我们需要每个用户的最后一个会话的上边界。 where not exists
过滤掉不在会话边界开头的行。 row_number() ...
中,将按行排序,按日期排序,以便每个UserGuid
都有SessionNumber
s 1..N + 1,其中N是会话数。我们稍后使用SessionNumbers来查找相邻边界。 (可能更好地称为SessionBoundaryNumber
。)from
子句以允许将关系连接在一起。更新指定了我们希望从from子句更新的表的表别名。 (注意这是T-SQL,而不是在标准视图中一起使用多个关系的ANSI标准方法。)
SessionBoundary
上的相等性和基于UserGuid
的邻居边界自我加入SessionNumber
。如果用户A有2个会话,则会有3个SessionNumber
s 1,2&amp;这个连接将导致(低,高)对(1,2)和&amp; (2,3)。现在,对于每个UserGuid
,我们都有每个会话的下边界和上边界。最后一次会话的上限是“时间结束”。 Sessions
连接条件是UserGuid
相等,Sessions
中行的日期在由{{1}}定义的范围内[LowBound,HighBound)。请记住,边界是会话开始的地方。因此,测试是LowBound&lt; = Sessions。“Date”&lt; HighBound。答案 2 :(得分:0)
这看起来不错吗?
UPDATE T Set SessionGuid =
(Select Min(UserGuid) FROM [mytable]
Where IPAddress=T.IPAddress
And Date = (Select Min(Date) From MyTable
Where IPAddress = T.IPAddress
And DateDiff(minute, Date, T.Date)
Between 0 and 30))
FROM [mytable] T