2014-09-30 2 views
2

Это данные базы данных SQL:счета SQL дней подряд

UserTable 

UserName | UserDate  | UserCode 
------------------------------------------- 
user1  | 08-31-2014 | 232 
user1  | 09-01-2014 | 232 
user1  | 09-02-2014 | 0 
user1  | 09-03-2014 | 121 
user1  | 09-08-2014 | 122 
user1  | 09-09-2014 | 0 
user1  | 09-10-2014 | 144 
user1  | 09-11-2014 | 166 
user2  | 09-01-2014 | 177 
user2  | 09-04-2014 | 188 
user2  | 09-05-2014 | 199 
user2  | 09-06-2014 | 0 
user2  | 09-07-2014 | 155 

Если рассчитывать только последовательные дни (как результат), если [UserCode] есть нечто иное, чем ноль. UserDate находится между 09-01-2014 и 09-11-2014. Показать результат, только если результат равен 2 или более.

То, что я хочу, чтобы мой SQL запрос на возвращение является:

UserName | StartDate  | EndDate  | Result 
---------------------------------------------------------- 
user1  | 09-01-2014 | 09-03-2014 | 2 
user1  | 09-08-2014 | 09-11-2014 | 3 
user2  | 09-04-2014 | 09-07-2014 | 3 

ли это возможно использование только SQL запрос?

+1

Какую версию MSSQL вы используете? – Arion

ответ

10

Это проблема Gaps and Islands. Самый простой способ решить это с помощью ROW_NUMBER() с целью выявления пробелов в последовательности:

SELECT UserName, 
     UserDate, 
     UserCode, 
     GroupingSet = DATEADD(DAY, 
          -ROW_NUMBER() OVER(PARTITION BY UserName 
                 ORDER BY UserDate), 
          UserDate) 
FROM UserTable; 

Это дает:

UserName | UserDate  | UserCode | GroupingSet 
------------+---------------+------------+------------- 
user1  | 09-01-2014 | 1   | 08-31-2014  
user1  | 09-02-2014 | 0   | 08-31-2014  
user1  | 09-03-2014 | 1   | 08-31-2014  
user1  | 09-08-2014 | 1   | 09-04-2014  
user1  | 09-09-2014 | 0   | 09-04-2014  
user1  | 09-10-2014 | 1   | 09-04-2014  
user1  | 09-11-2014 | 1   | 09-04-2014  
user2  | 09-01-2014 | 1   | 08-31-2014  
user2  | 09-04-2014 | 1   | 09-02-2014  
user2  | 09-05-2014 | 1   | 09-02-2014  
user2  | 09-06-2014 | 0   | 09-02-2014  
user2  | 09-07-2014 | 1   | 09-02-2014  

Как вы можете видеть, что это дает постоянное значение в GroupingSet для последовательных строк. Вы можете группировать по этой Colum, чтобы получить сводку вы хотите:

WITH CTE AS 
( SELECT UserName, 
      UserDate, 
      UserCode, 
      GroupingSet = DATEADD(DAY, 
           -ROW_NUMBER() OVER(PARTITION BY UserName 
                  ORDER BY UserDate), 
           UserDate) 
    FROM UserTable 
) 
SELECT UserName, 
     StartDate = MIN(UserDate), 
     EndDate = MAX(UserDate), 
     Result = COUNT(NULLIF(UserCode, 0)) 
FROM CTE 
GROUP BY UserName, GroupingSet 
HAVING COUNT(NULLIF(UserCode, 0)) > 1 
ORDER BY UserName, StartDate; 

Example on SQL Fiddle

+0

Спасибо за это, но я отредактировал мой вопрос. Виноват! Я не мог использовать сумму, потому что UserCode не всегда равен 1 или 0. – majukivi

+0

OK, я только что изменил его с 'SUM' на' COUNT (NULLIF (UserCode, 0)) ', поэтому, когда' UserCode' '0', , функция 'NULLIF' превратит ее в' NULL', и она не будет включена в 'COUNT' – GarethD

+0

. Спасибо. Отличная работа!!! – majukivi

0

Пожалуйста, попробуйте:

;with T1 as(
    select 
     *, 
     ROW_NUMBER() over ( order by UserName, UserDate) ID 
    from tbl 
) 
,T as (
    SELECT *, 1 CNT FROM T1 where ID=1 
    union all 
    SELECT b.*, (case when T.UserDate+1=b.UserDate and 
          T.UserName=b.UserName then t.CNT 
         else T.CNT+1 end) 
    from T1 b INNER JOIN T on b.ID=T.ID+1 
) 
select distinct UserName, MIN(UserDate), max(UserDate) 
,sum(case UserCode when 0 then 0 else 1 end) From T group by UserName, CNT 
having COUNT(*)>1 

SQL Fiddle Demo

+0

Спасибо за это, но я отредактировал мой вопрос. Виноват! Я не мог использовать сумму, потому что UserCode не всегда равен 1 или 0. – majukivi

Смежные вопросы