Minimum Travel Time - oracle

Display schedule_id, source, destination and travel_time which has minimum travel time. Sort the result based on schedule id.
I have tried this code and there is something missing in my query as I m getting the error.
select sh.schedule_id,sh.source,sh.destination,sh.duration as travel_time
from schedule sh
(select min(sh.duration) from schedule)
order by sh.schedule_id;

Almost correct. You forgot to define the minimum travel time in the where-clause.
SELECT sh.schedule_id,
sh.source,
sh.destination,
sh.duration as travel_time
FROM schedule sh
WHERE sh.duration = (select min(duration) from schedule) -- This is where the problem was.
ORDER BY sh.schedule_id;

Then only column which looks like travel time is DURATION, its datatype is NUMBER. What does that number represent? Minutes? Hours? Something else?
Anyway, here's one option you might consider. It "sorts" durations (i.e. "travel time") using RANK analytic function, and fetches a row (or rows) whose duration is minimal.
Advantage of such an approach is that you have to scan the table only once; if you select minimum duration in a subquery, and then use its result to fetch data you're interested in, you're accessing the same table twice which might matter when there are many rows involved. For a small sample data set, you won't notice any difference.
The SCHEDULE CTE represents some test data; you need code that starts at line 6.
SQL> with schedule (schedule_id, source, destination, duration) as
2 (select 1, 'Paris', 'London' , 8 from dual union all
3 select 2, 'Berlin', 'Prague' , 4 from dual union all
4 select 3, 'Zagreb', 'Budapest', 4 from dual
5 )
6 select schedule_id, source, destination, duration
7 from (select schedule_id, source, destination, duration,
8 rank() over (order by duration) rn
9 from schedule
10 )
11 where rn = 1;
SCHEDULE_ID SOURCE DESTINAT DURATION
----------- ------ -------- ----------
2 Berlin Prague 4
3 Zagreb Budapest 4
SQL>

I guess there was no need for alias names as you were accessing the data from the same table.
Code:
select
schedule_id,source,destination,duration from schedule
where duration = (select min(duration) from schedule)
order by schedule_id;

Related

reset index every 5th row - Oracle SQL

