SQL query for find out in/out time in a office - sql

This may be silly question for the DATABASE guys but for me its very tough because it's the first time when I am seeing the DATABASE..I am not the DATABASE guy but I have to do this.
I have DATABASE in SQL SERVER 2005 and there is a VIEW that contain the data related to employee in/out in the office.
There may be more then 2 entry in a day for a particular date but we consider first entry in a day is in time in the office and last entry as the out time from the office,In between these these employee can go out side any no of time but we consider only first and last time entry in a day.
So I have to to write QUERY for this...
EDIT :
List of coloumns in a view some other coloumn are also there but I do'nt think it is necessary to describe here ..
CardSrNo
LastName
FirstName
MidleName
PersonalID
Date
Time
YMD
HMS
CardNumber
Department

Try a good ol' group by.
Select employeeID, Date,
MIN(Time) as InTime,
MAX(Time) as OutTime
FROM Transactions
GROUP BY employeeID, Date

You can use ROW_NUMBER() ranking function to determine first and last time entry
;WITH cte AS
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY PersonalID ORDER BY [Date] ASC, [Time] ASC) AS rnASC,
ROW_NUMBER() OVER(PARTITION BY PersonalID ORDER BY [Date] DESC, [Time] DESC) AS rnDESC
FROM dbo.your_tableName
)
SELECT *
FROM cte
WHERE rnASC = 1 OR rnDESC = 1
Simple example on SQLFiddle
OR use option with EXISTS operator
SELECT *
FROM dbo.your_tableName t1
WHERE EXISTS (
SELECT 1
FROM dbo.your_tableName t2
WHERE t1.PersonalID = t2.PersonalID
HAVING (t1.[Date] = MAX(t2.[Date]) AND t1.[Time] = MAX(t2.[Time]))
OR (t1.[Date] = MIN(t2.[Date]) AND t1.[Time] = MIN(t2.[Time]))
)
Simple example on SQLFiddle

Now I am using that query :
SELECT FirstName,LastName,CardNumber,Department, Date , MIN(Time) AS intime , MAX(Time) AS outtime ,
convert(varchar(8),(convert(datetime,MAX(Time),110) - convert(datetime,MIN(Time),110)),108) AS Duration
FROM CARDENTRYEXITTRANSACTIONVIEW
GROUP BY FirstName, Date,LastName,CardNumber,Department
And its give the perfect result............

Related

Update bigquery value based on partition by row number

I have a table in which I have records on the wrong date. I want to update them to be the day before for "snapshot_date". I have written the query to select the values I want to update the date for, but I don't know how to write the update query to change it to the previous day.
See screenshot
Query to select problematic records
Select * FROM(
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY Period, User_Struct) rn
FROM `XXX.YYY.TABLE`
where Snapshot_Date = '2021-10-04'
order by Period, User_Struct, Num_Active_Users asc
) where rn = 1
Using DATE_SUB you may get the previous day i.e.
SELECT DATE_SUB(cast('2021-10-04' as DATE), interval '1' day)
will give 2021-10-03.
You may try the following using Big Query Update Statement Syntax
UPDATE
`XXX.YYY.TABLE` t0
SET
t0.Snapshot_Date = DATE_SUB(t2.Snapshot_Date, interval '1' day)
FROM (
SELECT * FROM(
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY Period, User_Struct) rn
FROM
`XXX.YYY.TABLE`
WHERE
Snapshot_Date = '2021-10-04'
ORDER BY -- recommend removing order by here and use recommendation below for row_number
Period, User_Struct, Num_Active_Users asc
) t1
WHERE rn = 1
) t2
WHERE
t0.Snapshot_Date = t2.Snapshot_Date AND -- include other columns to match/join subquery with main table on
You should also specify how your rows should be ordered when using ROW_NUMBER eg
ROW_NUMBER() OVER (PARTITION BY Period, User_Struct ORDER BY Num_Active_Users asc)
if this generates the same/desired results.
Let me know if this works for you.

To recieve one record per id and the one with the latest date

