根本问题:我有一个已经运行了几个月的应用程序。用户一直在报告它随着时间的推移一直在减速(因此在五月它比现在更快)。我需要得到一些证据来支持或驳斥这一主张。我对精确数字不感兴趣(所以我不需要知道登录花了10秒钟),我对趋势很感兴趣 - 过去需要x秒的东西现在需要大约几秒钟。< / p>
我拥有的数据是一个审计表,每次用户执行任何活动时都会存储一行 - 它包括主键,用户ID,日期时间戳和活动代码:
create table AuditData (
AuditRecordID int identity(1,1) not null,
DateTimeStamp datetime not null,
DateOnly datetime null,
UserID nvarchar(10) not null,
ActivityCode int not null)
(注意:DateOnly(datetime)是DateTimeStamp,其时间被剥离以使分组更容易进行日常分析 - 它实际上是重复数据以使查询更快)。
同样为了方便起见,您可以假设ID按照日期时间顺序分配,即1始终在2之前,始终在3之前 - 如果不是这样我可以这样做)。
ActivityCode是一个标识发生的活动的整数,例如1可能是用户登录,2可能是用户数据返回,3可能是返回的搜索结果等等。
那些喜欢那种东西的人的样本数据......:
1, 01/01/2009 12:39, 01/01/2009, P123, 1
2, 01/01/2009 12:40, 01/01/2009, P123, 2
3, 01/01/2009 12:47, 01/01/2009, P123, 3
4, 01/01/2009 13:01, 01/01/2009, P123, 3
登录后立即返回用户数据(活动代码2)(活动代码1),因此这可以用作登录所用时间的粗略基准(正如我所说,我对趋势感兴趣所以只要我在May测量同样的东西和7月一样,如果这不是整个登录过程并不重要 - 它需要足够的时间来提供一个粗略的想法。
(注意:用户数据也可以在其他情况下返回,因此不是一对一的映射。)
所以我要做的是选择登录之间的平均时间(比如ActivityID 1)和当天该用户的第一个实例返回的用户数据(比如ActivityID) 2)。
我可以通过使用游标遍历表,获取每个登录实例然后执行select操作来获取当天为该用户返回的最小用户数据,但这显然不是最佳的,并且是地狱慢了。
我的问题是(最后) - 是否有一种“正确的”SQL方法,使用自连接或类似方法,而不使用游标或类似的程序方法?我可以创建视图和任何内容,它不一定是一个选择。
我可以一起破解某些东西,但我想进行分析,我正在做一个标准的产品功能,所以希望它是正确的。
答案 0 :(得分:1)
SELECT TheDay, AVG(TimeTaken) AvgTimeTaken
FROM (
SELECT
CONVERT(DATE, logins.DateTimeStamp) TheDay
, DATEDIFF(SS, logins.DateTimeStamp,
(SELECT TOP 1 DateTimeStamp
FROM AuditData userinfo
WHERE UserID=logins.UserID
and userinfo.ActivityCode=2
and userinfo.DateTimeStamp > logins.DateTimeStamp )
)TimeTaken
FROM AuditData logins
WHERE
logins.ActivityCode = 1
) LogInTimes
GROUP BY TheDay
但在现实世界中,这可能会慢得多。
答案 1 :(得分:1)
在Oracle中,由于分析功能,这将是一个很好的结果。在这种情况下,LAG()可以轻松找到匹配的活动代码对1和2,也可以计算趋势。正如你所看到的那样,事情在1月2日变得更糟,并在3日有所改善(我在几秒钟而不是几分钟内工作)。
SQL> select DateOnly
2 , elapsed_time
3 , elapsed_time - lag (elapsed_time) over (order by DateOnly) as trend
4 from
5 (
6 select DateOnly
7 , avg(databack_time - prior_login_time) as elapsed_time
8 from
9 ( select DateOnly
10 , databack_time
11 , ActivityCode
12 , lag(login_time) over (order by DateOnly,UserID, AuditRecordID, ActivityCode) as prior_login_time
13 from
14 (
15 select a1.AuditRecordID
16 , a1.DateOnly
17 , a1.UserID
18 , a1.ActivityCode
19 , to_number(to_char(a1.DateTimeStamp, 'SSSSS')) as login_time
20 , 0 as databack_time
21 from AuditData a1
22 where a1.ActivityCode = 1
23 union all
24 select a2.AuditRecordID
25 , a2.DateOnly
26 , a2.UserID
27 , a2.ActivityCode
28 , 0 as login_time
29 , to_number(to_char(a2.DateTimeStamp, 'SSSSS')) as databack_time
30 from AuditData a2
31 where a2.ActivityCode = 2
32 )
33 )
34 where ActivityCode = 2
35 group by DateOnly
36 )
37 /
DATEONLY ELAPSED_TIME TREND
--------- ------------ ----------
01-JAN-09 120
02-JAN-09 600 480
03-JAN-09 150 -450
SQL>
就像我在评论中所说,我猜你在MSSQL工作。我不知道该产品是否具有任何等效的LAG()。
答案 2 :(得分:1)
如果假设是:
那么为什么不创建一个包含两个时间戳的表,第一列包含活动开始时间,第二列包含下一个活动开始时间。因此,这两者之间的差异将始终是第一次活动的总时间。因此,对于注销活动,第二列只有NULL。
因此,对于每个活动(除了登录和退出),它会有点奇怪和有趣,时间戳将记录在两个不同的行中 - 一次用于最后一个活动(作为时间“完成”) )并再次在新的一行(随着时间的推移)。你最终会得到一个雅各布的阶梯,但找到你想要的数据会更加简单。
事实上,要真正古怪,你可以让每一行都有用户启动活动A和活动代码的时间,时间开始活动B和时间戳(如上所述,它被放下再次为下一行)。这样,每一行都会告诉您任何两个活动的确切时间差异。
否则,您会遇到类似
的查询SELECT TIME_IN_SEC(row2-timestamp) - TIME_IN_SEC(row1-timestamp)
这将是非常慢的,正如你已经建议的那样。通过吞下冗余,您最终只是查询两列之间的差异。您可能不太需要知道用户信息,因为您知道任何行都显示两个活动代码,因此您可以查询任何给定日期所有用户的平均值并将其与第二天进行比较(除非你试图找出哪些用户也遇到了这个问题。)
答案 3 :(得分:1)
这是查找速度越快的查询,在一行中您将在datetime值之前有当前行和行,之后您可以使用DATEDIFF(datepart,startdate,enddate)。我使用@DammyVariable和DamyField,因为我记得如果不是第一个@ variable =更新语句中的字段就会出现问题。
SELECT *, Cast(NULL AS DateTime) LastRowDateTime, Cast(NULL As INT) DamyField INTO #T FROM AuditData
GO
CREATE CLUSTERED INDEX IX_T ON #T (AuditRecordID)
GO
DECLARE @LastRowDateTime DateTime
DECLARE @DammyVariable INT
SET @LastRowDateTime = NULL
SET @DammyVariable = 1
UPDATE #T SET
@DammyVariable = DammyField = @DammyVariable
, LastRowDateTime = @LastRowDateTime
, @LastRowDateTime = DateTimeStamp
option (maxdop 1)