SQL Server time sheet calculation - sql-server

I have a time punch program the outputs the data set below. RECTYP_43 are the (1) in and (2) out punches. I need a query to look at the look at the LOGINDATE_43 and LOGINTIME_43 and the RECTYPE_43 and get the difference between 1 and 2.
I thought this would be easier than it has proven to be.
empid_43 RECTYPE_43 LOGINDATE_43 LOGINTIME_43
------------------------------------------------------------
127 1 2016-10-21 00:00:00.000 0558
127 2 2016-10-21 00:00:00.000 1430
127 2 2016-10-21 00:00:00.000 1201
127 1 2016-10-21 00:00:00.000 1228
127 1 2016-10-24 00:00:00.000 0557
127 2 2016-10-24 00:00:00.000 1200
127 1 2016-10-24 00:00:00.000 1228
127 2 2016-10-24 00:00:00.000 1430
2589 2 2016-10-21 00:00:00.000 1431
2589 1 2016-10-21 00:00:00.000 0556
2589 1 2016-10-24 00:00:00.000 0550
2589 2 2016-10-24 00:00:00.000 1431
2589 2 2016-10-24 00:00:00.000 1201
2589 1 2016-10-24 00:00:00.000 1226
69 1 2016-10-24 00:00:00.000 1229
69 2 2016-10-24 00:00:00.000 1430
69 1 2016-10-24 00:00:00.000 0555
69 2 2016-10-24 00:00:00.000 1200

You can use a CTE to get all the punch-ins and then a subquery to find the first punch out that comes after that time...
;WITH ctePunchIn AS (
SELECT empid_43, LOGINDATE_43 AS Date_In, LOGINTIME_43 AS Time_In
FROM #Table1
WHERE [RECTYPE_43] = 1
)
SELECT
empid_43, Date_In, Time_In
,(SELECT TOP 1 LOGINTIME_43 FROM #Table1 WHERE
(empid_43 = ctePunchIn.empid_43)
AND
(LOGINDATE_43 = ctePunchIn.Date_In)
AND
(LOGINTIME_43 > ctePunchIn.Time_In)
AND
(RECTYPE_43 = 2)
ORDER BY empid_43, Date_In, LOGINTIME_43) AS Time_Out
FROM
ctePunchIn

Dazedandconfused's answer works if the logout Time is the same date as the login time, but if the user logs out on a different day to logging in, it will not work.
e.g.
INSERT into Punch (empId_43, RecType_43, LoginDate_43, LoginTime_43)
VALUES (15, 1, '2016-01-01', '2305'),
(15, 2, '2016-01-02', '0005');
In order to accomodate for this, you need to know what the next item in the table is for that employee. And with that, you can ensure that the next item is also a logout event. This will help capture situations where someone has forgotten to punch out.
Extending the CTE can provide a more complete solution:
WITH Data AS
(
SELECT empId_43,
RecType_43,
LoginDate_43,
LoginTime_43,
RowNum = ROW_NUMBER() OVER (PARTITION BY empId_43
ORDER BY LoginDate_43, LoginTime_43)
FROM Punch
)
SELECT PIn.empId_43 [Employee],
PIn.LoginDate_43 [LoginDate],
PIn.LoginTime_43 [LoginTime],
POut.LoginDate_43 [LogoutDate],
POut.LoginTime_43 [LogoutTime]
FROM Data PIn
LEFT JOIN Data POut ON PIn.empId_43 = POut.empId_43
AND POut.RecType_43 = 2
AND POut.RowNum = PIn.RowNum + 1
WHERE PIn.RecType_43 = 1
ORDER BY PIn.empId_43, PIn.LoginDate_43, PIn.LoginTime_43;
However, Row_Number can be inefficient. Doing this is best when looking at a small subset (e.g. a particular date range, etc).

