我需要按月分解每个政策生命周期。从政策开始到该月末,然后从开始到下个月结束,并且对于每个时间段需要计算天数,因此我可以计算每个政策的收入溢价。
请看图片我需要做些什么。
[DECLARE @EarnedToDate datetime ='2016-06-30'
;WITH Cte_Policies AS
(
SELECT
PolicyNumber
,TransactionEffectiveDate
,TransactionExpirationDate
,WrittenPremium
,DATEDIFF(DAY,TransactionEffectiveDate,TransactionExpirationDate) AS TotalDays
,CASE
WHEN TransactionEffectiveDate> @EarnedToDate THEN 0 --Policy not yet in effect
WHEN TransactionExpirationDate< @EarnedToDate THEN DATEDIFF(DAY,TransactionEffectiveDate,TransactionExpirationDate)
ELSE DATEDIFF(DAY,TransactionEffectiveDate,@EarnedToDate)
END AS EarnedDays
,CASE
WHEN TransactionEffectiveDate > @EarnedToDate THEN DATEDIFF(DAY,TransactionEffectiveDate,TransactionExpirationDate)
WHEN TransactionExpirationDate < @EarnedToDate THEN 0 -- Policy completed
ELSE DATEDIFF(DAY,@EarnedToDate,TransactionExpirationDate)
END AS UnearnedDays
FROM ##TempTable1
)
SELECT PolicyNumber,
TransactionEffectiveDate as TransactionEffectiveDate,
TransactionExpirationDate as TransactionExpirationDate
--WrittenPremium/TotalDays AS DayPremium,
,SUM(CASE WHEN EarnedDays = 0 THEN 0 ELSE WrittenPremium/TotalDays * EarnedDays END) AS EarnedPremium
,SUM(CASE WHEN UnearnedDays = 0 THEN 0 ELSE WrittenPremium/TotalDays * UnearnedDays END) AS UnearnedPremium
FROM
Cte_Policies where PolicyNumber ='PACA1000238-02'
GROUP BY
TransactionEffectiveDate
,TransactionExpirationDate
--,WrittenPremium/TotalDays
,PolicyNumber][1]
答案 0 :(得分:0)
正如我在你的推文中所想,这对代码来说非常简单,但需要做一些工作才能理解。
WITH raw_data AS (
SELECT
Cast(text_TransactionEffectiveDate AS DATE) AS TransactionEffectiveDate
, Cast(text_TransactionExpirationDate AS DATE) AS TransactionExpirationDate
FROM (VALUES
('1953-01-15', '1992-02-23')
, ('2012-08-12', '2012-08-26')
) AS z (text_TransactionEffectiveDate, text_TransactionExpirationDate )
), policy_dates AS (
SELECT
raw_data.*
, DateAdd(month, DateDiff(month, '1753-01-01', TransactionEffectiveDate), '1753-01-01') AS Policy_Start
, DateAdd(month, DateDiff(month, '1753-01-01', TransactionExpirationDate), '1753-02-01') AS Policy_Expired
FROM raw_data
)
SELECT DateDiff(day, Policy_start, Policy_Expired) AS Policy_days, *
FROM policy_dates
这是保险领域常见的计算。诀窍在于计算有效月的开始和到期月的开始(在策略结束之后)。诀窍是,使用的时间比其他人(1753-02-01)晚一个月。
-PatP
答案 1 :(得分:0)
添加一个或两个CTE,将策略生效和到期日期分为不同月份,然后重复使用现有代码/ CTE
请务必查看EarnedDays和UnearnedDays的所有边界条件,并确保它们符合您的业务规则
DECLARE @EarnedToDate DATETIME
SET @EarnedToDate = '2016-06-30'
DECLARE @tblPolicies TABLE
(
PolicyNumber VARCHAR(100)
, PolicyEffectiveDate DATETIME
, PolicyExpirationDate DATETIME
, WP MONEY --WrittenPremium
)
DECLARE @tblPolicyMonths TABLE
(
PolicyNumber VARCHAR(100),
MonthStart DATETIME,
MonthEnd DATETIME
)
DECLARE @CurPos INT
SET @CurPos = 1
WHILE @CurPos < 4000
BEGIN
--Create a bunch of policies
INSERT INTO @tblPolicies
SELECT 'P' + CONVERT(varchar, @CurPos), DATEADD(d, @CurPos, GETDATE()), DATEADD(YY, 1, DATEADD(d, @CurPos, GETDATE())), @CurPos
SET @CurPos = @CurPos + 1
END
DECLARE @LastPolicyDate DATETIME
SET @LastPolicyDate = (SELECT MAX(PolicyExpirationDate) FROM @tblPolicies)
;WITH Cte_All_Dates AS
(
SELECT MIN(PolicyEffectiveDate) DateValue
FROM @tblPolicies
UNION ALL
SELECT DateValue + 1
FROM Cte_All_Dates
WHERE DateValue + 1 < = @LastPolicyDate
)
INSERT INTO @tblPolicyMonths
SELECT P.PolicyNumber
,MIN(DateValue)
,MAX(DateValue)
FROM Cte_All_Dates PD
INNER JOIN @tblPolicies P
ON CONVERT(DATE, PD.DateValue) BETWEEN P.PolicyEffectiveDate AND P.PolicyExpirationDate
GROUP BY P.PolicyNumber
, DATEPART(MM, DateValue)
, DATEPART(YY, DateValue)
OPTION (MAXRECURSION 32767);
SELECT
P.PolicyNumber
,CONVERT(DATE, MonthStart) As StartRiskMonth
,CONVERT(DATE, MonthEnd) AS EndRiskMonth
,WP as WrittenPremium
,DATEDIFF(DAY,MonthStart,MonthEnd)+1 AS TotalDays
,CASE
WHEN MonthStart > @EarnedToDate THEN 0 --Policy not yet in effect
WHEN MonthEnd < @EarnedToDate THEN DATEDIFF(DAY,MonthStart,MonthEnd)+1
ELSE DATEDIFF(DAY,MonthStart,@EarnedToDate)+1
END AS EarnedDays
,CASE
WHEN MonthStart > @EarnedToDate THEN DATEDIFF(DAY,MonthStart,MonthEnd)+1
WHEN MonthEnd < @EarnedToDate THEN 0 -- Policy completed
ELSE DATEDIFF(DAY,@EarnedToDate,MonthEnd)
END AS UnearnedDays
, @EarnedToDate AS 'EarnedToDate'
FROM @tblPolicyMonths PM
INNER JOIN @tblPolicies P
ON PM.PolicyNumber = P.PolicyNumber
ORDER BY PolicyNumber, MonthStart, MonthEnd
答案 2 :(得分:0)
我的原始片段在Twitter上回答了您的问题,但是下面的代码片段更进一步,并在StackOverflow上提供了您问题中指定的结果集...
; WITH Earned_to_date AS (
SELECT Cast('2016-07-01' AS DATE) AS Earned_to_date
), policy_data AS (
SELECT
policy_number
, Cast(text_Effective AS DATE) AS TransactionEffectiveDate
, Cast(text_Expiration AS DATE) AS TransactionExpirationDate
, policy_premium
FROM (VALUES
('p1', '1993-01-01', '1994-12-31', 940.00)
, ('p3', '2011-12-01', '2012-05-31', 485.00)
, ('p5', '2011-12-16', '2012-05-15', 485.00)
, ('p7', '2015-12-16', '2016-11-15', 485.00)
) AS z (policy_number, text_Effective
, text_Expiration, policy_premium)
), digits AS (
SELECT digit
FROM (VALUES (0), (1), (2), (3), (4)
, (5), (6), (7), (8), (9)) AS z2 (digit)
), numbers AS (
SELECT 1000 * d4.digit + 100 * d3.digit + 10 * d2.digit + d1.digit AS number
FROM digits AS d1
CROSS JOIN digits AS d2
CROSS JOIN digits AS d3
CROSS JOIN digits AS d4
), calendar AS (
SELECT
DateAdd(month, number, '1753-01-01') AS month_of
, DateAdd(month, number, '1753-02-01') AS month_after
FROM numbers
), policy_dates AS (
SELECT
policy_number
, CASE
WHEN month_of < TransactionEffectiveDate THEN TransactionEffectiveDate
ELSE month_of
END AS StartRiskMonth
, CASE
WHEN TransactionExpirationDate < month_after THEN TransactionExpirationDate
WHEN Earned_to_date.Earned_to_date < month_after THEN Earned_to_date
ELSE month_after
END AS EndRiskMonth
, DateDiff(day, TransactionEffectiveDate, TransactionExpirationDate) AS policy_days
, policy_premium
FROM policy_data
JOIN calendar
ON (policy_data.TransactionEffectiveDate < calendar.month_after
AND calendar.month_of < policy_data.TransactionExpirationDate)
CROSS JOIN Earned_to_date
WHERE month_of < Earned_to_date
)
SELECT policy_number, StartRiskMonth, EndRiskMonth
, DateDiff(day, StartRiskMonth, EndRiskMonth) AS DaysInMonth
, policy_premium * DateDiff(day, StartRiskMonth, EndRiskMonth) / policy_days
FROM policy_dates
ORDER BY policy_number, StartRiskMonth