C# 复杂/算法SQL查询
我想知道是否有可能实现一个SQL查询,它将像一种算法一样,根据下表为我计算某个数字: 这是初始查询C# 复杂/算法SQL查询,c#,sql-server,sql,C#,Sql Server,Sql,我想知道是否有可能实现一个SQL查询,它将像一种算法一样,根据下表为我计算某个数字: 这是初始查询 SELECT Activity, TimeOfAction, Requestor FROM EventLog WHERE Requestor = 0 ORDER BY Requestor, TimeOfAction; 以及返回的数据样本 Login 2010-05-28 15:52:50.590 0 Login 2010-05-28 15:52:50.873 0 Logout 20
SELECT Activity, TimeOfAction, Requestor
FROM EventLog
WHERE Requestor = 0
ORDER BY Requestor, TimeOfAction;
以及返回的数据样本
Login 2010-05-28 15:52:50.590 0
Login 2010-05-28 15:52:50.873 0
Logout 2010-05-28 15:52:50.890 0
Logout 2010-05-28 16:22:57.983 0
Login 2010-05-29 11:29:36.967 0
Logout 2010-05-29 11:29:37.640 0
如您所见,此数据集中存在重复的登录和注销。我需要通过第一次登录和最后一次注销来计算会话的长度,如果存在重复的会话。因此,给出上述数据的第一次会议将来自
5-28 15:52:50.590 to 5-28 16:22:57.983
算法大致是
1按用户名,然后按操作时间,订购登录/注销列表
2如果条目是登录,则搜索下一次注销,然后立即登录,以确认它是所有重复项的最后一次注销
3使用第一次登录和最后一次注销创建新会话长度为注销时间-登录时间
4重复
目前我只是在代码中实现这一点,但我不太熟悉SQL,我想知道它是否可能在SQL中实现 当然。。。试试这样的
select e1.Requestor,
e1.TimeOfAction as LoginTime,
(select min(ActivityTime)
from EventLog where TimeOfAction > e1.TimeOfAction
and Activity = 'Logout') as LogoutTime
from EventLog e1
where e1.ActivityType = 'Login'
order by Requestor, LoginTime
第二个解决方案。。。看看这对你是否更有效
select requestor,
(select min(activitytime)
from eventlog
where activitytime < e.activitytime
and activity = 'Login' and e.activity = 'Logout') as LoginTime,
(select max(activitytime)
from eventlog
where activitytime > e.activitytime
and activity = 'Logout' and e.activity = 'Login') as LogoutTime,
from eventlog e
order by requestor, logintime
这里有一个选项供您使用一些CTE和行号。基本上,它为每个用户排序事件,然后查找在注销或不注销之后的登录列表,然后查找继续登录或不登录的注销列表,然后将它们成对关联
;with events as (
select *,
row_number() over(partition by Requestor order by TimeOfAction) row
from EventLog
), logins as (
select e1.Activity, e1.TimeOfAction, e1.Requestor,
row_number() over(partition by e1.Requestor order by e1.TimeOfAction) row
from events e1
left join events e2 on e1.Requestor=e2.Requestor
and e1.row=e2.row+1
where e1.Activity='Login'
and e1.Activity!=isnull(e2.Activity, 'Logout')
), logouts as (
select e1.Activity, e1.TimeOfAction, e1.Requestor,
row_number() over(partition by e1.Requestor order by e1.TimeOfAction) row
from events e1
left join events e2 on e1.Requestor=e2.Requestor
and e1.row=e2.row-1
where e1.Activity='Logout'
and e1.Activity!=isnull(e2.Activity, 'Login')
)
select i.Requestor, i.TimeOfAction as loginTime, o.TimeOfAction as logoutTime
from logins i
left join logouts o on i.Requestor=o.Requestor
and i.row=o.row
注意:查询性能可能会急剧下降?通过将部分或全部CTE查询拆分为临时表而增加。i、 e.类似于以下内容:
select *,
row_number() over(partition by Requestor order by TimeOfAction) row
into #events
from EventLog
select e1.Activity, e1.TimeOfAction, e1.Requestor,
row_number() over(partition by e1.Requestor order by e1.TimeOfAction) row
into #logins
from #events e1
left join #events e2 on e1.Requestor=e2.Requestor
and e1.row=e2.row+1
where e1.Activity='Login'
and e1.Activity!=isnull(e2.Activity, 'Logout')
select e1.Activity, e1.TimeOfAction, e1.Requestor,
row_number() over(partition by e1.Requestor order by e1.TimeOfAction) row
into #logouts
from #events e1
left join #events e2 on e1.Requestor=e2.Requestor
and e1.row=e2.row-1
where e1.Activity='Logout'
and e1.Activity!=isnull(e2.Activity, 'Login')
select i.Requestor, i.TimeOfAction as loginTime, o.TimeOfAction as logoutTime
from #logins i
left join #logouts o on i.Requestor=o.Requestor
and i.row=o.row
drop table #logouts
drop table #logins
drop table #events
我尝试了类似的方法,并意识到它只会为每个请求者返回一行。我认为报告需要包含每个请求者的每个登录注销记录。谢谢,这非常接近。唯一的问题是EventLog中的minTimeOfAction部分,其中TimeOfAction>e1.TimeOfAction和Activity='Logout'返回下一次的第一次注销。如果有重复的注销,我需要重复的最后一个,但是很明显,将min更改为max只会得到整个数据集中最大的注销。是否可以在where子句中封装下一次注销的逻辑,然后是一次登录,而不仅仅是下一次注销大于当前登录记录?因为我认为两者都必须。因为如果存在重复的注销,而不仅仅是重复的登录,我需要抓取这些重复的最后一次注销,而不仅仅是集中的下一次注销。IE如果我们有:登入登出登出重复登出。在这种情况下,将其更改为max将不起作用。查询只是将每个登录记录与注销记录进行匹配。如果我理解正确,您希望最后一次注销记录正好在另一次登录记录之前,因为可能会发生多次注销?这对我来说没有太大意义,一个用户怎么能在不再次登录的情况下多次注销?这是日志系统中的一个缺陷,正在处理中,但尚未修复,因此这是一个临时解决方案。这非常有效,速度惊人。不太清楚CTE/事件是什么,但我会调查一下。谢谢,没问题。CTE Common Table Expressions本质上是一种语法,用于分离子查询并只键入一次。。。至少这就是它在这里真正要做的,但它也可以做其他事情,比如递归查询。我正试图将其转换为使用临时表,正如你所说的。对于每个表,我基本上都会使用SELECT而不是WITH关键字,因为其他一切都很相似?我在尝试从我刚创建的临时表中执行操作时,遇到一个错误“对象名称无效”。@sean,我已编辑了我的答案,以包含一种可能的方法,即使用临时表而不是CTE。使用我放在一起的示例表,实际查询计划的百分比稍微好一些。。。但这在亚秒级的查询中并不意味着什么……在执行from-on事件时会出现一些错误,例如“无效对象名称事件”,而且“多部分标识符”在任何时候都不能绑定到e1.column。似乎表一创建就被删除了-drop table命令也显示错误b/c表不存在。我在网上读到,它说这可能是权限问题或临时表作用域的问题。是否有需要更改的设置?我正在使用SQL server management studio w/SQL Express 2008 R2。
select *,
row_number() over(partition by Requestor order by TimeOfAction) row
into #events
from EventLog
select e1.Activity, e1.TimeOfAction, e1.Requestor,
row_number() over(partition by e1.Requestor order by e1.TimeOfAction) row
into #logins
from #events e1
left join #events e2 on e1.Requestor=e2.Requestor
and e1.row=e2.row+1
where e1.Activity='Login'
and e1.Activity!=isnull(e2.Activity, 'Logout')
select e1.Activity, e1.TimeOfAction, e1.Requestor,
row_number() over(partition by e1.Requestor order by e1.TimeOfAction) row
into #logouts
from #events e1
left join #events e2 on e1.Requestor=e2.Requestor
and e1.row=e2.row-1
where e1.Activity='Logout'
and e1.Activity!=isnull(e2.Activity, 'Login')
select i.Requestor, i.TimeOfAction as loginTime, o.TimeOfAction as logoutTime
from #logins i
left join #logouts o on i.Requestor=o.Requestor
and i.row=o.row
drop table #logouts
drop table #logins
drop table #events