Pivot with Date and time - sql

I have this query for Employee's fingerprints
SELECT
bp.Name
vahruae_date date,
to_char(vahruae_timeofattendance, 'hh:mi') Time
FROM vahruae_hr_empattendancelog empl
INNER JOIN c_bpartner bp ON empl.vahruae_enroll_id = bp.enroll_id
And the value displays as follows
Name Date Time
-----------------------------------------
John 01-SEP-2022 1:00
John 01-SEP-2022 9:00
John 01-SEP-2022 13:00
John 01-SEP-2022 16:00
John 02-SEP-2022 1:00
John 02-SEP-2022 6:00
John 04-SEP-2022 5:00
. .
. .
John 31-SEP-2022 4:30
John 31-SEP-2022 7:00
Ali 01-SEP-2022 10:00
Ali 04-SEP-2022 5:00
. .
. .
I want to display all times in one date row, like this
Name Date Time1 Time2 Time3 Time4
-----------------------------------------------------------------------------------------------
John 01-SEP-2022 1:00 9:00 13:00 16:00
John 02-SEP-2022 1:00 6:00 (null) (null)
John 04-SEP-2022 5:00 (null) (null) (null)
John 31-SEP-2022 4:30 7:00 (null) (null)
Ali 01-SEP-2022 10:00 (null) (null) (null)
Ali 05-SEP-2022 5:00 (null) (null) (null)
I'm not very good with Pivot, but I know I cannot use a subquery inside the Pivot IN Clause.
How can I accomplish that?

Use the ROW_NUMBER analytic function to index the times within a sub-query and then PIVOT using that:
SELECT name,
dt,
time1,
time2,
time3,
time4
FROM (
SELECT empl.vahruae_enroll_id,
bp.Name,
vahruae_date AS dt,
to_char(vahruae_timeofattendance, 'hh:mi') AS Time,
ROW_NUMBER() OVER (
PARTITION BY empl.vahruae_enroll_id,
vahruae_date,
TRUNC(vahruae_timeofattendance)
ORDER BY vahruae_timeofattendance
) AS rn
FROM vahruae_hr_empattendancelog empl
INNER JOIN c_bpartner bp
ON empl.vahruae_enroll_id = bp.enroll_id
)
PIVOT (
MAX(time)
FOR rn IN (1 AS time1, 2 AS time2, 3 AS time3, 4 AS time4)
)

We need to number the hours by date and name using row_number() and then pivoting.
select *
from
(
select "Name" as "Name"
,to_char("Date", 'hh24:mi') as "Time"
,to_date(to_char("Date", 'DD-mon-YYYY')) as "Date"
,row_number() over(partition by "Name", to_char("Date", 'DD-mon-YYYY') order by to_char("Date", 'hh24:mi')) as rn
from t
) t
pivot(max("Time") for rn in('1' as Time1, '2' as Time2, '3' as Time3, '4' as Time4)) p
order by "Name" desc, "Date"
Name
Date
TIME1
TIME2
TIME3
TIME4
John
01-SEP-22
01:00
09:00
13:00
16:00
John
02-SEP-22
01:00
06:00
null
null
John
04-SEP-22
05:00
null
null
null
John
01-OCT-22
04:30
07:00
null
null
Ali
01-SEP-22
10:00
null
null
null
Ali
04-SEP-22
05:00
null
null
null
Fiddle

Related

Query to get unique status from tracking table for a given time

Have scenario to select unique value present in tracking table for a given time
Table values like,
HistoryId EmpId RoleName TimeStamp
1 1 Developer 8:00:00 AM
2 1 Lead 8:00:00 PM
3 1 Mgr 5:00:00 PM
4 2 Lead 8:00:00 AM
5 2 Developer2 5:00:00 PM
6 2 Mgr2 8:00:00 PM
7 3 Mgr 8:00:00 AM
8 3 Lead2 5:00:00 PM
9 3 Developer3 8:00:00 PM
11 3 Developer4 8:30:00 PM
12 1 lead5 8:15:00 PM
Want to select the unique RoleName values for user for a given time,
Example - If I pass time as 6PM, it should select latest status of each user at 6PM
I have tried to select as below, but it is given all the values, where as I need only latest values present for the time value passed
Select EmpId, RoleName, MAX(TimeStamp)
From [dbo].[EmpRoleHistory]
Where TimeStamp <= '2020-02-06 18:00:00.000'
GROUP BY EmpId, RoleName
ORDER BY EmpId
Expected Result:
1 Mgr 2020-02-06 17:00:00.000
2 Developer2 2020-02-06 17:00:00.000
3 Lead2 2020-02-06 17:00:00.000
Current Result:
1 Developer 2020-02-06 08:00:00.000
1 Mgr 2020-02-06 17:00:00.000
2 Developer2 2020-02-06 17:00:00.000
2 Lead 2020-02-06 08:00:00.000
3 Lead2 2020-02-06 17:00:00.000
3 Mgr 2020-02-06 08:00:00.000
The problem is here
GROUP BY EmpId, RoleName
As I can see, the same EmpId have different from RoleName.
So that's the reason why you are getting different values.
demo on db<>fiddle
;with cte_TempTable as
(
Select EmpId, MAX(TimeStamp) as TimeStamp
From [dbo].[EmpRoleHistory]
Where TimeStamp <= '2020-02-06 17:00:00.000'
GROUP BY EmpId
)
select e.*
from EmpRoleHistory e
inner join cte_TempTable c on e.EmpId = c.EmpId
where e.TimeStamp = c.TimeStamp
ORDER BY e.EmpId
Output
Use a subquery and not exists
with cte as
(
Select EmpId, RoleName, TimeStamp
From [dbo].[EmpRoleHistory]
Where TimeStamp <= '2020-02-06 17:00:00.000'
)
select * from cte a
where not exists
(
select 1 from cte b where a.EmpId= b.EmpId and a.TimeStamp < b.TimeStamp
)
Use a subquery or CTE to return the latest EmpRoleHistory record for each employee prior to the supplied datetime, and then join the subquery to the main table to pull out the RoleName value for those records.
Select erh.EmpId, erh.RoleName, erh.TimeStamp
From [dbo].[EmpRoleHistory] as erh
Inner Join (
Select EmpId, MAX(TimeStamp) as TimeStamp
From [dbo].[EmpRoleHistory]
Where TimeStamp <= '2020-02-06 18:00:00.000'
GROUP BY EmpId
) as filt on erh.EmpId = filt.EmpId and
erh.TimeStamp = filt.TimeStamp
Order By erh.EmpId

