views:

328

answers:

2

A table exists in Microsoft SQL Server with record ID, Start Date, End Date and Quantity.

The idea is that for each record, the quantity/total days in range = daily quantity.

Given that a table containing all possible dates exists, how can I generate a result set in SQL Server to look like the following example?

EX:

RecordID | Start Date | End Date  | Quantity
1        |  1/1/2010  | 1/5/2010  | 30000
2        |  1/3/2010  | 1/9/2010  | 20000
3        |  1/1/2010  | 1/7/2010  | 10000

Results as
1        | 1/1/2010 |  QTY (I can do the math easy, just need the dates view)
1        | 1/2/2010 | 
1        | 1/3/2010 | 
1        | 1/4/2010 | 
1        | 1/3/2010 | 
2        | 1/4/2010 | 
2        | 1/5/2010 | 
2        | 1/6/2010 | 
2        | 1/7/2010 | 
2        | 1/8/2010 | 
2        | 1/9/2010 | 
3        | 1/1/2010 | 
3        | 1/2/2010 | 
3        | 1/3/2010 | 
3        | 1/4/2010 | 
3        | 1/5/2010 | 
3        | 1/6/2010 | 
3        | 1/7/2010 | 

Grouping on dates I could get then get the sum of quantity on that day however the final result set can't be aggregate due to user provided filters that may exclude some of these records down the road.

EDIT

To clarify, this is just a sample. The filters are irrelevant as I can join to the side to pull in details related to the record ID in the results.

The real data contains N records which increases weekly, the dates are never the same. There could be 2000 records with different start and end dates... That is what I want to generate a view for. I can right join onto the data to do the rest of what I need

I should also mention this is for past, present and future data. I would love to get rid of a temporary table of dates. I was using a recursive query to get all dates that exist within a 50 year span but this exceeds MAXRECURSION limits for a view, that I cannot use.

+1  A: 

I think you can try this.

SELECT [Quantities].[RecordID], [Dates].[Date], SUM([Quantity])
FROM [Dates]
JOIN [Quantities] on [Dates].[Date] between [Quantities].[Start Date] and [End Date]
GROUP BY [Quantities].[RecordID], [Dates].[Date]
ORDER BY [Quantities].[RecordID], [Dates].[Date]
bobs
Beautiful. I didn't think to use between in my join on operation. I still have this temp table of dates which I would like to eliminate but if that cannot be done, this is a clean solution to the problem
Mohgeroth
+2  A: 

Answer

select RecordId,d.[Date], Qty/ COUNT(*) OVER (PARTITION BY RecordId) AS Qty
from EX join Dates d on d.Date between [Start Date] and [End Date]
ORDER BY RecordId,[Date]

NB: The below demo CTEs use the date datatype which is SQL Server 2008 the general approach should work for SQL2005 as well though.

Test Case

/*CTEs for testing purposes only*/

WITH EX AS
(
    SELECT 1 AS RecordId, 
    cast('1/1/2010' as date) as [Start Date], 
    cast('1/5/2010' as date) as  [End Date], 
    30000 AS Qty
union all
    SELECT 2 AS RecordId, 
    cast('1/3/2010' as date) as [Start Date], 
    cast('1/9/2010' as date) as  [End Date], 
    20000  AS Qty
),Dates AS /*Dates Table now adjusted to do greater range*/
(

SELECT  DATEADD(day,s1.number + 2048*s2.number,'1990-01-01') AS [Date] 
FROM master.dbo.spt_values s1 CROSS JOIN master.dbo.spt_values s2
where s1.type='P' AND s2.type='P' and s2.number <= 8
order by  [Date] 
)


select RecordId,d.[Date], Qty/ COUNT(*) OVER (PARTITION BY RecordId) AS Qty
from EX join Dates d on d.Date between [Start Date] and [End Date]
ORDER BY RecordId,[Date]

Results

RecordId    Date       Qty
----------- ---------- -----------
1           2010-01-01 6000
1           2010-01-02 6000
1           2010-01-03 6000
1           2010-01-04 6000
1           2010-01-05 6000
2           2010-01-03 2857
2           2010-01-04 2857
2           2010-01-05 2857
2           2010-01-06 2857
2           2010-01-07 2857
2           2010-01-08 2857
2           2010-01-09 2857
Martin Smith
I failed to mention this is for past, present and future data. I love the idea that I don't have to get the dates from a temp table but unfortunately this could go forward upwards two years and using this query against those results doesn't yield any results for future data. I would love to get rid of this temporary dates table though...
Mohgeroth
@Mohgeroth - The dedicated dates table with an index on date will be the most efficient solution. You should be able to easily adjust my query to look at that instead of my demo dates table.
Martin Smith
What do you mean "future dates"? His query works great for spans of up to 22+ years, whether they are past or future. If you mean you want to see future dates in the list with 0 quantity such as "2011-01-01 0" then you'll have to include a parameter specifying the start and end total date range.
Emtucifor
@Mohgeroth But having said that if you really want to get shot of it I've adjusted my answer so it will work for dates between 1 Jan 1990 to 18 June 2040. Obviously you might have to adjust these dates your end.
Martin Smith
Yes I didn't have enough time to look at what it was doing, well earned answer. I don't have much knowledge of the spt_values table and will be taking some time to figure out what it represents.
Mohgeroth