how to generate salary total sql server - sql-server

I have below tables called dailyshift,in 24 hours format
1 dailyshift
shiftid,employeeid,shiftdate ,starttime ,endtime hours
1 1 16th Aug 2019 , 08:00 15:00 7
2 1 18th Aug 2019 , 08:00 15:00 7
2 1 22th Aug 2019 , 08:00 15:00 7
3 2 17th Aug 2019 , 16:00 20:00 4
2 employeesalary
employeeid,salary, startdate , enddate
1 ,10, , 1st Aug 2019 , 16th Aug 2019
1 ,20, , 17st Aug 2019 , 20th Aug 2019
2 ,25, , 15st Aug 2019 , 20th Aug 2019
we need to have ouptput week wise salary total like, we need to make total salary by employee per week ,according to their configured salary per hour.
empleeid week weektotal
1 12th to 18th Aug 210
1 19th to 25th Aug 140
2 12th to 18th Aug 100
I have tried below but it is giving not accurate total
select employeeid,sum(d.hours*salary) as weektotal from dailyshift as d
inner join employeesalary as e
on d.employeeid=e.employeeid
group by employeeid
can we have way to make this?

Query may seems complex, but if you go through part by part, you will understand everything. CTE and CTE2 is just use for date manipulation to get the desired result.
WITH CTE AS(
SELECT *,
DATEADD(DAY, 1 - IIF(DATEPART(WEEKDAY, CAST(REPLACE(REPLACE(REPLACE(REPLACE(shiftdate,'st ','-'),'nd ','-'),'rd ','-'),'th ','-') AS DATETIME))-1 = 0,7,
DATEPART(WEEKDAY, CAST(REPLACE(REPLACE(REPLACE(REPLACE(shiftdate,'st ','-'),'nd ','-'),'rd ','-'),'th ','-') AS DATETIME))-1),
CAST(REPLACE(REPLACE(REPLACE(REPLACE(shiftdate,'st ','-'),'nd ','-'),'rd ','-'),'th ','-') AS DATETIME)) [Week_Start_Date],
DATEADD(DAY, 7 - IIF(DATEPART(WEEKDAY, CAST(REPLACE(REPLACE(REPLACE(REPLACE(shiftdate,'st ','-'),'nd ','-'),'rd ','-'),'th ','-') AS DATETIME))-1 = 0,7,
DATEPART(WEEKDAY, CAST(REPLACE(REPLACE(REPLACE(REPLACE(shiftdate,'st ','-'),'nd ','-'),'rd ','-'),'th ','-') AS DATETIME))-1),
CAST(REPLACE(REPLACE(REPLACE(REPLACE(shiftdate,'st ','-'),'nd ','-'),'rd ','-'),'th ','-') AS DATETIME)) [Week_End_Date]
FROM dailyshift
),
CTE2 AS(
SELECT A.shiftid,A.employeeid,A.shiftdate,A.starttime,A.endtime,A.hours,
CAST(DAY(Week_Start_Date) AS VARCHAR)+
CASE
WHEN DAY(Week_Start_Date) % 100 IN (11,12,13) THEN 'th ' --first checks for exception
WHEN DAY(Week_Start_Date) % 10 = 1 THEN 'st '
WHEN DAY(Week_Start_Date) % 10 = 2 THEN 'nd '
WHEN DAY(Week_Start_Date) % 10 = 3 THEN 'rd '
ELSE 'th ' --works for num % 10 IN (4,5,6,7,8,9,0)
END
+'To '+
CAST(DAY(Week_End_Date) AS VARCHAR) +
CASE
WHEN DAY(Week_End_Date) % 100 IN (11,12,13) THEN 'th ' --first checks for exception
WHEN DAY(Week_End_Date) % 10 = 1 THEN 'st '
WHEN DAY(Week_End_Date) % 10 = 2 THEN 'nd '
WHEN DAY(Week_End_Date) % 10 = 3 THEN 'rd '
ELSE 'th ' --works for num % 10 IN (4,5,6,7,8,9,0)
END +
LEFT(DATENAME(Month,Week_End_Date),3) +' '+
CAST(YEAR(Week_End_Date) AS VARCHAR) grp_clm_name
FROM CTE A
)
SELECT A.employeeid,
A.grp_clm_name Week,
SUM(A.hours*B.salary) WeekTotal
FROM CTE2 A
INNER JOIN employeesalary B
ON A.employeeid = B.employeeid
AND CAST(REPLACE(REPLACE(REPLACE(REPLACE(A.shiftdate,'st ','-'),'nd ','-'),'rd ','-'),'th ','-') AS DATETIME)
BETWEEN CAST(REPLACE(REPLACE(REPLACE(REPLACE(B.startdate,'st ','-'),'nd ','-'),'rd ','-'),'th ','-') AS DATETIME)
AND CAST(REPLACE(REPLACE(REPLACE(REPLACE(B.enddate,'st ','-'),'nd ','-'),'rd ','-'),'th ','-') AS DATETIME)
GROUP BY A.employeeid, A.grp_clm_name
ORDER BY 1
Output is-
employeeid Week eekTotal
1 2th To 18th Aug 2019 210
2 12th To 18th Aug 2019 100
Row with value "1 19th to 25th Aug 140" is missing as you have no salary definition for employee 1 for date 22th Aug 2019