I have the following table stamps with the columns:
Worker
Date
Transferred
Balance
I want out one row per worker,
the record with the latest day, and also have value 1 in Transferred
I have tried a lot of possibilities but none works the way I want to.
SELECT DISTINCT OUT.WORKER,OUT.DATE,OUT.TRANSFERRED,OUT.BALANCE
FROM (
SELECT WORKER,DATE,TRANSFERRED,BALANCE
FROM STAMPS
ORDER BY DATE DESC
) AS OUT
GROUP BY WORKER
You say you want the latest day (presumably the latest for a given worker), so you need the max function.
select s.Worker,
s.Date,
s.Transferred,
s.Balance
from
(select worker,
max(date) as date
from stamps
where transferred = 1
group by Worker) as max_dates,
join stamps s
on s.worked = max_dates.worker
and s.date = max_dates.date
The typical way is to use window functions:
SELECT S.WORKER, S.DATE, S.TRANSFERRED, S.BALANCE
FROM (SELECT S.*,
ROW_NUMBER() OVER (PARTITION BY WORKER ORDER BY DATE DESC) AS SEQNUM
FROM STAMPS S
) S
WHERE SEQNUM = 1;
With the right indexes, a correlated subquery often has the best performance:
select s.*
from stamps s
where s.date = (select max(s2.date)
from stamps s2
where s2.worker = s.worker
);
The appropriate index is on stamps(worker, date).
SELECT * FROM dbo.STAMPS[enter image description here][1]
SELECT subResult.WORKER,
subResult.Date,
subResult.Transferred,
subResult.Balance
FROM
(
SELECT WORKER,
DATE,
TRANSFERRED,
BALANCE,
ROW_NUMBER() OVER(PARTITION BY Worker ORDER BY date DESC) AS rowNum
FROM STAMPS
WHERE Transferred=1
) AS subResult
WHERE subResult.rowNum=1

The Maximum value of two columns with group by

I have a table that contains the followings data :
TRIP TRIP_DATE TRIP_TIME
A 2018-08-08 11:00
A 2018-08-09 11:00
A 2018-08-08 23:00
A 2018-08-20 11:00
A 2018-08-20 14:00
I want the select statement to retrieve the Number of trips, Count , the latest date and time.
Basically the output should be like this:
TRIPS MAX(TRIP_DATE) TRIP_TIME
5 2018-08-20 14:00
This is tricky. I think I would do:
select cnt, date, time
from (select t.*,
row_number() over (partition by trip order by date desc, time desc) as seqnum
count(*) over (partition by trip) as cnt
from t
) t
where seqnum = 1;
You can use the following using GROUP BY:
SELECT TRIP, COUNT(TRIP) AS cnt, MAX(CONCAT(TRIP_DATE, ' ', TRIP_TIME)) AS maxDateTime
FROM table_name
GROUP BY TRIP
To combine the DATE and TIME value you can use one of the following:
using CONCAT_WS: CONCAT_WS(' ', TRIP_DATE, TRIP_TIME)
using CONCAT: CONCAT(TRIP_DATE, ' ', TRIP_TIME)
You can use the above query as sub-query to get the DATE and TIME as seperate values:
SELECT TRIP, cnt, DATE(maxDateTime), TIME_FORMAT(TIME(maxDateTime), '%H:%i') FROM (
SELECT TRIP, COUNT(TRIP) AS cnt, MAX(CONCAT(TRIP_DATE, ' ', TRIP_TIME)) AS maxDateTime
FROM table_name
GROUP BY TRIP
)t;
Note: I recommend to split the DATE and TIME values on the application side. I would also store the DATE and TIME value in one column as DATETIME instead of separate columns.
demos: https://www.db-fiddle.com/f/xcMdmivjJa29rDhHxkUmuJ/2
You can use row_number() function :
select t.*
from (select *, row_number() over (partition by trip order by date desc, time desc) seq
from table t
) t
where seq = 1;
I would go with this (assuming you wanted the MAX Trip_Time as well, its a little difficult to tell from your example):
SELECT COUNT(TRIP) AS Trips,
MAX(TRIP_DATE) AS MAX(TRIP_DATE),
MAX(TRIP_TIME) AS TRIP_TIME
FROM myTable
GROUP BY TRIP
You have option of using analytic function as will as group function here.
All will do the job . Looking at final output I believe max function with group by is more suitable.
There is no hard and fast rule but personally I prefer grouping when final outcome need to be suppressed.

SQL: transposing a time series table into a start-end time table if an event occur

