Получать записи только в том случае, если число дней на пользователя составляет 30 или более - PullRequest
3 голосов
/ 11 августа 2011

У меня есть следующие данные, возвращаемые из запроса. По сути, я помещаю это во временную таблицу, так что теперь она находится во временной таблице, к которой я могу запросить (очевидно, в реальной жизни гораздо больше данных, я просто показываю пример):

EmpId      Date
1        2011-01-01
1        2011-01-02
1        2011-01-03
2        2011-02-03
3        2011-03-01
4        2011-03-02
5        2011-01-02

Мне нужно вернуть только EmpId, которые имеют 30 или более дней подряд в столбце даты. Мне также нужно вернуть количество дней для этих сотрудников, у которых есть 30 или более дней подряд. Потенциально может быть 2 или более набора различных последовательных дней, которые составляют 30 или более дней. В этом случае я хотел бы вернуть несколько строк. Поэтому, если у сотрудника есть дата с 2011-01-01 по 2011-02-20, верните ее и количество в одну строку. Затем, если у этого же сотрудника есть даты с 2011-05-01 по 2011-07-01, верните это в другой строке. По существу, все перерывы в последовательных днях рассматриваются как отдельная запись.

Ответы [ 4 ]

4 голосов
/ 11 августа 2011

Использование DENSE_RANK должно помочь:

;WITH sampledata
    AS (SELECT 1 AS id, DATEADD(day, -0, GETDATE())AS somedate
        UNION ALL SELECT 1, DATEADD(day, -1, GETDATE())
        UNION ALL SELECT 1, DATEADD(day, -2, GETDATE())
        UNION ALL SELECT 1, DATEADD(day, -3, GETDATE())
        UNION ALL SELECT 1, DATEADD(day, -4, GETDATE())
        UNION ALL SELECT 1, DATEADD(day, -5, GETDATE())
        UNION ALL SELECT 1, DATEADD(day, -10, GETDATE())
        UNION ALL SELECT 1, '2011-01-01 00:00:00'
        UNION ALL SELECT 1, '2010-12-31 00:00:00'
        UNION ALL SELECT 1, '2011-02-01 00:00:00'
        UNION ALL SELECT 1, DATEADD(day, -10, GETDATE())
        UNION ALL SELECT 2, DATEADD(day, 0, GETDATE())
        UNION ALL SELECT 2, DATEADD(day, -1, GETDATE())
        UNION ALL SELECT 2, DATEADD(day, -2, GETDATE())
        UNION ALL SELECT 2, DATEADD(day, -6, GETDATE())
        UNION ALL SELECT 3, DATEADD(day, 0, GETDATE())
        UNION ALL SELECT 4, DATEADD(day, 0, GETDATE())
        UNION ALL SELECT 5, DATEADD(day, 0, GETDATE()))
   , ranking
    AS (SELECT *, DENSE_RANK()OVER(PARTITION BY id ORDER BY DATEDIFF(day, 0, somedate)) - DATEDIFF(day, 0, somedate)AS dategroup
          FROM sampledata)
    SELECT id
         , MIN(somedate)AS range_start
         , MAX(somedate)AS range_end
         , DATEDIFF(day, MIN(somedate), MAX(somedate)) + 1 AS consecutive_days
      FROM ranking
     GROUP BY id, dategroup
     --HAVING DATEDIFF(day, MIN(somedate), MAX(somedate)) + 1 >= 30 --change as needed
     ORDER BY id, range_start
1 голос
/ 11 августа 2011

Что-то вроде этого должно сработать, хотя и не проверял.

SELECT 
  a.empid
  , count(*) as consecutive_count
  , min(a.mydate) as startdate