How can I make index column start over after reaching 5th row? I can't do that with a window function as there are no groups, I just need an index with max number of 5 like this:
date
index
01.01.21
1
02.01.21
2
03.01.21
3
04.01.21
4
05.01.21
5
06.01.21
1
07.01.21
2
and so on.
Appreciate any ideas.
You can use below solution for that purpose.
First, rank (row_number analytic function)the rows in your table within inline view
Then, use again the row_number function with partition by clause to group the previously ranked rows by TRUNC((rnb - 1)/5)
SELECT t."DATE"
, row_number()over(PARTITION BY TRUNC((rnb - 1)/5) ORDER BY rnb) as "INDEX"
FROM (
select "DATE", row_number()OVER(ORDER BY "DATE") rnb
from Your_table
) t
ORDER BY 1
;
demo on db<>fiddle
Your comment about using analytic functions is wrong; you can use analytic functions even when there are no "groups" (or "partitions"). Here you do need an analytic function, to order the rows (even if you don't need to partition them).
Here is a very simple solution, using just row_number(). Note the with clause, which is not part of the solution; I included it just for testing. In your real-life case, remove the with clause, and use your actual table and column names. The use of mod(... , 5) is pretty much obvious; it looks a little odd (subtracting 1, taking the modulus, then adding 1) because in Oracle we seem to count from 1 in all cases, instead of the much more natural counting from 0 common in other languages (like C).
Note that both date and index are reserved keywords, which shouldn't be used as column names. I used one common way to address that - I added an underscore at the end.
alter session set nls_date_format = 'dd.mm.rr';
with
sample_inputs (date_) as (
select date '2021-01-01' from dual union all
select date '2021-01-02' from dual union all
select date '2021-01-03' from dual union all
select date '2021-01-04' from dual union all
select date '2021-01-05' from dual union all
select date '2021-01-06' from dual union all
select date '2021-01-07' from dual
)
select date_, 1 + mod(row_number() over (order by date_) - 1, 5) as index_
from sample_inputs
;
DATE_ INDEX_
-------- ----------
01.01.21 1
02.01.21 2
03.01.21 3
04.01.21 4
05.01.21 5
06.01.21 1
07.01.21 2
You can combine MOD() with ROW_NUMBER() to get the index you want. For example:
select date, 1 + mod(row_number() over(order by date) - 1, 5) as idx from t

SQL: Is there a way to exclude duplicate results if a condition is met

I am trying to build a query that will search for a certain user and the most recent job they were associated with. I want to only pull the most recent date but some users have two jobs associated with the same date. Is there a way to only pull one of those while not excluding that result if it was a user's only job?
For example
User Job Date
1 Chef 7/13/21
1 Server 7/13/21
2 Server 7/3/21
3 Chef 7/1/21
Desired result:
User Job Date
1 Chef 7/13/21
2 Server 7/3/21
3 Chef 7/1/21
Thanks!
Is it possible? Yes, but data you posted as example doesn't reflect what you're saying because for user = 1 both jobs have same date.
Anyway, here's one option how to do that: use one of analytic functions (I used row_number; rank might also be an option) to "rank" rows partitioned by user & sorted by date, and then fetch the one with the lowest rank.
Sample data till line #6; query begins at line #7.
SQL> with test (cuser, job, datum) as
2 (select 1, 'chef' , date '2021-07-13' from dual union all
3 select 1, 'server', date '2021-07-13' from dual union all
4 select 2, 'server', date '2021-07-03' from dual union all
5 select 3, 'chef' , date '2021-07-01' from dual
6 ),
7 temp as
8 (select cuser, job, datum,
9 row_number() over (partition by cuser order by datum desc) rn
10 from test
11 )
12 select cuser, job, datum
13 from temp
14 where rn = 1;
CUSER JOB DATUM
--------- ------ ----------
1 chef 07/13/2021
2 server 07/03/2021
3 chef 07/01/2021
SQL>
Make use of keep dense_rank
https://oracle-base.com/articles/misc/rank-dense-rank-first-last-analytic-functions
select user, date,
MIN(job) KEEP (DENSE_RANK FIRST ORDER BY date)
from table
group by user, date
A rough example
http://sqlfiddle.com/#!4/14ee4d/2
Now this is not quite deterministic as when there are two jobs on the same date you need to specify how to sort them. If your date column includes time then you should be OK

Find max number of open tasks

What I am attempting to do is find out the MAX number of tasks I may receive on a day during the next 6 months.
for example
task 1 runs 1-jan-16 and ends 10-jan-16
Task 2 runs 3-Jan-16 and ends 15-jan-16
task 3 runs 6-Jan-16 and ends 10-Jan-16
Task 4 runs 9-Jan-16 and ends 20-Jan-16
So in this example there are 4 tasks that are open between 1-Jan and 10th Jan so I want the outcome to be 4 in this scenario. The reason being is I'm displaying them in a Gantt chart so they'll be all underneath each other.
All I have to work with so far is:
select schedule_start_date,am.AC,count(wo) as from ac_master am
left outer join wo on wo.ac = am.ac and ac_type = '190'
where wo.status = 'OPEN'
group by am.ac,schedule_start_date
This will show the count per day but some of these may overlap.
Is there anyway to do what I am trying to accomplish?
If you just want the count for each scheduled group at a given point in time, then you can just use BETWEEN with the start and end dates:
SELECT schedule_start_date,
am.AC,
COUNT(*) AS theCount
FROM ac_master am
LEFT OUTER JOIN wo
ON wo.ac = am.ac AND
ac_type = '190'
WHERE wo.status = 'OPEN' AND
'2016-01-10' BETWEEN schedule_start_date AND schedule_end_date
GROUP BY schedule_start_date,
am.ac
Regardless of how you develop a set of rows with start_date and end_date, here is a method to show how the task count changes over time. Each date is the first date when the task count changes from the previous value to the new one.
If you only need max(tasks), that's a simple matter of grouping by whatever is needed. (Or, in Oracle 12, you can order by tasks and use the new fetch first feature.) Notice also the partition by clause - if you need different groups for different categories (for example: for different "departments" etc.) you can use this clause so that the computations are done separately for each group, all in one pass over the data.
with
intervals ( start_date, end_date ) as (
select date '2016-01-01', date '2016-01-10' from dual union all
select date '2016-01-03', date '2016-01-15' from dual union all
select date '2016-01-06', date '2016-01-10' from dual union all
select date '2016-01-09', date '2016-01-20' from dual
),
u ( dt, flag ) as (
select start_date , 1 from intervals
union all
select end_date + 1, -1 from intervals
)
select distinct dt, sum(flag) over (partition by null order by dt) as tasks
from u
order by dt;
DT TASKS
---------- ---------
2016-01-01 1
2016-01-03 2
2016-01-06 3
2016-01-09 4
2016-01-11 2
2016-01-16 1
2016-01-21 0

oracle sql, counting working hours

I have been trying to find something related but couldn't.
I have an issue that i need to produce an availability percentage of something. I have a table that includes events that are happening, which i managed to count them by the day they are happening, but i am finding issues to count the total number of working hours in a quarter or a year.
when each day of the week has a different weight.
Basically my question is: can i do it without making a table with all dates in that month/year?
An example of the data:
ID DATE duration Environment
1 23/10/15 25 a
2 15/01/15 50 b
3 01/01/15 43 c
8 05/06/14 7 b
It can work for me by a calculated field or just a general query to get the information.
sorry I don't really understand the question but if you want to generate dates using connect by level is an easy way to do it (you could also use the model clause or recursive with) I did it here for just 10 days but you get the idea. I put in your dates as t1 and generated a list of dates (t) and then did a left outer join to put them together.
WITH t AS
(SELECT to_date('01-01-2015', 'mm-dd-yyyy') + level - 1 AS dt,
NULL AS duration
FROM dual
CONNECT BY level < = 10
),
t1 AS
(
SELECT to_date('10/01/15', 'dd-mm-yy') as dt, 50 as duration FROM dual
UNION ALL
SELECT to_date('01/01/15', 'dd-mm-yy'), 43 FROM dual
UNION ALL
SELECT to_date('06/01/15', 'dd-mm-yy'), 43 FROM dual
)
SELECT t.dt,
NVL(NVL(t1.duration, t.duration),0) duration
FROM t,
t1
WHERE t.dt = t1.dt(+)
ORDER BY dt
results
DT Duration
01-JAN-15 43
02-JAN-15 0
03-JAN-15 0
04-JAN-15 0
05-JAN-15 0
06-JAN-15 43
07-JAN-15 0
08-JAN-15 0
09-JAN-15 0
10-JAN-15 50
This was my intention, and the full answer below.
WITH t AS
(SELECT to_date('01-01-2015', 'mm-dd-yyyy') + level - 1 AS dt
FROM dual
CONNECT BY level < =365
),
t1 as
(
SELECT dt,
CASE
WHEN (to_char(TO_DATE( t.dt,'YYYY-MM-DD HH24:MI:SS'),'DY') in ('MON', 'TUE', 'WED', 'THU', 'FRI'))
THEN 14*60
WHEN (to_char(TO_DATE( t.dt,'YYYY-MM-DD HH24:MI:SS'),'DY') in ('SAT'))
THEN 8*60
WHEN (to_char(TO_DATE( t.dt,'YYYY-MM-DD HH24:MI:SS'),'DY') in ('SUN'))
THEN 10*60
ELSE 0 END duration ,
to_char(t.dt,'Q') as quarter
FROM t
)
select to_char(t1.dt,'yyyy'), to_char(t1.dt,'Q'),sum(t1.duration)
from t1
group by
to_char(t1.dt,'yyyy'), to_char(t1.dt,'Q');

Finding a count of rows in an arbitrary date range using Oracle

The question I need to answer is this "What is the maximum number of page requests we have ever received in a 60 minute period?"
I have a table that looks similar to this:
date_page_requested date;
page varchar(80);
I'm looking for the MAX count of rows in any 60 minute timeslice.
I thought analytic functions might get me there but so far I'm drawing a blank.
I would love a pointer in the right direction.
You have some options in the answer that will work, here is one that uses Oracle's "Windowing Functions with Logical Offset" feature instead of joins or correlated subqueries.
First the test table:
Wrote file afiedt.buf
1 create table t pctfree 0 nologging as
2 select date '2011-09-15' + level / (24 * 4) as date_page_requested
3 from dual
4* connect by level <= (24 * 4)
SQL> /
Table created.
SQL> insert into t values (to_date('2011-09-15 11:11:11', 'YYYY-MM-DD HH24:Mi:SS'));
1 row created.
SQL> commit;
Commit complete.
T now contains a row every quarter hour for a day with one additional row at 11:11:11 AM. The query preceeds in three steps. Step 1 is to, for every row, get the number of rows that come within the next hour after the time of the row:
1 with x as (select date_page_requested
2 , count(*) over (order by date_page_requested
3 range between current row
4 and interval '1' hour following) as hour_count
5 from t)
Then assign the ordering by hour_count:
6 , y as (select date_page_requested
7 , hour_count
8 , row_number() over (order by hour_count desc, date_page_requested asc) as rn
9 from x)
And finally select the earliest row that has the greatest number of following rows.
10 select to_char(date_page_requested, 'YYYY-MM-DD HH24:Mi:SS')
11 , hour_count
12 from y
13* where rn = 1
If multiple 60 minute windows tie in hour count, the above will only give you the first window.
This should give you what you need, the first row returned should have
the hour with the highest number of pages.
select number_of_pages
,hour_requested
from (select to_char(date_page_requested,'dd/mm/yyyy hh') hour_requested
,count(*) number_of_pages
from pages
group by to_char(date_page_requested,'dd/mm/yyyy hh')) p
order by number_of_pages
How about something like this?
SELECT TOP 1
ranges.date_start,
COUNT(data.page) AS Tally
FROM (SELECT DISTINCT
date_page_requested AS date_start,
DATEADD(HOUR,1,date_page_requested) AS date_end
FROM #Table) ranges
JOIN #Table data
ON data.date_page_requested >= ranges.date_start
AND data.date_page_requested < ranges.date_end
GROUP BY ranges.date_start
ORDER BY Tally DESC
For PostgreSQL, I'd first probably write something like this for a "window" aligned on the minute. You don't need OLAP windowing functions for this.
select w.ts,
date_trunc('minute', w.ts) as hour_start,
date_trunc('minute', w.ts) + interval '1' hour as hour_end,
(select count(*)
from weblog
where ts between date_trunc('minute', w.ts) and
(date_trunc('minute', w.ts) + interval '1' hour) ) as num_pages
from weblog w
group by ts, hour_start, hour_end
order by num_pages desc
Oracle also has a trunc() function, but I'm not sure of the format. I'll either look it up in a minute, or leave to see a friend's burlesque show.
WITH ranges AS
( SELECT
date_page_requested AS StartDate,
date_page_requested + (1/24) AS EndDate,
ROWNUMBER() OVER(ORDER BY date_page_requested) AS RowNo
FROM
#Table
)
SELECT
a.StartDate AS StartDate,
MAX(b.RowNo) - a.RowNo + 1 AS Tally
FROM
ranges a
JOIN
ranges b
ON a.StartDate <= b.StartDate
AND b.StartDate < a.EndDate
GROUP BY a.StartDate
, a.RowNo
ORDER BY Tally DESC
or:
WITH ranges AS
( SELECT
date_page_requested AS StartDate,
date_page_requested + (1/24) AS EndDate,
ROWNUMBER() OVER(ORDER BY date_page_requested) AS RowNo
FROM
#Table
)
SELECT
a.StartDate AS StartDate,
( SELECT MIN(b.RowNo) - a.RowNo
FROM ranges b
WHERE b.StartDate > a.EndDate
) AS Tally
FROM
ranges a
ORDER BY Tally DESC

Resources