slightly different way of doing it:
select
punchIn.empid_43,
punchIn.login as dateTime_in,
punchout.login as dateTime_out
from
(
SELECT empId_43,
RecType_43,
LoginDate_43,
LoginTime_43,
dateadd('n',right(logintime_43,2),
dateadd('hh',left(LoginTime_43,2),
LoginDate_43)) as login,
RowNum = ROW_NUMBER() OVER (PARTITION BY empId_43
ORDER BY LoginDate_43, LoginTime_43)
FROM Punch
where rectype_43 = 1
) punchIn left outer join
(
SELECT empId_43,
RecType_43,
LoginDate_43,
LoginTime_43,
dateadd('n',right(logintime_43,2),
dateadd('hh',left(LoginTime_43,2),
LoginDate_43)) as login,
RowNum = ROW_NUMBER() OVER (PARTITION BY empId_43
ORDER BY LoginDate_43, LoginTime_43)
FROM Punch
where rectype_43 = 2
) punchOut on
punchin.empID = punchout.empID and
punchin.rownum = punchout.rownum
assuming all punchin rows have a corresponding punchout row

Related

SQL Server - Convert record per day into date range (with gaps)

I have found a lot of questions and answers asking how to convert a date range to records per day, but I need the opposite and can't find anything yet.
So let's say I have this dataset:
User | Available
1 | 01-01-2019
1 | 02-01-2019
1 | 03-01-2019
1 | 04-01-2019
2 | 05-01-2019
2 | 06-01-2019
2 | 07-01-2019
2 | 10-01-2019
2 | 11-01-2019
2 | 12-01-2019
So we have user 1 who is available from 01/01/2019 to 04/01/2019. Then we have user 2 who is available from 05/01/2019 to 07/01/2019 and 10/01/2019 to 12/01/2019.
The result I am looking for should look like this:
User | Start | End
1 | 01-01-2019 | 04-01-2019
2 | 05-01-2019 | 07-01-2019
2 | 10-01-2019 | 12-01-2019
User 1 was fairly easy to calculate using min/max dates, but with the gaps of user 2, I am completely lost. Any suggestions?
I had to do this before somewhere too, this is the solution I used. Basically use a row number split by your grouping columns and ordered by date, and additionally calculate the amount of days from a particular date onwards (any hard-coded day will work).
The key here is that while the row number increases 1 by 1, the anchor difference will only increase 1 by 1 if the days are consecutive. Thus, the rest between the anchor diff and the row number will stay the same only if there are consecutive dates, allowing you to group by and calculate min/max.
IF OBJECT_ID('tempdb..#Availabilities') IS NOT NULL
DROP TABLE #Availabilities
CREATE TABLE #Availabilities (
[User] INT,
Available DATE)
INSERT INTO #Availabilities
VALUES
(1, '2019-01-01'),
(1, '2019-01-02'),
(1, '2019-01-03'),
(1, '2019-01-04'),
(2, '2019-01-05'),
(2, '2019-01-06'),
(2, '2019-01-07'),
(2, '2019-01-10'),
(2, '2019-01-11'),
(2, '2019-01-12')
;WITH WindowFunctions AS
(
SELECT
A.[User],
A.Available,
AnchorDayDifference = DATEDIFF(DAY, '2018-01-01', A.Available),
RowNumber = ROW_NUMBER() OVER (PARTITION BY A.[User] ORDER BY A.Available)
FROM
#Availabilities AS A
)
SELECT
T.[User],
Start = MIN(T.Available),
[End] = MAX(T.Available)
FROM
WindowFunctions AS T
GROUP BY
T.[User],
T.AnchorDayDifference - T.RowNumber
Result:
User Start End
1 2019-01-01 2019-01-04
2 2019-01-05 2019-01-07
2 2019-01-10 2019-01-12
The WindowFunctions values are (added the posterior rest result):
User Available AnchorDayDifference RowNumber GroupingRestResult
1 2019-01-01 365 1 364
1 2019-01-02 366 2 364
1 2019-01-03 367 3 364
1 2019-01-04 368 4 364
2 2019-01-05 369 1 368
2 2019-01-06 370 2 368
2 2019-01-07 371 3 368
2 2019-01-10 374 4 370
2 2019-01-11 375 5 370
2 2019-01-12 376 6 370
This is a "common" Groups and Island question. Provided you're on SQL Server 2012+ (and if you're not, it's time to upgrade) this gets you the result you're after:
USE Sandbox;
GO
WITH VTE AS(
SELECT V.[User],
CONVERT(date,Available,105) AS Available
FROM (VALUES(1,'01-01-2019'),
(1,'02-01-2019'),
(1,'03-01-2019'),
(1,'04-01-2019'),
(2,'05-01-2019'),
(2,'06-01-2019'),
(2,'07-01-2019'),
(2,'10-01-2019'),
(2,'11-01-2019'),
(2,'12-01-2019')) V([User],Available)),
Diffs AS(
SELECT V.[User],
V.Available,
DATEDIFF(DAY, LAG(V.Available,1,DATEADD(DAY, -1, V.Available)) OVER (PARTITION BY V.[User] ORDER BY V.Available), V.Available) AS Diff
FROM VTE V),
Groups AS(
SELECT D.[User],
D.Available,
COUNT(CASE WHEN D.Diff > 1 THEN 1 END) OVER (PARTITION BY D.[User] ORDER BY D.Available
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Grp
FROM Diffs D)
SELECT G.[User],
MIN(G.Available) AS [Start],
MAX(G.Available) AS [End]
FROM Groups G
GROUP BY G.[User],
G.Grp
ORDER BY G.[User],
[Start];
The first CTE Diffs, excluding VTE ("Value Table Expression") for the sample data, gets the difference in days between the different rows. The second CTE Groups then puts the dates into groups (surprise that), base on if the difference was more than 1. Then we can use those groups to get a MIN and MAX for that group in the final SELECT.
I'm reading as MONTHS not DAYS
Example
Select [User]
,[Start] = min([Available])
,[End] = max([Available])
From (
Select *
,Grp = DateDiff(MONTH,'1900-01-01',[Available]) - Row_Number() over (Partition By [User] Order by [Available])
From YourTable
) A
Group By [User],[Grp]
Returns
User Start End
1 2019-01-01 2019-04-01
2 2019-05-01 2019-07-01
2 2019-10-01 2019-12-01

13 Period Calendar 4-4-5 Calendar T-SQL MSSQL

I am trying to create a 13 period calendar in mssql but I am a bit stuck. I am not sure if my approach is the best way to achieve this. I have my base script which can be seen below:
Set DateFirst 1
Declare #Date1 date = '20180101' --startdate should always be start of
financial year
Declare #Date2 date = '20181231' --enddate should always be start of
financial year
SELECT * INTO #CalendarTable
FROM dbo.CalendarTable(#Date1,#Date2,0,0,0)c
DECLARE #StartDate datetime,#EndDate datetime
SELECT #StartDate=MIN(CASE WHEN [Day]='Monday' THEN [Date] ELSE NULL END),
#EndDate=MAX([Date])
FROM #CalendarTable
;With Period_CTE(PeriodNo,Start,[End])
AS
(SELECT 1,#StartDate,DATEADD(wk,4,#StartDate) -1
UNION ALL
SELECT PeriodNo+1,DATEADD(wk,4,Start),DATEADD(wk,4,[End])
FROM Period_CTE
WHERE DATEADD(wk,4,[End])< =#EndDate
OR PeriodNo+1 <=13
)
select * from Period_CTE
Which gives me this:
PeriodNo Start End
1 2018-01-01 00:00:00.000 2018-01-28 00:00:00.000
2 2018-01-29 00:00:00.000 2018-02-25 00:00:00.000
3 2018-02-26 00:00:00.000 2018-03-25 00:00:00.000
4 2018-03-26 00:00:00.000 2018-04-22 00:00:00.000
5 2018-04-23 00:00:00.000 2018-05-20 00:00:00.000
6 2018-05-21 00:00:00.000 2018-06-17 00:00:00.000
7 2018-06-18 00:00:00.000 2018-07-15 00:00:00.000
8 2018-07-16 00:00:00.000 2018-08-12 00:00:00.000
9 2018-08-13 00:00:00.000 2018-09-09 00:00:00.000
10 2018-09-10 00:00:00.000 2018-10-07 00:00:00.000
11 2018-10-08 00:00:00.000 2018-11-04 00:00:00.000
12 2018-11-05 00:00:00.000 2018-12-02 00:00:00.000
13 2018-12-03 00:00:00.000 2018-12-30 00:00:00.000
The result i am trying to get is
Even if I have to take a different approach I would not mind, as long as the result is the same as the above.
dbo.CalendarTable() is a function that returns the following results. I can share the code if desired.
I'd create a general number's table like suggested here and add a column Periode13.
The trick to get the tiling is the integer division:
DECLARE #PeriodeSize INT=28; --13 "moon-months" a 28 days
SELECT TOP 100 (ROW_NUMBER() OVER(ORDER BY (SELECT NULL))-1)/#PeriodeSize
FROM master..spt_values --just a table with many rows to show the principles
You can add this to an existing numbers table with a simple update statement.
UPDATE A fully working example (using the logic linked above)
DECLARE #RunningNumbers TABLE (Number INT NOT NULL
,CalendarDate DATE NOT NULL
,CalendarYear INT NOT NULL
,CalendarMonth INT NOT NULL
,CalendarDay INT NOT NULL
,CalendarWeek INT NOT NULL
,CalendarYearDay INT NOT NULL
,CalendarWeekDay INT NOT NULL);
DECLARE #CountEntries INT = 100000;
DECLARE #StartNumber INT = 0;
WITH E1(N) AS(SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(N)), --10 ^ 1
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b), -- 10 ^ 2 = 100 rows
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b), -- 10 ^ 4 = 10,000 rows
E8(N) AS(SELECT 1 FROM E4 a CROSS JOIN E4 b), -- 10 ^ 8 = 10,000,000 rows
CteTally AS
(
SELECT TOP(ISNULL(#CountEntries,1000000)) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) -1 + ISNULL(#StartNumber,0) As Nmbr
FROM E8
)
INSERT INTO #RunningNumbers
SELECT CteTally.Nmbr,CalendarDate.d,CalendarExt.*
FROM CteTally
CROSS APPLY
(
SELECT DATEADD(DAY,CteTally.Nmbr,{ts'2018-01-01 00:00:00'})
) AS CalendarDate(d)
CROSS APPLY
(
SELECT YEAR(CalendarDate.d) AS CalendarYear
,MONTH(CalendarDate.d) AS CalendarMonth
,DAY(CalendarDate.d) AS CalendarDay
,DATEPART(WEEK,CalendarDate.d) AS CalendarWeek
,DATEPART(DAYOFYEAR,CalendarDate.d) AS CalendarYearDay
,DATEPART(WEEKDAY,CalendarDate.d) AS CalendarWeekDay
) AS CalendarExt;
--The mockup table from above is now filled and can be queried
WITH AddPeriode AS
(
SELECT Number/28 +1 AS PeriodNumber
,CalendarDate
,CalendarWeek
,r.CalendarDay
,r.CalendarMonth
,r.CalendarWeekDay
,r.CalendarYear
,r.CalendarYearDay
FROM #RunningNumbers AS r
)
SELECT TOP 100 p.*
,(SELECT MIN(CalendarDate) FROM AddPeriode AS x WHERE x.PeriodNumber=p.PeriodNumber) AS [Start]
,(SELECT MAX(CalendarDate) FROM AddPeriode AS x WHERE x.PeriodNumber=p.PeriodNumber) AS [End]
,(SELECT MIN(CalendarDate) FROM AddPeriode AS x WHERE x.PeriodNumber=p.PeriodNumber AND x.CalendarWeek=p.CalendarWeek) AS [wkStart]
,(SELECT MAX(CalendarDate) FROM AddPeriode AS x WHERE x.PeriodNumber=p.PeriodNumber AND x.CalendarWeek=p.CalendarWeek) AS [wkEnd]
,(ROW_NUMBER() OVER(PARTITION BY PeriodNumber ORDER BY CalendarDate)-1)/7+1 AS WeekOfPeriode
FROM AddPeriode AS p
ORDER BY CalendarDate
Try it out...
Hint: Do not use a VIEW or iTVF for this.
This is non-changing data and much better placed in a physically stored table with appropriate indexes.
Not abundantly sure external links are accepted here, but I wrote an article that pulls of a 5-4-4 'Crop Year' fiscal year with all the code. Feel free to use all the code in these articles.
SQL Server Calendar Table
SQL Server Calendar Table: Fiscal Years

How to show order fulfilment in a SQL Server 2008 query

I am trying to think of a way on a SQL Server 2008 database to run through a sales order table and get open demand for a part, order it by due date, then look at a purchase order table and fulfill the sales orders by PO, ordering the PO supply by due date as well. At the same time, I need to show what PO(s) are fulfilling the sales order.
For example:
SO table
SO# DueDate Part Number Required QTY
---------------------------------------------
100 9/3/16 1012 2
101 9/12/16 1012 1
107 10/11/16 1012 4
103 10/17/16 1012 7
PO table:
PO# DueDate Part Number Ordered QTY
--------------------------------------------
331 9/1/16 1012 1
362 9/2/16 1012 1
359 9/24/16 1012 5
371 10/1/16 1012 3
380 10/10/16 1012 10
With this data, I would like to see this result:
SO# DueDate Part Number Required QTY PO number QTY Used QTY Remain
--------------------------------------------------------------------------
100 9/3/16 1012 2 331 1 0
100 9/3/16 1012 1 362 1 0
101 9/12/16 1012 1 359 1 4
107 10/11/16 1012 4 359 4 0
103 10/17/16 1012 7 371 3 0
103 10/17/16 1012 7 380 4 6
I have done this sales order fulfillment process before, but not to the point of breaking down what PO(s) are fulfilling the order, only to the point of summing all open supply, then running through and subtracting the supply from each sales order to get a running balance of supply left.
Many thanks in advance for your help.
I found a bit weird solution, hope it helps you. Maybe later I could optimize it, but now I post it as is:
;WITH cte AS (
SELECT 1 as l
UNION ALL
SELECT l+1
FROM cte
WHERE l <= 1000000
), SO_cte AS (
SELECT *,
ROW_NUMBER() OVER (ORDER BY DueDate ASC) as rn
FROM SO s
CROSS JOIN cte c
WHERE c.l <= s.[Required QTY]
), PO_cte AS (
SELECT *,
ROW_NUMBER() OVER (ORDER BY DueDate ASC) as rn
FROM PO p
CROSS JOIN cte c
WHERE c.l <= p.[Ordered QTY]
), almost_done AS (
SELECT DISTINCT
s.SO#,
s.DueDate,
s.[Part Number],
p.PO#,
s.[Required QTY],
p.[Ordered QTY]
FROM SO_cte s
LEFT JOIN PO_cte p
ON p.rn = s.rn
), final AS (
SELECT *,
ROW_NUMBER() OVER (ORDER BY DueDate) AS RN
FROM almost_done
)
SELECT f.SO#,
f.DueDate,
f.[Part Number],
f.[Required QTY],
f.PO#,
CASE WHEN f.[Ordered QTY]>f.[Required QTY]
THEN ISNULL(ABS(f1.[Required QTY]-f1.[Ordered QTY]),f.[Required QTY])
ELSE f.[Ordered QTY] END
as [QTY Used],
f.[Ordered QTY] -
CASE WHEN f1.PO# = f.PO#
THEN f1.[Ordered QTY]
ELSE
CASE WHEN f.[Ordered QTY]>f.[Required QTY]
THEN ISNULL(ABS(f1.[Required QTY]-f1.[Ordered QTY]),f.[Required QTY])
ELSE f.[Ordered QTY] END
END as [QTY Remain]
FROM final f
LEFT JOIN final f1
ON f.RN = f1.RN+ 1
AND (f.SO# = f1.SO# OR f.PO# = f1.PO#)
OPTION(MAXRECURSION 0)
Output for data you provided:
SO# DueDate Part Number Required QTY PO# QTY Used QTY Remain
100 2016-09-03 1012 2 331 1 0
100 2016-09-03 1012 2 362 1 0
101 2016-09-12 1012 1 359 1 4
107 2016-10-11 1012 4 359 4 0
103 2016-10-17 1012 7 371 3 0
103 2016-10-17 1012 7 380 4 6

Fetching dates monthly from the table

Can you help out with a problem
I have table price table which has daily prices starting 31st Dec 2010 till todays date.The table contains daily prices
2009-12-31 00:00:00.000 1.0020945351
2010-01-01 00:00:00.000 1.0021009300
2010-01-04 00:00:00.000 1.0021910181
2010-01-05 00:00:00.000 1.0022005986
2010-01-06 00:00:00.000 1.0022428696
2010-01-07 00:00:00.000 1.0022647147
2010-01-08 00:00:00.000 1.0022842726
2010-01-11 00:00:00.000 1.0023374302
2010-01-12 00:00:00.000 1.0023465374
2010-01-13 00:00:00.000 1.0023638081
2010-01-14 00:00:00.000 1.0023856533
2010-01-00 00:00:00.000 1.0024083955
2010-01-18 00:00:00.000 1.0024779677
2010-01-19 00:00:00.000 1.0025020553
2010-01-20 00:00:00.000 1.002521135
2010-01-21 00:00:00.000 1.0025420688
2010-01-22 00:00:00.000 1.0025593397
2010-01-25 00:00:00.000 1.0026180146
2010-01-26 00:00:00.000 1.002637573
2010-01-27 00:00:00.000 1.0026648447
2010-01-28 00:00:00.000 1.0026957934
2010-01-29 00:00:00.000 1.0027267421
2010-02-01 00:00:00.000 1.0028195885
2010-02-02 00:00:00.000 1.0028573523
2010-02-03 00:00:00.000 1.0028964611
2010-02-04 00:00:00.000 1.00293557
2010-02-05 00:00:00.000 1.002973334
2010-02-08 00:00:00.000 1.0030879717
2010-02-09 00:00:00.000 1.0031279777
2010-02-10 00:00:00.000 1.003171166
2010-02-11 00:00:00.000 1.0032007452
2010-02-12 00:00:00.000 1.0032575895
2010-02-00 00:00:00.000 1.0033749191
2010-02-1 00:00:00.000 1.0034140292
2010-02-17 00:00:00.000 1.003452691
2010-02-18 00:00:00.000 1.0034918013
2010-02-19 00:00:00.000 1.0035395633
2010-02-22 00:00:00.000 1.0036664439
2010-02-23 00:00:00.000 1.0037042097
2010-02-24 00:00:00.000 1.0037510759
2010-02-25 00:00:00.000 1.0038001834
2010-02-26 00:00:00.000 1.003850077
I need to write a query to get index based on
(Last day of current month/Previous month last day) - 1 * 100.So that output comes something like this
31-Jan-10 0.01%
28-Feb-10 0.02%
31-Mar-10 0.00%
Following is one of the solution I thought about however please share best ideas to implement this problem
Extract last day of all the months with values into a temp table and then order by dates so that they subtract and put the values into another temp table
Looking forward to your help.
Try this....
DECLARE #StartDate DATETIME = '2010-01-01',
#EndDate DATETIME = GETDATE();
WITH data AS (
SELECT 1 AS i, CONVERT(DATETIME, NULL) AS StartDate, DATEADD(MONTH, 0, #StartDate) - 1 AS EndDate
UNION ALL
SELECT i + 1, data.EndDate, DATEADD(MONTH, i, #StartDate) - 1 AS EndDate
FROM data
WHERE DATEADD(MONTH, i, #StartDate) - 1 < #EndDate
)
SELECT (
((SELECT TOP 1 Rate FROM RateTable WHERE Date <= data.EndDate ORDER BY Date DESC) /
(SELECT TOP 1 Rate FROM RateTable WHERE Date <= data.StartDate ORDER BY Date DESC)- 1) * 100)
FROM DATA -- parenthesis were causing issues
WHERE data.StartDate IS NOT NULL
OPTION (MAXRECURSION 10000);
You'll need to replace the
(SELECT Rate FROM RateTable WHERE Date = data.StartDate)
and
(SELECT Rate FROM RateTable WHERE Date = data.EndDate)
With the values for your rate table. as you didn't mention column and table names in your question.
rwking indicated that there might be gaps in the rates table that would cause issues.
I've modified the subquery to bring back the first rate on or nearest the start and end dates.
Hope that helps
You can use the LAG function introduced in SQL2012 to make it a bit easier:
WITH DataWithOrder AS
(
SELECT DateField, PriceField,
ROW_NUMBER() OVER(PARTITION BY YEAR(DateField), Month(DateField) ORDER BY DateField DESC) AS Pos
FROM PriceTable
)
SELECT
DateField,
PriceField,
LAG(PriceField) OVER(ORDER BY DateField) AS PriceLastMonth,
((PriceField / LAG(PriceField) OVER(ORDER BY DateField)) - 1) * 100 AS PCIncrease
FROM DataWithOrder
WHERE Pos = 1
ORDER BY DateField
I took a very different approach than the other guy. His is more elegant and would work better if the daily data does represent every single day of every month. If there are gaps in days, however, as your sample data represents, you can try the following code.
with cte as (select mydate
, price
, ROW_NUMBER() over(partition by YEAR(mydate), MONTH(mydate)
order by day(mydate) desc) row_n
from #temp)
select mydate, price, ROW_NUMBER() over(order by mydate desc) row_num
into #temp2
from cte
where row_n = 1
alter table #temp2
add idx float
declare #counter int = 1
while #counter < (select MAX(row_num)+1 from #temp2)
begin
update t2
set t2.idx = ((t2.price/t3.price)-1)*100
from #temp2 t2 left join
#temp2 t3 on 1 = 1
where t2.row_num = #counter and t3.row_num = #counter + 1
set #counter = #counter + 1
end
select mydate, idx
from #temp2
As the other poster mentioned, you didn't provide column or table names. My process was to insert your data into a table called [#temp] with column names [mydate] and [price].
Also, the data sample you provided contains two invalid dates that I changed to arbitrary dates just for the purposes of getting code to run. (2010-01-00 and 2010-02-00)

Datediff SQL-server query for statistical data with count & group by

So what I'm trying to attain is to count how many Users trigger EventCode 90 relative to when they last recieved a Notification.
Source tables are the following:
ServiceOne
UserNr RegisteredUntil NotificationMonth
532091985 2016-05-15 00:00:00.000 5
950628185 2016-03-15 00:00:00.000 3
561007126 2016-09-15 00:00:00.000 9
Notifications
UserNr NotificationNr NotificationDate Service
532091985 134567 2013-04-16 00:00:00.000 1
532091985 153468 2014-04-15 00:00:00.000 1
950628185 235481 2014-02-17 00:00:00.000 1
561007126 354812 2012-08-15 00:00:00.000 1
EventLog
Time EventCode UserNr
2012-12-19 00:00:00.000 90 561007126
2014-05-02 00:00:00.000 90 120456873
2009-08-24 00:00:00.000 90 935187423
The table I want is something like this:
CancMonth CancAmount
0 49091
1 53564
2 14308
What I have so far is
Select Max(datediff(month, I.NotificationDate, E.Time) ) as CancMonth
,Count(datediff(month, I.NotificationDate, E.Time) ) as CancAmount
From ServiceOne P, Eventlog E, Notifications N
Where P.UserNr=E.UserNr
AND P.UserNr=N.UserNr
AND E.EventCode = 90 --EventCode 90 is both flagging for deregistration and manual deregistration
AND N.Service=1
AND P.Status In (0,4) -- 0 is not registered and 4 is flagged for deregistration
AND datediff(month, N.NotificationDate, E.Time ) < 13 --Notifications are sent once a year
AND N.NotificationDate < E.Time
Group By datediff(month, N.NotificationDate, E.Time )
Order By CancMonth
I did a count on how many total records this gave and it returns about 35 000 more than I have passive and flagged users in ServiceOne.
Help is much appreciated since this has given me a massive headache the last couple of days.
EDIT: I added my source-tables and all possibly usable columns with some random sample-data
Is this What you are looking for?
--I assue that Latest NotificationDate has Largest NotificationNr
SELECT MAX(DATEDIFF(MONTH, I.NotificationDate, E.Time)) AS CancMonth,
COUNT(DATEDIFF(MONTH, I.NotificationDate, E.Time)) AS CancAmount
FROM ServiceOne P
JOIN Eventlog E ON P.UserNr =E.UserNr
JOIN (
SELECT N.*
FROM Notifications N
JOIN (
SELECT UserNr,
MAX(NotificationDate) NotificationDate,
MAX(NotificationNr) NotificationNr
FROM Notifications) LU
ON N.UserNr = LU.UserNr
AND N.NotificationDate = LU.NotificationDate
AND N.NotificationNr = LU.NotificationNr
) N ON P.UserNr = N.UserNr
WHERE E.EventCode = 90
AND N.Service=1
AND P.Status In (0,4)
AND DATEDIFF(MONTH, N.NotificationDate, E.Time ) < 13
AND N.NotificationDate < E.Time
GROUP BY DATEDIFF(MONTH, N.NotificationDate, E.Time )
ORDER BY CancMonth

Resources