【问题标题】:Select most recent record (with expiration date)选择最近的记录(带有到期日期)
【发布时间】:2021-07-21 03:16:09
【问题描述】:

假设我们有 2 个名为 Records 和 Opportunities 的表:

记录:

RecordID CustomerID CreateDate
777 1 1/1/2021
888 2 1/1/2021
999 1 2/1/2021

机会:

OppID CustomerID 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

期望的输出:

RecordID CustomerID CreateDate #Opportunities
777 1 1/1/2021 1
888 2 1/1/2021 1
999 1 2/1/2021 1

如您所见,Records 表提供了所需输出的前 3 列,而“#Opportunities”列是通过计算在创建记录之后发生的机会数创建的对于给定的客户。

关于这个逻辑需要注意两点:

  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 并生成所需的输出?

【问题讨论】:

  • RecordID 是否会阻止您为该客户获得正确的机会计数?
  • 好吧,这样做看起来很难,因为记录和机会之间似乎没有关系——只有客户和机会。你的计数永远是一,对吗?
  • 所以让我直截了当地说 - (RecordID,CustomerID) 是否有主键定义?因为从表面上看,您所说的要么是糟糕的数据结构设计,要么是您遗漏了我们需要的细节。
  • 当你提到糟糕的数据结构时,我笑了。我必须破解这个坚果的原因是因为这个事实!让我试着这样解释:可以为客户创建许多记录;可以为客户创造很多机会;创造机会并不总是需要记录;创建机会时,我想将其归因于最接近该机会创建日期的记录(只要它在 [记录] 创建日期的 6 个月内。这有帮助吗?
  • 是的,哎呀,真疼,哈哈

标签: sql sql-server date join duplicates


【解决方案1】:

根据#records.CreateDate 确定哪个#records 行与#Opportunities 行相关

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

【讨论】:

  • 感谢您对此进行尝试。我想我原来的帖子也让你失望了。正如我与上面的严重错误所分享的,观察单位是 RecordID,而不是 CustomerID。输出应该反映我在原始帖子的“所需输出”部分中的内容。这有助于改进你的方法吗?谢谢你的帮助!!
  • 我已将其包含在我的原始帖子中。这是第三张桌子。如果您仍然没有看到它,请告诉我。谢谢!
  • 我明白了。不明白为什么 10 不会记入任一 RecordID。为什么不是 999 或 777?
  • 10 发生在 777 和 999(2020 年 12 月 31 日)之前。这有意义吗?
  • 是的,你的数据,你的规则。已重写答案。
【解决方案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 |
+----------+------------+------------+----------------+

【讨论】:

  • 感谢您的帮助!我赞成您的帖子,因为您的解决方案似乎解决了 Serg 也能够通过使用“分区”功能解决的问题。也就是说,您的方法更符合我对这个特定问题的思考方式。伟大的工作,再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-25
相关资源
最近更新 更多