Calculate salary base on total Hours in SQL Server - sql-server

How can I calculate salary base on total Hours? I have two tables and a query that will compute the total hours per day.
Table officer_timelogs
employee_id record_time Day type
--------------------------------------------------------
125 2018-02-27 18:03:31.000 Tuesday 1
125 2018-02-27 07:54:03.000 Tuesday 0
Table officer_rate
employee_id designation salary
125 programmer 100 (hour)
SQL Query:
select
employee_id,
[Date], DATENAME(WEEKDAY, date)as [Day],
[Hours] = right(concat('00', diff / 3600), 2) + ' : ' + right(concat('00', diff % 3600 / 60), 2) + ' : ' + right(concat('00', diff % 60), 2)
from (
select
employee_id,
[date] = cast(record_time as date),
diff = datediff(ss, min(iif(type = 0, record_time, null)), max(iif(type = 1, record_time, null)))
from
officer_timelogs
where employee_id = '125'
group by employee_id, cast(record_time as date)
) t
order by date desc
Output:
employee_id Date Day Hours
125 2018-03-02 Friday 09 : 00 : 00
125 2018-03-01 Thursday 10 : 10 : 49
125 2018-02-28 Wednesday 10 : 14 : 11
125 2018-02-27 Tuesday 10 : 09 : 28
125 2018-02-26 Monday 10 : 13 : 34
Desired output (I want to retrieve data like this)
employee_id Date Day Hours Salary
125 2018-03-02 Friday 09 : 00 : 00 900

I think you've done the most complex part.
Since you already have the worked time, you just need to multiply by the salary, like this:
select
t.employee_id,
[Date], DATENAME(WEEKDAY, date)as [Day],
[Hours] = right(concat('00', diff / 3600), 2) + ' : ' + right(concat('00', diff % 3600 / 60), 2) + ' : ' + right(concat('00', diff % 60), 2),
diff * r.salary / 3600 AS Salary
from (
select
employee_id,
[date] = cast(record_time as date),
diff = datediff(ss, min(iif(type = 0, record_time, null)), max(iif(type = 1, record_time, null)))
from
officer_timelogs
where employee_id = '125'
group by employee_id, cast(record_time as date)
) t
INNER JOIN officer_rate r ON t.employee_id = r.employee_id
order by date desc
You can check it live on this SQL Fiddle.
Edit: Code and demo updated with OT rate:
In this case, you need to check if OT should be applied. If the person worked 8h or less, we consider the regular rate. The difference for the 8h is calculated by considering 20% of the hourly salary:
select
t.employee_id,
[Date], DATENAME(WEEKDAY, date)as [Day],
[Hours] = right(concat('00', diff / 3600), 2) + ' : ' + right(concat('00', diff % 3600 / 60), 2) + ' : ' + right(concat('00', diff % 60), 2),
CAST(
CASE WHEN diff <= 3600 * 8
THEN diff * r.salary / 3600
ELSE
(3600 * 8 * r.salary / 3600) -- salary x 8h / work
+ (diff - (3600 * 8)) * r.salary * 0.2 /3600 -- OT work
END AS decimal(10, 2)) AS Salary
from (
select
employee_id,
[date] = cast(record_time as date),
diff = datediff(ss, min(iif(type = 0, record_time, null)), max(iif(type = 1, record_time, null)))
from
officer_timelogs
where employee_id = '125'
group by employee_id, cast(record_time as date)
) t
INNER JOIN officer_rate r ON t.employee_id = r.employee_id
order by date desc;
Demo updated here

Maybe this. I bet you need to convert hours to decimal such that 1 hour and 30 minutes will make 1.5
select
employee_id,
[Date], DATENAME(WEEKDAY, date)as [Day],
[Hours] = right(concat('00', diff / 3600), 2) + ' : ' + right(concat('00', diff % 3600 / 60), 2) + ' : ' + right(concat('00', diff % 60), 2),
Salary = <However you need to get hour(s) as a decimal> * tl2.salary
from (
select
employee_id,
[date] = cast(record_time as date),
diff = datediff(ss, min(iif(type = 0, record_time, null)), max(iif(type = 1, record_time, null)))
from
officer_timelogs
where employee_id = '125'
group by employee_id, cast(record_time as date)
) t
INNER JOIN officer_rate tl2 ON tl2.employee_id=t.employee_id
order by date desc

