【问题标题】:Use recursive CTE to handle date logic使用递归 CTE 处理日期逻辑
【发布时间】:2017-03-30 17:53:51
【问题描述】:

在工作中,我的一项任务是计算销售人员的佣金。一项规则比其他规则更具挑战性。

两个销售团队 A 和 B 一起工作,各自销售不同的产品。 A 团队可以将潜在客户发送给 B 团队。同一个客户可能会被多次发送。首次向客户(例如潜在客户 1)* 发送佣金时,将向 A 团队中创建潜在客户的销售人员支付佣金。现在,客户在接下来的 365 天内被“锁定”(从创建线索 1 的日期算起)。这意味着在此期间,没有人可以通过发送额外的线索为该客户获得额外的佣金(例如,线索 2 和 3 没有佣金)。 365 天到期后。可以创建新的潜在客户并获得佣金(例如潜在客户 4)。然后,从创建线索 4 之日算起,客户再次被锁定 365 天。因此,lead 5 没有佣金。棘手的部分是重置计算 365 天的日期。

'* 引用表#LEADS 和#DISERED 结果。

我已经使用游标解决了 tSQL 中的问题,但我想知道是否可以使用递归 CTE。我做了几次尝试,最好的一个贴在下面。我的解决方案的问题是,我不止一次引用递归表。我试图通过在 CTE 中嵌套 CTE 来解决这个问题。这是不允许的。我曾尝试在 CTE 中使用一个临时表,这也是不允许的。我尝试了几次重新编码 CTE 的递归部分,以便递归表只被引用一次,但是我无法让逻辑工作。

我正在使用 SQL 2008

IF OBJECT_ID('tempdb.dbo.#LEADS', 'U') IS NOT NULL
DROP TABLE #LEADS;

CREATE TABLE #LEADS (LEAD_ID INT, CUSTOMER_ID INT, LEAD_CREATED_DATE DATETIME, SALESPERSON_NAME varchar(20))
INSERT INTO #LEADS
VALUES   (1,    1,  '2013-09-01', 'Rasmus')
        ,(2,    1,  '2013-11-01', 'Christian')
        ,(3,    1,  '2014-01-01', 'Nadja')
        ,(4,    1,  '2014-12-24', 'Roar')
        ,(5,    1,  '2015-12-01', 'Kristian')
        ,(6,    2,  '2014-01-05', 'Knud')
        ,(7,    2,  '2015-01-02', 'Rasmus')
        ,(8,    2,  '2015-01-08', 'Roar')
        ,(9,    2,  '2016-02-05', 'Kristian')
        ,(10,   2,  '2016-03-05', 'Casper')

SELECT  *
FROM    #LEADS;

IF OBJECT_ID('tempdb.dbo.#DISERED_RESULT', 'U') IS NOT NULL
DROP TABLE #DISERED_RESULT;

CREATE TABLE #DISERED_RESULT (LEAD_ID INT, DESIRED_COMMISION_RESULT CHAR(3))
INSERT INTO #DISERED_RESULT
VALUES   (1, 'YES')
        ,(2, 'NO')
        ,(3, 'NO')
        ,(4, 'YES')
        ,(5, 'NO')
        ,(6, 'YES')
        ,(7, 'NO')
        ,(8, 'YES')
        ,(9, 'YES')
        ,(10, 'NO')

    SELECT  *
    FROM    #DISERED_RESULT;

WITH COMMISSION_CALCULATION AS
(
    SELECT  T1.*
            ,COMMISSION = 'YES'
            ,MIN_LEAD_CREATED_DATE AS COMMISSION_DATE
    FROM     #LEADS AS T1
    INNER JOIN  (
                SELECT  A.CUSTOMER_ID
                        ,MIN(A.LEAD_CREATED_DATE) AS MIN_LEAD_CREATED_DATE
                FROM    #LEADS AS A
                GROUP BY A.CUSTOMER_ID
                ) AS T2 ON T1.CUSTOMER_ID = T2.CUSTOMER_ID  AND T1.LEAD_CREATED_DATE = T2.MIN_LEAD_CREATED_DATE

UNION ALL

SELECT  T10.LEAD_ID
        ,T10.CUSTOMER_ID
        ,T10.LEAD_CREATED_DATE
        ,T10.SALESPERSON_NAME
        ,T10.COMMISSION
        ,T10.COMMISSION_DATE
FROM    (SELECT ROW_NUMBER() OVER(PARTITION BY T5.CUSTOMER_ID ORDER BY T5.LEAD_CREATED_DATE ASC) AS RN 
                ,T5.*
                ,T6.MAX_COMMISSION_DATE
                ,DATEDIFF(DAY, T6.MAX_COMMISSION_DATE, T5.LEAD_CREATED_DATE) AS ANTAL_DAGE_SIDEN_SIDSTE_COMMISSION
                ,CASE 
                    WHEN DATEDIFF(DAY, T6.MAX_COMMISSION_DATE, T5.LEAD_CREATED_DATE) > 365      THEN 'YES'
                    ELSE 'NO'
                END AS COMMISSION
                ,CASE 
                    WHEN DATEDIFF(DAY, T6.MAX_COMMISSION_DATE, T5.LEAD_CREATED_DATE) > 365      THEN T5.LEAD_CREATED_DATE
                    ELSE NULL
                END AS COMMISSION_DATE
        FROM        #LEADS AS T5
            INNER JOIN (SELECT      T4.CUSTOMER_ID
                                    ,MAX(T4.COMMISSION_DATE) AS MAX_COMMISSION_DATE
                        FROM        COMMISSION_CALCULATION AS T4
                        GROUP BY    T4.CUSTOMER_ID) AS T6   ON T5.CUSTOMER_ID = T6.CUSTOMER_ID
        WHERE   T5.LEAD_ID   NOT IN (SELECT LEAD_ID FROM COMMISSION_CALCULATION)
        ) AS T10
WHERE   RN = 1


)   
    SELECT  *
    FROM    COMMISSION_CALCULATION; 

