SQL Server 2012 - Using ROW_NUMBER() on DISTINCT [duplicate] - sql

This question already has answers here:
sql query distinct with Row_Number
(8 answers)
Closed 5 years ago.
I have a query below.
SELECT DISTINCT
FORMAT(CAST(SchedTi AS DATETIME),'hh:mm tt') AS SchedTimeIn,
FORMAT(CAST(SchedTO AS DATETIME),'hh:mm tt') AS SchedTimeOut
FROM
tblemployee_schedule
ORDER BY
SchedTimeIn
It returns this resultset:
SchedTimeIn SchedTimeOut
01:00 AM 09:00 AM
01:00 AM 10:00 AM
01:00 AM 10:00 PM
01:15 AM 05:15 AM
01:15 AM 10:15 AM
01:30 AM 05:30 AM
01:30 PM 10:30 PM
01:45 AM 05:45 AM
My desired result is:
SchedTimeIn SchedTimeOut ROWNUM
01:00 AM 09:00 AM 1
01:00 AM 10:00 AM 2
01:00 AM 10:00 PM 3
01:15 AM 05:15 AM 4
01:15 AM 10:15 AM 5
01:30 AM 05:30 AM 6
01:30 PM 10:30 PM 7
01:45 AM 05:45 AM 8
I tried the following query:
SELECT DISTINCT
ROW_NUMBER() OVER(ORDER BY SchedTi),
FORMAT(CAST(SchedTi AS DATETIME),'hh:mm tt') AS SchedTimeIn,
FORMAT(CAST(SchedTO AS DATETIME),'hh:mm tt') AS SchedTimeOut
FROM
tblemployee_schedule
ORDER BY
SchedTimeIn
But it returns too many redundant rows and NULL values.
Thank you in advance.

Try the below one,
SELECT * ,ROW_NUMBER() OVER(ORDER BY SchedTimeIn) AS ROWNUM
FROM (
SELECT DISTINCT
FORMAT(CAST(SchedTi AS DATETIME),'hh:mm tt') AS SchedTimeIn,
FORMAT(CAST(SchedTO AS DATETIME),'hh:mm tt') AS SchedTimeOut
FROM tblemployee_schedule
) AS D
ORDER BY ROWNUM
In your query ROW_NUMBER() produce a new sequence number for each records , so the DISTINCT key will not wok for the same, that's why you are getting too many redundant records. So you can use the ROW_NUMBER() in an outer query to overcome this.

Main problem is you are asking only part of problem,
In firstplace using distinct,order by,row_number in same query seem wrong.
using only partition function is enough.
any way using your query,
select *
,ROW_NUMBER()over(order by SchedTimeIn)rn
FROM
(
SELECT DISTINCT
FORMAT(CAST(SchedTi AS DATETIME),'hh:mm tt') AS SchedTimeIn,
FORMAT(CAST(SchedTO AS DATETIME),'hh:mm tt') AS SchedTimeOut
FROM tblemployee_schedule
ORDER BY SchedTimeIn
)tbl

Related

SQL Query how to get rows that are in range of AM time and PM time with Current Date

So i have this table of reports:
report_Id
report_date
01
2022-12-07 08:00:00
02
2022-12-07 12:00:00
03
2022-12-07 22:00:00
What I wanted to do is to get rows that are reported in 05:00:00 between 12:00:00 which I would like to name as AM and rows that are reported in 13:00:00 between 23:00:00 as PM.
This would be the output example for AM:
report_Id
report_date
01
2022-12-07 08:00:00
02
2022-12-07 12:00:00
And this would be the output example for PM:
report_Id
report_date
03
2022-12-07 22:00:00
Did try to search but it would be really helpful if I can get answers from here. Thank You
Postgres variant:
You can use CASE for creating new column for example to mark AM and PM as following using CTE, where you can filter rows by new column:
With t1 as(
select
report_id,
report_date,
case when report_date::time between '05:00:00'::time and '12:00:00'::time then 'AM' else 'PM' end as time_of_day
from table)
Select * from t1 where time_of_day = 'AM'
In MYSQL ::
FOR AM :
select * from table where report_date between '2022-12-07 00:00:00' and '2022-12-07 11:59:59'
FOR PM:
select * from table where report_date between '2022-12-07 12:00:00' and '2022-12-07 23:59:59'

How to get the min/max time in continuous times and the count of times in this range?

