Related
I have a table with the columns Age, Period and Year. The column Age always starts with 0 and doesn't have a fixed maximum value (I used 'Age' 0 to 30 in this example but the range could also be 0 to 100 etc.), the values Period and Year only appear in certain rows at certain ages.
However at what Age the values for Period and Year appear, changes and the solution should therefore be dynamic. What is the best way to fill in the NULL values with correct Period and Year?
I am using SQL Server.
Age Period Year
-----------------
0 NULL NULL
1 NULL NULL
2 NULL NULL
3 NULL NULL
4 NULL NULL
5 NULL NULL
6 NULL NULL
7 NULL NULL
8 NULL NULL
9 NULL NULL
10 NULL NULL
11 NULL NULL
12 NULL NULL
13 NULL NULL
14 NULL NULL
15 NULL NULL
16 NULL NULL
17 NULL NULL
18 NULL NULL
19 NULL NULL
20 NULL NULL
21 46 2065
22 NULL NULL
23 NULL NULL
24 NULL NULL
25 NULL NULL
26 51 2070
27 NULL NULL
28 NULL NULL
29 NULL NULL
30 NULL NULL
The result should look like this, the numbers for Period and Year should be increased and/or decrease from the last known values for Period and Year.
Age Period Year
-----------------
0 25 2044
1 26 2045
2 27 2046
3 28 2047
4 29 2048
5 30 2049
6 31 2050
7 32 2051
8 33 2052
9 34 2053
10 35 2054
11 36 2055
12 37 2056
13 38 2057
14 39 2058
15 40 2059
16 41 2060
17 42 2061
18 43 2062
19 44 2063
20 45 2064
21 46 2065
22 47 2066
23 48 2067
24 49 2068
25 50 2069
26 51 2070
27 52 2071
28 53 2072
29 54 2073
30 55 2074
Here is an UPDATE to my question as I didn't specify my requirement detailed enough:
The solution should be able to handle different combinations of Age, Period and Year. My start point will always be a known Age, Period and Year combination. However, the combination Age = 21, Period = 46 and Year = 2065 (or 26|51|2070 as the second combination) in my example is not static. The value at Age = 21 could be anything e.g. Period = 2 and Year = 2021. Whatever the combination (Age, Period, Year) is, the solution should fill in the gaps and finish the sequence counting up and down from the known values for Period and Year. If a Period value sequence becomes negative the solutions should return NULL values, if possible.
Seem you have always the same increment for age and year
so
select age, isnull(period,age +25) Period, isnull(year,age+44) year
from yourtable
or the standard function coalesce (as suggested by Gordon Linoff)
select age, coalesce(period,age +25) Period, coalesce(year,age+44) year
from yourtable
Tabel creation code
create table yourtable ( AGE int , Period int, Year int )
insert into yourtable
Select 0 AS AGE , null As Period , null As Year UNION all
Select 1 AS AGE , null As Period , null As Year UNION all
Select 2 AS AGE , null As Period , null As Year UNION all
Select 3 AS AGE , null As Period , null As Year UNION all
Select 4 AS AGE , null As Period , null As Year UNION all
Select 5 AS AGE , null As Period , null As Year UNION all
Select 6 AS AGE , null As Period , null As Year UNION all
Select 7 AS AGE , null As Period , null As Year UNION all
Select 8 AS AGE , null As Period , null As Year UNION all
Select 9 AS AGE , null As Period , null As Year UNION all
Select 10 AS AGE , null As Period , null As Year UNION all
Select 11 AS AGE , null As Period , null As Year UNION all
Select 12 AS AGE , null As Period , null As Year UNION all
Select 13 AS AGE , null As Period , null As Year UNION all
Select 14 AS AGE , null As Period , null As Year UNION all
Select 15 AS AGE , null As Period , null As Year UNION all
Select 16 AS AGE , null As Period , null As Year UNION all
Select 17 AS AGE , null As Period , null As Year UNION all
Select 18 AS AGE , null As Period , null As Year UNION all
Select 19 AS AGE , null As Period , null As Year UNION all
Select 20 AS AGE , null As Period , null As Year UNION all
Select 21 AS AGE ,46 As Period ,2065 As Year UNION all
Select 22 AS AGE , null As Period , null As Year UNION all
Select 23 AS AGE , null As Period , null As Year UNION all
Select 24 AS AGE , null As Period , null As Year UNION all
Select 25 AS AGE , 51 As Period ,2070 As Year UNION all
Select 26 AS AGE , null As Period , null As Year UNION all
Select 27 AS AGE , null As Period , null As Year UNION all
Select 28 AS AGE , null As Period , null As Year UNION all
Select 29 AS AGE , null As Period , null As Year UNION all
Select 30 AS AGE , null As Period , null As Year
**Steps **
We need to get one row with non null value for Period and year.
Using age get first value for both the column .
Now just add respective age column value and fill full table .
Code to fix the serial
;with tmp as
(select top 1 * from yourtable where Period is not null and year is not null)
update yourtable
set Period = (tmp.Period - tmp.age) + yourtable.age
, year = (tmp.year - tmp.age) + yourtable.age
from yourtable , tmp
OR
Declare #age int ,#Year int ,#Period int
select #age = age , #Year = year - (age +1) ,#Period = Period- (AGE +1)
from yourtable where Period is not null and year is not null
update yourtable
set Period =#Period + age
,Year =#year + age
from yourtable
You finally want three sequences with different start values. Then you simply need to calculate an offset and add it to age:
with cte as
(
select age
,max(period - age) over () + age as period -- adjusted period
,max(yr - age) over () + age as yr -- adjusted yr
from #yourtable
)
select age
-- If a Period value sequence becomes negative the solutions should return NULL
,case when period >0 then period end as period
,yr
from cte
See fiddle
-- hope you can manage the syntax error. but some logic like given below should work in this case where we can make period an origin to calculate other missing values. good luck!
declare #knownperiod int;
declare #knownperiodage int;
declare #agetop int;
declare #agebottom int;
#knownperiod = select top 1 period from table1 where period is not null
#knownperiodage = select top 1 age from table1 where period is not null
while(#knownperiodage >= 0)
begin
#knownperiod = #knownperiod -1 ;
#knownperiodage = #knownperiodage -1;
update table1 set period = #knownperiod, year = YEAR(GetDate())+#knownperiod-1 where age = #knownperiodage
end
-- now for bottom age
#knownperiod = select top 1 period from table1 where period is null or year is null
#knownperiodage = select top 1 age from table1 where period is null or year is null
while(#knownperiodage <= (Select max(age) from table1))
begin
#knownperiod = #knownperiod +1 ;
#knownperiodage = #knownperiodage +1;
update table1 set period = #knownperiod, year = YEAR(GetDate())+#knownperiod-1 where age = #knownperiodage
end
Is the process to first calculate the increments (age -> period and age -> year) then simply add those increments to the age values?
This assumes the differences between age and period, and age and year, are consistent across rows (just not filled in sometimes).
As such, you could use the following to first calculate the increments (PeriodInc, YrInc) and then select the values with the increments added (noting that if period goes negative, it gets NULL).
; WITH PeriodInc AS (SELECT TOP 1 Period - Age AS PeriodInc FROM #yourtable WHERE Period IS NOT NULL),
YrInc AS (SELECT TOP 1 Yr - Age AS YrInc FROM #yourtable WHERE Yr IS NOT NULL)
SELECT Age,
CASE WHEN (Age + PeriodInc) >= 0 THEN (Age + PeriodInc) ELSE NULL END AS Period,
Age + YrInc AS Yr
FROM #yourtable
CROSS JOIN PeriodInc
CROSS JOIN YrInc
Here is a DB_Fiddle with the code
This solution takes 4 inputs:
#list_length -- (integer) the number of rows to generate (up to 12^5=248,832)
#start_age -- (integer) beginning age
#start_period -- (integer) beginning period
#start_year -- (integer) beginning year
For any combination of inputs this code generates the requested output. If either the Age or Year is calculated to be negative then it is converted to NULL. The current limit to the list length could be increased to whatever is necessary. The technique of creating a row_number using cross applied rows is known to be very fast when generating large sequences. Above about 500 rows it's always faster than a recursion based CTE. At small row numbers there's little to no performance difference between the two techniques.
Here are the code and output to match the example data.
Inputs
declare
#list_length int=31,
#start_age int=21,
#start_period int=46,
#start_year int=2065;
Code
with
n(n) as (select * from (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) v(n)),
tally_cte(n) as (
select row_number() over (order by (select null))
from n n1 cross join n n2 cross join n n3 cross join n n4 cross join n n5)
select p.Age,
case when p.[Period]<0 then null else p.[Period] end [Period],
case when p.[Year]<0 then null else p.[Year] end [Year]
from tally_cte t
cross apply
(select (t.n-1) [Age], (t.n-1)+(#start_period-#start_age) [Period],
(t.n-1)+(#start_year-#start_age) [Year]) p
where n<=#list_length;
Output
Age Period Year
0 25 2044
1 26 2045
2 27 2046
3 28 2047
4 29 2048
5 30 2049
6 31 2050
7 32 2051
8 33 2052
9 34 2053
10 35 2054
11 36 2055
12 37 2056
13 38 2057
14 39 2058
15 40 2059
16 41 2060
17 42 2061
18 43 2062
19 44 2063
20 45 2064
21 46 2065
22 47 2066
23 48 2067
24 49 2068
25 50 2069
26 51 2070
27 52 2071
28 53 2072
29 54 2073
30 55 2074
Suppose both the Period and the Year are less than the start Age. When the calculated values are negative the value is replaced with a NULL.
Inputs
declare
#list_length int=100,
#start_age int=10,
#start_period int=5,
#start_year int=8;
Output
Age Period Year
0 NULL NULL
1 NULL NULL
2 NULL 0
3 NULL 1
4 NULL 2
5 0 3
6 1 4
7 2 5
8 3 6
9 4 7
10 5 8
11 6 9
12 7 10
...
99 94 97
Imo this is a flexible and efficient way to meet all of the requirements. Please let me know if there are any issues.
This reads like a gaps-and-islands problem, where "empty" rows are the gaps and non-empty rows are the islands.
You want to fill the gaps. Your question is a bit tricky, because you do not clearly describe how to proceed when a gap row has both preceding and following islands - and what to do if they are not consistent.
Let me assume that you want to derive the value from the following island if there is one available, and fall back of the precedng island.
Here is an approach using lateral joins to retrieve the next and preceding non-empty row:
select t.age,
coalesce(t.period, n.period - n.diff, p.period - p.diff) period,
coalesce(t.year, n.year - n.diff, p.year - p.diff) year
from mytable t
outer apply (
select top (1) t1.*, t1.age - t.age diff
from mytable t1
where t1.age > t.age and t1.period is not null and t1.year is not null
order by t1.age
) n
outer apply (
select top (1) t1.*, t1.age - t.age diff
from mytable t1
where t1.age < t.age and t1.period is not null and t1.year is not null
order by t1.age desc
) p
order by t.age
Actually, this would probably be more efficiently performed with window functions. We can implement the very same logic by building groups of records with window counts, then doing the computation within the groups:
select
age,
coalesce(
period,
max(period) over(partition by grp2) - max(age) over(partition by grp2) + age,
max(period) over(partition by grp1) - min(age) over(partition by grp1) + age
) period,
coalesce(
year,
max(year) over(partition by grp2) - max(age) over(partition by grp2) + age,
max(year) over(partition by grp1) - min(age) over(partition by grp1) + age
) year
from (
select t.*,
count(period) over(order by age) grp1,
count(period) over(order by age desc) grp2
from mytable t
) t
order by age
Demo on DB Fiddle - both queries yield:
age | period | year
--: | -----: | ---:
0 | 25 | 2044
1 | 26 | 2045
2 | 27 | 2046
3 | 28 | 2047
4 | 29 | 2048
5 | 30 | 2049
6 | 31 | 2050
7 | 32 | 2051
8 | 33 | 2052
9 | 34 | 2053
10 | 35 | 2054
11 | 36 | 2055
12 | 37 | 2056
13 | 38 | 2057
14 | 39 | 2058
15 | 40 | 2059
16 | 41 | 2060
17 | 42 | 2061
18 | 43 | 2062
19 | 44 | 2063
20 | 45 | 2064
21 | 46 | 2065
22 | 47 | 2066
23 | 48 | 2067
24 | 49 | 2068
25 | 50 | 2069
26 | 51 | 2070
27 | 52 | 2071
28 | 53 | 2072
29 | 54 | 2073
30 | 55 | 2074
Also you can use recursive CTE (it can handle any variation of data in the table except only one that has no populated period and year at all):
WITH cte AS ( -- get any filled period and year
SELECT TOP 1 period - age delta,
[year]-period start_year
FROM tablename
WHERE period is not null and [year] is not null
), seq AS ( --get min and max age values
SELECT MIN(age) as min_age, MAX(age) as max_age
FROM tablename
), go_recursive AS (
SELECT min_age age,
min_age+delta period ,
start_year+min_age+delta year,
max_age
FROM seq
CROSS JOIN cte --That will generate the initial first row
UNION ALL
SELECT age + 1,
period +1,
year + 1,
max_age
FROM go_recursive
WHERE age < max_age --This part increments the data from first row
)
SELECT age,
period,
[year]
FROM go_recursive
OPTION (MAXRECURSION 0)
-- If you know there are some limit of rows in that kind of tables
--use this row count instead 0
I'm having a issue with dates. I have a table with given from and to dates for an employee. For an evaluation, I'd like to display each date of the month with corresponding values from the second sql table.
SQL Table:
EmpNr | datefrom | dateto | hours
0815 | 01.01.2019 | 03.01.2019 | 15
0815 | 05.01.2019 | 15.01.2019 | 15
0815 | 20.01.2019 | 31.12.9999 | 40
The given employee (0815) worked during 01.01.-15.01. 15 hours, and during 20.01.-31.01. 40 hours
I'd like to have the following result:
0815 | 01.01.2019 | 15
0815 | 02.01.2019 | 15
0815 | 03.01.2019 | 15
0815 | 04.01.2019 | NULL
0815 | 05.01.2019 | 15
...
0815 | 15.01.2019 | 15
0815 | 16.01.2019 | NULL
0815 | 17.01.2019 | NULL
0815 | 18.01.2019 | NULL
0815 | 19.01.2019 | NULL
0815 | 20.01.2019 | 40
0815 | 21.01.2019 | 40
...
0815 | 31.01.2019 | 40
as for the dates, we have:
declare #year int = 2019, #month int = 1;
WITH numbers
as
(
Select 1 as value
UNion ALL
Select value + 1 from numbers
where value + 1 <= Day(EOMONTH(datefromparts(#year,#month,1)))
)
SELECT b.empnr, b.hours, datefromparts(#year,#month,numbers.value) Datum FROM numbers left outer join
emptbl b on b.empnr = '0815' and (datefromparts(#year,#month,numbers.value) >= b.datefrom and datefromparts(#year,#month,numbers.value) <= case b.dateto )
which is working quite well, yet I have the odd issue, that this code is only shoes the dates between 01.01.2019 and 03.01.2019
thank you very much in advance!
Did you check, if datefrom and dateto is in correct range?
Minimum value of DateTime field is 1753-01-01 and maximum value is 9999-12-31.
Look at your source table to check initial values.
The recursive CTE needs to begin with MIN(datefrom) and MAX(dateto):
DECLARE #t TABLE (empnr INT, datefrom DATE, dateto DATE, hours INT);
INSERT INTO #t VALUES
(815, '2019-01-01', '2019-01-03', 15),
(815, '2019-01-05', '2019-01-15', 15),
(815, '2019-01-20', '9999-01-01', 40),
-- another employee
(999, '2018-01-01', '2018-01-31', 15),
(999, '2018-03-01', '2018-03-31', 15),
(999, '2018-12-01', '9999-01-01', 40);
WITH rcte AS (
SELECT empnr
, MIN(datefrom) AS refdate
, ISNULL(NULLIF(MAX(dateto), '9999-01-01'), CURRENT_TIMESTAMP) AS maxdate -- clamp year 9999 to today
FROM #t
GROUP BY empnr
UNION ALL
SELECT empnr
, DATEADD(DAY, 1, refdate)
, maxdate
FROM rcte
WHERE refdate < maxdate
)
SELECT rcte.empnr
, rcte.refdate
, t.hours
FROM rcte
LEFT JOIN #t AS t ON rcte.empnr = t.empnr AND rcte.refdate BETWEEN t.datefrom AND t.dateto
ORDER BY rcte.empnr, rcte.refdate
OPTION (MAXRECURSION 1000) -- approx 3 years
Demo on db<>fiddle
It could be in your select, try:
SELECT b.empnr, b.hours, datefromparts(#year,#month,numbers.value) Datum
FROM numbers
LEFT OUTER JOIN emptbl b ON b.empnr = '0815' AND
datefromparts(#year,#month,numbers.value) BETWEEN b.datefrom AND b.dateto
Your CTE produces only 31 number and therefore it is showing only January dates.
declare #year int = 2019, #month int = 1;
WITH numbers
as
(
Select 1 as value
UNion ALL
Select value + 1 from numbers
where value + 1 <= Day(EOMONTH(datefromparts(#year,#month,1)))
)
SELECT *
FROM numbers
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=a24e58ef4ce522d3ec914f90907a0a9e
You can try below code,
with t0 (i) as (select 0 union all select 0 union all select 0),
t1 (i) as (select a.i from t0 a ,t0 b ),
t2 (i) as (select a.i from t1 a ,t1 b ),
t3 (srno) as (select row_number()over(order by a.i) from t2 a ,t2 b ),
tbldt(dt) as (select dateadd(day,t3.srno-1,'01/01/2019') from t3)
select tbldt.dt
from tbldt
where tbldt.dt <= b.dateto -- put your condition here
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=b16469908b323b8d1b98d77dd09bab3d
Following the previous question
I have this query:
SELECT Acc.DocTLItem.TLRef ,
Acc.DocTLItem.Debit AS deb,
Acc.DocTLItem.Credit AS cred,
info.MiladiToShamsi(Acc.DocTLItem.StartDocDate) Date,
Acc.TL.TLCode ,
Acc.DocTLItem.DocTLHeaderRef ,
Acc.DocTLHeader.Num
FROM Acc.DocTLItem
INNER JOIN Acc.TL ON Acc.DocTLItem.TLRef = Acc.TL.Id
INNER JOIN Acc.DocTLHeader ON Acc.DocTLItem.DocTLHeaderRef = Acc.DocTLHeader.Id
ORDER BY ( CASE WHEN debit > 0 THEN 0 ELSE 1 END ) ,
Acc.TL.TLCode ,
debit
Result:
TLRef deb cred Date TLCode DocTLHeaderRef Num
--------------------------------------------------------------------------
44 1 0 1396/09/12 111 16 2
44 1 0 1396/09/21 111 18 4
28 13 0 1396/09/11 982 15 1
28 10 0 1396/09/19 982 17 3
44 0 10 1396/09/19 111 17 3
44 0 1 1396/09/21 111 18 4
44 0 9 1396/09/11 111 15 1
44 0 1 1396/09/12 111 16 2
How can I Group by Date then sort by Date?
I need to generate a result set like this that debt comes first and then ordered by TLCode column after all group by date.
Expected result:
TLRef deb cred Date TLCode DocTLHeaderRef Num
--------------------------------------------------------------------------------
44 1 0 1396/09/12 111 16 2
28 13 0 1396/09/11 982 15 1
28 10 0 1396/09/19 982 17 3
44 0 9 1396/09/11 111 15 1
44 0 1 1396/09/12 111 16 2
44 0 10 1396/09/19 111 17 3
Sum 24 20
44 1 0 1396/09/21 111 18 4
44 0 1 1396/09/21 111 18 4
Sum 1 1
May be following query block can help you:
This query will work in 4 steps:
--1. Create a temporary table that we can take as base table (#TMP)
Select *
INTO #TMP
From
(
Select 44 as TLRef, 1 as deb, 0 as cred, '1396/09/12' as Date, 111 as TLCode, 16 as DocTLHeaderRef, 2 as Num Union All
Select 44 as TLRef, 1 as deb, 0 as cred, '1396/09/21' as Date, 111 as TLCode, 18 as DocTLHeaderRef, 4 as Num Union All
Select 28 as TLRef, 13 as deb, 0 as cred, '1396/09/11' as Date, 982 as TLCode, 15 as DocTLHeaderRef, 1 as Num Union All
Select 28 as TLRef, 10 as deb, 0 as cred, '1396/09/19' as Date, 982 as TLCode, 17 as DocTLHeaderRef, 3 as Num Union All
Select 44 as TLRef, 0 as deb, 10 as cred, '1396/09/19' as Date, 111 as TLCode, 17 as DocTLHeaderRef, 3 as Num Union All
Select 44 as TLRef, 0 as deb, 1 as cred, '1396/09/21' as Date, 111 as TLCode, 18 as DocTLHeaderRef, 4 as Num Union All
Select 44 as TLRef, 0 as deb, 9 as cred, '1396/09/11' as Date, 111 as TLCode, 15 as DocTLHeaderRef, 1 as Num Union All
Select 44 as TLRef, 0 as deb, 1 as cred, '1396/09/12' as Date, 111 as TLCode, 16 as DocTLHeaderRef, 2 as Num
) X
--2. Group table by "Date" and select sum of "deb", "cred" columns and insert result in another temporary table (#TMP2)
Select null as TLRef, SUM(deb) as deb, SUM(cred) as cred, Date, null as TLCode, null as DocTLHeaderRef, null as Num
INTO #TMP2
From #TMP
GROUP BY Date
--3. Union both tables to resulting table gets both detail and grouped data.
Select *
From
(
Select *, 0 as IsDetail From #TMP
Union All
Select *, 1 as IsDetail From #TMP2
) X
Order By Date,IsDetail
--4. Drop both temporary table
DROP TABLE #TMP
DROP TABLE #TMP2
You can try this for sorting.
;WITH CTE AS (
SELECT Acc.DocTLItem.TLRef ,
Acc.DocTLItem.Debit AS deb,
Acc.DocTLItem.Credit AS cred,
info.MiladiToShamsi(Acc.DocTLItem.StartDocDate) Date,
Acc.TL.TLCode ,
Acc.DocTLItem.DocTLHeaderRef ,
Acc.DocTLHeader.Num,
ROW_NUMBER() OVER(PARTITION BY Acc.DocTLItem.Debi, Acc.DocTLItem.Credit, Acc.TL.TLCode ORDER BY Acc.DocTLItem.StartDocDate ) AS RN
FROM Acc.DocTLItem
INNER JOIN Acc.TL ON Acc.DocTLItem.TLRef = Acc.TL.Id
INNER JOIN Acc.DocTLHeader ON Acc.DocTLItem.DocTLHeaderRef = Acc.DocTLHeader.Id
)
SELECT * FROM CTE
ORDER BY
RN,
( CASE WHEN deb > 0 THEN 0 ELSE 1 END ) ,
TLCode ,
[Date],
deb
I have a Table with these columns
- ProjectId
- Generation
- Expected
- CarryOver
I am trying to update my already populated table in this fashion:
Generation = Integral Part of ((Generation + CarryOver of Previous Row)/10 )
CarryOver = decimal part of ((Generation + CarryOver of Previous Row)/10 )
where Previous Row and Current Row both have same projectId
Below is the query am using to achieve this:
UPDATE TTable
SET
TTable.Expected=(TTable.Generation+ ISNULL(STable.CarryOver,0)),
TTable.CarryOver =(TTable.Generation+ISNULL(STable.CarryOver,0))-CONVERT(INT,(TTable.Generation+ISNULL(STable.CarryOver,0)))
FROM
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
)TTable,
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) STable
Where
TTable.RowNumber = STable.RowNumber+1 AND
TTable.ProjectId = STable.ProjectId
....but something strange happens, update happens only for first two rows. For other rows,
ISNULL(STable.CarryOver,0) returns 0. why??
Please help me. or suggest some other way to achieve this
EDIT: on running the query
ProjectId MonthYear Month Year Generation Expected CarryOver
10 2011-10-01 00:00:00.000 10 2011 56.748 56 0.748
10 2011-11-01 00:00:00.000 11 2011 12.004 12 0.752
10 2011-12-01 00:00:00.000 12 2011 10.632 10 0.632
10 2012-01-01 00:00:00.000 01 2012 11.928 11 0.928
10 2012-02-01 00:00:00.000 02 2012 7.580 7 0.580
100 2011-12-01 00:00:00.000 12 2011 5.897 5 0.897
100 2012-01-01 00:00:00.000 01 2012 0.881 1 0.778
data is generated as shown above. notice how the logic doesn't work after 3row
Original Ouput. Before running the update query:
ProjectId MonthYear Month Year Generation Expected CarryOver
10 2011-10-01 00:00:00.000 10 2011 56.748 56 0.748
10 2011-11-01 00:00:00.000 11 2011 12.004 NULL NULL
10 2011-12-01 00:00:00.000 12 2011 10.632 NULL NULL
10 2012-01-01 00:00:00.000 01 2012 11.928 NULL NULL
10 2012-02-01 00:00:00.000 02 2012 7.580 NULL NULL
100 2011-12-01 00:00:00.000 12 2011 5.897 5 0.897
100 2012-01-01 00:00:00.000 01 2012 0.881 NULL NULL
The following query will give you the expected results
(Test code https://gist.github.com/1969171 for those that want to play)
PID Date M Y Generation Expected CarryOver
10 2011-10-01 10 2011 56.748 56 0.748
10 2011-11-01 11 2011 12.004 12 0.752
10 2011-12-01 12 2011 10.632 11 0.384
10 2012-01-01 01 2012 11.928 12 0.312
10 2012-02-01 02 2012 7.58 7 0.892
100 2011-12-01 12 2011 5.897 5 0.897
100 2012-01-01 01 2012 0.881 1 0.778
DECLARE rowItems CURSOR FOR
SELECT ProjectId, [Month], [Year], Generation FROM TTable
ORDER BY ProjectId,[Year] ,CAST([Month] as int)
DECLARE #p int, #m VARCHAR(5), #y int, #g FLOAT, #priorP int,
#carryOver FLOAT, #expected FLOAT
OPEN rowItems
FETCH NEXT FROM rowItems INTO #p, #m, #y, #g
SET #priorP = -1
SET #carryOver = 0.0
WHILE ##FETCH_STATUS = 0
BEGIN
IF NOT #p = #priorP SET #carryOver = 0.0
SET #expected = #g+#carryOver
SET #carryOver = ROUND(#expected-FLOOR(#expected),3,0)
UPDATE TTable
SET EXPECTED = FLOOR(#expected), CarryOver = #carryOver
WHERE ProjectId = #p and [Month] = #m and [Year] = #y
SET #priorP = #p
FETCH NEXT FROM rowItems INTO #p, #m, #y, #g
END
CLOSE rowItems
DEALLOCATE rowItems
SELECT * FROM TTable
You need to do this in a loop. The query you use is getting the original value of the carry over column not the updated value.
Sorry about the confutation. Here is my suggestion.
Test data
CREATE TABLE TTable
(
ProjectId INT,
MonthYear DATETIME,
Month VARCHAR(5),
Year INT,
Generation FLOAT,
Expected FLOAT,
CarryOver FLOAT
)
INSERT INTO TTable
VALUES
(10,'2011-10-01 00:00:00.000','10',2011,56.748,56,0.748),
(10,'2011-11-01 00:00:00.000','11',2011,12.004,NULL,NULL),
(10,'2011-12-01 00:00:00.000','12',2011,10.632,NULL,NULL),
(10,'2012-01-01 00:00:00.000','01',2012,11.928,NULL,NULL),
(10,'2012-02-01 00:00:00.000','02',2012,7.580,NULL,NULL),
(100,'2011-12-01 00:00:00.000','12',2011,5.897,5,0.897),
(100,'2012-01-01 00:00:00.000','01',2012,0.881,NULL,NULL)
Query
;WITH CTE
AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY tbl.ProjectId,tbl.Month) AS RowNbr,
tbl.ProjectId,
tbl.MonthYear,
tbl.Month,
tbl.Year,
tbl.Generation,
(
FLOOR(tbl.Generation)
) AS Expected,
(
ROUND(tbl.Generation-FLOOR(tbl.Generation),3,0)
)AS CarryOver
FROM
TTable AS tbl
)
SELECT
CTE.ProjectId,
CTE.MonthYear,
CTE.Month,
CTE.Year,
CTE.Generation,
CTE.Expected,
(
CTE.CarryOver+ISNULL(Previus.CarryOver,0)
) AS CarryOver
FROM
CTE
LEFT JOIN CTE AS Previus
ON CTE.RowNbr=Previus.RowNbr+1
Result
ProjectId MonthYear Month Year Generation Expected CarryOver
10 2011-10-01 00:00:00.000 10 2011 56,748 56 0,748
10 2011-11-01 00:00:00.000 11 2011 12,004 12 0,752
10 2011-12-01 00:00:00.000 12 2011 10,632 10 0,636
10 2012-01-01 00:00:00.000 01 2012 11,928 11 1,56
10 2012-02-01 00:00:00.000 02 2012 7,58 7 1,508
100 2011-12-01 00:00:00.000 12 2011 5,897 5 1,477
100 2012-01-01 00:00:00.000 01 2012 0,881 0 1,778
Update query
;WITH CTE
AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY tbl.ProjectId,tbl.Month) AS RowNbr,
tbl.ProjectId,
tbl.MonthYear,
tbl.Month,
tbl.Year,
tbl.Generation,
(
FLOOR(tbl.Generation)
) AS Expected,
(
ROUND(tbl.Generation-FLOOR(tbl.Generation),3,0)
)AS CarryOver
FROM
TTable AS tbl
)
UPDATE TTable
SET TTable.CarryOver=CTE.CarryOver+ISNULL(Previus.CarryOver,0),
TTable.Expected=CTE.Expected
FROM
(
SELECT
ROW_NUMBER() OVER(ORDER BY TTable.ProjectId,
TTable.Month) AS RowNbr,
TTable.CarryOver,
TTable.Expected
FROM
TTable
) AS TTable
JOIN CTE
ON TTable.RowNbr=CTE.RowNbr
LEFT JOIN CTE AS Previus
ON CTE.RowNbr=Previus.RowNbr+1
Now when selecting for the updated table. Then you will get this result:
ProjectId MonthYear Month Year Generation Expected CarryOver
10 2011-10-01 00:00:00.000 10 2011 56,748 56 0,748
10 2011-11-01 00:00:00.000 11 2011 12,004 12 0,752
10 2011-12-01 00:00:00.000 12 2011 10,632 10 0,636
10 2012-01-01 00:00:00.000 01 2012 11,928 11 1,56
10 2012-02-01 00:00:00.000 02 2012 7,58 7 1,508
100 2011-12-01 00:00:00.000 12 2011 5,897 5 1,477
100 2012-01-01 00:00:00.000 01 2012 0,881 0 1,778
This is how I did it:
For updating the first record corresponding to each ProjectId:
Update TTable
SET
TTable.Expected=(TTable.Generation/10),
TTable.CarryOver =(TTable.Generation/10)-CONVERT(INT,TTable.Generation/10)
FROM
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) TTable where TTable.RowNumber in
(
Select Min(TTable.RowNumber)as RowNumber From
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) as TTable GROUP by ProjectId
)
And for updating rest of the records:
DECLARE #RowNumber INT = -99
DECLARE #CarryOver decimal(4,3) =0
DECLARE #Expected INT =0
DECLARE #ProjectId int =0
WHILE #RowNumber IS NOT NULL
BEGIN
SET #RowNumber= (SELECT MIN(RowNumber) FROM (
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) as TTable
WHERE TTable.RowNumber> #RowNumber)
IF #RowNumber IS NULL
BREAK
SELECT #CarryOver= TTable.CarryOver,#ProjectId=TTable.ProjectId FROM
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) as TTable where TTable.RowNumber = #RowNumber
UPDATE TTable
SET TTable.Expected =(TTable.Generation+#CarryOver)/10,
CarryOver = (TTable.Generation+#CarryOver)/10 - CONVERT(INT,(TTable.Generation+#CarryOver)/10)
FROM
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) as TTable where TTable.RowNumber = #RowNumber+1 AND TTable.ProjectId=#ProjectId
END
I have doubts in sql query.
I have slots table. It basically contain maximum slots ,maximum slots for am and Pm
DayName slots AM PM
1 Monday 50 30 20
2 Tuesday 50 30 20
3 Wednesday 50 30 20
4 Thursday 50 30 20
5 Friday 25 25 0
6 Saturday 15 15 0
7 Sunday 0 0 0
I have appointment table. This table is used for adding appointment
table structure
Appointdate iS_AM
8/7/2011 12:00:00 AM 1
8/5/2011 12:00:00 AM 1
8/6/2011 12:00:00 AM 1
8/2/2011 12:00:00 AM 1
8/2/2011 12:00:00 AM 1
8/2/2011 12:00:00 AM 0
8/3/2011 12:00:00 AM 0
8/4/2011 12:00:00 AM 1
8/4/2011 12:00:00 AM 0
If it is 1 it is Am else PM.
I need to display remaining available slots for the next four days.
I need to avoid sundays.
How can we avoid sundays.
my query so far is this
with cte as
(
select dateName(dw,appoint_date) dayN,convert(varchar(12),appoint_date,101) appoint_date, sum(case is_am when 1 then 1 else 0 end) as AM,
sum(case is_am when 0 then 1 else 0 end) as PM ,sum (case is_am when 0 then 1 when 1 then 1 end) as Total
from pda_appoint where
convert(varchar(12),appoint_date,111) between
Convert(varchar(10), getdate() ,111) and Convert(varchar(10), dateadd(dd,3,getdate()) ,111)
group by appoint_date
)
select p.AM-cte.AM as [Rem AM],p.PM-cte.PM as [Rem PM],p.slots-cte.Total as [Rem Total] from cte inner join pda_slots p on cte.dayN=day_name
Output is as follows
remMax remAm remPM
28 19 47
30 19 49
29 19 48
23 0 23
I need to avoid sundays when calculating next four days and is my sql query is correct
How about that.
SELECT TOP 4
dateName(dw,a.appoint_date) dayN,
(s.AM - SUM(case a.is_am when 1 then 1 else 0 end)) AS Remaining AM,
(s.PM - SUM(case a.is_am when 0 then 1 else 0 end)) as Remaining PM,
(s.slots - COUNT(a.is_am)) AS Remaining Total Slots
FROM
pda_appoint a, slot s
WHERE
dateName(dw,a.appoint_date) = s.DayName
AND dateName(dw,a.appoint_date) != 'Sunday'
AND a.appoint_date > GETDATE()
GROUP BY a.appoint_date
ORDER BY a.appoint_date
How about this
declare #t table (DayName1 varchar(25), slots int, am int, pm int)
insert #t values('Monday',50,30,20)
insert #t values('Tuesday',50,30,20)
insert #t values('Wednesday',50,30,20)
insert #t values('Thursday',50,30,20)
insert #t values('Friday',50,30,20)
insert #t values('Saturday',50,30,20)
insert #t values('Sunday',50,30,20)
declare #t1 table (appoint_date datetime, is_am int)
insert #t1 values('8/9/2011',0)
insert #t1 values('8/10/2011',0)
insert #t1 values('8/10/2011',1)
/* You can create the below as a Table valued function that will return the values for next 4 days .you need to pass #appoint_date as a parameter*/
declare #appoint_date datetime
set #appoint_date='8/6/2011'
;with cte as
(
select dateName(dw,#appoint_date) dayN,
convert(varchar(12),#appoint_date,101) appoint_date,
1 as num
Union all
select
dateName(dw,DATEADD(day, 1, appoint_date)) dayN,
convert(varchar(12),DATEADD(day, 1, appoint_date),101) appoint_date,
num+1
from cte
where num<5
)
select top 4 dayN,(c.AM-temp.AM) as AM,(c.PM-temp.PM) as PM,(c.Slots-Temp.Total) as Total
from
(
select TOP 4
dateName(dw,a.appoint_date) dayN,
SUM(case b.is_am when 1 then 1 else 0 end) AS AM,
SUM(case b.is_am when 0 then 1 else 0 end) as PM,
COUNT(b.is_am) AS Total
from cte a left outer join #t1 b
on a.appoint_date=b.appoint_date
where a.dayN !='Sunday'
group by a.appoint_date
)Temp
inner join #t c on Temp.dayN=c.dayname1
dayN AM PM Total
Saturday 30 20 50
Monday 30 20 50
Tuesday 30 19 49
Wednesday 29 19 48