Related

How to sort varchar week of the month column in SQL Server 2012?

I have calendar table where last column is varchar "WeekOfMonth". What would be the most efficient way to create column WeekOfMonthSort?
DECLARE #MinDate DATE = CAST(DATEADD(yy, -2, DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)) AS DATE),
#MaxDate DATE = CAST(GETDATE() + 90 as DATE);
;WITH cte_Calendar AS
(
SELECT TOP (DATEDIFF(DAY, #MinDate, #MaxDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #MinDate)
FROM
sys.all_objects a
CROSS JOIN
sys.all_objects b
)
SELECT
Date,
MONTH(Date) AS MonthNum,
YEAR(Date) AS YearNum,
FORMAT(Date, 'MMM') AS Month,
FORMAT(Date, 'MMM') + '-'+ FORMAT(Date, 'yy') AS 'MM-YY',
YEAR(Date) * 12 + MONTH(Date) -1 AS 'MM-YY Sort',
--'Week-' + CONVERT(VARCHAR(3), DATEPART(wk, Date)) AS Week,
YEAR(Date) + DATEPART(wk, Date) AS 'Week Sort',
WeekOfMonth = 'Week-' + CONVERT(VARCHAR(1), CONVERT(TINYINT, DENSE_RANK() OVER (PARTITION BY YEAR(Date), MONTH(Date) ORDER BY DATEPART(wk, Date))))
FROM
cte_Calendar
I tried to combine Year and week number but when new year starts Sort value for previous year becomes less than next year.
I will attempt to answer this ....
YEAR(Date) + DATEPART(wk, Date) AS 'Week Sort',
The above statement does not make sense. I suspect you are mixing up string concatenation with number addition, i.e.
YEAR(Date) + DATEPART(wk, Date) gives you 2019 + 10 = 2029 which is not what you expect but then I also notice that
YEAR(Date) * 12 + MONTH(Date) -1 AS 'MM-YY Sort'
is coded correctly.
To combine year and month numbers together you need to do an equivalent of string append or in other words similar to what is done for 'MM-YY Sort'.
Arithmetically (because output of YEAR and DATEPART(wk functions is numeric) what you need to do is:
YEAR(Date) * 100 + DATEPART(wk, Date) AS 'Week Sort'
The result for week 10 will be 2019 * 100 + 10 = 201900 + 10 = 201910 and 201911 for week 11.
You can also achieve the same result using string concatenation:
CAST( YEAR(Date) AS VARCHAR ) + RIGHT( '0' + CAST( DATEPART(wk, DATE ) AS VARCHAR ), 2 ) AS 'Week Sort'

Dynamic Monthly Pivot Table

I have an invoice table that I want to create a rolling monthly dynamic pivot table out of. I want the current month to be month "1" and this month last year to be month "13"
Sample data:
INVOICEDATE | ITEMCODE | UNITS
2018-05-07 | 123456 | 20
2018-05-04 | 123456 | 5
2018-04-07 | 123456 | 10
....
2017-05-25 | 123456 | 50
Desired Output:
ITEMCODE | 01 | 02 | .... | 13
123456 | 25 | 10 | .... | 50
I have start with the following but am getting stuck with the month numbering /ordering piece, especially with the month of last year rolling into the same month of this year.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#NulltoZero nvarchar(max)
select #cols = STUFF((SELECT distinct ',' +
quotename(substring(CONVERT(varchar,INVOICEDATE,112),5,2)) [Month]
FROM MAS_RDP..AR_InvoiceHistoryHeader
where invoicedate >= DATEADD(MONTH, -12, DATEADD(month, DATEDIFF(month, 0, getdate()), 0))
order by [Month] DESC
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #NulltoZero = STUFF((SELECT distinct ',ISNULL(' +
quotename(substring(CONVERT(varchar,INVOICEDATE,112),5,2)) + ',0) AS ' + quotename(substring(CONVERT(varchar,INVOICEDATE,112),5,2))
FROM MAS_RDP..AR_InvoiceHistoryHeader
where invoicedate >= DATEADD(MONTH, -12, DATEADD(month, DATEDIFF(month, 0, getdate()), 0))
order by ',ISNULL(' +
quotename(substring(CONVERT(varchar,INVOICEDATE,112),5,2)) + ',0) AS ' + quotename(substring(CONVERT(varchar,INVOICEDATE,112),5,2)) DESC
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT itemcode, ' + #NulltoZero + ' from
(
Select ARD.itemcode
,(12 + DATEPART(MONTH, GETDATE()) - DATEPART(MONTH, invoicedate)) % 12 + 1 [MONTH]
,cast(ISNULL(quantityshipped*[UnitOfMeasureConvFactor],0) as int) as units
FROM [MAS_RDP].[dbo].[AR_InvoiceHistoryDetail] ARD
inner join MAS_RDP..AR_InvoiceHistoryHeader ARH on ARD.InvoiceNo = ARH.InvoiceNo and ARD.HeaderSeqNo = ARH.HeaderSeqNo
inner join MAS_RDP..CI_Item CI on ARD.Itemcode = CI.itemcode and CI.Inactiveitem = ''N'' and CI.itemcode not like ''2%'' and len(ci.itemcode) = 6
inner join MAS_RDP..IM_Itemwarehouse IMW on ARD.itemcode = IMW.itemcode and IMW.warehousecode = ''000'' and imw.udf_is_stocked = ''Y''
where invoicedate >= DATEADD(MONTH, -12, DATEADD(month, DATEDIFF(month, 0, getdate()), 0))
) x
pivot (sum( units ) for month in (' + #cols + ') ) p
order by itemcode'
execute(#query)
Try this solution. Correct me if I'm wrong, but you don't need a dynamic pivot if you know that you want months 1 to 13.
In the first CTE we group data by month, then calculate each month's difference against current month with DATEDIFF MONTH and finally pivot the values just for the month differences 1 to 13.
IF OBJECT_ID('tempdb..#Data') IS NOT NULL
DROP TABLE #Data
CREATE TABLE #Data (
InvoiceDate DATE,
ItemCode INT,
Units INT)
INSERT INTO #Data (
InvoiceDate,
ItemCode,
Units)
VALUES
('2018-05-07', 123456, 20),
('2018-05-04', 123456, 5),
('2018-04-07', 123456, 10),
('2017-05-25', 123456, 50),
('2017-09-07', 123456, 40),
('2018-01-07', 123456, 35)
;WITH GroupedMonths AS
(
SELECT
ItemCode = D.ItemCode,
Year = DATEPART(YEAR, D.InvoiceDate),
Month = DATEPART(MONTH, D.InvoiceDate),
Units = SUM(D.Units),
InitialDate = DATEFROMPARTS(
DATEPART(YEAR, D.InvoiceDate),
DATEPART(MONTH, D.InvoiceDate),
1),
CurrentInitialDate = DATEFROMPARTS(
DATEPART(YEAR, GETDATE()),
DATEPART(MONTH, GETDATE()),
1)
FROM
#Data AS D
GROUP BY
D.ItemCode,
DATEPART(YEAR, D.InvoiceDate),
DATEPART(MONTH, D.InvoiceDate)
),
MonthRankings AS
(
SELECT
G.ItemCode,
G.Units,
MonthRanking = DATEDIFF(MONTH, G.InitialDate, G.CurrentInitialDate) + 1
FROM
GroupedMonths AS G
)
SELECT
T.ItemCode,
'01' = ISNULL(T.[1], 0),
'02' = ISNULL(T.[2], 0),
'03' = ISNULL(T.[3], 0),
'04' = ISNULL(T.[4], 0),
'05' = ISNULL(T.[5], 0),
'06' = ISNULL(T.[6], 0),
'07' = ISNULL(T.[7], 0),
'08' = ISNULL(T.[8], 0),
'09' = ISNULL(T.[9], 0),
'10' = ISNULL(T.[10], 0),
'11' = ISNULL(T.[11], 0),
'12' = ISNULL(T.[12], 0),
'13' = ISNULL(T.[13], 0)
FROM
MonthRankings AS R
PIVOT (
MAX(R.Units) FOR R.MonthRanking IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13])
) AS T

