2011-01-04 4 views
1

мне интересно, если его можно реализовать SQL-запрос, который будет действовать как своего род алгоритм для вычисления определенных фигур для меня, основываясь на следующую таблице:комплексы/алгоритмической SQL Query

Это первоначальный запрос,

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 

Как вы можете видеть, есть повторяющиеся подключения и отключения в этом наборе данных. Мне нужно рассчитать длину сеанса, взяв FIRST login и LAST logout, когда есть дубликаты. Таким образом, первая сессия, приведенные выше данные будут из,

5-28 15:52:50.590 to 5-28 16:22:57.983 

Алгоритм примерно,

1) Заказать список логинов/выхода из системы по имени пользователя, а затем по времени действия

2) Если запись является логином, выполните поиск следующего выхода из системы, за которым следует сразу логин (для подтверждения его последнего выхода из всех дубликатов)

3) Используйте первый вход и последний выход для создания нового сеанса (длина время выхода из системы - время входа в систему)

4) Повторите

В настоящее время я просто реализую это в коде, но был интересен, если его можно даже в SQL (я не слишком хорошо знаком с SQL).

ответ

2

Несомненно ... попробуйте что-нибудь подобное.

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 
+0

Спасибо, это довольно близко. Единственная проблема заключается в том, что часть min (TimeOfAction) из EventLog, где TimeOfAction> e1.TimeOfAction и Activity = 'Logout' возвращает первый следующий выход из системы. Если есть дубликаты журналов, мне нужен последний дубликат, но, очевидно, изменение min до max просто приведет к самому большому выходу из системы во всем наборе данных. – sean

+0

Можно ли инкапсулировать в предложение where логику «следующего выхода из системы, за которым следует логин», а не только «следующий выход из системы, который больше, чем текущая запись входа»? Потому что я думаю, что это должно быть и то, и другое. Поскольку, если есть повторяющиеся выходы (а не только дублирование логинов), мне нужно захватить LAST-выход этих дубликатов, а не только следующий в наборе.IE, если у нас есть: Выход Выход (дублированные выходы) – sean

+0

Истина. Изменение этого параметра в max() не будет работать в этом случае. Запрос просто соответствует каждой записи входа в запись Logout. Если я правильно понимаю вас, вы хотите, чтобы последняя запись Logout была непосредственно перед другой регистрационной записью, поскольку может произойти несколько выходов из системы? Это не имеет для меня никакого смысла, как пользователь может выйти из системы более одного раза без входа в систему снова? –

1
select min(TimeOfAction) Login, null Logout, Requestor 
    from EventLog 
where Activity = 'Login' 
group by Requestor 
union 
select null Login, max(TimeOfAction) Logout, Requestor 
    from EventLog 
where Activity = 'Logout' 
group by Requestor 
+0

Я пробовал что-то подобное и понял, что он вернет только одну строку для каждого запрашивающего. Я думаю, что отчет должен содержать каждую запись входа в систему для каждого запрашивающего. –

0

Heres вариант для вас с помощью некоторых КТР и row_numbers. В основном, он заказывает события для каждого пользователя, затем находит список логинов, которые либо следуют за выходом из системы, либо ничего, затем находит список логарифмов, которые запускают логины или ничего, а затем связывает их с парами.

;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 

Примечание: Производительность запросов может быть (сильно?) Увеличился на отщепления некоторые или все из запросов КТР в временных таблиц. то есть примерно следующее:

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 
+0

Это отлично работает, на удивление быстро. Не совсем уверен, что такое CTE/события, но я это рассмотрю. Благодарю. – sean

+0

Нет проблем. CTE (Common Table Expressions) по существу является синтаксисом 'with' для разделения подзапросов и только набирает их один раз ... по крайней мере, это все, что он действительно делает здесь, но он может делать и другие вещи (например, рекурсивные запросы). – chezy525

+0

Я пытаюсь преобразовать это, чтобы использовать временные таблицы, как вы сказали. Я бы использовал в основном синтаксис SELECT INTO #temptable для каждой таблицы вместо ключевого слова WITH, где все остальное во многом схоже? Я получаю сообщение об ошибке «Недопустимое имя объекта» при попытке сделать FROM из таблицы temp, которую я только что создал. – sean