SQL Server Pivot/Unpivot

I am trying to find a way to convert this table I have that has sorted everything in columns into rows
EmpID Day StartTime
-------------------------------------------------
4 SUNDAY 10:00:00 AM
4 MONDAY 8:00:00 AM
4 TUESDAY 8:00:00 AM
4 WEDNESDAY 8:00:00 AM
4 THURSDAY 8:00:00 AM
4 FRIDAY NULL
4 SATURDAY NULL
800 SUNDAY 10:00:00 AM
800 MONDAY 8:00:00 AM
800 TUESDAY 8:00:00 AM
800 WEDNESDAY 8:00:00 AM
800 THURSDAY 8:00:00 AM
800 FRIDAY NULL
800 SATURDAY NULL
INTO
EmpID Sunday(Start Time) Monday(Start Time)
-------------------------------------------------
4 10:00:00 AM 8:00:00 AM
etc...
I've seen examples of Pivot and Unpivot, but would this be to complex for the pivot/unpivot function?
If you want to use PIVOT
Example
Select *
From YourTable src
Pivot ( max(StartTime) for Day in ([Sunday],[Monday],[Tuesday] ) ) pvt
Note: This assumes YourTable is the 3 columns as displayed in your sample. If you have extra columns, YourTable should be replaced with a sub-query such as
From (Select EmpID,Day,StartTime From YourTable) src
I would just use conditional aggregation:
select empid,
max(case when day = 'Sunday' then StartTime end) as Sunday_StartTime,
max(case when day = 'Monday' then StartTime end) as Monday_StartTime,
. . .
from t
group by empid;

Oracle query - without temporary table

I'm new to Oracle and I need to help with this query. I have table with data samples /records like:
name | datetime
-----------
A | 20140414 10:00
A | 20140414 10:30
A | 20140414 11:00
B | 20140414 11:30
B | 20140414 12:00
A | 20140414 12:30
A | 20140414 13:00
A | 20140414 13:30
And I need to "group"/get informations into this form:
name | datetime_from | datetime_to
----------------------------------
A | 20140414 10:00 | 20140414 11:00
B | 20140414 11:30 | 20140414 12:00
A | 20140414 12:30 | 20140414 13:30
I couldnt find any solution for query similar to this. Could anyone please help me?
Note: I dont want do use temporary tables.
Thanks,
Pavel
SQL> with t (name, datetime) as
2 (
3 select 'A', to_date('20140414 10:00','YYYYMMDD HH24:MI') from dual union all
4 select 'A', to_date('20140414 10:30','YYYYMMDD HH24:MI') from dual union all
5 select 'A', to_date('20140414 11:00','YYYYMMDD HH24:MI') from dual union all
6 select 'B', to_date('20140414 11:30','YYYYMMDD HH24:MI') from dual union all
7 select 'B', to_date('20140414 12:00','YYYYMMDD HH24:MI') from dual union all
8 select 'A', to_date('20140414 12:30','YYYYMMDD HH24:MI') from dual union all
9 select 'A', to_date('20140414 13:00','YYYYMMDD HH24:MI') from dual union all
10 select 'A', to_date('20140414 13:30','YYYYMMDD HH24:MI') from dual
11 )
12 select name, min(datetime) datetime_from, max(datetime) datetime_to
13 from (
14 select name, datetime,
15 datetime-(1/48)*(row_number() over(partition by name order by datetime)) dt
16 from t
17 )
18 group by name,dt
19 order by 2,1
20 /
N DATETIME_FROM DATETIME_TO
- -------------- --------------
A 20140414 10:00 20140414 11:00
B 20140414 11:30 20140414 12:00
A 20140414 12:30 20140414 13:30
You need to find periods where the values are the same. The easiest way in Oracle is to use the lag() function, some logic, and aggregation:
select name, min(datetime), max(datetime)
from (select t.*,
sum(case when name <> prevname then 1 else 0 end) over (order by datetime) as cnt
from (select t.*, lag(name) over (order by datetime) as prevname
from table t
) t
) t
group by name, cnt;
What this does is count, for a given value of datetime, the number of times that the name has switched on or before that datetime. This identifies the periods of "constancy", which are then used for aggregation.
As 9000 is suggesting you can have a query like the following:
select
a.name,
Max(a.datetime),
Min(b.datetime)
from
table a,
table b
group by
a.name
where a.name = b.name