I am trying to use a select statement to create a view, transposing a table with datetime into a table with records in each row, the start-end time when the consecutive values by time (partition by station) in 'record' field is not 0.
Here is a sample of the initial table.
And how it should look like after transposing.
Can anyone help?
You can use the conditional_change_event analytical function to create a special grouping identifier to split these out in a simple query:
select row_number() over () unique_id,
station,
min(datetime) startdate,
max(datetime) enddate
from (
select t.*, CONDITIONAL_CHANGE_EVENT(decode(recording,0,0,1))
over (partition by station order by datetime) chg
from mytable t
) x
where recording > 0
group by station, chg
order by 1, 2
The decode is just to set up your islands and gaps (where gaps are recording <= 0 and islands are recording > 0). Then the change event on that will generate a new identifier for grouping. Also note that I am grouping on the change event even though it isn't part of the output.
ROW_NUMBER() is the best for partitioning. Next, you can do a self join on the partitioned tables to see if the difference between times is greater than five minutes. I think the best solution is to partition on the rolling sum of the timestamp difference, offset by 5 minutes based on your pattern. If the five minutes is not a regular pattern then there is probably a generalized approach that can be used with the zeroes.
Solution written as a CTE below for easy view creation (it's a slow view though).
WITH partitioned as (
SELECT datetime, station, recording,
ROW_NUMBER() OVER(PARTITION BY station
ORDER BY datetime ASC) rn
FROM table --Not sure what the tablename is
WHERE recording != 0),
diffed as (
SELECT a.datetime, a.station,
DATEDIFF(mi,ISNULL(b.datetime,a.datetime),a.datetime)-5) Difference
--The ISNULL logic is for when a.datetime is the beginning of the block,
--we want a 0
FROM partitioned a
LEFT JOIN partitioned b on a.rn = b.rn + 1 and a.station=b.station
GROUP BY a.datetime,a.station),
cumulative as (
SELECT a.datetime, a.station, SUM(b.difference) offset_grouping
FROM diff a
LEFT JOIN diff b on a.datetime >= b.datetime and a.station = b.station ),
ordered as (SELECT datetime,station,
ROW_NUMBER() OVER(PARTITION BY station,offset_grouping ORDER BY datetime asc) starter,
ROW_NUMBER() OVER(PARTITION BY station,offset_grouping ORDER BY datetime desc) ender
FROM cumulative)
SELECT ROW_NUMBER() OVER(ORDER BY a.datetime) unique_id,a.station,a.datetime startdate, b.datetime enddate
FROM ordered a
JOIN ordered b on a.starter = b.ender and a.station=b.station and a.starter=1
This is the only solution I can think of but again, it's slow depending on the amount of data you have.

Oracle - select rows with minimal value in a subset

I have a following table of dates:
dateID INT (PK),
personID INT (FK),
date DATE,
starttime VARCHAR, --Always in a format of 'HH:MM'
What I want to do is I want to pull rows (all columns, including PK) with lowest date (primary condition) and starttime (secondary condition) for every person. For example, if we have
row1(date = '2013-04-01' and starttime = '14:00')
and
row2(date = '2013-04-02' and starttime = '08:00')
row1 will be retrieved, along with all other columns.
So far I have come up with gradual filtering the table, but it`s quite a mess. Is there more efficient way of doing this?
Here is what I made so far:
SELECT
D.id
, D.personid
, D.date
, D.starttime
FROM table D
JOIN (
SELECT --Select lowest time from the subset of lowest dates
A.personid,
B.startdate,
MIN(A.starttime) AS starttime
FROM table A
JOIN (
SELECT --Select lowest date for every person to exclude them from outer table
personid
, MIN(date) AS startdate
FROM table
GROUP BY personid
) B
ON A.personid = B.peronid
AND A.date = B.startdate
GROUP BY
A.personid,
B.startdate
) C
ON C.personid = D.personid
AND C.startdate = D.date
AND C.starttime = D.starttime
It works, but I think there is a more clean/efficient way to do this. Any ideas?
EDIT: Let me expand a question - I also need to extract maximum date (only date, without time) for each person.
The result should look like this:
id
personid
max(date) for each person
min(date) for each person
min(starttime) for min(date) for each person
It is a part of a much larger query (the resulting table is joined with it), and the resulting table must be lightweight enough so that the query won`t execute for too long. With single join with this table (just using min, max for each field I wanted) the query took about 3 seconds, and I would like the resulting query not to take longer than 2-3 times that.
you should be able to do this like:
select a.dateID, a.personID, a.date, a.max_date, a.starttime
from (select t.*,
max(t.date) over (partition by t.personID) max_date,
row_number() over (partition by t.personID
order by t.date, t.starttime) rn
from table t) a
where a.rn = 1;
sample data added to fiddle: http://sqlfiddle.com/#!4/63c45/1
This is the query you can use and no need to incorporate in your query. You can also use #Dazzal's query as stand alone
SELECT ID, PERSONID, DATE, STARTTIME
(
SELECT ID, PERONID, DATE, STARTTIME, ROW_NUMBER() OVER(PARTITION BY personid ORDER BY STARTTIME, DATE) AS RN
FROM TABLE
) A
WHERE
RN = 1
select a.id,a.accomp, a.accomp_name, a.start_year,a.end_year, a.company
from (select t.*,
min(t.start_year) over (partition by t.company) min_date,
max(t.end_year) over (partition by t.company) max_date,
row_number() over (partition by t.company
order by t.end_year desc) rn
from temp_123 t) a
where a.rn = 1;