#sql #sql-server #partition #rolling-computation
#sql #sql-сервер #раздел #скользящее вычисление
Вопрос:
Я использую SQL Server 2012 и хочу получить общее количество неудачных попыток входа в систему. В моей таблице есть
| Login_Name | Is_Success | Login_Date |
Мой запрос
select login_name,
sum(case when is_success = 1 THEN 0 ELSE 1 END) over (partition by login_name, issuccess order by login_name, login_date, is_success)
from login_table
Запрос дает мне результаты, но я хочу, чтобы счетчик сбрасывался, когда is_success становится 1
| Login_Name | Is_Success | Unsuccessful Attempts |
| admin | 1 | 0 |
| admin | 0 | 1 |
| admin | 0 | 2 |
| admin | 0 | 3 |
| admin | 0 | 4 |
| admin | 0 | 5 |
| admin | 1 | 0 |
| admin | 0 | 1 |
Примечание: я использую SQL Server 2012
Ответ №1:
Это классическая проблема с пробелами и островами. Вы можете использовать windowed COUNT
для размещения данных в группы, а затем использовать ROW_NUMBER
для этих групп:
WITH YourTable AS(
SELECT *
FROM (VALUES('admin',1,CONVERT(datetime2(0),'2020-10-30T20:00:00')),
('admin',0,CONVERT(datetime2(0),'2020-10-30T20:01:00')),
('admin',0,CONVERT(datetime2(0),'2020-10-30T20:02:00')),
('admin',0,CONVERT(datetime2(0),'2020-10-30T20:03:00')),
('admin',0,CONVERT(datetime2(0),'2020-10-30T20:04:00')),
('admin',0,CONVERT(datetime2(0),'2020-10-30T20:05:00')),
('admin',1,CONVERT(datetime2(0),'2020-10-30T20:06:00')),
('admin',0,CONVERT(datetime2(0),'2020-10-30T20:07:00')))V(Login_Name,Is_Success,Login_Date)),
Grps AS(
SELECT Login_Name,
Is_Success,
Login_Date,
COUNT(CASE Is_Success WHEN 1 THEN 1 END) OVER (PARTITION BY Login_Name ORDER BY Login_Date) AS Grp
FROM YourTable)
SELECT Login_NAme,
Is_Success,
ROW_NUMBER() OVER (PARTITION BY Login_NAme, Grp ORDER BY Login_Date) -1 AS UnSuccessfulAttempts
FROM Grps
ORDER BY Login_Name,
Grp,
Login_Date;
Комментарии:
1. Это выглядит как правильный подход, и я поддержал его — однако будет
COUNT(Is_Success)
ли работать, если неудачные попытки имеют значение (например, 0 или -1)? Вы могли бы использовать COUNT (CASE …), аналогичный их. Кроме того, в вашем заказе by есть избыточное поле — может быть, вы имели в виду дату?2. Вместо этого вы бы использовали
CASE
выражение @seanb . Я просто предположил, что они, вероятно, были просто1
or0
, и это то, что говорят ваши данные образца.3. Я попытался запустить этот запрос, но он не возвращает мне результат. Столбец безуспешных попыток всегда увеличивается на 1
4. Примеры данных помогут мне помочь вам, @Ankit. Я только ожидал результатов, поэтому должен был сделать предположения о ваших данных.
5. Я обновил предположения о ваших данных, @Ankit . Как я уже упоминал, нет образцов данных, только ожидаемые результаты.
Login_date
Например, у вас есть столбец, о котором я понятия не имею, что на самом деле хранится.
Ответ №2:
Я полагаю, вы ищете сводку, а не журнал специфики? Я бы подошел к этому так:
SELECT login_name,
( SELECT count(*) FROM login_table
WHERE login_name = lt.login_name
AND login_date > ( SELECT MAX(login_date) FROM login_table WHERE login_name = lt.login_name AND is_success = 1 )
) AS failures
FROM login_table AS lt
ORDER BY 1
Как это работает:
Внутренний коррелированный подзапрос получает последний успешный вход для пользователя:
SELECT MAX(login_date) FROM login_table WHERE login_name = lt.login_name AND is_success = 1
Внешний коррелированный запрос:
(ВЫБЕРИТЕ count(*) ИЗ login_table, ГДЕ login_name = lt.login_name И login_date > —последний успешный вход в систему — ) КАК сбои
подсчитывает, сколько сбоев произошло после последнего успеха. Я не тестировал это. Удачи с вашим проектом!