Date:
2015-04-01 12:00
2015-04-01 11:00
2015-04-01 10:
2015-04-01 09:
2015-04-01 08:00 // <---
2015-04-01 05:00
2015-04-01 04:00
2015-04-01 03:00
2015-04-01 02:00
2015-04-01 01:00 // <---
2015-03-31 22:00
2015-03-31 21:00
2015-03-31 20:00
2015-03-31 19:00 // <---
I want to get this result:
MaxTime | MinTime | Count
2015-04-01 12:00 | 2015-04-01 08:00 | 5
2015-04-01 05:00 | 2015-04-01 01:00 | 5
2015-03-31 22:00 | 2015-03-31 19:00 | 4
Try this query
select
max(dateColumn), min(dateColumn), count(dateColumn)
from (
select
dateColumn, datepart(dayofyear, dateColumn)*24 + datepart(hh, dateColumn) - row_number() over (order by dateColumn) grp
from
MyTable
) t
group by grp
Sample Data
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
DROP TABLE #Temp
;With cte(Dates)
AS
(
SELECT '2015-04-01 12:00' UNION ALL
SELECT '2015-04-01 11:00' UNION ALL
SELECT '2015-04-01 10:00' UNION ALL
SELECT '2015-04-01 09:00' UNION ALL
SELECT '2015-04-01 08:00' UNION ALL
SELECT '2015-04-01 05:00' UNION ALL
SELECT '2015-04-01 04:00' UNION ALL
SELECT '2015-04-01 03:00' UNION ALL
SELECT '2015-04-01 02:00' UNION ALL
SELECT '2015-04-01 01:00' UNION ALL
SELECT '2015-03-31 22:00' UNION ALL
SELECT '2015-03-31 21:00' UNION ALL
SELECT '2015-03-31 20:00' UNION ALL
SELECT '2015-03-31 19:00'
)
SELECT * INTO #Temp FROM cte
SELECT * FROM #Temp
Your Expected Result script using Row_number()Over() Function
SELECT DISTINCT
MAX(Dates)OVER(PArtition by BatchSeq Order by (SELECT 1)) AS MaxTime
,MIN(Dates)OVER(PArtition by BatchSeq Order by (SELECT 1)) AS MinTime
,COUNT(Dates)OVER(PArtition by BatchSeq Order by (SELECT 1)) As [Count]
FROM
(
SELECT Dates , ((ROW_NUMBER()OVER(ORDER BY (SELECT 1))-1)/5+1) AS BatchSeq FROM
#Temp
)dt
ORDER BY 1 DESC
Your Expected Result script using Group by() Function
SELECT
MAX(Dates) AS MaxTime
,MIN(Dates) AS MinTime
,COUNT(Dates) As [Count]
FROM
(
SELECT Dates ,((ROW_NUMBER()OVER(ORDER BY (SELECT 1))-1)/5+1) AS BatchSeq FROM
#Temp
)dt
GROUP BY BatchSeq
ORDER BY 1 DESC
Result
MaxTime MinTime Count
--------------------------------------------
2015-04-01 12:00 2015-04-01 08:00 5
2015-04-01 05:00 2015-04-01 01:00 5
2015-03-31 22:00 2015-03-31 19:00 4

SQL - Order by time, then text on beginning

I have a query below :
SELECT DISTINCT TimeSched from tbl_schedule
It returns this result:
TimeSched
Rest Day
11:00 AM - 08:00 PM
No Schedule
09:00 AM - 06:00 PM
10:00 AM - 07:00 PM
When I use ORDER BY TimeSched, it returns this result:
TimeSched
09:00 AM - 06:00 PM
10:00 AM - 07:00 PM
11:00 AM - 08:00 PM
No Schedule
Rest Day
However, my desired result is I want 'Rest Day' and 'No Schedule' on the first and second row by default, then followed by the order of schedules in ascending order. As seen below :
TimeSched
Rest Day
No Schedule
09:00 AM - 06:00 PM
10:00 AM - 07:00 PM
11:00 AM - 08:00 PM
try this.
select * from timetable
order by
iif(timesched in ('No Schedule','Rest Day'),'01' + timesched,timesched)
Append a prefix for No Schedule and Restday with
01 when ordering.. so 01No will go 1, 01R will go next then your 09 to
24
How about simply doing:
SELECT DISTINCT TimeSched
FROM tbl_schedule
ORDER BY (CASE WHEN timesched LIKE '[a-zA-Z]%' THEN 1 ELSE 0 END),
timesched;
Try to make use of the below code :
DECLARE #TimeSched TABLE
(ID INT IDENTITY(1,1),TimeSched VARCHAR(20))
INSERT INTO #TimeSched
VALUES
('Rest Day'),
('11:00 AM - 08:00 PM'),
('No Schedule'),
('09:00 AM - 06:00 PM'),
('10:00 AM - 07:00 PM')
SELECT * FROM #TimeSched
ORDER BY
CASE
WHEN TimeSched ='Rest Day' THEN 1
WHEN TimeSched ='No Schedule' THEN 2
ELSE 3 END
Here is the answer to my question.
SELECT CASE TimeSched WHEN 'Restday' THEN 1 WHEN 'No Schedule' THEN 2 ELSE 3 END
AS TimeSked, TimeSched ORDER BY TimeSked, TimeSched