How to get difference of two date in same column in SQL Server

How can I get the time difference of the two same date in the same column? And what if there is multiple in/out in the same day?
Table tbl_Employee_MasterList:
id First_Name Last_Name biometric_no
----------------------------------------------
125 ABRAHAM JOSEPH MOQUETE 78
Table officer_timelogs
employee_id record_time Day type
--------------------------------------------------------
125 2018-02-27 18:03:31.000 Tuesday 1
125 2018-02-27 07:54:03.000 Tuesday 0
SQL query:
select
a.employee_id, a.record_time,
--CONVERT(char(10), a.record_time, 103) as [Date],
DATENAME(WEEKDAY, a.record_time) as [Day],
a.[type],
case
when a.[type] = 0
then 'in'
when a.[type] = 1
then 'out'
end as Status
from
officer_timelogs a
left join
tbl_Employee_MasterList b on a.employee_id = b.biometric_no
where
a.employee_id = '125'
order by
a.record_time desc
Output:
employee_id record_time Day type Status
---------------------------------------------------------------
125 2018-02-28 07:47:23.000 Wednesday 0 in
125 2018-02-27 18:03:31.000 Tuesday 1 out
125 2018-02-27 07:54:03.000 Tuesday 0 in
125 2018-02-26 18:01:59.000 Monday 1 out
125 2018-02-26 07:48:25.000 Monday 0 in
125 2018-02-24 12:50:00.000 Saturday 1 out
125 2018-02-24 07:44:16.000 Saturday 0 in
125 2018-02-23 17:02:06.000 Friday 1 out
125 2018-02-23 07:48:26.000 Friday 0 in
125 2018-02-22 18:02:35.000 Thursday 1 out
125 2018-02-22 07:48:41.000 Thursday 0 in
Desired output (I want to retrieve data like this):
employee_id Date Day Hours
-----------------------------------------------------
125 2018-02-27 Tuesday 10:09:28 (h:m:s)
I am using SQL Server 2012.
I just used one table to simplify. I t will not be hard to join and get emplyee names. This query calculates time difference between first in and last out as you asked. And returns '00:00:00' if either in or out is missing
declare #t table (
employee_id int
, record_time datetime
, type int
)
insert into #t
values (125, '20180228 07:47:23.000', 0)
, (125, '20180227 18:03:31.000', 1), (125, '20180227 07:54:03.000', 0)
, (125, '20180226 18:01:59.000', 1), (125, '20180226 07:48:25.000', 0)
, (125, '20180224 12:50:00.000', 1), (125, '20180224 07:44:16.000', 0)
, (125, '20180223 17:02:06.000', 1), (125, '20180223 07:48:26.000', 0)
, (125, '20180222 18:02:35.000', 1), (125, '20180222 07:48:41.000', 0)
select
employee_id, [date]
, [hours] = right(concat('00', diff / 3600), 2) + ':' + right(concat('00', diff % 3600 / 60), 2) + ':' + right(concat('00', diff % 60), 2)
from (
select
employee_id, [date] = cast(record_time as date)
, diff = datediff(ss, min(iif(type = 0, record_time, null)), max(iif(type = 1, record_time, null)))
from
#t
group by employee_id, cast(record_time as date)
) t
But I recommend to look at this query. It calculates each in-out times separately and then gets total sum per day
select
employee_id, [date]
, [hours] = right(concat('00', diff / 3600), 2) + ':' + right(concat('00', diff % 3600 / 60), 2) + ':' + right(concat('00', diff % 60), 2)
from (
select
employee_id, [date] = cast(isnull([in], [out]) as date)
, diff = sum(diff)
from (
select
employee_id, [in] = max(iif(type = 0, record_time, null))
, [out] = max(iif(type = 1, record_time, null))
, diff = datediff(ss, max(iif(type = 0, record_time, null)), max(iif(type = 1, record_time, null)))
from (
select
*, grp = sum(iif(type = 0, 1, 0)) over (partition by employee_id order by record_time)
from
#t
) t
group by employee_id, grp
) t
group by employee_id, cast(isnull([in], [out]) as date)
) t

