SQL Server : count date with biweekly date - sql-server

I having trouble to count items biweekly (end at Friday)
Table is like:
+------------+------------+
| ItemNumber | Date |
+------------+------------+
| 235 | 2016-03-02 |
| 456 | 2016-03-04 |
| 454 | 2016-03-08 |
| 785 | 2016-03-10 |
| 123 | 2016-03-15 |
| 543 | 2016-03-18 |
| 863 | 2016-03-20 |
| 156 | 2016-03-26 |
+------------+------------+
Result:
+-------+------------+
| Total | biWeek |
+-------+------------+
| 4 | 2016-03-11 |
| 3 | 2016-03-25 |
| 1 | 2016-04-08 |
+-------+------------+

If I understood your problem correctly, something like this should work:
select
sum(1),
dateadd(day, ceiling(datediff(day,4, [Date]) / 14.0) * 14, 4)
from
yourtable
group by
dateadd(day, ceiling(datediff(day,4, [Date]) / 14.0) * 14, 4)
This calculates the date difference in days to "day 4" aka. 5.1.1900, divides it with 14 (rounding up) and multiplies by 14 to get biweeks and then adds that to the "day 4".

SELECT anyColumn, …, dateadd(week, datediff(week, 0, dateColumn) / 2 * 2 , 0) as biweekly
FROM table
WHERE condition
GROUP BY anyColumn, …, dateadd(week, datediff(week, 0, dateColumn) / 2 * 2 , 0)
This calculates how many weeks dateColumn is far from date 0, then the quotient of dividing this difference by 2 is multiplied by 2 which gives you 2 week intervals. By adding these 2 week intervals to date 0, you will get 2 week periods.

Related

SQL Server : filling in sparse values

