Найти первое вхождение последовательных столбцов начала / конца - PullRequest
0 голосов
/ 12 мая 2018

У меня есть таблица с датами работы.Новая строка добавляется каждый раз, когда происходит изменение - изменение зарплаты является наиболее распространенным.Таким образом, новая строка будет равна последней дате TO указанного человека плюс один (1).Если моя зарплата изменилась 2014-04-01, моя предыдущая строка заканчивалась бы TO датой в 2013-03-31, а моя новая строка начинала бы FROM с 2014-04-01.

Я хочу получить дата трудоустройства только, а не те, которые следуют из-за изменений.Посмотрите на эту таблицу:

SSN         FROM        TO
----------------------------------
0987654321  2011-01-01  2011-12-31
0987654321  2012-01-01  2012-12-31
1234567890  2012-01-01  2012-12-31
0987654321  2013-01-01  2013-12-31
1234567890  2013-01-01  2013-06-30
0987654321  2014-01-01  2014-08-31
1234567890  2016-01-01  2016-12-31
1234567890  2017-01-01  2017-12-31
1234567890  2018-01-01  null

Вывод, который я хочу:

SSN         FROM        TO
----------------------------------
0987654321  2011-01-01  2014-08-31
1234567890  2012-01-01  2013-06-30
1234567890  2016-01-01  null

Я подумал, что мог бы создать поле, равное на один день больше, чем TO:

SELECT 
    SSN, TO, FROM, DATEADD(DAY, 1, TO) AS NEW 
FROM 
    table

Но я понятия не имею, как мне сопоставить NEW с TO в разных строках.Возможно WHERE NOT EXISTS или что-то?Я не могу заставить это работать.

Тогда я подумал, что мог бы использовать LAG, но предыдущая строка в таблице не связана со следующей строкой по умолчанию, и я не смог использовать ORDER BY в подзапросе.Я этого не допускаю, не знаю почему (T-SQL?).

К вашему сведению, я не могу CREATE TABLE или INSERT INTO TABLE и т.д., и я также не могу объявлять переменные.Мы получим модуль, который позволяет все это, но пока у меня нет этих привилегий.

Обновление: Первый ответ был действительно правильным, но я заметил другое поле, которое мешаетс этим.Один SSN может содержать несколько идентификаторов, поэтому идентификаторы также должны быть разделены.Вот фактические данные из моей таблицы.

CREATE TABLE Samples
    (
     SSN varchar(10), 
     ID varchar(4),
     FromDate Date, 
     ToDate Date
    );

INSERT INTO Samples
(SSN, ID, FromDate, ToDate)
VALUES
( '6612140000', '1000', '2005-01-01', '2005-03-31' ),
( '6612140000', '1000', '2005-04-01', '2005-09-30' ),
( '6612140000', '1000', '2005-10-01', '2006-03-31' ), 
( '6612140000', '2000', '2005-10-01', '2006-04-30' ),
( '6612140000', '1000', '2006-04-01', '2007-03-31' ),
( '6612140000', '1000', '2007-04-01', '2008-03-31' ),
( '6612140000', '1000', '2008-04-01', '2009-03-31' ),
( '6612140000', '1000', '2009-04-01', '2010-03-31' ),
( '6612140000', '1000', '2010-04-01', '2010-11-30' ),
( '6612140000', '1000', '2010-12-01', '2011-03-31' ),
( '6612140000', '1000', '2011-04-01', '2011-08-21' ),
( '6612140000', '1000', '2011-08-22', '2011-11-13' ),
( '6612140000', '1000', '2011-11-14', '2011-11-30' ),
( '6612140000', '1000', '2011-12-01', '2012-01-31' ),
( '6612140000', '1000', '2016-07-01', '2017-03-31' ),
( '6612140000', '1000', '2017-04-01', '2017-11-30' ),
( '6612140000', '1000', '2017-12-01', '2018-03-31' ),
( '6612140000', '1000', '2018-04-01', null ),
( '7605140000', '1000', '2013-11-01', '2013-11-30' ),
( '7605140000', '1000', '2013-12-01', '2013-12-31' ),
( '7605140000', '1000', '2014-01-01', '2014-03-31' ),
( '7605140000', '1000', '2014-04-01', '2014-12-31' ),
( '7605140000', '1000', '2015-05-01', '2015-05-31' ),
( '7605140000', '1000', '2015-06-01', '2015-09-30' ),
( '7605140000', '1000', '2015-10-01', '2015-10-31' ),
( '7605140000', '1000', '2016-01-25', '2016-07-24' ),
( '7605140000', '1000', '2016-07-25', '2016-08-31' ),
( '7605140000', '1000', '2016-09-01', '2017-03-31' ),
( '7605140000', '1000', '2017-04-01', '2017-11-30' ),
( '7605140000', '1000', '2017-12-01', null );