Get classroom available hours between date time range

I'm, using Oracle 11g and I have this problem. I couldn't come up with any ideas to solve it yet.
I have a table with occupied classrooms. What I need to find are the hours available between a datetime range. For example, I have rooms A, B and C, the table of occupied classrooms looks like this:
Classroom start end
A 10/10/2013 10:00 10/10/2013 11:30
B 10/10/2013 09:15 10/10/2013 10:45
B 10/10/2013 14:30 10/10/2013 16:00
What I need to get is something like this:
with date time range between '10/10/2013 07:00' and '10/10/2013 21:15'
Classroom avalailable_from available_to
A 10/10/2013 07:00 10/10/2013 10:00
A 10/10/2013 11:30 10/10/2013 21:15
B 10/10/2013 07:00 10/10/2013 09:15
B 10/10/2013 10:45 10/10/2013 14:30
B 10/10/2013 16:00 10/10/2013 21:15
C 10/10/2013 07:00 10/10/2013 21:15
Is there a way I can accomplish that with sql or pl/sql?
I was looking at a solution similar in concept at least to Wernfried's, but I think it's different enough to post as well. The start is the same idea, first generating the possible time slots, and assuming you're looking at 15-minute windows: I'm using CTEs because I think they're clearer than nested selects, particularly with this many levels.
with date_time_range as (
select to_date('10/10/2013 07:00', 'DD/MM/YYYY HH24:MI') as date_start,
to_date('10/10/2013 21:15', 'DD/MM/YYYY HH24:MI') as date_end
from dual
),
time_slots as (
select level as slot_num,
dtr.date_start + (level - 1) * interval '15' minute as slot_start,
dtr.date_start + level * interval '15' minute as slot_end
from date_time_range dtr
connect by level <= (dtr.date_end - dtr.date_start) * (24 * 4) -- 15-minutes
)
select * from time_slots;
This gives you the 57 15-minute slots between the start and end date you specified. The CTE for date_time_range isn't strictly necessary, you could put your dates straight into the time_slots conditions, but you'd have to repeat them and that then introduces a possible failure point (and means binding the same value multiple times, from JDBC or wherever).
Those slots can then be cross-joined to the list of classrooms, which I'm assuming are already in another table, which gives you 171 (3x57) combinations; and those can be compared with existing bookings - once those are eliminated you're left with the 153 15-minute slots that have no booking.
with date_time_range as (...),
time_slots as (...),
free_slots as (
select c.classroom, ts.slot_num, ts.slot_start, ts.slot_end,
lag(ts.slot_end) over (partition by c.classroom order by ts.slot_num)
as lag_end,
lead(ts.slot_start) over (partition by c.classroom order by ts.slot_num)
as lead_start
from time_slots ts
cross join classrooms c
left join occupied_classrooms oc on oc.classroom = c.classroom
and not (oc.occupied_end <= ts.slot_start
or oc.occupied_start >= ts.slot_end)
where oc.classroom is null
)
select * from free_slots;
But then you have to collapse those into contiguous ranges. There are various ways of doing that; here I'm peeking at the previous and next rows to decide if a particular value is the edge of a range:
with date_time_range as (...),
time_slots as (...),
free_slots as (...),
free_slots_extended as (
select fs.classroom, fs.slot_num,
case when fs.lag_end is null or fs.lag_end != fs.slot_start
then fs.slot_start end as slot_start,
case when fs.lead_start is null or fs.lead_start != fs.slot_end
then fs.slot_end end as slot_end
from free_slots fs
)
select * from free_slots_extended
where (fse.slot_start is not null or fse.slot_end is not null);
Now we're down to 12 rows. (The outer where clause eliminates all 141 of the 153 slots from the previous step which are mid-range, since we only care about the edges):
CLASSROOM SLOT_NUM SLOT_START SLOT_END
--------- ---------- ---------------- ----------------
A 1 2013-10-10 07:00
A 12 2013-10-10 10:00
A 19 2013-10-10 11:30
A 57 2013-10-10 21:15
B 1 2013-10-10 07:00
B 9 2013-10-10 09:15
B 16 2013-10-10 10:45
B 30 2013-10-10 14:30
B 37 2013-10-10 16:00
B 57 2013-10-10 21:15
C 1 2013-10-10 07:00
C 57 2013-10-10 21:15
So those represent the edges, but on separate rows, and a final step combines them:
...
select distinct fse.classroom,
nvl(fse.slot_start, lag(fse.slot_start)
over (partition by fse.classroom order by fse.slot_num)) as slot_start,
nvl(fse.slot_end, lead(fse.slot_end)
over (partition by fse.classroom order by fse.slot_num)) as slot_end
from free_slots_extended fse
where (fse.slot_start is not null or fse.slot_end is not null)
Or putting all that together:
with date_time_range as (
select to_date('10/10/2013 07:00', 'DD/MM/YYYY HH24:MI') as date_start,
to_date('10/10/2013 21:15', 'DD/MM/YYYY HH24:MI') as date_end
from dual
),
time_slots as (
select level as slot_num,
dtr.date_start + (level - 1) * interval '15' minute as slot_start,
dtr.date_start + level * interval '15' minute as slot_end
from date_time_range dtr
connect by level <= (dtr.date_end - dtr.date_start) * (24 * 4) -- 15-minutes
),
free_slots as (
select c.classroom, ts.slot_num, ts.slot_start, ts.slot_end,
lag(ts.slot_end) over (partition by c.classroom order by ts.slot_num)
as lag_end,
lead(ts.slot_start) over (partition by c.classroom order by ts.slot_num)
as lead_start
from time_slots ts
cross join classrooms c
left join occupied_classrooms oc on oc.classroom = c.classroom
and not (oc.occupied_end <= ts.slot_start
or oc.occupied_start >= ts.slot_end)
where oc.classroom is null
),
free_slots_extended as (
select fs.classroom, fs.slot_num,
case when fs.lag_end is null or fs.lag_end != fs.slot_start
then fs.slot_start end as slot_start,
case when fs.lead_start is null or fs.lead_start != fs.slot_end
then fs.slot_end end as slot_end
from free_slots fs
)
select distinct fse.classroom,
nvl(fse.slot_start, lag(fse.slot_start)
over (partition by fse.classroom order by fse.slot_num)) as slot_start,
nvl(fse.slot_end, lead(fse.slot_end)
over (partition by fse.classroom order by fse.slot_num)) as slot_end
from free_slots_extended fse
where (fse.slot_start is not null or fse.slot_end is not null)
order by 1, 2;
Which gives:
CLASSROOM SLOT_START SLOT_END
--------- ---------------- ----------------
A 2013-10-10 07:00 2013-10-10 10:00
A 2013-10-10 11:30 2013-10-10 21:15
B 2013-10-10 07:00 2013-10-10 09:15
B 2013-10-10 10:45 2013-10-10 14:30
B 2013-10-10 16:00 2013-10-10 21:15
C 2013-10-10 07:00 2013-10-10 21:15
SQL Fiddle.
It is always a challenge when you like to "select something which does not exist". First you need a list of all available classrooms and times (in interval of 15 Minutes). Then you can select them by skipping the occupied items.
I managed to make a query without any PL/SQL:
CREATE TABLE Table1
(Classroom VARCHAR2(10), start_ts DATE, end_ts DATE);
INSERT INTO Table1 VALUES ('A', TIMESTAMP '2013-01-10 10:00:00', TIMESTAMP '2013-01-10 11:30:00');
INSERT INTO Table1 VALUES ('B', TIMESTAMP '2013-01-10 09:15:00', TIMESTAMP '2013-01-10 10:45:00');
INSERT INTO Table1 VALUES ('B', TIMESTAMP '2013-01-10 14:30:00', TIMESTAMP '2013-01-10 16:00:00');
WITH all_rooms AS
(SELECT CHR(64+LEVEL) AS ROOM FROM dual CONNECT BY LEVEL <= 3),
all_times AS
(SELECT CAST(TIMESTAMP '2013-01-10 07:00:00' + (LEVEL-1) * INTERVAL '15' MINUTE AS DATE) AS TIMES, LEVEL AS SLOT
FROM DUAL
CONNECT BY TIMESTAMP '2013-01-10 07:00:00' + (LEVEL-1) * INTERVAL '15' MINUTE <= TIMESTAMP '2013-01-10 21:15:00'),
all_free_slots AS
(SELECT ROOM, TIMES, SLOT,
CASE SLOT-LAG(SLOT, 1, 0) OVER (PARTITION BY ROOM ORDER BY SLOT)
WHEN 1 THEN 0
ELSE 1
END AS NEW_WINDOW
FROM all_times
CROSS JOIN all_rooms
WHERE NOT EXISTS
(SELECT 1 FROM TABLE1 WHERE ROOM = CLASSROOM AND TIMES BETWEEN START_TS + INTERVAL '1' MINUTE AND END_TS - INTERVAL '1' MINUTE)),
free_time_windows AS
(SELECT ROOM, TIMES, SLOT,
SUM(NEW_WINDOW) OVER (PARTITION BY ROOM ORDER BY SLOT) AS WINDOW_ID
FROM all_free_slots)
SELECT ROOM,
TO_CHAR(MIN(TIMES), 'yyyy-mm-dd hh24:mi') AS free_time_start,
TO_CHAR(MAX(TIMES), 'yyyy-mm-dd hh24:mi') AS free_time_end
FROM free_time_windows
GROUP BY ROOM, WINDOW_ID
HAVING MAX(TIMES) - MIN(TIMES) > 0
ORDER BY ROOM, 2;
ROOM FREE_TIME_START FREE_TIME_END
---- ----------------------------------
A 2013-01-10 07:00 2013-01-10 10:00
A 2013-01-10 11:30 2013-01-10 21:15
B 2013-01-10 07:00 2013-01-10 09:15
B 2013-01-10 10:45 2013-01-10 14:30
B 2013-01-10 16:00 2013-01-10 21:15
C 2013-01-10 07:00 2013-01-10 21:15
In order to understand the query you can split the sub-queries from top, e.g.
WITH all_rooms AS
(SELECT CHR(64+LEVEL) AS ROOM FROM dual CONNECT BY LEVEL <= 3),
all_times AS
(SELECT CAST(TIMESTAMP '2013-01-10 07:00:00' + (LEVEL-1) * INTERVAL '15' MINUTE AS DATE) AS TIMES, LEVEL AS SLOT
FROM DUAL
CONNECT BY TIMESTAMP '2013-01-10 07:00:00' + (LEVEL-1) * INTERVAL '15' MINUTE <= TIMESTAMP '2013-01-10 21:15:00')
SELECT ROOM, TIMES, SLOT,
CASE SLOT-LAG(SLOT, 1, 0) OVER (PARTITION BY ROOM ORDER BY SLOT)
WHEN 1 THEN 0
ELSE 1
END AS NEW_WINDOW
FROM all_times
CROSS JOIN all_rooms
WHERE NOT EXISTS (SELECT 1 FROM TABLE1 WHERE ROOM = CLASSROOM AND TIMES BETWEEN START_TS + INTERVAL '1' MINUTE AND END_TS - INTERVAL '1' MINUTE)
ORDER BY ROOM, SLOT