You need the applicable salary for the shift date so you need to do the following:
select d.employeeid, sum(d.hours * e.salary) as weektotal
from dailyshift as d
inner join employeesalary as e
on d.employeeid = e.employeeid
and d.shiftdate >= e.startdate
and d.shiftdate <= e.endate
group by d.employeeid

Assuming EmployeeSalary tables contains the hourly salary of employees, each employee can have different salary for different period.
Following query must give you expected result:
select employeeid,
DATEADD(day, -(DATEPART(dw, d.shiftdate)-1), d.shiftdate) [WeekStart],
DATEADD(day, 7-(DATEPART(dw, d.shiftdate)), d.shiftdate) [WeekEnd],
sum(d.hours*salary) as Weektotal
from dailyshift as d
inner join employeesalary as e
on d.employeeid=e.employeeid and d.shiftdate between e.startdate and e.enddate
group by employeeid, DATEADD(day, -(DATEPART(dw, d.shiftdate)-1), d.shiftdate), DATEADD(day, 7-(DATEPART(dw, d.shiftdate)), d.shiftdate)

Related

Get datediff from three tables

How can I get the date difference based on schedule of the employee and First Time In and Last Time Out?
Below are the queries I'm using.
select * from tbl_payroll_schedule where id in (3, 17, 18)
Output:
id schedule_description time_in time_out schedule_status
3 REST DAY 1900-01-01 00:00:00.000 1900-01-01 00:00:00.000 1
17 08:00 AM - 05:00 PM 1900-01-01 08:00:00.000 1900-01-01 17:00:00.000 1
18 08:00 AM - 12:00 PM 1900-01-01 08:00:00.000 1900-01-01 12:00:00.000 1
select mon.schedule_description as Monday,
tue.schedule_description as Tuesday,
wed.schedule_description as Wednesday,
thu.schedule_description as Thursday,
fri.schedule_description as Friday,
sat.schedule_description as Saturday,
sun.schedule_description as Sunday
from employee_default_schedule ds
LEFT JOIN tbl_payroll_schedule mon on mon.id = ds.mon_sched
LEFT JOIN tbl_payroll_schedule tue on tue.id = ds.tues_sched
LEFT JOIN tbl_payroll_schedule wed on wed.id = ds.wed_sched
LEFT JOIN tbl_payroll_schedule thu on thu.id = ds.thurs_sched
LEFT JOIN tbl_payroll_schedule fri on fri.id = ds.fri_sched
LEFT JOIN tbl_payroll_schedule sat on sat.id = ds.sat_sched
LEFT JOIN tbl_payroll_schedule sun on sun.id = ds.sun_sched
WHERE ds.employee_id = 125
Output:
select ot.id,
ot.employee_id as EID,
(el.Last_Name + ', ' + el.First_Name) as Name,
FORMAT(ot.record_time, 'ddd MM-dd-yyyy HH:mm:ss') as Time,
(CASE WHEN ot.type = 0 then 'Time In'
WHEN ot.type = 1 then 'Time Out'
END) as Status
from officer_timelogs ot
left join tbl_Employee_MasterList el on el.ID = ot.employee_id
where ot.employee_id = (CASE WHEN 125 = 0 THEN ot.employee_id else 125 END)
and ot.record_time >= CONVERT(datetime, '08/16/2018 00:00:00', 121) and ot.record_time <= CONVERT(datetime, '08/16/2018 23:59:59', 121)
order by el.Last_Name asc, ot.record_time asc
Output:
id EID Name Time Status
194664 125 MOQUETE, ABRAHAM JOSEPH Thu 08-16-2018 07:55:57 Time In
194703 125 MOQUETE, ABRAHAM JOSEPH Thu 08-16-2018 12:11:36 Time Out
194705 125 MOQUETE, ABRAHAM JOSEPH Thu 08-16-2018 12:28:12 Time In
194757 125 MOQUETE, ABRAHAM JOSEPH Thu 08-16-2018 20:45:08 Time Out
Currently I am able get the total work hours, and I want to get the Late and Overtime.
select
employee_id as EID,
FORMAT(date, 'MM-dd-yyyy') as [Date],
DATENAME(WEEKDAY, date) as [Day],
right(concat('00', diff / 4100), 2) + ':' + right(concat('00', diff % 3600 / 60), 2) + ':' + right(concat('00', diff % 60), 2) as TWH
from (select
employee_id,
[date] = cast(actual_time as date),
diff = datediff(ss, min(iif(type = 0, actual_time, null)), max(iif(type = 1, actual_time, null)))
from
officer_timelogs
where employee_id = (CASE WHEN #employee_id = 0 THEN employee_id else #employee_id END)
group by employee_id, cast(actual_time as date)
) t
where t.date >= #dateFrom and t.date <= #dateTo
order by date asc
Expected Output:
EID Date Day TWH Late OT
125 08-16-2018 Thursday 11:49:11

Count number of items which hadn't been checked so far in the last x months

We have a list of items. Each item may be checked/examined one or more times in the last #FromDate to #ToDate (12 months, for example). How do we count the number of items which were checked in a month that had never been check since #FromDate and display that count monthly.
The result would look like below:
2014-05-01 00:00:00.000 1 May 2014 381
2014-06-01 00:00:00.000 2 June 2014 296
2014-07-01 00:00:00.000 3 July 2014 24
2014-08-01 00:00:00.000 4 August 2014 260
2014-09-01 00:00:00.000 5 September 2014 249
2014-10-01 00:00:00.000 6 October 2014 177
2014-11-01 00:00:00.000 7 November 2014 298
2014-12-01 00:00:00.000 8 December 2014 274
2015-01-01 00:00:00.000 9 January 2015 41
2015-02-01 00:00:00.000 10 February 2015 0
2015-03-01 00:00:00.000 11 March 2015 0
2015-04-01 00:00:00.000 12 April 2015 0
So far we could count the number of items which were checked monthly by using the following query:
;WITH d AS
(
SELECT DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, '2014-05-15'), 0)) as d, ROW_NUMBER() OVER (ORDER BY n) as rn
FROM ( SELECT TOP (DATEDIFF(MONTH, '2014-05-15', '2015-04-15') + 1)
n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
FROM sys.all_objects ORDER BY [object_id] ) AS n
)
SELECT
d.d,
d.rn,
DATENAME(MONTH, d.d) as [Month],
YEAR(d.d) as [Year],
COUNT(DISTINCT ItemNumber) AS Count
FROM d LEFT OUTER JOIN ItemCheck
ON CheckedTime >= d.d
AND CheckedTime < DATEADD(MONTH, 1, d.d)
GROUP BY d.d, d.rn
ORDER BY d.d;
However, we still can't figure out how to count the number of items which were checked say in July 2014 but had never been checked in May and June 2014.
A simple single query below would be able to display that for a particular month but that doesn't display in monthly.
select count(distinct ItemNumber) from ItemCheck
where CheckTime >= '2014-07-01' AND CheckTime < '2014-08-01' and
ItemNumber NOt in (SELECT ItemNumber FROM ItemCheck as t1 where t1.CheckTime >= '2014-05-01' AND t1.CheckTime < '2014-07-01')
order by ItemNumber
Update: The ItemCheck table looks like below:
CheckID | ItemNumber | CheckTime
1 i1 2014-05-02
2 i4 2014-06-12
3 i5 2014-07-03
4 i1 2014-08-01
5 i1 2014-08-02
6 i2 2014-09-15
7 i3 2014-10-11
Suppose you have a table called months with one column called month.
Then you could do it something along these lines:
DECLARE #date DATE
SET #date = '2014-01-01'
SELECT months.month AS currentMonth, COUNT(ItemNumber) ItemNumber from ItemCheck
CROSS JOIN months
WHERE CheckTime >= dateadd(m, months.month - 1, #date) AND CheckTime < dateadd(m, months.month, #date) and
ItemNumber NOT IN (SELECT ItemNumber FROM ItemCheck AS t1 WHERE t1.CheckTime >= DATEADD(m, months.month - 3, #date) AND t1.CheckTime < dateadd(m, months.month - 1, #date))
GROUP BY month
ORDER BY ItemNumber
You could also do it with a function, but you have to write it (function is called dbo.Split and you could write it this way: select data from dbo.Split(',', '1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12'). This way you could avoid creating another table (in-memory or a real one..)
If you want the code for dbo.Split I can provide it.

Always show empty weeks

I need to be able to show all week numbers per month regardless of whether there are hours for that week or not.
Change this:
2.00 2 July
2.25 3 July
7.25 5 July
To this:
0.00 1 July
2.00 2 July
2.25 3 July
0.00 4 July
7.25 5 July
Here is the current query I am working with:
SELECT SUM(te.Hours_Bill) AS HoursBilled
, DATEPART(dd, DATEDIFF(dd, 0, te.Date_Start) / 7 * 7 ) / 7 + 1 AS HoursWeekNumber
, DATENAME(mm, te.Date_Start) AS HoursMonthName
FROM AGR_Header ah
INNER
JOIN Time_Entry te
ON te.Agr_Header_RecID = ah.AGR_Header_RecID
INNER
JOIN Company co
ON co.Company_RecID = ah.Company_RecID
WHERE (te.Date_Start >= DATEADD(mm, DATEDIFF(mm, 0, GETDATE()) -6, 0) AND te.Date_Start <= DATEADD(mm, DATEDIFF(mm, -1, GETDATE()) -1, -1))
GROUP
BY DATEPART(dd, DATEDIFF(dd, 0, te.Date_Start) / 7 * 7 ) / 7 + 1
, DATENAME(mm, te.Date_Start)
, DATEPART(mm, te.Date_Start)
ORDER
BY DATEPART(mm, te.Date_Start) ASC
, DATEPART(dd, DATEDIFF(dd, 0, te.Date_Start) / 7 * 7 ) / 7 + 1 ASC
By default I want to show all week numbers for a month and if no results for the week, display 0.
This may help you
CREATE TABLE #t
(
hr NUMERIC(22, 6),
weekno INT,
mon VARCHAR(20)
)
INSERT INTO #t
VALUES
(2.00 , 2 , 'October'),
(2.25 , 3 , 'October'),
(7.25 , 5 , 'October')
WITH dates AS (
SELECT 1 id,datename(month,(getdate())) mon
UNION ALL
SELECT id+1, mon FROM dates WHERE id<5
)
SELECT Isnull(hr, 0) hrs,
id AS weekno,
d.mon AS [month]
FROM #t t
RIGHT JOIN dates d
ON t.mon = d.mon
AND t.weekno = d.id
output
hrs weekno month
0.000000 1 October
2.000000 2 October
2.250000 3 October
0.000000 4 October
7.250000 5 October

How to count the number of days in a given month and in a given year in MSSQL

EmployeeID RecordID DateRecord
1 1 2/19/2013 12:00:00 AM
1 2 2/21/2013 12:00:00 AM
1 3 2/23/2013 12:00:00 AM
1 4 2/27/2013 12:00:00 AM
1 5 3/3/2013 12:00:00 AM
2 11 3/10/2013 12:00:00 AM
2 12 3/14/2013 12:00:00 AM
1 14 3/16/2013 12:00:00 AM
How can I count the number of days?
Example in February 2013 which has "19, 21, 23, 27" that should be count to "4" days .. ??
I found this method ..
SELECT DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord),
DATEPART(dd, Daterecord),
COUNT(*)
FROM Records
GROUP BY DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord),
DATEPART(dd, Daterecord)
and resulted to ..
2013 2 19 1
2013 2 21 1
2013 2 23 1
2013 2 27 1
2013 3 3 1
2013 3 10 1
2013 3 14 1
2013 3 16 1
it just get the specific dates but didm't count the total number of days in each month .. help me .. pls
I have change few names hopr you won't mind
WITH Emp_CTE AS (
SELECT EmployeeID ,DATEPART(yy, Daterecord) AS years,
DATEPART(mm, Daterecord) AS months
-- DATEPART(dd, Daterecord) AS days
FROM testTrial
)
SELECT COUNT(months) AS noOfMonths ,* FROM Emp_CTE GROUP BY months,EmployeeID,years
SqlFiddle
Let you try this:-
1: Find the number of days in whatever month we're currently in
DECLARE #dt datetime
SET #dt = getdate()
SELECT #dt AS [DateTime],
DAY(DATEADD(mm, DATEDIFF(mm, -1, #dt), -1)) AS [Days in Month]Solution
2: Find the number of days in a given month-year combo
DECLARE #y int, #m int
SET #y = 2012
SET #m = 2
SELECT #y AS [Year],
#m AS [Month],
DATEDIFF(DAY,
DATEADD(DAY, 0, DATEADD(m, ((#y - 1900) * 12) + #m - 1, 0)),
DATEADD(DAY, 0, DATEADD(m, ((#y - 1900) * 12) + #m, 0))
) AS [Days in Month]
If your table is called Employee then this will do the trick:
select convert(varchar, DateRecord, 112)/ 100, count(*)
from Employee
group by convert(varchar, DateRecord, 112)/ 100
Your initial query was almost right, just needed to remove the DATEPART(dd, Daterecord) from the grouping and it would work. Add in a HAVING clause to find the records from the month of February:
SELECT
DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord),
COUNT(1)
FROM
Records
GROUP BY
DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord)
HAVING
DATEPART(yy, eCreationTime) = 2013
AND DATEPART(mm, Daterecord) = 2
there is no 'yearmonth' in the suggested code ??
try this perhaps
select
datename(month,daterecord) as [Month]
, year(DateRecord) as [Year]
, count(distinct DateRecord ) as day_count
, count(distinct dateadd(day, datediff(day,0, DateRecord ), 0)) as daytime_count
from your_table
where ( DateRecord >= '20130201' and DateRecord < '20130301' )
group by
datename(month,daterecord)
, year(DateRecord)
note the column [daytime_count] is only required if the field [DateRecord] has times othe than 12:00 AM (i.e. it "trims off" times so you deal with dates at 12:AM)
Regarding date range selections: many people will feel that using 'between' is the solution however that isn't true and the safest most reliable method is as I shown above. Note that the higher date is 1st March, but we are asking for information that is less than the 1st March, so we don't need to worry about leap years and we don't have to worry about hours and minutes either.
see: What do BETWEEN and the devil have in common?
try this...
declare #date2 nvarchar(max)
set #date2 = (select getdate())
select DateDiff(Day,#date2,DateAdd(month,1,#date2))

TSQL: How to Split dates and count months in between

I need to split these dates by FY and get the number of months between the dates. I have the FY split answered previously at How to duplicate Rows with new entries link.
Having the following dates:-
ID Start dt End dt
2550 10/1/2010 9/30/2011
2551 8/1/2014 7/31/2015
2552 6/1/2013 5/31/2015
2553 5/10/2012 6/11/2014
I would like the following result set:
ID FY # of Months Start Dt End Dt
2550 2011 12 10/1/2010 9/30/2011
2551 2014 2 8/1/2014 9/30/2014
2551 2015 10 10/1/2014 7/31/2015
2552 2013 4 6/1/2013 9/30/2013
2552 2014 12 10/1/2013 9/30/2014
2552 2015 8 10/1/2014 5/31/2015
2553 2012 5 5/10/2012 9/30/2012
2553 2013 12 10/1/2012 9/30/2013
2553 2014 9 10/1/2013 6/11/2014
An FY is considered from Oct to Sept of the next year.
Thanks in advance!
It was a fun query to build:
declare #t table(id int, start_dt datetime, end_dt datetime)
Insert into #t(id, start_dt, end_dt) Values
(2550, '2010/10/1', '2011/9/30')
, (2551, '2014/8/1', '2015/7/31')
, (2552, '2013/6/1', '2015/5/31')
, (2553, '2012/5/10', '2014/6/11')
, (2554, '2012/5/10', '2012/11/1')
, (2555, '2012/5/10', '2012/8/11')
; with data as(
Select id, start_dt, end_dt, next_y = DATEADD(month, 9, DATEADD(year, DATEDIFF(year, 0, DATEADD(month, 3, start_dt)), 0))
From #t
), split as (
Select id, start_dt
, end_dt = case when next_y > end_dt then end_dt else DATEADD(day, -1, next_y) end
, next_y = DATEADD(year, 1, next_y)
From data as d
Union All
Select s.id
, DATEADD(day, 1, s.end_dt)
, case when DATEADD(day, -1, next_y) < t.end_dt then DATEADD(day, -1, next_y) else t.end_dt end
, next_y = DATEADD(year, 1, s.next_y)
From split as s
Inner Join #t as t on t.id = s.id
Where s.end_dt < t.end_dt
)
Select ID, FY = year(end_dt), '# of Months' = DATEDIFF(month, start_dt, end_dt)+1, 'Start Dt' = start_dt, 'End Dt' = end_dt
From split
Order by id, start_dt, end_dt

Resources