I have a table with shifts history along with emp ids.
I'm using this query to retrieve a list of employees and their total shifts by specifying the range to count from:
SELECT ope_id, count(ope_id)
FROM operator_shift
WHERE ope_shift_date >=to_date( '01-MAR-10','dd-mon-yy') and ope_shift_date
<= to_date('31-MAR-10','dd-mon-yy')
GROUP BY OPE_ID
which gives
OPE_ID COUNT(OPE_ID)
1 14
2 7
3 6
4 6
5 2
6 5
7 2
8 1
9 2
10 4
10 rows selected.
How do I choose the employee with the highest number of shifts under the specified range date?
Assuming your version of Oracle is new enough to support common table expressions:
With ShiftCounts As
(
SELECT ope_id, count(ope_id) ShiftCount
, ROW_NUMBER() OVER( ORDER BY Count(ope_id) Desc ) ShiftRank
FROM operator_shift
WHERE ope_shift_date >=to_date( '01-MAR-10','dd-mon-yy')
and ope_shift_date <= to_date('31-MAR-10','dd-mon-yy')
GROUP BY OPE_ID
)
Select ope_id, ShiftCount
From ShiftCounts
Where ShiftRank = 1
something like this maybe:
SELECT TOP 1 ope_id, Count(ope_id)
FROM operator_shift
WHERE ope_shift_date >=to_date( '01-MAR-10','dd-mon-yy') and ope_shift_date
<= to_date('31-MAR-10','dd-mon-yy')
GROUP BY OPE_ID
ORDER BY Count(ope_id) DESC
Use:
SELECT t.ope_id,
t.num
FROM (SELECT os.ope_id,
COUNT(os.ope_id) AS num
FROM OPERATOR_SHIFT os
WHERE os.ope_shift_date BETWEEN TO_DATE('01-MAR-10','dd-mon-yy')
AND TO_DATE('31-MAR-10','dd-mon-yy')
GROUP BY os.ope_id
ORDER BY num DESC) t
WHERE ROWNUM = 1
Reference:
Ask Tom: On ROWNUM and Limiting Results
Related
Suppose I have a table like
id
1
3
4
10
12
19
and I'd like to group the ids (in sorted order) into the same group if they differ by 5 or less, and a new group if they differ by 6 or more. So the output would be:
id
group
1
1
3
1
4
1
10
2
12
2
19
3
Is this possible in SQL? It will be a query in Trino, and I see they have commands like lag and partition. Has anyone made a query like this that can help out?
You can use a cte with lead:
with cte(id, l1) as (
select t.id, abs(coalesce(lead(t.id) over (order by t.id), 0) - t.id) < 6 from tbl t
)
select c.id, (select sum(c1.id < c.id and c1.l1 = 0) from cte c1) + 1 from cte c
Below is my table students (having 100K+ rows in orignal, showing just a set):
id
name
class
marks
1
Ryan
5
8
2
Max
5
7
3
Max1
5
10
4
Ryan1
6
8
5
Max2
6
10
6
Ryan2
6
7
7
Ryan3
7
8
8
Max3
7
10
9
Ryan4
7
7
I want to fetch two rows per class ( 5 & 7) having marks <= 10 , also sorted by class, marks ASC
So, the expected result will be:-
id
name
class
marks
1
Ryan
5
8
3
Max1
5
10
7
Ryan3
7
8
8
Max3
7
10
To execute below I tried:-
SELECT DISTINCT t_out.class, t_top.marks, t_top.name
FROM students t_out
JOIN LATERAL (
select *
from students t_in
where t_in.class = t_out.class
ORDER BY t_in.id ASC
) t_top ON TRUE
WHERE t_top.marks <= 10
AND (t_out.class = 5 OR t_out.class = 7)
ORDER BY t_top.marks DESC LIMIT 2;
Result on original database:- it's loading since long time
Result on sample :- Error: near line 20: near "(": syntax error
Is 10 the highest marks?
You would use row_number():
select s.*
from (select s.*,
row_number() over (partition by class order by marks desc) as seqnum
from students s
where marks < 10 and class in (5, 7)
) s
where seqnum <= 2
order by class, marks;
Note: Your question is a little confusing. You seem to want two rows with the highest marks per class ordered in descending order by marks.
EDIT:
Based on your comment:
select s.*
from (select s.*,
row_number() over (partition by class order by marks desc) as seqnum,
count(*) over (partition by class) as cnt
from students s
where marks < 10
) s
where seqnum <= 2 and cnt >= 2
order by class, marks;
I have a table with values
id sales date
1 5 "2015-01-04"
1 3 "2015-01-03"
1 1 "2015-01-01"
1 1 "2015-01-01"
2 7 "2015-01-05"
2 6 "2015-01-04"
2 4 "2015-01-03"
3 11 "2015-01-08"
3 10 "2015-01-07"
3 9 "2015-01-06"
3 8 "2015-01-05"
I want to select top two values of each id as shown in desired output.
Desired output:
id sales date
1 5 "2015-01-04"
1 3 "2015-01-03"
2 7 "2015-01-05"
2 6 "2015-01-04"
3 11 "2015-01-08"
3 10 "2015-01-07"
My attempt:
can someone help me with this. Thank you in advance!
select transactions.salesperson_id, transactions.id, transactions.date
from transactions
ORDER BY transactions.salesperson_id ASC, transactions.date DESC;
This can be done using window functions:
select id, sales, "date"
from (
select id, sales, "date",
dense_rank() over (partition by id order by "date" desc) as rnk
from transactions
) t
where rnk <= 2;
If there are multiple rows on the same date this might return more than two rows for the same ID. If you don't want that, use row_number() instead of dense_rank()
row_number() will get what you want.
select * from
(select row_number() over (partition by id order by date) as rn, sales, date from transactions) t1
where t1.rn <= 2
Suppose the table has 1 column ID and the values are as below:
ID
5
5
5
6
5
5
6
6
the output should be
ID count
5 3
6 1
5 2
6 2
How can we do that in a single SQL query.
If you want to find the Total count of the Records you have you can write like
select count(*) from database_name order by column_name;
In relational databases data in the table has no any order, see this: https://en.wikipedia.org/wiki/Table_(database)
the database system does not guarantee any ordering of the rows unless
an ORDER BY clause is specified in the SELECT statement that queries
the table.
therefore, in order to get desired results, you must have an additional colum in the table that defines an order of rows (and can by used in ORDER BY clause).
In the below examle cn column defines such an order:
select * from tab123 ORDER BY rn;
RN ID
---------- -------
1 5
2 5
3 5
4 6
5 5
6 5
7 6
8 6
Starting from Oracle version 12c new MATCH_REGOGNIZE clause can be used:
select * from tab123
match_recognize(
order by rn
measures
strt.id as id,
count(*) as cnt
one row per match
after match skip past last row
pattern( strt ss* )
define ss as ss.id = prev( ss.id )
);
On earlier versions that support windows function (Oracle 10 and above) you can use two windows functions: LAG ... over and SUM ... over, in this way
select max( id ) as id, count(*) as cnt
FROM (
select id, sum( xxx ) over (order by rn ) as yyy
from (
select t.*,
case lag( id ) over (order by rn )
when id then 0 else 1 end as xxx
from tab123 t
)
)
GROUP BY yyy
ORDER BY yyy;
The question is: For each day, list the User ID who has read the most number of messages.
user_id msgID read_date
1 1 10
1 2 10
2 2 10
2 2 23
3 2 23
I believe the date is an outer group and user_id is an inner group, but how to do group nesting in sql? Or somehow avoid this?
This is a task for a Window Function:
select *
from
(
select user_id, read_date, count(*) as cnt,
rank()
over (partition by read_date -- each day
order by count(*) desc) as rnk -- maximum number
from tab
group by user_id, read_date
) dt
where rnk = 1
This might return multiple users for one with the same maximum count, if you want just one (randomly) switch to ROW_NUMBER
select user_id
from
(
select user_id,count(msgID)
from table
group by read_date
)
where rownum <= 1;