И код из ответа, который я попытался добавить в поле ID, без удачи:

with

  FromDates as (
    -- All of the   FromDates   for each   SSN   for which there is not
    --   a contiguous preceding period.
    select SO.SSN, SO.ID, SO.FromDate, SO.ToDate,
      Row_Number() over ( partition by SO.SSN order by SO.FromDate ) as RN
      from Samples as SO
      where not exists (
        select 42 from Samples as SI where SI.SSN = SO.SSN and SI.ID = SO.ID and
          SI.ToDate = DateAdd( day, -1, SO.FromDate ) ) ),

  ToDates as (
    -- All of the   ToDates   for each   SSN   for which there is not
    --   a contiguous following period.
    select SSN, ID, FromDate, ToDate, Row_Number() over ( partition by SSN order by FromDate ) as RN
      from Samples as SO
      where not exists (
        select 42 from Samples as SI where SI.SSN = SO.SSN and SI.ID = SO.ID and
          SI.FromDate = DateAdd( day, 1, SO.ToDate ) ) ),

  Ranges as (
    -- Pair the   FromDate   and   ToDate   entries for each   SSN .
    select F.SSN, F.ID, F.FromDate, T.ToDate
      from FromDates as F inner join
        ToDates as T on T.SSN = F.SSN and T.ID = F.ID and T.RN = F.RN ) 

-- Use any ONE of the following   select   statements to see what is going on:
-- select * from FromDates
--  select * from ToDates
  select * from Ranges 
  -- where SSN = '6612140000'
  order by SSN, ID, FromDate

Возвращает:

SSN         ID      FromDate    ToDate
6612140000  1000    2016-07-01  (null)
7605140000  1000    2013-11-01  2014-12-31
7605140000  1000    2014-03-01  2014-12-31
7605140000  1000    2015-05-01  2015-10-31
7605140000  1000    2015-05-01  2015-10-31
7605140000  1000    2016-01-25  (null)

Ответы [ 2 ]

0 голосов
/ 15 мая 2018

Это проблема пробелов и островков, стандартное решение основано на вложенных аналитических функциях:

# 1: Сравните каждую строку с предыдущей строкой и отметьте ее 1, когда начинается новая группа.

# 2: Рассчитать кумулятивную сумму по флагу, чтобы присвоить число каждой группе строк.

# 3: Теперь вы можете делать с этими группами все, что хотите.

-- data must be correct, i.e. a Slowly Changing Dimension without gaps or overlapping periods
with calcFlag as
 (
   select SSN, Id, FromDate, ToDate,
      -- new group starts when the previous end date
      -- is not the current start date -1
      case when lag(ToDate)
                over (partition by SSN, Id
                      order by FromDate ) = DateAdd( day, -1, FromDate )
           then 0
           else 1
      end as flag
   from samples
 ),
calcGroup as 
 (
   select SSN, Id, FromDate, ToDate, flag,
      -- Cumulative Sum to dynamically assign group number
      sum(flag)
      over ( partition by SSN, Id 
             order by FromDate 
             rows unbounded preceding ) as grp#
   from calcFlag
 )
select SSN, Id, 
   min(FromDate), 
   -- either max date or NULL 
   nullif(max(coalesce(ToDate, '9999-12-31')), '9999-12-31')