FROM (SELECT * FROM logins ORDER BY mydate) a
INNER JOIN (SELECT * FROM logins ORDER BY mydate) b 
  ON (a.empid = b.empid AND datediff(day,a.mydate,b.mydate) = 1
GROUP BY a.empid, startdate
HAVING consecutive_count > 30
0 голосов
/ 11 августа 2011

Если для одного и того же сотрудника нет повторяющихся дат:

;WITH ranged AS (
  SELECT
    EmpId,
    Date,
    RangeId = DATEDIFF(DAY, 0, Date)
            - ROW_NUMBER() OVER (PARTITION BY EmpId ORDER BY Date)
  FROM atable
)
SELECT
  EmpId,
  StartDate = MIN(Date),
  EndDate   = MAX(Date),
  DayCount  = DATEDIFF(DAY, MIN(Date), MAX(Date)) + 1
FROM ranged
GROUP BY EmpId, RangeId
HAVING DATEDIFF(DAY, MIN(Date), MAX(Date)) + 1 >= 30
ORDER BY EmpId, MIN(Date)

DATEDIFF преобразует даты в целые числа (разница дней между датой 0 (1900-01-01) и Date).Если даты являются последовательными, целые числа также являются последовательными.Используя образец данных в вопросе в качестве примера, результаты DATEDIFF будут такими:

EmpId  Date        DATEDIFF
-----  ----------  --------
1      2011-01-01  40542
1      2011-01-02  40543
1      2011-01-03  40544
2      2011-02-03  40575
3      2011-03-01  40601
4      2011-03-02  40602
5      2011-01-02  40543

Теперь, если вы берете строки каждого сотрудника, присваиваете им номера строк в порядке дат и получаете разницумежду числовыми представлениями и номерами строк вы обнаружите, что разница остается неизменной для последовательных чисел (и, следовательно, последовательных дат).Используя немного другой образец для лучшей иллюстрации, он будет выглядеть следующим образом:

Date        DATEDIFF  RowNum  RangeId
----------  --------  ------  -------
2011-01-01  40542     1       40541
2011-01-02  40543     2       40541
2011-01-03  40544     3       40541
2011-01-05  40546     4       40542
2011-01-07  40548     5       40543
2011-01-08  40549     6       40543
2011-01-09  40550     7       40543

Конкретное значение RangeId не важно, важен только тот факт, что он остается неизменным для последовательных дат.Исходя из этого, вы можете использовать его в качестве критерия группировки для подсчета дат в группе и получения границ диапазона.

В приведенном выше запросе для подсчета дней используется DATEDIFF(DAY, MIN(Date), MAX(Date)) + 1, но вы также можете просто использоватьCOUNT(*) вместо.

0 голосов
/ 11 августа 2011

Это хороший случай для рекурсивного CTE.Я украл таблицу данных у @Davin:

with data AS --sample data 
( SELECT 1 as id ,DATEADD(DD,-0,GETDATE()) as date UNION ALL 
SELECT 1 as id ,DATEADD(DD,-1,GETDATE()) as date UNION ALL 
SELECT 1 as id ,DATEADD(DD,-2,GETDATE()) as date UNION ALL 
SELECT 1 as id ,DATEADD(DD,-3,GETDATE()) as date UNION ALL 
SELECT 1 as id ,DATEADD(DD,-4,GETDATE()) as date UNION ALL 
SELECT 1 as id ,DATEADD(DD,-5,GETDATE()) as date UNION ALL 
SELECT 1 as id ,DATEADD(DD,-10,GETDATE()) as date UNION ALL 
SELECT 1 as id ,'2011-01-01 00:00:00.000' as date UNION ALL 
SELECT 1 as id ,'2010-12-31 00:00:00.000' as date UNION ALL 
SELECT 1 as id ,'2011-02-01 00:00:00.000' as date UNION ALL 
SELECT 1 as id ,DATEADD(DD,-10,GETDATE()) as date UNION ALL 
SELECT 2 as id ,DATEADD(DD,0,GETDATE()) as date UNION ALL 
SELECT 2 as id ,DATEADD(DD,-1,GETDATE()) as date UNION ALL 
SELECT 2 as id ,DATEADD(DD,-2,GETDATE()) as date UNION ALL 
SELECT 2 as id ,DATEADD(DD,-6,GETDATE()) as date UNION ALL 
SELECT 3 as id ,DATEADD(DD,0,GETDATE()) as date UNION ALL 
SELECT 4 as id ,DATEADD(DD,0,GETDATE()) as date UNION ALL 
SELECT 5 as id ,DATEADD(DD,0,GETDATE()) as date   ) 
,CTE AS
(
    SELECT id, CAST(date as date) Date, Consec = 1
    FROM data
    UNION ALL
    SELECT t.id, CAST(t.date as DATE) Date, Consec = (c.Consec + 1)
    FROM data T
    INNER JOIN CTE c
        ON T.id = c.id
        AND CAST(t.date as date) = CAST(DATEADD(day, 1, c.date) as date)

)

SELECT id, MAX(consec)
FROM CTE
GROUP BY id
ORDER BY id

В основном это создает много строк на человека и измеряет, сколько дней подряд представляет каждая дата.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...