【问题讨论】:

  • 样本数据加1,请发布您的预期结果
  • 样本数据的最后一列是想要的结果。感谢您的宝贵时间。
  • 您的查询有一些问题,您能否在问题中粘贴预期结果
  • 我在原始问题中添加了一个新表,并获得了所需的结果。
  • 我对这种说法感到困惑。 " 如果第二个潜在客户是在第一个潜在客户的 365 天之后创建的,那么客户将获得佣金。第二个潜在客户将在接下来的 365 天获得所有佣金。"

标签: sql sql-server sql-server-2008 tsql common-table-expression


【解决方案1】:

我做了一些假设,您的描述并不完全有意义,但以下实现了您想要的结果:

if object_id('tempdb.dbo.#leads', 'u') is not null
drop table #leads;

create table #leads (lead_id int, customer_id int, lead_created_date datetime, salesperson_name varchar(20))
insert into #leads
values (1,    1,  '2013-09-01', 'rasmus')
      ,(2,    1,  '2013-11-01', 'christian')
      ,(3,    1,  '2014-01-01', 'nadja')
      ,(4,    1,  '2014-12-24', 'roar')
      ,(5,    1,  '2015-12-01', 'kristian')
      ,(6,    2,  '2014-01-05', 'knud')
      ,(7,    2,  '2015-01-02', 'rasmus')
      ,(8,    2,  '2015-01-08', 'roar')
      ,(9,    2,  '2016-02-05', 'kristian')
      ,(10,   2,  '2016-03-05', 'casper')

if object_id('tempdb.dbo.#disered_result', 'u') is not null
drop table #disered_result;

create table #disered_result (lead_id int, desired_commision_result char(3))
insert into #disered_result
values (1, 'yes'),(2, 'no'),(3, 'no'),(4, 'yes'),(5, 'no'),(6, 'yes'),(7, 'no'),(8, 'yes'),(9, 'yes'),(10, 'no')

with rownum
as
(
    select row_number() over (order by customer_id, lead_created_date) as rn                                -- This is to ensure an incremantal ordering id
            ,lead_id
            ,customer_id
            ,lead_created_date
            ,salesperson_name
    from #leads
)
,cte
as
(
    select rn
            ,lead_id
            ,customer_id
            ,lead_created_date
            ,salesperson_name
            ,'yes' as commission_result
            ,lead_created_date as commission_window_start
    from rownum
    where rn = 1

    union all

    select r.rn
            ,r.lead_id
            ,r.customer_id
            ,r.lead_created_date
            ,r.salesperson_name

            ,case when r.customer_id <> c.customer_id       -- If the customer ID has changed, we are at a new commission window.
                then 'yes'
                else case when r.lead_created_date > dateadd(d,365,c.commission_window_start)   -- This assumes the window is 365 days and not one year (ie. Leap years don't add a day)
                        then 'yes'
                        else 'no'
                        end
                end as commission_result

            ,case when r.customer_id <> c.customer_id
                then r.lead_created_date
                else case when r.lead_created_date > dateadd(d,365,c.commission_window_start)   -- This assumes the window is 365 days and not one year (ie. Leap years don't add a day)
                        then r.lead_created_date
                        else c.commission_window_start
                        end
                end as commission_window_start

    from rownum r
        inner join cte c
            on(r.rn = c.rn+1)
)
select lead_id
        ,commission_result
from cte
order by customer_id
        ,lead_created_date;

【讨论】:

  • 感谢您抽出宝贵时间制定解决方案。该解决方案提供了所需的结果。我期待在我的佣金计算项目中实现它,所以看看性能与我的基于光标的解决方案相比如何。
猜你喜欢
  • 1970-01-01
  • 2017-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-15
  • 2020-03-30
  • 1970-01-01
相关资源
最近更新 更多