from calcGroup
group by SSN, Id, grp# -- include dynamically calculated group number
order by SSN, Id, min(FromDate)
;
0 голосов
/ 13 мая 2018

Следующий пример собирает острова из ваших данных.Изменяя, какие из окончательных операторов select включены / прокомментированы, вы можете видеть промежуточные результаты в процессе.

Обновление : изменено сравнение дат в CTE, чтобы они могли принести пользуиз индексов SSN, FromDate и SSN, ToDate.

-- Sample data.
declare @Samples table ( SSN VarChar(10), FromDate Date, ToDate Date );
insert into @Samples ( SSN, FromDate, ToDate ) values
  ( '0987654321', '2011-01-01', '2011-12-31' ),
  ( '0987654321', '2012-01-01', '2012-12-31' ),
  ( '1234567890', '2012-01-01', '2012-12-31' ),
  ( '0987654321', '2013-01-01', '2013-12-31' ),
  ( '1234567890', '2013-01-01', '2013-06-30' ),
  ( '0987654321', '2014-01-01', '2014-08-31' ),
  ( '1234567890', '2016-01-01', '2016-12-31' ),
  ( '1234567890', '2017-01-01', '2017-12-31' ),
  ( '1234567890', '2018-01-01', null );
select *
  from @Samples;

-- Sample data made a little easier to read.
select *,
  case when exists (
    select 42 from @Samples as SI where SI.SSN = S.SSN and
      DateDiff( day, S.ToDate, SI.FromDate ) = 1 ) then 1 else 0 end as Continued
  from @Samples as S
  order by SSN, FromDate;

-- Process the data.
with
  FromDates as (
    -- All of the   FromDates   for each   SSN   for which there is not
    --   a contiguous preceding period.
    select SO.SSN, SO.FromDate, SO.ToDate,
      Row_Number() over ( partition by SO.SSN order by SO.FromDate ) as RN
      from @Samples as SO
      where not exists (
        select 42 from @Samples as SI where SI.SSN = SO.SSN and
          SI.ToDate = DateAdd( day, -1, SO.FromDate ) ) ),
  ToDates as (
    -- All of the   ToDates   for each   SSN   for which there is not
    --   a contiguous following period.
    select SSN, FromDate, ToDate, Row_Number() over ( partition by SSN order by FromDate ) as RN
      from @Samples as SO
      where not exists (
        select 42 from @Samples as SI where SI.SSN = SO.SSN and
          SI.FromDate = DateAdd( day, 1, SO.ToDate ) ) ),
  Ranges as (
    -- Pair the   FromDate   and   ToDate   entries for each   SSN .
    select F.SSN, F.FromDate, T.ToDate
      from FromDates as F inner join
        ToDates as T on T.SSN = F.SSN and T.RN = F.RN )
  -- Use any ONE of the following   select   statements to see what is going on:
--  select * from FromDates order by SSN, FromDate;
--  select * from ToDates order by SSN, FromDate;
  select * from Ranges order by SSN, FromDate;

Конечно, если бы в пределах SSN s были фактически отдельные значения Id, которые должны были обрабатываться независимо, ответ изменился бы на что-токак это:

