Выберите самую последнюю запись (с датой истечения срока действия)

#sql #sql-server #date #join #duplicates

Вопрос:

Допустим, у нас есть 2 таблицы с именами Записи и возможности:

Записи:

РекордИД Идентификатор клиента Дата создания
777 1 1/1/2021
888 2 1/1/2021
999 1 2/1/2021

Возможности:

ОппИД Идентификатор клиента OppCreateDate
10 1 12/31/2020
11 1 1/10/2021
12 2 2/1/2021
13 1 4/1/2021
14 1 8/5/2025

Желаемый Результат:

РекордИД Идентификатор клиента Дата создания #Возможности
777 1 1/1/2021 1
888 2 1/1/2021 1
999 1 2/1/2021 1

Как вы можете видеть, таблица записей содержит первые 3 столбца желаемых выходных данных, а столбец «#Возможности» создается путем подсчета количества возможностей, которые возникают после создания записи для данного клиента.

Две ключевые вещи, которые следует отметить в этой логике:

  1. Учитывайте возможности только в том случае, если они возникают в течение 6 месяцев с момента записи.
  2. Если для клиента создана другая запись, учитывайте возможности только для самой последней записи.

Более конкретно, OppID = 11 будет зачислен на RecordID = 777; с 12 по 888; и с 13 по 999. 10 и 14 не будут зачислены ни на один из RecordID.

Я написал приведенный ниже код, который не учитывает #2 выше:

 CREATE TABLE #Records
(
RecordID int
, CustomerID int
, CreateDate Date
)

INSERT INTO #Records
VALUES 
(777, 1, '2021-01-01')
, (888, 2, '2021-01-31')
, (999, 1, '2021-02-01')


CREATE TABLE #Opportunities
(
OppID int
, CustomerID int
, OppCreateDate Date
)

INSERT INTO #Opportunities
VALUES 
(10, 1, '2020-12-31')
, (11, 1, '2021-01-10')
, (12, 2, '2021-02-01')
, (13, 1, '2021-04-01')
, (14, 1, '2025-08-25')

select * 
from #Records

select * 
from #Opportunities

select rec.*
, (select count(*)
from #Opportunities opp
where rec.CustomerID=opp.CustomerID
and rec.CreateDate<=opp.OppCreateDate --record happened on the same day or before the opportunity
and datediff(month,rec.CreateDate,opp.OppCreateDate) < 6 --opened and created within 6 months
) as [#Opportunities]
from #Records rec
 

Есть какие-либо предложения по включению #2 выше и получению желаемого результата?

Комментарии:

1. разве RecordID не мешает вам правильно подсчитать возможности для этого клиента?

2. Ну, это кажется трудным сделать, так как, похоже, нет связи между записями и возможностями — только клиенты и возможности. Ваши подсчеты всегда будут равны одному, верно?

3. Итак, позвольте мне уточнить — существует ли определение первичного ключа (идентификатор записи,идентификатор клиента)? Потому что на первый взгляд то, что вы говорите, либо плохой дизайн структуры данных, либо вы упускаете детали, которые нам нужны.

4. Я усмехнулся, когда вы упомянули плохую структуру данных. Причина, по которой я должен расколоть этот орех, заключается в этом факте! Позвольте мне попытаться объяснить это следующим образом: для клиента может быть создано много записей; для клиента может быть создано много возможностей; запись не всегда требуется для создания возможности; когда создается возможность, я хотел бы отнести это к записи, которая произошла ближе всего к дате создания этой возможности (если это было в течение 6 месяцев с даты создания [записи]. Это помогает?

5. Да, и ой, как больно, лол

Ответ №1:

Определите, какая #records строка связана со #Opportunities строкой на основе #records.CreateDate

 select RecordID, CustomerID, CreateDate, count(*) cnt
from (
   select r.RecordID, r.CustomerID, r.CreateDate,
      row_number() over(partition by op.OppID  order by r.CreateDate desc) rn
   from #records r
   join #Opportunities op on r.CustomerID = op.CustomerID and datediff(month, r.CreateDate, op.OppCreateDate) < 6 and r.CreateDate <= op.OppCreateDate
   ) t
where rn = 1 
group by RecordID, CustomerID, CreateDate
 

ВОЗВРАТ

 RecordID    CustomerID  CreateDate  cnt
777 1   2021-01-01  1
888 2   2021-01-31  1
999 1   2021-02-01  1
 

Комментарии:

1. Спасибо, что попытались это сделать. Я думаю, что мой первоначальный пост тоже сбил вас с толку. Как я уже говорил выше с Критической ошибкой, единицей наблюдения является идентификатор записи, а не идентификатор клиента. Результат должен отражать то, что я написал выше в разделе «Желаемый результат» моего исходного поста. Помогает ли это усовершенствовать ваш подход? Спасибо вам за вашу помощь!!

2. Я включил это в свой первоначальный пост. Это третий стол. Пожалуйста, дайте мне знать, если вы все еще этого не видите. Спасибо!

3. Я понимаю. Не могу понять, почему 10 не будут зачислены ни на один из идентификаторов записи. Почему не 999 или 777?

4. 10 произошло до 777 и 999 (12/31/2020). Имеет ли это смысл?

5. Да, ваши данные, вы правите. Переписали ответ.

Ответ №2:

Попробуйте это:

 DECLARE @Records table ( RecordID int, CustomerID int, CreateDate date );
INSERT INTO @Records VALUES 
    ( 777, 1, '2021-01-01' ), ( 888, 2, '2021-01-31' ), ( 999, 1, '2021-02-01' );


DECLARE @Opportunities table ( OppID int, CustomerID int, OppCreateDate date );
INSERT INTO @Opportunities VALUES 
      ( 10, 1, '2020-12-31' )
    , ( 11, 1, '2021-01-10' )
    , ( 12, 2, '2021-02-01' )
    , ( 13, 1, '2021-04-01' )
    , ( 14, 1, '2025-08-25' );

SELECT
    *
FROM @Records r
OUTER APPLY (

    SELECT
        COUNT ( * ) AS [#Opportunities]
    FROM @Opportunities AS o
    WHERE
        o.CustomerID = r.CustomerID
        AND o.OppCreateDate >= r.CreateDate
        AND DATEDIFF ( month, r.CreateDate, o.OppCreateDate ) <= 6
        AND o.OppID NOT IN (

            SELECT
                OppID
            FROM @Records AS r2 
            INNER JOIN @Opportunities AS o2
                ON r2.CustomerID = o2.CustomerID
            WHERE
                r2.CustomerID = o.CustomerID
                AND o2.OppCreateDate >= r2.CreateDate
                AND r2.RecordID > r.RecordID
        
        )

) AS Opps
ORDER BY
    r.RecordID;
 

ВОЗВРАТ

  ---------- ------------ ------------ ---------------- 
| RecordID | CustomerID | CreateDate | #Opportunities |
 ---------- ------------ ------------ ---------------- 
|      777 |          1 | 2021-01-01 |              1 |
|      888 |          2 | 2021-01-31 |              1 |
|      999 |          1 | 2021-02-01 |              1 |
 ---------- ------------ ------------ ---------------- 
 

Комментарии:

1. Спасибо вам за вашу помощь! Я поддержал ваш пост, поскольку ваше решение, похоже, решает проблему, которую Серг также смог решить с помощью функции «раздел». Тем не менее, ваш подход больше соответствует тому, как я думал об этой конкретной проблеме. Отличная работа и еще раз спасибо!!