How to lists the maximum of counting with where syntax? - oracle

For Oracle,
I have 2 tables; first is Store and another is Book whereas they are connected by store ID (PK FK).
I would like to lists the name of the store which has the highest numbers of books.
However, the result showed every store in orders but I just want the highest.
SELECT STORE.STORE_NAME
FROM STORE, BOOK
WHERE STORE.STORE_ID=BOOK.BOOK_STOREID
GROUP BY STORE.STORE_NAME
ORDER BY COUNT(BOOK.BOOK_STOREID) DESC;
the result is
Store:
D
E
F
B
A
C
It should be only 'D'. What should I do? Thank you.

Try
SELECT STORE_NAME
FROM
(SELECT STORE.STORE_NAME
FROM STORE, BOOK
WHERE STORE.STORE_ID=BOOK.BOOK_STOREID
GROUP BY STORE.STORE_NAME
ORDER BY COUNT(BOOK.BOOK_STOREID) DESC)
WHERE rownum = 1
Here is a sqlfiddle demo
BTW, you can also use row_number() function
select STORE_NAME
from
(SELECT STORE.STORE_NAME,
row_number() over( order by COUNT(BOOK.BOOK_STOREID)desc) rn
FROM STORE join BOOK on STORE.STORE_ID=BOOK.BOOK_STOREID
group by STORE.STORE_NAME)
where rn = 1;
UPDATE If you want to see all stores which have the max number of books you can use rank instead of row_number:
select STORE_NAME
from
(SELECT STORE.STORE_NAME,
rank() over( order by COUNT(BOOK.BOOK_STOREID)desc) rn
FROM STORE join BOOK on STORE.STORE_ID=BOOK.BOOK_STOREID
group by STORE.STORE_NAME)
where rn = 1;

Just for fun, here's another formulation:
with store_counts as (
select store_name,
count(*) books
from store join book on store_id=book_storeid
group by store_name)
select *
from store_counts
where books = (
select max(books)
from store_counts)

Related

Write a query to find the name of the student(s) who has scored maximum mark in Software Engineering. Sort the result based on name

Write a query to find the name of the student(s) who has scored maximum mark in Software Engineering. Sort the result based on name.
This is what I tried.
select s.student_name
from student s,mark m,subject su
where s.student_id=m.student_id and su.subject_id=m.subject_id and max(m.value)in
(select value from mark where lower(su.subject_name)='software engineering')
order by s.student_name;
I am not getting the proper result.
Apart from the fact that you're using outdated implicit comma syntax for joins, you are also combining columns of the tables in the wrong way in the sub query.
subject_name is a column of subject which has nothing to do with the student's relation to marks. So, mark may be joined separately with subject while determining the student_ids with highest mark. We can then obtain the name of the student using those student_ids
So, In Oracle 12c and above, you could do
SELECT s.student_name
FROM student s
WHERE s.student_id IN ( SELECT m.student_id
FROM mark m JOIN subject su
ON su.subject_id = m.subject_id
WHERE lower(su.subject_name) = 'software engineering'
ORDER BY m.value DESC
FETCH FIRST 1 ROWS WITH TIES ) order by 1;
For previous versions, you may use dense_rank or rank
SELECT s.student_name
FROM student s
WHERE s.student_id IN ( SELECT student_id
FROM ( SELECT m.*,DENSE_RANK() OVER(
ORDER BY m.value DESC
) AS rnk
FROM mark m JOIN subject su
ON su.subject_id = m.subject_id
WHERE lower(su.subject_name) = 'software engineering'
) WHERE rnk = 1
) order by 1;
you need to add the max function in the subquery instead of the main query.
select s.student_name
from student s, mark m, subject su
where s.student_id = m.student_id
and su.subject_id = m.subject_id
and m.value in
(select max(value)
from mark
where lower(su.subject_name) = 'software engineering')
order by s.student_name;
You can try this code. It must work perfectly :
select s.student_name
from student s
join mark m
on s.student_id=m.student_id
join subject s1
on s1.subject_id=m.subject_id
where m.value=(select max(m1.value) from mark m1
join subject s2
on s2.subject_id=m1.subject_id
where lower(s2.subject_name)='software engineering'
group by s2.subject_id)
group by s.student_name
order by s.student_name;
select student_name
from student join mark using(student_id) join subject using(subject_id)
where subject_name = 'Software Engineering'
and value = (select max(value)
from mark join subject using (subject_id)
where subject_name = 'Software Engineering')
order by student_name;
This is very self-explanatory solution.
select student_name
from student s join mark m on s.student_id=m.student_id
join subject sub on m.subject_id=sub.subject_id
where lower(sub.subject_name)='software engineering' and
m.value = (select max(m1.value)
from mark m1 join subject sub1 on m1.subject_id=sub1.subject_id
where lower(sub1.subject_name)='software engineering')
order by student_name;
select student_name
from stundent
where student_id in(
select student_id
from mark
where value=(select max(value) from mark
where subject_id=(
select subject_id
from subject
where lower(subject_name)='software engineering'
)
)
)
)
order by student_name;
select student_name
from student
where student_id in
(select student_id
from subject join mark using(subject_id)
where value in
(select max(value)
from mark
group by student_id
having initcap(subject_name) = 'Software Engineering'))
order by student_name;