-- Sample data.
declare @Samples as Table ( SSN VarChar(10), Id VarChar(4), FromDate Date, ToDate Date );
insert into @Samples ( SSN, ID, FromDate, ToDate ) values
    ( '6612140000', '1000', '2005-01-01', '2005-03-31' ),
    ( '6612140000', '1000', '2005-04-01', '2005-09-30' ),
    ( '6612140000', '1000', '2005-10-01', '2006-03-31' ), 
    ( '6612140000', '2000', '2005-10-01', '2006-04-30' ),
    ( '6612140000', '1000', '2006-04-01', '2007-03-31' ),
    ( '6612140000', '1000', '2007-04-01', '2008-03-31' ),
    ( '6612140000', '1000', '2008-04-01', '2009-03-31' ),
    ( '6612140000', '1000', '2009-04-01', '2010-03-31' ),
    ( '6612140000', '1000', '2010-04-01', '2010-11-30' ),
    ( '6612140000', '1000', '2010-12-01', '2011-03-31' ),
    ( '6612140000', '1000', '2011-04-01', '2011-08-21' ),
    ( '6612140000', '1000', '2011-08-22', '2011-11-13' ),
    ( '6612140000', '1000', '2011-11-14', '2011-11-30' ),
    ( '6612140000', '1000', '2011-12-01', '2012-01-31' ),
    ( '6612140000', '1000', '2016-07-01', '2017-03-31' ),
    ( '6612140000', '1000', '2017-04-01', '2017-11-30' ),
    ( '6612140000', '1000', '2017-12-01', '2018-03-31' ),
    ( '6612140000', '1000', '2018-04-01', null ),
    ( '7605140000', '1000', '2013-11-01', '2013-11-30' ),
    ( '7605140000', '1000', '2013-12-01', '2013-12-31' ),
    ( '7605140000', '1000', '2014-01-01', '2014-03-31' ),
    ( '7605140000', '1000', '2014-03-01', '2014-12-31' ),
    ( '7605140000', '1000', '2014-04-01', '2014-12-31' ),
    ( '7605140000', '1000', '2015-05-01', '2015-05-31' ),
--  ( '7605140000', '1000', '2015-05-01', '2015-05-31' ), -- Duplicate row?!
    ( '7605140000', '1000', '2015-06-01', '2015-09-30' ),
--  ( '7605140000', '1000', '2015-06-01', '2015-09-30' ), -- Duplicate row?!
    ( '7605140000', '1000', '2015-10-01', '2015-10-31' ),
--  ( '7605140000', '1000', '2015-10-01', '2015-10-31' ), -- Duplicate row?!
    ( '7605140000', '1000', '2016-01-25', '2016-07-24' ),
    ( '7605140000', '1000', '2016-07-25', '2016-08-31' ),
    ( '7605140000', '1000', '2016-09-01', '2017-03-31' ),
    ( '7605140000', '1000', '2017-04-01', '2017-11-30' ),
    ( '7605140000', '1000', '2017-12-01', null );
select *
  from @Samples;

-- Sample data made a little easier to read.
select *,
  case when exists (
    select 42 from @Samples as SI where SI.SSN = S.SSN and SI.Id = S.Id and
      DateDiff( day, S.ToDate, SI.FromDate ) = 1 ) then 1 else 0 end as Continued
  from @Samples as S
  order by SSN, Id, FromDate;

-- Process the data.
with
  FromDates as (
    -- All of the   FromDates   for each   SSN   for which there is not
    --   a contiguous preceding period.
    select SO.SSN, SO.Id, SO.FromDate, SO.ToDate,
      Row_Number() over ( partition by SO.SSN, SO.Id order by SO.FromDate ) as RN
      from @Samples as SO
      where not exists (
        select 42 from @Samples as SI where SI.SSN = SO.SSN and SI.Id = SO.Id and
          SI.ToDate = DateAdd( day, -1, SO.FromDate ) ) ),
  ToDates as (
    -- All of the   ToDates   for each   SSN   for which there is not
    --   a contiguous following period.
    select SO.SSN, SO.Id, SO.FromDate, SO.ToDate,
      Row_Number() over ( partition by SSN, SO.Id order by FromDate ) as RN
      from @Samples as SO
      where not exists (
        select 42 from @Samples as SI where SI.SSN = SO.SSN and SI.Id = SO.Id and
          SI.FromDate = DateAdd( day, 1, SO.ToDate ) ) ),
  Ranges as (
    -- Pair the   FromDate   and   ToDate   entries for each   SSN .
    select F.SSN, F.Id, F.FromDate, T.ToDate
      from FromDates as F inner join
        ToDates as T on T.SSN = F.SSN and T.Id = F.Id and T.RN = F.RN )
  -- Use any ONE of the following   select   statements to see what is going on:
--  select * from FromDates order by SSN, Id, FromDate;
--  select * from ToDates order by SSN, Id, FromDate;
  select * from Ranges order by SSN, Id, FromDate;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...