SQL query get first punch and lastpunch for every employee - sql

I have the following data in SQL Server:
What I need is that for every day by employee (employeeId) I get in the follwing data:
AccessCode column means I = PunchIn and O = PunchOut and we have to filter by lunchtype = 'N'
So basically the result should return only one row per day and all the punch ins and punch outs in the middle of the first entrance and last exist shouldn't be considered.
Any clue?

You can do conditional aggregation :
select employeeid, In, Out,
dateadd(second, datediff(second, in, out), 0) as Hours
from(select employeeid,
min(case when AccessCode = 'I' then timestamp end) as In,
max(case when AccessCode = 'O' then timestamp end) as Out
from table t
where lunchtype = 'N'
group by employeeid, convert(date, times)
) t;

You can try this
with cte as
(select
*,
cast(times as date) as myda
from myTable
)
select
employeeid,
mn as punch_in,
mx as punch_out,
datediff(minute, mn, mx)/60.0 as hours
from
(select
employeeid,
min(times) over (partition by myda) as mn,
max(times) over (partition by myda) as mx
from cte
) t
group by
employeeid, mn, mx

Try this:
select employeeId,
min(case when accessCode = 'I' then timestamp end) punchIn,
max(case when accessCode = 'O' then timestamp end) punchOut
from myTable
where lunchtype = 'N'
group by employeeId

Related

Merge two consecutive rows into a column

Assuming that, I have a table something like this :
EmployeeCode
EntryType
TIME
ABC200413
IN
8:48AM
ABC200413
OUT
4:09PM
ABC200413
IN
4:45PM
ABC200413
OUT
6:09PM
ABC200413
IN
7:45PM
ABC200413
OUT
10:09PM
Now I want to convert my data something like this :
EmployeeCode
IN_TIME
OUT_TIME
ABC200413
8:48AM
4:09PM
ABC200413
4:45PM
6:09PM
ABC200413
7:45PM
10:09PM
Is there any way I can achieve this using SQL server query?
Thanks in advance.
Provided mytable contains only valid pairs of in / out events
select EmployeeCode,
max(case EntryType when 'IN' then TIME end ) IN_TIME,
max(case EntryType when 'OUT' then TIME end ) OUT_TIME
from (
select EmployeeCode, EntryType, TIME,
row_number() over(partition by EmployeeCode, EntryType order by TIME) rn
from mytable
)t
group by EmployeeCode, rn
order by EmployeeCode, rn
Otherwise a kind of clean-up is required first.
One solution that may work is using inner joins .. as you may have multiple In & Out records.
Select A.EmployeeCode,
Min(TIME) IN_TIME,
(Select Max(A2.TIME) From Attendance A2 Where A2.EmployeeCode = A.EmployeeCode And A2.EntryType = 'OUT') OUT_TIME
From Attendance A
Where A.EntryType = 'IN'
Group By A.EmployeeCode
So, The main query get the max Out time for each employee & the inner query get the min In time. That solution supposes that at least each employee has one IN record
Assuming there are always pairs of IN/OUT, you can use LEAD to get the next value
select
EmployeeCode,
TIME as IN_TIME,
nextTime AS OUT_TIME
from (
select *,
lead(case EntryType when 'OUT' then TIME end) over (partition by EmployeeCode order by TIME) nextTime
from mytable
) t
where EntryType = 'IN';

How to calculate total hours from multiple in time and out time from below?