Count rows per hour in SQL Server with full date-time value as result

How can I count the number of rows per hour in SQL Server with full date-time as result.
I've already tried this, but it returns only the hours
SELECT DATEPART(HOUR,TimeStamp), Count(*)
FROM [TEST].[dbo].[data]
GROUP BY DATEPART(HOUR,TimeStamp)
ORDER BY DATEPART(HOUR,TimeStamp)
Now the result is:
Hour Occurrence
---- ----------
10 2157
11 60740
12 66189
13 77096
14 90039
But I need this:
Timestamp Occurrence
------------------- ----------
2013-12-21 10:00:00 2157
2013-12-21 11:00:00 60740
2013-12-21 12:00:00 66189
2013-12-21 13:00:00 77096
2013-12-21 14:00:00 90039
2013-12-22 09:00:00 84838
2013-12-22 10:00:00 64238
You actually need to round the TimeStamp to the hour. In SQL Server, this is a bit ugly, but easy to do:
SELECT dateadd(hour, datediff(hour, 0, TimeStamp), 0) as TimeStampHour, Count(*)
FROM [TEST].[dbo].[data]
GROUP BY dateadd(hour, datediff(hour, 0, TimeStamp), 0)
ORDER BY dateadd(hour, datediff(hour, 0, TimeStamp), 0);