T-SQL - Minutes per hour between two datetimes

I have to following data:
| tid | startdate | enddate |
| 1 | 2016-12-26 12:30 | 2016-12-26 15:30 |
| 2 | 2016-12-26 13:15 | 2016-12-26 15:15 |
I would like to create a result with the hour number and then the amount of minutes the date time falls within that hour.
Example result:
| tid | hour | minutes_in |
| 1 | 12 | 30 |
| 1 | 13 | 60 |
| 1 | 14 | 60 |
| 1 | 15 | 30 |
| 2 | 13 | 45 |
| 2 | 14 | 60 |
| 2 | 15 | 15 |
Any suggestions?
First You need a numbers table to get your hours from 0 - 23, which can be fairly easily created on the fly with a table value constructor:
SELECT N
FROM (VALUES
(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),
(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23)
) n (N);
Then you can join this to your original data to split rows out into the number required. Then you just need a case expression to apply the correct logic for calculating the minutes:
WITH Numbers (Number) AS
( SELECT N
FROM (VALUES
(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),
(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23)
) n (N)
), SampleData (tid, StartDate, EndDate) AS
( SELECT tid, CONVERT(DATETIME2, StartDate), CONVERT(DATETIME2, EndDate)
FROM (VALUES
(1, '2016-12-26 12:30', '2016-12-26 15:30'),
(2, '2016-12-26 13:15', '2016-12-26 15:15')
) d (tid, StartDate, EndDate)
)
SELECT d.tid,
[Hour] = n.Number,
Minutes_in = CASE
-- SPECIAL CASE: START HOUR = END HOUR
WHEN DATEPART(HOUR, d.StartDate) = DATEPART(HOUR, d.EndDate)
THEN DATEDIFF(MINUTE, d.StartDate, d.EndDate)
-- FULL HOURS IN BETWEEN START AND END
WHEN n.Number > DATEPART(HOUR, d.StartDate)
AND n.Number < DATEPART(HOUR, d.EndDate) THEN 60
-- START HOUR
WHEN n.Number = DATEPART(HOUR, d.StartDate)
THEN 60 - DATEPART(MINUTE, d.StartDate)
-- END HOUR
WHEN n.Number = DATEPART(HOUR, d.EndDate)
THEN DATEPART(MINUTE, d.EndDate)
END
FROM SampleData d
INNER JOIN Numbers n
ON n.Number >= DATEPART(HOUR, d.StartDate)
AND n.Number <= DATEPART(HOUR, d.EndDate);
ADDENDUM
If you need to span days, then you could alter the logic slightly, generate a larger set of numbers to cover more hours difference, then rather than joining on the hour of the day, join the numbers on the hours difference from the start datetime to the end datetime:
SELECT *
FROM SampleData d
INNER JOIN Numbers n
ON n.Number <= DATEDIFF(HOUR, d.StartDate, d.EndDate)
This means where the range crosses over days, then there is no issue, the hours just keep incrementing. e.g.
WITH Numbers (Number) AS
( SELECT ROW_NUMBER() OVER(ORDER BY N1.N) - 1
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N1(N)
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N2 (N)
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N3 (N)
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N4 (N)
), SampleData (tid, StartDate, EndDate) AS
( SELECT tid, CONVERT(DATETIME2, StartDate), CONVERT(DATETIME2, EndDate)
FROM (VALUES
(1, '2016-12-26 12:30', '2016-12-26 15:30'),
(2, '2016-12-26 13:15', '2016-12-26 15:15'),
(3, '2016-12-26 13:15', '2016-12-27 15:15')
) d (tid, StartDate, EndDate)
)
SELECT d.tid,
[Date] = CONVERT(DATE, d.StartDate),
[Hour] = CONVERT(TIME(0), DATEADD(HOUR, DATEPART(HOUR, d.StartDate) + n.Number, 0)),
Minutes_in = CASE
-- SPECIAL CASE: START HOUR = END HOUR
WHEN DATEPART(HOUR, d.StartDate) = DATEPART(HOUR, d.EndDate)
AND DATEDIFF(DAY, d.StartDate, d.EndDate) = 0
THEN DATEDIFF(MINUTE, d.StartDate, d.EndDate)
-- START HOUR
WHEN n.Number = 0
THEN 60 - DATEPART(MINUTE, d.StartDate)
-- END HOUR
WHEN n.Number = DATEDIFF(HOUR, d.StartDate, d.EndDate)
THEN DATEPART(MINUTE, d.EndDate)
-- FULL HOURS IN BETWEEN START AND END
ELSE 60
END
FROM SampleData d
INNER JOIN Numbers n
ON n.Number <= DATEDIFF(HOUR, d.StartDate, d.EndDate)
ORDER BY d.tid, n.Number;
Method -I
You can achieve this with a UDF (Another Simplest Way)
Lets build schema of your provided data
CREATE TABLE #TAB ( TID INT, STARTDATE DATETIME, ENDDATE DATETIME)
INSERT INTO #TAB
SELECT 1,'2016-12-26 12:30','2016-12-26 15:30'
UNION ALL
SELECT 2,'2016-12-26 13:15','2016-12-26 15:15'
Create one UDF to generate values between from and To
ALTER FUNCTION [dbo].[FN_GENERATE] (#FROM_NBR INT, #TO_NBR INT)
RETURNS
#RESULT TABLE(HR INT)
AS
BEGIN
;WITH CTE AS
(
SELECT #FROM_NBR AS FROM_NBR,#TO_NBR AS TO_NBR
UNION ALL
SELECT FROM_NBR+1 ,TO_NBR FROM CTE WHERE FROM_NBR<TO_NBR
)
INSERT INTO #RESULT
SELECT FROM_NBR FROM CTE
RETURN
END
Now query for data by calling the function.
;WITH CTE AS (
SELECT TID,STARTDATE,ENDDATE,DATEPART(HH,STARTDATE) FROM_HR , DATEPART(HH,ENDDATE) TO_HR FROM #TAB T
)
SELECT C1.TID,F.HR, COALESCE(DATEPART(MINUTE,FRM_HR_MINUTS.STARTDATE),DATEPART(MINUTE,TO_HR_MINUTS.ENDDATE),60 )
FROM CTE C1
CROSS APPLY
(
SELECT * FROM DBO.[FN_GENERATE] (C1.FROM_HR, C1.TO_HR)
)AS F
LEFT JOIN CTE FRM_HR_MINUTS ON C1.TID= FRM_HR_MINUTS.TID AND DATEPART(HH,FRM_HR_MINUTS.STARTDATE)= F.HR
LEFT JOIN CTE TO_HR_MINUTS ON C1.TID= TO_HR_MINUTS.TID AND DATEPART(HH,TO_HR_MINUTS.ENDDATE)= F.HR
Edit :
Method - II
Without using UDF & using MASTER.DBO.SPT_VALUES
;WITH CTE AS (
--PREPARING START HR, END HR, START_MIN, END_MIN FROM #TAB
SELECT TID,STARTDATE,ENDDATE
,DATEPART(HH,STARTDATE) FROM_HR
, DATEPART(HH,ENDDATE) TO_HR
, DATEPART(MINUTE, STARTDATE) AS STARTMIN
, DATEPART(MINUTE, ENDDATE) ENDMIN
FROM #TAB T
)
SELECT TID
, NUMBER AS HRS
--if Outer APply produce Null Display Minutes from CTE else 60 Mins
, CASE ISNULL(OA.FRM_MINS, C1.STARTMIN) + ISNULL(TO_MINS,C1.ENDMIN)
WHEN 0
THEN 60
ELSE ISNULL(OA.FRM_MINS, C1.STARTMIN) + ISNULL(TO_MINS,C1.ENDMIN)
END AS MINS
FROM CTE C1
OUTER APPLY --JOINING NUMBERS BETWEEN FROM_HR & TO_HR using MASTER.DBO.SPT_VALUES
(
SELECT NUMBER
--IF FROM_HR matched NULL Else 0
, CASE C1.FROM_HR WHEN NUMBER THEN NULL ELSE 0 END AS FRM_MINS
--IF TO_HR matched NULL Else 0
,CASE C1.TO_HR WHEN NUMBER THEN NULL ELSE 0 END AS TO_MINS
FROM MASTER.DBO.SPT_VALUES
WHERE [type]='P' AND number>0 AND number BETWEEN FROM_HR AND TO_HR
)AS OA

SQLSERVER 2008: Breaking a output for the entire day into 2 records for 12 hours

I am looking for the count of records as below.
PLANNED_SHIP_From_Date PLANNED_SHIP_To Date Total_Lines_Count ....
1) 09-04-2016 07:00:01 09-04-2016 18:59:59 165 .....
2) 09-04-2016 19:00:00 10-04-2016 07:00:00 121 .....
3) 10-04-2016 07:00:01 10-04-2016 18:59:59 165 .....
4) 10-04-2016 19:00:00 11-04-2016 07:00:00 123 .....
5) 11-04-2016 07:00:01 11-04-2016 18:59:59 234 .....
.
Currently my query is counting the records as per date.
SELECT
cast(shdr.PLANNED_SHIP_DATE as date),
SUM(sdtl_1_1.TOTAL_LINES_COUNT) AS TOTAL_LINES_COUNT
FROM
dbo.SHIPMENT_HEADER AS shdr WITH (NOLOCK)
INNER JOIN
(
SELECT
SHIPMENT_ID,
COUNT(*) AS TOTAL_LINES_COUNT
FROM
dbo.SHIPMENT_DETAIL AS SHIPMENT_DETAIL_1 WITH (NOLOCK)
WHERE
(
STATUS1 >= 401
)
AND (
DATEDIFF(day, PLANNED_SHIP_DATE, CONVERT(date, SYSDATETIME())) < 4
)
GROUP BY
SHIPMENT_ID
) AS sdtl_1_1
ON sdtl_1_1.SHIPMENT_ID = shdr.SHIPMENT_ID
WHERE
(
shdr.TRAILING_STS >= 401
)
AND (
DATEDIFF(day, shdr.PLANNED_SHIP_DATE, CONVERT(date, SYSDATETIME())) < 4
)
GROUP BY
cast(shdr.PLANNED_SHIP_DATE as date)
Try this -
DECLARE #ReportDays int = 30,
#StartHr int = 7,
#Today DATETIME2 = CAST(SYSDATETIME() AS DATE);
--http://sqlblog.com/blogs/adam_machanic/archive/2006/07/12/you-require-a-numbers-table.aspx
WITH
a AS (SELECT 1 AS i UNION ALL SELECT 1),
b AS (SELECT 1 AS i FROM a AS x, a AS y),
c AS (SELECT 1 AS i FROM b AS x, b AS y),
d AS (SELECT 1 AS i FROM c AS x, c AS y),
e AS (SELECT 1 AS i FROM d AS x, d AS y),
numbers as (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 AS number FROM e),
StartDates AS (
SELECT
DATEADD(
HH,
#StartHr + (n2.number * 12),
DATEADD(D, 0-n1.number, #Today)
) AS StartDT
FROM
(SELECT * FROM numbers WHERE Number BETWEEN 0 AND #ReportDays) n1
CROSS JOIN (SELECT * FROM numbers WHERE Number IN (0,1)) n2
),
DateRanges AS
(SELECT StartDT, DATEADD(hh, 12, StartDT) AS EndDT FROM StartDates),
Shipments AS
(SELECT
StartDT AS PLANNED_SHIP_From_Date,
EndDT AS PLANNED_SHIP_To_Date,
1 AS Shipment
FROM
DateRanges dr
LEFT JOIN dbo.SHIPMENT_DETAIL sd
ON sd.Status1 >=401
AND sd.PLANNED_SHIP_DATE BETWEEN dr.StartDT AND dr.EndDT)
SELECT
PLANNED_SHIP_From_Date,
PLANNED_SHIP_To_Date,
SUM(Shipment) AS TOTAL_LINES_COUNT
FROM
Shipments
ORDER BY
PLANNED_SHIP_From_Date;
What we're doing is -
Building a numbers table
Using that to pull a list of days, with two records per day
Working out the start & finish times for each time window
Joining the time windows to the records and summing
Hope that helps :-)
Add another column to your select....
CASE
WHENE DATEPART(HOUR, Planned_SHIP_DATE) < 12 THEN 'AM' ELSE 'PM'
END AS ShipPeriod
You could then add that column into a GROUPING to seperate the 'AM's from 'PM's
Of course I have assuumed you wanted AM/PM. But you can modify the CASE statement to break the hours up as you see fit.
Hope this helps
Thank you all for helping me out.
I have created a SQL query which worked for me. This query gives the count of records from morning 7 AM to 7 PM as MORNING_SHIFT count and 7PM to next day 7AM morning as EVENING_SHIFT for dates greater than 14 days in the past.
SELECT
CASE
WHEN convert(VARCHAR(50), sh.PLANNED_SHIP_DATE, 120) BETWEEN
(convert(VARCHAR(10), sh.PLANNED_SHIP_DATE, 120) + ' 07:00:00') AND
(convert(VARCHAR(10), sh.PLANNED_SHIP_DATE, 120) + ' 18:59:59')
THEN (CONCAT(cast(sh.PLANNED_SHIP_DATE as date),' ','morning_shift'))
WHEN convert(VARCHAR(50), sh.PLANNED_SHIP_DATE, 120) BETWEEN
(convert(VARCHAR(10), sh.PLANNED_SHIP_DATE, 120) + ' 00:00:00') AND
(convert(VARCHAR(10), sh.PLANNED_SHIP_DATE, 120) + ' 06:59:59')
then (CONCAT(cast(DATEADD(DAY, -1, sh.PLANNED_SHIP_DATE) as date),' ','EVENING_shift'))
when
convert(VARCHAR(50), DATEADD(DAY, -1, sh.PLANNED_SHIP_DATE) , 120) BETWEEN (convert(VARCHAR(10), cast(DATEADD(DAY, -1, sh.PLANNED_SHIP_DATE) as date), 120) + ' 19:00:00') AND
(convert(VARCHAR(10), cast(DATEADD(DAY, -1, sh.PLANNED_SHIP_DATE) as date), 120) + ' 23:59:59')
THEN (CONCAT(cast(DATEADD(DAY, -1, sh.PLANNED_SHIP_DATE) as date),' ','EVENING_shift'))
END AS 'actual_date_time', sh.PLANNED_SHIP_DATE
FROM dbo.SHIPMENT_HEADER AS sh WITH (nolock)
WHERE (shdr.TRAILING_STS >= 401) AND (DATEDIFF(day, shdr.ACTUAL_SHIP_DATE_TIME, CONVERT(date, SYSDATETIME())) < 14)
group by sh.ACTUAL_SHIP_DATE_TIME;

Resources