SQL Server Split Day in 4 Blocks of 2 hours

I have few records of tasks with StartTime and StopTime and I would like to split it in 4 records of 2 hours (8:30 - 10:30 / 10:30 - 12:30 / 13:00 - 15:00 / 15:00 - 17:00)
Typically, i would like to split this row :
From
Title StartTime StopTime
Task1 2013-11-12 09:00 2013-11-12 14:00
To
Title StartTime StopTime
Task1 2013-11-12 09:30 2013-11-12 10:30
Task1 2013-11-12 10:30 2013-11-12 12:30
Task1 2013-11-12 12:30 2013-11-12 14:00
Any suggestion is welcome, thanks.
Try this query:
SQLFiddle demo
select [TaskName],
CASE WHEN CONVERT(varchar,StartTime,108)<pStart
THEN floor(CAST(StartTime as float))+Cast(pStart as Datetime)
ELSE StartTime
END as StartTime,
CASE WHEN CONVERT(varchar,StopTime,108)>pEnd
THEN floor(CAST(StopTime as float))+Cast(pEnd as Datetime)
ELSE StopTime
END as StopTime
FROM T
JOIN
(
SELECT '08:30:00' as pStart, '10:30:00' as pEnd
UNION ALL
SELECT '10:30:00' as pStart, '12:30:00' as pEnd
UNION ALL
SELECT '13:00:00' as pStart, '15:00:00' as pEnd
UNION ALL
SELECT '15:00:00' as pStart, '17:00:00' as pEnd
) as DayParts
ON (CONVERT(varchar,StartTime,108)<=pEnd
AND
CONVERT(varchar,StopTime,108)>=pEnd
)
OR
(
CONVERT(varchar,StopTime,108)>=pStart
AND
CONVERT(varchar,StartTime,108)<=pStart
)
ORDER BY TaskName,StartTime

how to query the count of records from first day to last day of the month

I would like to get the count of every day records from my table.
For example I have a table “Employee” with the following fields ID, EmpNo, DateHired.
And I have the following records
ID EmpNo DateHired
1 000001 3/2/2013 12:00:00 AM
2 000002 3/14/2013 12:00:00 AM
3 000003 3/14/2013 12:00:00 AM
4 000004 3/21/2013 12:00:00 AM
5 000005 4/2/2013 12:00:00 AM
6 000006 4/3/2013 12:00:00 AM
7 000007 4/3/2013 12:00:00 AM
8 000008 4/3/2013 12:00:00 AM
9 000009 4/3/2013 12:00:00 AM
10 000010 4/4/2013 12:00:00 AM
11 000011 4/5/2013 12:00:00 AM
12 000012 5/1/2013 12:00:00 AM
And the current month is april,
how can I get this value:
Count Day
0 4/1/2013 12:00:00 AM
1 4/2/2013 12:00:00 AM
4 4/3/2013 12:00:00 AM
1 4/4/2013 12:00:00 AM
1 4/5/2013 12:00:00 AM
0 4/6/2013 12:00:00 AM
0 4/7/2013 12:00:00 AM
0 4/8/2013 12:00:00 AM
Up to
0 4/30/2013 12:00:00 AM
You need to create a calendar for the whole month of April in order to get the whole dates of the month. With the aid of using Common Table Expression, you can get what you want.
After creating a calendar, join it with table Employee using LEFT JOIN so dates will have no matches on table Employee will still be included on the result.
WITH April_Calendar
AS
(
SELECT CAST('20130401' as datetime) AS [date]
UNION ALL
SELECT DATEADD(dd, 1, [date])
FROM April_Calendar
WHERE DATEADD(dd, 1, [date]) <= '20130430'
)
SELECT a.date, COUNT(b.DateHired) totalCount
FROM April_Calendar a
LEFT JOIN Employee b
ON a.date = b.DateHired
GROUP BY a.date
ORDER BY a.date
SQLFiddle Demo
Try
SELECT COUNT(*) as 'Count', DateHired as 'Day'
FROM Employee
WHERE DateHired BEWTEEN %date1 AND %date2
GROUP BY DateHired
Untested, should work though.
This could be the query...
SELECT COUNT(ID) as Count, DateHired FROM Employee GROUP BY DateHired
Following can be helpful in the case...
http://www.sqlite.org/lang_datefunc.html
Hope it helps..