first punch as in time,
second punch as out time
if possible avoid duplicate punch on same time within a minute
I need to get all in time ,outtime in a row with total hours
like below any format.
I tried below query but can't get my expected output
WITH Level1
AS (
SELECT A.emp_reader_id,
DT
,A.EventCatId
,A.Belongs_to
,ROW_NUMBER() OVER ( PARTITION BY A.Belongs_to,A.emp_reader_id ORDER BY DT ) AS RowNum
FROM dbo.trnevents A
)
,
LEVEL2
AS (-- find the last and next event type for each row
SELECT A.emp_reader_id,A.DT , A.EventCatId ,COALESCE(LastVal.EventCatId, 10) AS LastEvent,
COALESCE(NextVal.EventCatId, 10) AS NextEvent ,A.Belongs_to
FROM Level1 A
LEFT JOIN Level1 LastVal
ON A.emp_reader_id = LastVal.emp_reader_id and A.Belongs_to=LastVal.Belongs_to
AND A.RowNum - 1 = LastVal.RowNum
LEFT JOIN Level1 NextVal
ON A.emp_reader_id = NextVal.emp_reader_id and A.Belongs_to=NextVal.Belongs_to
AND A.RowNum + 1 = NextVal.RowNum
)
select * from level2 where emp_reader_id=92 order by dt desc
Expected output:
Try this below script. I considered all DT with Sam Minutes as single entry for the calculation.
WITH CTE AS
(
SELECT MAX(emp_reader_id) emp_reader_id,
CAST(DT AS DATE) Date_for_Group,
LEFT(CAST(DT AS VARCHAR),16) Time_For_Group,
ROW_NUMBER() OVER(PARTITION BY CAST(DT AS DATE) ORDER BY LEFT(CAST(DT AS VARCHAR),16)) RN,
CASE
WHEN ROW_NUMBER() OVER(PARTITION BY CAST(DT AS DATE) ORDER BY LEFT(CAST(DT AS VARCHAR),16))%2 = 0 THEN 'OUT'
ELSE 'IN'
END In_Out
FROM your_table
GROUP BY CAST(DT AS DATE),LEFT(CAST(DT AS VARCHAR),16)
)
SELECT A.emp_reader_id,A.Date_for_Group,
SUM(DATEDIFF(Minute,CAST(A.Time_For_Group AS DATETIME),CAST(B.Time_For_Group AS DATETIME)))/60 Hr,
SUM(DATEDIFF(Minute,CAST(A.Time_For_Group AS DATETIME),CAST(B.Time_For_Group AS DATETIME)))%60 Min
FROM CTE A
INNER JOIN CTE B
ON A.emp_reader_id = B.emp_reader_id
AND A.RN = B.RN -1
AND A.Date_for_Group = B.Date_for_Group
WHERE A.In_Out = 'IN'
GROUP BY A.emp_reader_id,A.Date_for_Group
first assign rownumber to datetime column then start the same result set with rownumber+1
Then Inner join them on rownumbers. After that select min an max from timein and out columns and group by on date to get total workhours of that day. hope it helps.
select empid
,date
,min(timein) as timein,max (timeout) timeout,convert(nvarchar(20),datediff(hh,min (timein),max(timeout))%24)
+':'+
convert(nvarchar(20),datediff(mi,min (timein),max(timeout))%60) as totalhrs
from(
Select a.empid,cast(a.dt as date) date,b.dt as timein,a.dt as timeout from(
SELECT DT
,[empid]
, id
,row_number() over(order by dt) as inn
FROM [test1].[dbo].[Table_2]
)a
inner join(
SELECT distinct DT
,[empid]
, id
,rank() over(order by dt)+1 as out
FROM [test1].[dbo].[Table_2])b
on FORMAT(a.dt,'hh:mm') <> FORMAT(b.dt,'hh:mm')
and cast(a.dt as date)=cast(b.dt as date)
and a.inn=b.out)b
group by b.empid,b.date

Case statement based in max min dates

I have a columns as Memnumber, activity type, activity date, activity ID. One member can have activities after few days. I want to write a case statement that if the activity date is most initial then INITIAL and if activity is most recent then MR and if there is any activity in between these 2 dates then BETWEEN. They need to be grouped by Memnumber and treatment type.
I wrote query as :
--MR County Tree
SELECT T0.MEMBERNUMBER,
T0.ACTIVITYTYPE,
T1.MR_CY17,
T1.IN_CY17,
T0.ACTIVITY_DATE,
(T0.ACTIVITYID)
FROM DLA_EXTRACT_FINAL T0
INNER JOIN (
SELECT MEMBERNUMBER,
ACTIVITYTYPE,
MAX(ACTIVITY_DATE) MR_CY17,
MIN(ACTIVITY_DATE) IN_CY17
FROM DLA20_EXTRACT_FINAL
WHERE to_char(ACTIVITY_DATE, 'YYYYMMDD') >= 20170101
AND to_char(ACTIVITY_DATE, 'YYYYMMDD') <= 20171231
GROUP BY MEMBERNUMBER,
ACTIVITYTYPE
) T1 ON T0.MEMBERNUMBER = T1.MEMBERNUMBER
AND T0.ACTIVITYTYPE = T1.ACTIVITYTYPE
AND T0.ACTIVITY_DATE = T1.MR_CY17
--where T0.ACTIVITYTYPE='MT'
WHERE t0.MEMBERNUMBER = 'M500085268'
GROUP BY T0.MEMBERNUMBER,
T0.ACTIVITYTYPE,
T1.MR_CY17,
T1.IN_CY17,
T0.ACTIVITYID,
T0.ACTIVITY_DATE
ORDER BY T0.MEMBERNUMBER,
T0.ACTIVITYTYPE,
T1.MR_CY17,
T1.IN_CY17.
Looking for a solution.
You want to use window functions. Something like:
SELECT T0.MEMBERNUMBER,
T0.ACTIVITYTYPE,
T0.ACTIVITY_DATE,
T0.ACTIVITYID,
case when row_number() over (partition by T0.MEMBERNUMBER, T0.ACTIVITYTYPE
order by T0.ACTIVITY_DATE) = 1 then 1 else 0 end most_initial,
case when row_number() over (partition by T0.MEMBERNUMBER, T0.ACTIVITYTYPE
order by T0.ACTIVITY_DATE desc) = 1 then 1 else 0 end most_recent
FROM DLA_EXTRACT_FINAL T0
Then you can use case statements to label as INITIAL if most_intial = 1, MR if most_recent = 1, or BETWEEN if both are 0.