Oracle SQL join query to find highest salary

So I have two tables salary and emp whose definition is shown as below
[
I am tring to create a query that Find the employee who draws the maximum salary and Display the employee details along with the nationality.
I created this query
select empcode,
max(basic) as "Highest Sal"
from salary
join emp on empcode;
Please help with this
Your query uses a simple aggregate max(basic) which would find the highest salary. Except you need to join to the EMP table to display other details. This means you can't use aggregation, because we need to GROUP BY the non-aggregated columns, which would make a nonsense of the query.
Fortunately we can solve the problem with an analytic function. The subquery selects all the relevant information and ranks each employee by salary, with a rank of 1 being the highest paid. We use rank() here because that will handle ties: two employees with the same basic will be in the same rank.
select empcode
, empname
, nationality
, "Highest Sal"
from (
select emp.empcode
, emp.empname
, emp.nationality
, salary.basic as "Highest Sal"
, rank() over (order by salary.basic desc ) as rnk
from salary join emp on emp.empcode = salary.empcode
)
where rnk = 1;
Find the employee who draws the maximum salary
An employee can have multiple salaries in your datamodel. An employee's (total) salary hence is the sum of these. You want to find the maximum salary per employee and show the employee(s) earning that much.
You can use MAX OVER to find the maximum sum:
select e.*, s.total_salary
from emp e
join
(
select
empcode,
sum(basic) as total_salary,
max(sum(basic)) over () as max_total_salary
from salary
) s on s.empcode = e.empcode and s.total_salary = s.max_total_salary
order by e.empcode;
Try this:
SELECT * FROM
(SELECT E.EmpCode, E.EmpName, E.DOB, E.DOJ, E.DeptCode, E.DesgCode, E.PhNo,
E.Qualification, E.Nationality, S.Basic, S.HRA, S.TA, S.UTA, S.OTRate
FROM EMP AS E JOIN SALARY AS S ON (E.EmpCode = S.EmpCode) order by S.Basic desc)
WHERE rownum = 1

Selecting one random data from a column from multiple rows in oracle

I am creating a view that needs to select only one random row for each customer. Something like:
select c.name, p.number
from customers c, phone_numbers p
where p.customer_id = c.id
If this query returns:
NAME NUMBER
--------- ------
Customer1 1
Customer1 2
Customer1 3
Customer2 4
Customer2 5
Customer3 6
I need it to be something like:
NAME NUMBER
--------- ------
Customer1 1
Customer2 4
Customer3 6
Rownum wont work because it will select only the first from all 6 records, and i need the first from each customer. I need solution that won't affect performance much, because the query that selects the data is pretty complex, this is just an example to explain what I need. Thanks in advance.
Use the ROW_NUMBER() analytic function:
SELECT name,
number
FROM (
SELECT c.name,
p.number,
ROW_NUMBER() OVER ( PARTITION BY c.id ORDER BY DBMS_RANDOM.VALUE ) AS rn
FROM customers c
INNER JOIN phone_numbers p
ON ( p.customer_id = c.id )
)
WHERE rn = 1
You can use group by clause to return only one phone number:
select c.name, MAX(p.number) as phone
from customers c, phone_numbers p
where p.customer_id = c.id
group by c.name
You can also use the min or max aggregate function with the keep dense_rank syntax:
select c.name,
min(p.number) keep (dense_rank last order by dbms_random.value) as number
from customers c
join phone_numbers p on p.customer_id = c.id
group by c.id, c.name
order by c.name;
(number isn't a valid column or alias name as it's a reserved word, so use your own real name of course).
If the phone number needs to be arbitrary rather than actually random, you can order by something else:
select c.name,
min(p.number) keep (dense_rank last order by null) as number
from customers c
join phone_numbers p on p.customer_id = c.id
group by c.id, c.name
order by c.name;
You'll probably get the same number back for each customer each time, but not always, and data/stats/plan changes will affect which you see. It seems like you don't care though. But then, using a plain aggregate might work just as well for you, as in #under's answer.
If you're getting lots of columns from that random row, rather than just the phone number, MTO's subquery might be simpler; for one or two values I find this a bit clearer though.

Top 3 Max Score for each section SQL Developer

I looked at the other questions related to this but it did not work. I even played around with Dense_Rank and still couldn't get the right answer.
Using the Oracle Student Scheme Data. I am trying to find the highest 3 grades for each section based on two tables ( NOT the top 3 rows for each sections. )
This is based off the Student and the Grade table from the Student Scheme
I am using the Oracle Developer 10 and it does not look like it takes the LIMIT function. Cause it gives me the error
missing right parenthesis
select
g.SECTION_ID,g.NUMERIC_GRADE, s.First_Name,s.Last_Name
from GRADE g Join Student s
On g.student_ID = s.student_ID
and Grade_Type_Code='MT'
Where numeric_grade in(Select distinct numeric_grade
from grade
order by numeric_grade desc limit 3);
You can use ROWNUM in Oracle:
SELECT g.SECTION_ID,
g.NUMERIC_GRADE,
s.First_Name,
s.Last_Name
FROM grade g
JOIN Student s
ON g.student_ID = s.student_ID
AND Grade_Type_Code='MT'
WHERE numeric_grade in (SELECT numeric_grade
FROM (SELECT DISTINCT numeric_grade
FROM grade
ORDER BY numeric_grade desc)
WHERE ROWNUM <=3);
Solution with dense_rank:
select section_id, numeric_grade, first_name, last_name
from (
select g.section_id, g.numeric_grade, s.First_Name,s.Last_Name,
dense_rank() over (partition by g.section_id
order by g.numeric_grade desc) rnk
from GRADE g
join Student s on g.student_ID = s.student_ID
and Grade_Type_Code='MT' )
where rnk < 4
order by section_id, numeric_grade desc
SQLFiddle demo

Efficiently get min,max, and summary data

I have a table of accounts and a table of transactions. In a report I need to show the following for each account:
First Purchase Date,
First Purchase Amount,
Last Purchase Date,
Last Purchase Amount,
# of Purchases,
Total of All Purchases.
The transaction table looks like this:
TX_UID
Card_Number
Post_Date
TX_Type
TX_Amount
Currently the query I've inherited has a sub-query for each of these elements. It seems to me that there's got to be a more efficient way. I'm able to use a stored procedure for this and not a single query.
A sample of a query to get all transactions for a single account would be:
select * from tx_table where card_number = '12345' and TX_Type = 'Purchase'
Any ideas?
try this:
select tt1.post_date as first_purchase_date,
tt1.tx_amount as first_purchase_amount,
tt2.post_date as last_purchase_date,
tt2.tx_amount as last_purchase_amount,
tg.pc as purchase_count,
tg.amount as Total
from (select Card_Number,min(post_date) as mipd, max(post_date) as mxpd, count(*) as pc, sum(TX_Amount) as Amount from tx_table where TX_Type = 'Purchase' group by card_number) tg
join tx_table tt1 on tg.card_number=tt1.card_number and tg.mipd=tt1.post_date
join tx_table tt2 on tg.card_number=tt2.card_number and tg.mxpd=tt2.post_date
where TX_Type = 'Purchase'
I added the count .. I didn't see it first time.
If you need also the summary on multiple TX_Types, you have to take it from the where clause and put it in the group and the inner selection join. But I guess you need only for purchases
;with cte as
(
select
Card_Number,
TX_Type,
Post_Date,
TX_Amount,
row_number() over(partition by TX_Type, Card_Number order by Post_Date asc) as FirstP,
row_number() over(partition by TX_Type, Card_Number order by Post_Date desc) as LastP
from tx_table
)
select
F.Post_Date as "First Purchase Date",
F.TX_Amount as "First Purchase Amount",
L.Post_Date as "Last Purchase Date",
L.TX_Amount as "Last Purchase Amount",
C.CC as "# of Purchases",
C.Amount as "Total of All Purchases"
from (select Card_Number, TX_Type, count(*) as CC, sum(TX_Amount) as Amount
from cte
group by Card_Number, TX_Type) as C
inner join cte as F
on C.Card_Number = F.Card_Number and
C.TX_Type = F.TX_Type and
F.FirstP = 1
inner join cte as L
on C.Card_Number = L.Card_Number and
C.TX_Type = L.TX_Type and
L.LastP = 1

Resources