Need help with SQL Server; what will be the easiest way to update the missing begin and end inventory values? Values shown are verified numbers for that week.
+------+--------+-------+----------+-----+
| Week | ItemNr | Begin | Increase | End |
+------+--------+-------+----------+-----+
| 1 | 1001 | 100 | -10 | 90 |
| 2 | 1001 | | 0 | |
| 3 | 1001 | 90 | 0 | 90 |
| 4 | 1001 | | 20 | |
| 5 | 1001 | | 100 | |
| 6 | 1001 | | -20 | |
| 7 | 1001 | | 0 | |
| 8 | 1001 | 200 | 10 | 210 |
| 9 | 1001 | | 0 | |
| 10 | 1001 | | -50 | -50 |
| 11 | 1001 | | 0 | |
+------+--------+-------+----------+-----+
if Begin is NULL then previous week End
END = Begin + Increase
A couple of window functions gets you the result. ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW is the default scope when you specify an ORDER BY in the OVER clause, however, as the other window function has it explicitly stated and doesn't use the default scope, I felt it was important to show; as you can see the difference.
WITH VTE AS(
SELECT *
FROM (VALUES ( 1,1001,100,-10),
( 2,1001,NULL, 0),
( 3,1001, 90, 0),
( 4,1001,NULL, 20),
( 5,1001,NULL,100),
( 6,1001,NULL,-20),
( 7,1001,NULL, 0),
( 8,1001,200, 10),
( 9,1001,NULL, 0),
(10,1001,NULL,-50),
(11,1001,NULL, 0)) V(Week, ItemNr, [Begin],Increase))
SELECT Week,
ItemNr,
ISNULL([Begin],S.Starting + SUM(Increase) OVER (PARTITION BY ItemNr ORDER BY Week ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)) AS [Begin],
Increase,
S.Starting + SUM(Increase) OVER (PARTITION BY ItemNr ORDER BY Week ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS [End]
FROM VTE V
CROSS APPLY (SELECT TOP 1 [Begin] AS Starting
FROM VTE ca
WHERE ca.ItemNr = V.ItemNr
ORDER BY Week ASC) S;
Note: This appears to be some kind of stock system. it's worth noting that this doesn't take into account that the stock level could go wrong. For example, say an item is stolen; the value of [End] and [Begin] (when it has a value of NULL) would be wrong in those events. If this needs to be taken into consideration, then we need to know this in the question.
Edit: Solution to cater for "lost" stock. With this, this takes the last "known" value for the stock and aggregates. So, for this example, in Week 1, although 10 items were "sold", the start of the week 2 shows the beginning value as 35. This means that 5 items are missing (stolen?). This there needs to effect all stock levels going forward. Thus you get:
WITH VTE AS(
SELECT *
FROM (VALUES ( 1,1001,100,-10),
( 2,1001,NULL, 0),
( 3,1001, 90, 0),
( 4,1001,NULL, 20),
( 5,1001,NULL,100),
( 6,1001,NULL,-20),
( 7,1001,NULL, 0),
( 8,1001,200, 10),
( 9,1001,NULL, 0),
(10,1001,NULL,-50),
(11,1001,NULL, 0),
(1,1002,50,-10),
(2,1002,35,0),--Begin value lowered. Some items went "missing"
(3,1002,NULL,5),
(4,1002,40,10)) V(Week, ItemNr, [Begin],Increase))
SELECT Week,
ItemNr,
[Begin],
Increase,
LastKnown,
WeekKnown,
ISNULL([Begin],S.LastKnown + SUM(Increase) OVER (PARTITION BY ItemNr, WeekKnown ORDER BY Week ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)) AS ActualBegin,
ISNULL([Begin],S.LastKnown + SUM(Increase) OVER (PARTITION BY ItemNr, WeekKnown ORDER BY Week ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)) AS [End]
FROM VTE V
CROSS APPLY (SELECT TOP 1 [Begin] AS LastKnown, Week AS WeekKnown
FROM VTE ca
WHERE ca.ItemNr = V.ItemNr
AND ca.Week <= V.Week
AND ca.[Begin] IS NOT NULL
ORDER BY Week DESC) S
ORDER BY V.ItemNr, V.Week;
Here is another way too
SELECT T1.Week,
T1.ItemNr,
CASE WHEN T1.[Begin] IS NULL THEN
(SELECT MAX([Begin]) + SUM(Increase) FROM #T WHERE Week < T1.Week AND ItemNr = T1.ItemNr)
ELSE
T1.[Begin]
END [Begin],
T1.Increase,
CASE WHEN T1.[Begin] IS NULL THEN
(SELECT MAX([Begin]) + SUM(Increase) FROM #T WHERE Week < T1.Week AND ItemNr = T1.ItemNr)
ELSE
T1.[Begin]
END + T1.Increase [End]
FROM #T T1;
Returns:
+------+--------+-------+----------+-----+
| Week | ItemNr | Begin | Increase | End |
+------+--------+-------+----------+-----+
| 1 | 1001 | 100 | -10 | 90 |
| 2 | 1001 | 90 | 0 | 90 |
| 3 | 1001 | 90 | 0 | 90 |
| 4 | 1001 | 90 | 20 | 110 |
| 5 | 1001 | 110 | 100 | 210 |
| 6 | 1001 | 210 | -20 | 190 |
| 7 | 1001 | 190 | 0 | 190 |
| 8 | 1003 | 200 | 10 | 210 |
| 9 | 1003 | 210 | 0 | 210 |
| 10 | 1003 | 210 | -50 | 160 |
| 11 | 1003 | 160 | 0 | 160 |
+------+--------+-------+----------+-----+
Demo

How to add biweekly periods in a year to a table? (SQL Server)

This SQL Statement is not enough to add the right biweekly periods.
What i want is something like this:
Column 1 (period) / Column 2 (start period) / Column 3 (end period)
20160115 / 2016-01-01 / 2016-01-15
20160131 / 2016-01-15 / 2016-01-31
20160215 / 2016-02-01 / 2016-02-15
20160229 / 2016-02-16 / 2016-02-29
and so on...
This is what I have now, which is wrong / incomplete.
the value is being inserted correctly in the table columns but the gap is wrong since months don't have the same amount of days
Can someone help me? Thank you very much!
DECLARE #d date= '20020101'
WHILE #d<'20030101'
BEGIN
INSERT INTO [TABLE]
VALUES ((SELECT CONVERT(VARCHAR(8), #d, 112) AS [YYYYMMDD]), #d, #d, 'Fechado', '1')
SET #d=DATEADD(DAY,15,#d)
END
using a common table expression for an adhoc numbers table, and union all for two queries with some date math using dateadd():
declare #startdate date = '2016-01-01'
declare #enddate date = '2016-12-31'
;with cte as (
select top (datediff(month,#startdate,#enddate)+1)
Month = convert(date,dateadd(month,row_number() over (order by (select 1))-1,#startdate))
from master..spt_values
order by month
)
select
Period = convert(char(8), dateadd(day,14,month), 112)
, PeriodStart = month
, PeriodEnd = dateadd(day,14,month)
from cte
union all
select
Period = convert(char(8), eomonth(month), 112)
, PeriodStart = dateadd(day,14,month)
, PeriodEnd = eomonth(month)
from cte
order by period
rextester demo: http://rextester.com/BSYIXW7864
returns:
+----------+-------------+------------+
| Period | PeriodStart | PeriodEnd |
+----------+-------------+------------+
| 20160115 | 2016-01-01 | 2016-01-15 |
| 20160131 | 2016-01-15 | 2016-01-31 |
| 20160215 | 2016-02-01 | 2016-02-15 |
| 20160229 | 2016-02-15 | 2016-02-29 |
| 20160315 | 2016-03-01 | 2016-03-15 |
| 20160331 | 2016-03-15 | 2016-03-31 |
| 20160415 | 2016-04-01 | 2016-04-15 |
| 20160430 | 2016-04-15 | 2016-04-30 |
| 20160515 | 2016-05-01 | 2016-05-15 |
| 20160531 | 2016-05-15 | 2016-05-31 |
| 20160615 | 2016-06-01 | 2016-06-15 |
| 20160630 | 2016-06-15 | 2016-06-30 |
| 20160715 | 2016-07-01 | 2016-07-15 |
| 20160731 | 2016-07-15 | 2016-07-31 |
| 20160815 | 2016-08-01 | 2016-08-15 |
| 20160831 | 2016-08-15 | 2016-08-31 |
| 20160915 | 2016-09-01 | 2016-09-15 |
| 20160930 | 2016-09-15 | 2016-09-30 |
| 20161015 | 2016-10-01 | 2016-10-15 |
| 20161031 | 2016-10-15 | 2016-10-31 |
| 20161115 | 2016-11-01 | 2016-11-15 |
| 20161130 | 2016-11-15 | 2016-11-30 |
| 20161215 | 2016-12-01 | 2016-12-15 |
| 20161231 | 2016-12-15 | 2016-12-31 |
+----------+-------------+------------+
Create a function, then call in your query:
Function:
create FUNCTION advance_biweekly
(
#current date
)
RETURNS date
AS
BEGIN
--if it's the first day of month, advance two weeks
if (DAY(#current) = 1)
return DATEADD(WEEK, 2, #current)
else
--if its last day of month, advance one day
if (DAY(DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, #current) + 1, 0))) = DAY(#current))
return DATEADD(DAY, 1, #current)
else
--else, it's the middle of the month, go to end
return dateadd(month,((YEAR(#current)-1900)*12)+MONTH(#current)-1,DAY(DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, #current) + 1, 0)))-1)
return null
END
GO
code:
DECLARE #d date= '20020101'
WHILE #d<'20030101'
begin
set #d = dbo.advance_biweekly(#d)
print #d
end
result:
2002-01-15
2002-01-31
2002-02-01
2002-02-15
2002-02-28
2002-03-01
2002-03-15
2002-03-31
2002-04-01
2002-04-15
2002-04-30
2002-05-01

Postgres: Calculate ratio of table entries that match condition

I have the following two tables in a PostgreSQL database:
dummy=# select * from employee;
id | name
----+-------
1 | John
2 | Susan
3 | Jim
4 | Sarah
(4 rows)
dummy=# select * from stats;
id | arrival | day | employee_id
----+----------+------------+-------------
2 | 08:31:34 | monday | 2
4 | 08:15:00 | monday | 3
5 | 08:43:00 | monday | 4
1 | 08:34:00 | monday | 1
7 | 08:29:00 | midweek | 1
8 | 08:31:00 | midweek | 2
9 | 08:10:00 | midweek | 3
10 | 08:40:00 | midweek | 4
11 | 08:28:00 | midweek | 1
12 | 08:33:00 | midweek | 2
14 | 08:21:00 | midweek | 3
15 | 08:45:00 | midweek | 4
16 | 08:25:00 | midweek | 1
17 | 08:35:00 | midweek | 2
18 | 08:44:00 | midweek | 4
19 | 08:10:00 | friday | 1
20 | 08:40:00 | friday | 2
21 | 08:30:00 | friday | 3
22 | 08:30:00 | friday | 4
(19 rows)
I want to select all employees that arrive between 8:25 and 8:35 on midweek and friday. I can accomplish that relatively easy with the following query:
SELECT * FROM stats
WHERE
arrival >= (time '8:30' - interval '5 minutes')
AND
arrival <= (time '8:30' + interval '5 minutes')
AND
(day = 'midweek' or day = 'friday');
However, an additional criterion is that I only want to select those employees that arrive at least 60% of the time within the aforementioned time window. This is where I am stuck. I do not know how to calculate that ratio.
What does the Query look like which fulfills all the criteria?
CLARIFICATION
Apparently the above description for the ratio is misleading.
When calculating the ratio then only the rows that meet the criteria (day = 'midweek' or day = 'friday') shall be considered. So in the sample data John and Susan show up four times for work on midweek and friday. Three out of those four times they are punctual. Hence, the ratio for Susan and John is 75%.
Use a common table expression to calculate needed counts, e.g.
with in_time as (
select *
from stats
where arrival >= (time '8:30' - interval '5 minutes')
and arrival <= (time '8:30' + interval '5 minutes')
and (day = 'midweek' or day = 'friday')
),
count_in_time as (
select employee_id, count(*)
from in_time
group by employee_id
),
total_count as (
select employee_id, count(*)
from stats
where day = 'midweek' or day = 'friday'
group by employee_id
)
select
i.*,
c.count as in_time,
t.count as total_count,
round(c.count* 100.0/t.count, 2) as ratio
from in_time i
join count_in_time c using(employee_id)
join total_count t using(employee_id);
Results:
id | arrival | day | employee_id | in_time | total_count | ratio
----+----------+---------+-------------+---------+-------------+-------
16 | 08:25:00 | midweek | 1 | 3 | 4 | 75.00
11 | 08:28:00 | midweek | 1 | 3 | 4 | 75.00
7 | 08:29:00 | midweek | 1 | 3 | 4 | 75.00
17 | 08:35:00 | midweek | 2 | 3 | 4 | 75.00
12 | 08:33:00 | midweek | 2 | 3 | 4 | 75.00
8 | 08:31:00 | midweek | 2 | 3 | 4 | 75.00
21 | 08:30:00 | friday | 3 | 1 | 3 | 33.33
22 | 08:30:00 | friday | 4 | 1 | 4 | 25.00
(8 rows)
You can add an appropriate condition in the WHERE clause of the final query.
If you want to get aggregated data only with employees and their ratios, use count() with filter:
select employee_id, name, in_time* 1.0/ total as ratio
from (
select
employee_id,
count(*) filter (where arrival >= time '8:30' - interval '5 minutes' and arrival <= time '8:30' + interval '5 minutes') as in_time,
count(*) as total
from stats
where day in ('midweek', 'friday')
group by 1
) s
join employee e on e.id = s.employee_id
where in_time* 1.0/ total >= 0.6;
employee_id | name | ratio
-------------+-------+------------------------
1 | John | 0.75000000000000000000
2 | Susan | 0.75000000000000000000
(2 rows)
You can get the arrival rate like so, for example:
SELECT name,
AVG(CASE WHEN arrival >= (time '8:30' - interval '5 minutes') AND
arrival <= (time '8:30' + interval '5 minutes') THEN 1 ELSE 0 END) AS arrival_rate
FROM employee
INNER JOIN stats ON stats.employee_id = employee.id
GROUP BY name
and to select only those where rate > 60% you just use having condition
SELECT name,
AVG(CASE WHEN arrival >= (time '8:30' - interval '5 minutes') AND
arrival <= (time '8:30' + interval '5 minutes') THEN 1 ELSE 0 END) AS arrival_rate
FROM employee
INNER JOIN stats ON stats.employee_id = employee.id
GROUP BY name
HAVING
AVG(CASE WHEN arrival >= (time '8:30' - interval '5 minutes') AND
arrival <= (time '8:30' + interval '5 minutes') THEN 1 ELSE 0 END)
> 0.6

SQL Server : how to use variable values from CTE in WHERE clause?

First of all please correct me if my title are not specific/clear enough.
I have use the following code to generate the start dates and end dates :
DECLARE #start_date date, #end_date date;
SET #start_date = '2016-07-01';
with dates as
(
select
#start_date AS startDate,
DATEADD(DAY, 6, #start_date) AS endDate
union all
select
DATEADD(DAY, 7, startDate) AS startDate,
DATEADD(DAY, 7, endDate) AS endDate
from
dates
where
startDate < '2017-03-31'
)
select * from dates
Below is part of the output from above query :
+------------+------------+
| startDate | endDate |
+------------+------------+
| 2016-07-01 | 2016-07-07 |
| 2016-07-08 | 2016-07-14 |
| 2016-07-15 | 2016-07-21 |
| 2016-07-22 | 2016-07-28 |
| 2016-07-29 | 2016-08-04 |
+------------+------------+
Now I have another table named sales, which have 3 columns sales_id,sales_date and sales_amount as below :
+----------+------------+--------------+
| sales_ID | sales_date | sales_amount |
+----------+------------+--------------+
| 1 | 2016-07-04 | 10 |
| 2 | 2016-07-06 | 20 |
| 3 | 2016-07-13 | 30 |
| 4 | 2016-07-19 | 15 |
| 5 | 2016-07-21 | 20 |
| 6 | 2016-07-25 | 25 |
| 7 | 2016-07-26 | 40 |
| 8 | 2016-07-29 | 20 |
| 9 | 2016-08-01 | 30 |
| 10 | 2016-08-02 | 30 |
| 11 | 2016-08-03 | 40 |
+----------+------------+--------------+
How can I create the query to show the total sales amount of each week (which is between each startDate and endDate from the first table)? I suppose I will need to use a recursive query with WHERE clause to check if the dates are in between startDate and endDate but I cant find a working example.
Here are my expected result (the startDate and endDate are the records from the first table) :
+------------+------------+--------------+
| startDate | endDate | sales_amount |
+------------+------------+--------------+
| 2016-07-01 | 2016-07-07 | 30 |
| 2016-07-08 | 2016-07-14 | 30 |
| 2016-07-15 | 2016-07-21 | 35 |
| 2016-07-22 | 2016-07-28 | 65 |
| 2016-07-29 | 2016-08-04 | 120 |
+------------+------------+--------------+
Thank you!
Your final Select (after the cte) should be something like this
Select D.*
,Sales_Amount = sum(Sales)
From dates D
Join Sales S on (S.sales_date between D.startDate and D.endDate)
Group By D.startDate,D.endDate
Order By D.startDate
EDIT: You could use a Left Join if you want to see missing dates from
Sales

SQL Server: - Split a Single Record and divide the Amount Values and insert as Rows

PROBLEM
Have been trying to work on this where for a particular From and ToDate, we need to take the difference between the Dates and the divide the Amount values by the difference and split the single record in Sample 1 to 5 rows in Resultant.
sr| Number |From_date|To_Date|Amount_1|Amount_2|Amount_3|Amount_4|Type
----------------------------------------------------------------------------
1 |20140911204120|Jan-14 |May-14 |5000 |2500 |1000 |200 |A
2 |20140911204122|Feb-14 |Apr-14 |6000 |3500 |2000 |1200 |R
3 |20140911204124|Feb-14 |Jun-14 |7000 |4500 |3000 |2200 |R
4 |20140911204126|Jul-14 |Sep-14 |8000 |5500 |4000 |3200 |R
5 |20140911204128|Mar-14 |Aug-14 |9000 |6500 |5000 |4200 |A
Resultant: - Record 1 after the process
So here in record 1
1. We subtract the From Date and the To Date which gives us 5.
2. We then Divide the Amount Values by 5 and split them as 5 rows and add the
row values for each individual month
--------------------------------------------------------------------
Sr| Number |Months |Amount_1|Amount_2|Amount_3|Amount_4|Type
--------------------------------------------------------------------
1 |20140911204120|January |1000 |500 |200 |10 |A
2 |20140911204120|February|1000 |500 |200 |10 |A
3 |20140911204120|March |1000 |500 |200 |10 |A
4 |20140911204120|April |1000 |500 |200 |10 |A
5 |20140911204120|May |1000 |500 |200 |10 |A
You didn't mention which version of SQL Server you are using, so I did this with 2012, but it should probably work on 2008 too if needed (although theTRY_CONVERTused for parsing dates has to be changed). Also, I'm guessing your sample desired output is wrong forAmount_4as 200/5=40 not 10.
Note that all the divisions are integer which meant potential loss of precision - if you need more precise values you have to cast to some appropriate type (decimal or float).
SQL Fiddle
MS SQL Server 2012 Schema Setup:
CREATE TABLE YourTable
([sr] int, [Number] bigint, [From_date] varchar(6), [To_date] varchar(6), [Amount_1] int, [Amount_2] int, [Amount_3] int, [Amount_4] int, [Type] varchar(1));
INSERT INTO YourTable ([sr], [Number], [From_date], [To_date], [Amount_1], [Amount_2], [Amount_3], [Amount_4], [Type])
VALUES
(1, 20140911204120, 'Jan-14', 'May-14', 5000, 2500, 1000, 200, 'A'),
(2, 20140911204122, 'Feb-14', 'Apr-14', 6000, 3500, 2000, 1200, 'R'),
(3, 20140911204124, 'Feb-14', 'Jun-14', 7000, 4500, 3000, 2200, 'R'),
(4, 20140911204126, 'Jul-14', 'Sep-14', 8000, 5500, 4000, 3200, 'R'),
(5, 20140911204128, 'Mar-14', 'Aug-14', 9000, 6500, 5000, 4200, 'A');
Query 1:
;WITH cte (Date, To_date, [Number], [Amount_1], [Amount_2], [Amount_3], [Amount_4], [Type]) AS
(
SELECT
Date = TRY_PARSE(From_date AS DATE),
To_date = TRY_PARSE(To_date AS DATE),
[Number],
[Amount_1],
[Amount_2],
[Amount_3],
[Amount_4],
[Type]
FROM YourTable
UNION ALL
SELECT
DATEADD(MONTH, 1, Date),
To_date,
[Number],
[Amount_1],
[Amount_2],
[Amount_3],
[Amount_4],
[Type]
FROM cte
WHERE Date < To_date
)
SELECT
Sr = ROW_NUMBER() OVER (PARTITION BY Number ORDER BY Number, Date),
Number,
Months,
Amount_1 = Amount_1/c,
Amount_2 = Amount_2/c,
Amount_3 = Amount_3/c,
Amount_4 = Amount_4/c,
Type
FROM (
SELECT
Number,
Months = DATENAME(MONTH, Date),
Date,
Amount_1,
Amount_2,
Amount_3,
Amount_4,
Type,
(SELECT COUNT(*) FROM cte WHERE Number = c.Number GROUP BY Number) AS c
FROM cte c
) a
ORDER BY Number, Date
OPTION (MAXRECURSION 1000)
Results:
| SR | NUMBER | MONTHS | AMOUNT_1 | AMOUNT_2 | AMOUNT_3 | AMOUNT_4 | TYPE |
|----|----------------|-----------|----------|----------|----------|----------|------|
| 1 | 20140911204120 | January | 1000 | 500 | 200 | 40 | A |
| 2 | 20140911204120 | February | 1000 | 500 | 200 | 40 | A |
| 3 | 20140911204120 | March | 1000 | 500 | 200 | 40 | A |
| 4 | 20140911204120 | April | 1000 | 500 | 200 | 40 | A |
| 5 | 20140911204120 | May | 1000 | 500 | 200 | 40 | A |
| 1 | 20140911204122 | February | 2000 | 1166 | 666 | 400 | R |
| 2 | 20140911204122 | March | 2000 | 1166 | 666 | 400 | R |
| 3 | 20140911204122 | April | 2000 | 1166 | 666 | 400 | R |
| 1 | 20140911204124 | February | 1400 | 900 | 600 | 440 | R |
| 2 | 20140911204124 | March | 1400 | 900 | 600 | 440 | R |
| 3 | 20140911204124 | April | 1400 | 900 | 600 | 440 | R |
| 4 | 20140911204124 | May | 1400 | 900 | 600 | 440 | R |
| 5 | 20140911204124 | June | 1400 | 900 | 600 | 440 | R |
| 1 | 20140911204126 | July | 2666 | 1833 | 1333 | 1066 | R |
| 2 | 20140911204126 | August | 2666 | 1833 | 1333 | 1066 | R |
| 3 | 20140911204126 | September | 2666 | 1833 | 1333 | 1066 | R |
| 1 | 20140911204128 | March | 1500 | 1083 | 833 | 700 | A |
| 2 | 20140911204128 | April | 1500 | 1083 | 833 | 700 | A |
| 3 | 20140911204128 | May | 1500 | 1083 | 833 | 700 | A |
| 4 | 20140911204128 | June | 1500 | 1083 | 833 | 700 | A |
| 5 | 20140911204128 | July | 1500 | 1083 | 833 | 700 | A |
| 6 | 20140911204128 | August | 1500 | 1083 | 833 | 700 | A |
You can join your table with a month table, to expend your data, that will be more efficient than recursive query.
SQLFiddle
;WITH months (month,month_name) AS
(select number,DATENAME(MONTH,cast(rtrim(20140000+number*100+1) as datetime))
from master..spt_values
where number between 1 and 12 AND TYPE='P'
)
SELECT Sr = ROW_NUMBER() OVER (PARTITION BY Number ORDER BY Number, month),
Number,
Month_name,
Amount_1 = Amount_1/(datediff(month,TRY_PARSE(from_date AS DATE),TRY_PARSE(to_date AS DATE))+1),
Amount_2 = Amount_2/(datediff(month,TRY_PARSE(from_date AS DATE),TRY_PARSE(to_date AS DATE))+1),
Amount_3 = Amount_3/(datediff(month,TRY_PARSE(from_date AS DATE),TRY_PARSE(to_date AS DATE))+1),
Amount_4 = Amount_4/(datediff(month,TRY_PARSE(from_date AS DATE),TRY_PARSE(to_date AS DATE))+1),
Type
FROM YourTable
JOIN Months
on month between datepart(month,TRY_PARSE(from_date AS DATE)) and datepart(month,TRY_PARSE(to_date AS DATE))

Resources