Я работаю над извлечением данных из таблицы.

declare @SampleData as Table(Id int, ContactId int, Item varchar(25),CreatedOn date)

insert into @SampleData
VALUES(100,2500,'Some item name 1212', '9/5/2020'),
      (104,2500,'Some item name 2232', '9/15/2020'),
      (109,2500,'Some item name 3434', '9/20/2020'),

      (112,3000,'Some item name 5422', '8/1/2020'),
      (132,3000,'Some item name 344', '9/5/2020'),
      (134,3000,'Some item name 454', '9/15/2020'),

      (139,3500,'Some item name 6455', '7/5/2020'),
      (146,3500,'Some item name 546', '8/5/2020'),
      (142,3500,'Some item name 867', '9/5/2020'),
      (149,3500,'Some item name 677', '9/15/2020'),
      (150,3500,'Some item name 888', '9/19/2020')

Логика здесь такова, что вы можете найти новый идентификатор контакта каждый месяц (так что логика заключается в том, что если один и тот же контакт не имеет записи за последние 28 дней с 1-го числа этого месяца, он считается новым контактом)

Если у вас есть два периода дат, это легко сделать, поэтому вы можете исключить нужные записи, как показано ниже.

SELECT *
FROM @SampleData
WHERE CreatedOn> = @FromDate
    and CreatedOn <=@Date
    and ContactId not in (SELECT ContactId
                          FROM @SampleData
                          WHERE CreatedOn >= DateAdd(Day, -28,@FromDate)
                            AND CreatedOn < @FromDate)

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

В этом примере данных я ожидаю контакта 3500 за июль, 3000 за август и 2500 и 3000 за сентябрь.

Также необходимо отображать только записи для каждого контакта, а не дублировать.

DECLARE @From date,
        @To date
DECLARE date_cursor CURSOR FOR 
select distinct DATEADD(month, DATEDIFF(month, 0, CreatedOn), 0) FromDate,EOMONTH(CreatedOn) ToDate
from @SampleData

OPEN date_cursor  
FETCH NEXT FROM date_cursor INTO @From,@To  

WHILE @@FETCH_STATUS = 0  
BEGIN  

     SELECT *
     FROM (
             SELECT DISTINCT ContactId,@From 'From Date', @To 'To Date'
             FROM @SampleData D
             WHERE D.CreatedOn>= @From AND D.CreatedOn <= @To
                AND  ContactId NOT IN (SELECT ContactId
                                  FROM @SampleData
                                  WHERE CreatedOn >= DateAdd(Day, -28,@From)
                                    AND CreatedOn < @From)) ContactData
                    OUTER APPLY (
                            --pick first row for the contact as per the period
                            SELECT TOP 1 *
                            FROM @SampleData D
                            WHERE D.ContactId = ContactData.ContactId
                                AND D.CreatedOn >= ContactData.[From Date]
                                AND D.CreatedOn < ContactData.[To Date]
                            ORDER BY CreatedOn 
                            ) Records


      FETCH NEXT FROM date_cursor INTO  @From,@To   
END 

CLOSE date_cursor  
DEALLOCATE date_cursor 

Результат

ContactId   From Date   To Date     Id  Item                 CreatedOn
3500       01/07/2020   31/07/2020  139 Some item name 6455 05/07/2020
3000       01/08/2020   31/08/2020  112 Some item name 5422 01/08/2020
2500       01/09/2020   30/09/2020  100 Some item name 1212 05/09/2020
3000       01/09/2020   30/09/2020  132 Some item name 344  05/09/2020

Я бы хотел избавиться от курсора, есть ли возможность

1
huMpty duMpty 21 Сен 2020 в 14:36

1 ответ

Лучший ответ

Вы можете назначить группировку контактам, используя lag() и сравнивая строки:

select sd.*,
       sum(case when prev_createdon > dateadd(day, -28, createdon) then 0 else 1 end) over
           (partition by contactid order by createdon) as grouping
from (select sd.*,
             lag(createdon) over (partition by contactid order by createdon) as prev_createdon
      from SampleData sd
     ) sd;

Если вам просто нужна первая строка в серии смежных записей, тогда:

select sd.*
from (select sd.*,
             lag(createdon) over (partition by contactid order by createdon) as prev_createdon
      from SampleData sd
     ) sd
where prev_createdon < dateadd(day, -28, createdon) or prev_createdon is null;

Вот скрипка db <>.

РЕДАКТИРОВАТЬ:

Основываясь на исправленном вопросе, вы хотите подвести итоги по группам. Вы можете сделать это, используя:

select contactid, min(createdon), max(createdon), min(id),
       max(case when seqnum = 1 then item end) as item
from (select sd.*,
             row_number() over (partition by contactid, grouping order by createdon) as seqnum
      from (select sd.*,
                   sum(case when prev_createdon > dateadd(day, -28, createdon) then 0 else 1 end) over
                        (partition by contactid order by createdon) as grouping
            from (select sd.*,
                         lag(createdon) over (partition by contactid order by createdon) as prev_createdon
                  from SampleData sd
                 ) sd
           ) sd
     ) sd
group by contactid, grouping;

Я обновил скрипку БД, чтобы это тоже было.

1
Gordon Linoff 21 Сен 2020 в 12:22