SQL Server 2008 multiple column filter

I have stuck on sql query to bring the wanted data. I have table as following
I have tried cte table but did not work . I need the get source 'O' if available else 'T' with max sequence as above result table.
select district
, id
, building
, year
, date
, period
, sequence
, source from GetAttData gt with (nolock) where sequence in (select max(sequence) from GetAttData with (nolock)
where district = gt.district
and building = gt.building
and year = gt.year
and id= gt.id
group by district, id, building, year, date, period)
and source = 'O'
select
district, id, building, year, date, period, sequence, source
from (
select district, id, building, year, date, period, sequence, source,
row_number() over(partition by district, id, building, year, date, period
order by case when source = 'O' then 0 else 1 end, sequence desc
) as takeme
) foo
where takeme = 1

insert 0 into successive row with same fields value

I have a table containing the following line:
I'd like to create a view that displays the following result (without changing my original table) :
For each line having the same id,day,month and year I'd like to leave a single line with the cost and count and insert 0 in the others.
Here is a portable approach not requiring PARTITION. I have assumed you will not have the same datetimeIN value for more than one row in a group:
select t.id, t.day, t.month, t.year,
case when tm.id is null then 0 else t.cost end as cost,
case when tm.id is null then 0 else t.Count end as Count,
t.datetimeIN, t.datetimeOUT
from MyTable t
left outer join (
select id, day, month, year, min(datetimeIN) as minIN
from MyTable
group by id, day, month, year
) tm on t.id = tm.id
and t.day = tm.day
and t.month = tm.month
and t.year = tm.year
and t.datetimeIN = tm.minIN
You can do something like this:
SELECT id, day, month, year,
CASE WHEN nNum = 1 then cost else 0 end as cost,
CASE WHEN nNum = 1 then "Count" else 0 end as "Count",
datetimeIN, datetimeOUT
FROM (
SELECT id, day, month, year,
cost, "Count", datetimeIN, datetimeOUT,
row_number() OVER (PARTITION BY id, day, month, year
ORDER BY datetimeIN) as nNum
FROM TableName
) A
It uses row_number() to number the rows, and then a CASE statement to single out the first one and make it behave differently.
See it working on SQL Fiddle here.
or, using a common table expression:
with commonTableExp ([day], [month], [year], minDate) as (
select [day], [month], [year], min(datetimeIn)
from #temp
group by [day], [month], [year])
select id,
dt.[day],
dt.[month],
dt.[year],
case when datetimein = minDate then [cost] else 0 end,
case when datetimein = minDate then [count] else 0 end,
dateTimeIn
from #temp dt join commonTableExp cte on
dt.[day] = cte.[day] and
dt.[month] = cte.[month] and
dt.[year] = cte.[year]
order by dateTimeIn
Query
Select id, [day], [month], [year], Case When K.RowID = 1 Then [cost] Else 0 End as Cost, Case When K.RowID = 1 Then [count] Else 0 End as [count], [DateTimeIN], [DateTimeOut] From
(
select ROW_NUMBER() Over(Partition by id, [day], [month], [year] Order by ID ) as RowID, * From Testing
)K
Drop table Testing
Click here to see SQL Profiler details for Red Filter's Query
Click here to see SQL Profiler details for my Query
For More Information You can see SQL Fiddle