SQL拆分号码超过日期范围

时间:2014-10-23 08:28:05

标签: sql sql-server tsql date-range

我有一张表,例如这个数据

ID |start_date  |end_date   |amount
---|------------|-----------|-------
a1 |2013-12-01  |2014-03-31 |100

我想要一个分割日期的查询,这样我就可以像这样分出一年中的金额:

ID |org_start_date  |org_end_date   |new_start_date  |new_end_date    |amount
---|----------------|---------------|----------------|----------------|-------
a1 |2013-12-01      |2014-03-31     |2013-12-01      |2013-12-31      |25
a1 |2013-12-01      |2014-03-31     |2014-01-01      |2014-03-31      |75

2013年的25个是因为2013年有一个月和75个,因为这有3个月

有没有办法在T-SQL中执行此操作?

提前谢谢!

4 个答案:

答案 0 :(得分:2)

以下是使用数字表的解决方案:

SQL Fiddle Example

DECLARE @STARTYR INT = (SELECT MIN(YEAR([Start Date])) FROM Table1)
DECLARE @ENDYR INT = (SELECT MAX(YEAR([End Date])) FROM Table1)

SELECT [Id]
     , @STARTYR + Number AS [Year]
     , CASE WHEN YEAR([Start Date]) < @STARTYR + Number 
            THEN DATEADD(YEAR, @STARTYR - 1900 + Number,0) 
            ELSE [Start Date] END AS [Start]
     , CASE WHEN YEAR([End Date]) > @STARTYR + Number 
            THEN DATEADD(YEAR, @STARTYR - 1900 + Number + 1,0) 
            ELSE [End Date] END AS [End]
     , DATEDIFF(MONTH,CASE WHEN YEAR([Start Date]) < @STARTYR + Number 
                           THEN DATEADD(YEAR, @STARTYR - 1900 + Number,0) 
                           ELSE [Start Date] END
                     ,CASE WHEN YEAR([End Date]) > @STARTYR + Number 
                           THEN DATEADD(YEAR, @STARTYR - 1900 + Number + 1,0) 
                           ELSE DATEADD(MONTH,DATEDIFF(MONTH,0,DATEADD(MONTH,1,[End Date])),0) END) AS [Months]
     , DATEDIFF(MONTH,[Start Date],[End Date]) + 1 [Total Months]
     , ([Amount] / (DATEDIFF(MONTH,[Start Date],[End Date]) + 1)) 
       *
       DATEDIFF(MONTH,CASE WHEN YEAR([Start Date]) < @STARTYR + Number 
                           THEN DATEADD(YEAR, @STARTYR - 1900 + Number,0) 
                           ELSE [Start Date] END
                     ,CASE WHEN YEAR([End Date]) > @STARTYR + Number 
                           THEN DATEADD(YEAR, @STARTYR - 1900 + Number + 1,0) 
                           ELSE DATEADD(MONTH,DATEDIFF(MONTH,0,DATEADD(MONTH,1,[End Date])),0) END) AS [Proportion]

FROM Numbers
LEFT JOIN Table1 ON YEAR([Start Date]) <= @STARTYR + Number
                 AND YEAR([End Date]) >= @STARTYR + Number


WHERE Number <= @ENDYR - @STARTYR

答案 1 :(得分:2)

使用spt_values表创建日历表,然后加入您的表格,将日期范围拆分为您想要的任何部分。

如果按年份划分并按月划分金额,您可以:

with dates as
(
select number,DATEADD(day,number,'20130101') as dt
    from master..spt_values
    where number between 0 and 1000 AND TYPE='P'
)
select
    m.start_date as org_start_date,
    m.end_date as org_end_date,
    min(d.dt) as new_start_date,
    max(d.dt) as new_end_date,
    m.amount*count(distinct month(d.dt))/(datediff(month,m.start_date,m.end_date)+1) as amount
from 
    MonthSplit m
join
    dates d
on 
    d.dt between m.start_date and m.end_date
group by 
    m.start_date, m.end_date, year(d.dt),m.amount

这是SQL FIDDLE DEMO

答案 2 :(得分:0)

这与这个问题非常相似:

<强> Split date range into one row per month in sql server

虽然您按年度进行分组,但根据该答案,您可以通过将MINMAX添加到日期值并按{{1}分组来修改它以执行您想要的操作}:

SQL Fiddle Demo

架构设置

YEAR()

递归CTE按年份分组

CREATE TABLE MonthSplit
    ([ID] varchar(2), [start_date] datetime, [end_date] datetime, [amount] int)
;

INSERT INTO MonthSplit
    ([ID], [start_date], [end_date], [amount])
VALUES
    ('a1', '2013-12-01 00:00:00', '2014-03-31 00:00:00', 100),
    ('a2', '2013-10-01 00:00:00', '2015-05-01 00:00:00', 400)
;

<强> Results

WITH cte AS
(SELECT ID
      , start_date
      , end_date
      , start_date AS from_date
      , DATEADD(day, day(start_date)* -1 + 1, start_date) AS first_of_month
 FROM MonthSplit
 UNION ALL
 SELECT ID
     , start_date
     , end_date
     , DATEADD(month,1,first_of_month)
     , DATEADD(month,1,first_of_month)
  FROM cte
  WHERE DATEADD(month,1,from_date) < end_date
)
SELECT ID as ID, 
       min(start_date) as org_start_date, 
       min(end_date) as org_end_date, 
       min(from_date) AS new_start_date,
       CASE when max(end_date) < DATEADD(month,1,max(first_of_month)) THEN
           max(end_date)
       ELSE
           DATEADD(day, -1, DATEADD(month,1,max(first_of_month)))
       END AS new_end_date
FROM cte
group by year(from_date), ID

答案 3 :(得分:0)

我没有为您准备好现成的SQL,只是想解决这个问题。如果您对SQL有一些经验,那么很难用SQL表达它。

您可以通过为开始日期和结束日期之间的月份定义参考表来完成此操作:

ID    |   month       |   year  |   month start   |  month end    |   count |
-----------------------------------------------------------------------------
1001  |   dec-2013    |   2013  |   1-12-2013     |   31-12-2013  |     1   |
1001  |   jan-2014    |   2014  |   1-1-2014      |   31-1-2014   |     1   |
1001  |   feb-2014    |   2014  |   1-2-2014      |   28-2-2014   |     1   |
1001  |   mar-2014    |   2014  |   1-3-2014      |   31-3-2014   |     1   |

也许你的DWH中已经有了这样的时间参考表。

当您使用包含每行的开始日期和结束日期的记录加入(使用之间声明)您的表时,使用此参考表,您将从1行拆分到包含的行数范围正确。计数栏将帮助您通过以后的一年中的分组来获得每年的分配比率(例如:2013年的1/4和2014年的3/4)。您必须将该比率应用于字段&#39; amount&#39;你想要分手。