SQL, joining two tables and selecting their max - sql

I have two tables
customers_bought:
PID SID QUANTITY
---------- ---------- ----------
3289 11 12
74591 11 10
74591 12 1
74591 23 1
74591 31 1
88331 13 21
88331 31 48
customers:
PID NAME
---------- --------------------
3289 George
88331 Alice
74591 Jane
10234 Able
What I need to be able to do is list the customers who bought the greatest number of sandwiches of a single type. I also need to display the customer name, pid, sid, and quantity. This being SQL, of course it's more complicated than it sounds. I'm unsure how to join the two results I have below, here's what I have managed to do:
Code:
Select C.pid, C.name, CB.sid, CB.quantity
From customers C
Inner Join customers_bought CB
On CB.PID = C.PID;
Result:
PID NAME SID QUANTITY
---------- -------------------- ---------- ----------
3289 George 11 12
74591 Jane 11 10
74591 Jane 12 1
74591 Jane 23 1
74591 Jane 31 1
88331 Alice 13 21
88331 Alice 31 48
Code:
Select CB.sid, MAX(CB.Quantity)
From customers_bought CB
Group By CB.sid
Result:
SID MAX(CB.QUANTITY)
---------- ---------------------------------------
11 12
13 21
31 48
23 1
12 1

Try this one
Select
C.pid, C.name, CB.sid, ms.MaxQuentity
From customers C
Inner Join customers_bought CB
On CB.PID = C.PID
INNER JOIN
(
Select
CB.sid,
MAX(CB.Quantity) AS MaxQuentity
From customers_bought CB
Group By CB.sid
) ms ON ms.sid = CB.sid AND ms.MaxQuentity = CB.QUANTITY
You can find the Sample code in Fiddle

Related

How to get top values when there is a tie

I am having difficulty figuring out this dang problem. From the data and queries I have given below I am trying to see the email address that has rented the most movies during the month of September.
There are only 4 relevant tables in my database and they have been anonymized and shortened:
Table "cust":
cust_id
f_name
l_name
email
1
Jack
Daniels
jack.daniels#google.com
2
Jose
Quervo
jose.quervo#yahoo.com
5
Jim
Beam
jim.beam#protonmail.com
Table "rent"
inv_id
cust_id
rent_date
10
1
9/1/2022 10:29
11
1
9/2/2022 18:16
12
1
9/2/2022 18:17
13
1
9/17/2022 17:34
14
1
9/19/2022 6:32
15
1
9/19/2022 6:33
16
3
9/1/2022 18:45
17
3
9/1/2022 18:46
18
3
9/2/2022 18:45
19
3
9/2/2022 18:46
20
3
9/17/2022 18:32
21
3
9/19/2022 22:12
10
2
9/19/2022 11:43
11
2
9/19/2022 11:42
Table "inv"
mov_id
inv_id
22
10
23
11
24
12
25
13
26
14
27
15
28
16
29
17
30
18
31
19
31
20
32
21
Table "mov":
mov_id
titl
rate
22
Anaconda
3.99
23
Exorcist
1.99
24
Philadelphia
3.99
25
Quest
1.99
26
Sweden
1.99
27
Speed
1.99
28
Nemo
1.99
29
Zoolander
5.99
30
Truman
5.99
31
Patient
1.99
32
Racer
3.99
and here is my current query progress:
SELECT cust.email,
COUNT(DISTINCT inv.mov_id) AS "Rented_Count"
FROM cust
JOIN rent ON rent.cust_id = cust.cust_id
JOIN inv ON inv.inv_id = rent.inv_id
JOIN mov ON mov.mov_id = inv.mov_id
WHERE rent.rent_date BETWEEN '2022-09-01' AND '2022-09-31'
GROUP BY cust.email
ORDER BY "Rented_Count" DESC;
and here is what it outputs:
email
Rented_Count
jack.daniels#google.com
6
jim.beam#protonmail.com
6
jose.quervo#yahoo.com
2
and what I want it to be outputting:
email
jack.daniels#google.com
jim.beam#protonmail.com
From the results I am actually getting I have a tie for first place (Jim and Jack) and that is fine but I would like it to list both tieing email addresses not just Jack's so you cant do anything with rows or max I don't think.
I think it must have something to do with dense_rank but I don't know how to use that specifically in this scenario with the count and Group By?
Your creativity and help would be appreciated.
You're missing the FETCH FIRST ROWS WITH TIES clause. It will work together with the ORDER BY clause to get you the highest values (FIRST ROWS), including ties (WITH TIES).
SELECT cust.email
FROM cust
INNER JOIN rent
ON rent.cust_id = cust.cust_id
INNER JOIN inv
ON inv.inv_id = rent.inv_id
INNER JOIN mov
ON mov.mov_id = inv.mov_id
WHERE rent.rent_date BETWEEN '2022-09-01' AND '2022-09-31'
GROUP BY cust.email
ORDER BY COUNT(DISTINCT inv.mov_id) DESC
FETCH FIRST 1 ROWS WITH TIES

How to display total count in descending order but group by id

i have the following codes which displays the following result
select distinct
srcaccountid,
srccharid,
srccharname,
action,
itemname,
sum(itemcount) over (partition by srccharname, itemname)
as total_count,
sum(price) over(partition by srccharname, itemname)
as total_price
from
itemlog
where
action = 6
and logtime >='2023-02-13'
order by
total_count desc,
srcaccountid
the result is as follows
srcaccountid srccharid srccharname action itemname total_count total_price
1 21 abc 6 dog 2222 231
2 22 sdd 6 cat 1234 122
1 21 abc 6 cat 324 77
1 21 abc 6 mouse 122 32
2 22 sdd 6 mouse 12 3
i will like the result to show as follow
srcaccountid srccharid srccharname action itemname total_count
total_price
1 21 abc 6 dog 2222 231
1 21 abc 6 cat 324 77
1 21 abc 6 mouse 122 32
2 22 sdd 6 cat 1234 122
2 22 sdd 6 mouse 12 3
i cant seem to be able to show the highest sales amount while grouping the ids together and not splitting them up
Change the order by. Put the srcaccountid first.
order by
srcaccountid,
total_count desc

Selecting from a table that contains ALL of another table

Let's say I have three tables:
Employees:
PID NAME WAGE
---------- -------------------- ----------
10234 Able 8
11567 Baker 9
3289 George 10
88331 Alice 11
Employee_made:
PID SID QUANTITY HOURS
---------- ---------- ---------- ----------
10234 11 24 3
10234 12 6 1
10234 13 24 1
10234 21 6 1
10234 23 4 1
10234 31 48 6
11567 23 4 1
11567 31 1 1
88331 11 6 1
Sandwich:
SID PRICE NAME
---------- ---------- ------------------------------
12 2 hamburger on wheat
13 2 cheese burger
21 1.75 fish burger on rye
23 1.75 fish burger on wheat
31 3 veggie burger on wheat
11 2 hamburger on rye
I need to list all the employees who have made ALL the different sandwiches, and display their names and PID. What I've gotten so far is:
Select E.name, E.pid
From employees E, employee_made EM, sandwich S
Where E.pid = EM.pid
Which tells me the matching PIDs from the employees and employee_made table. Where I'm not sure to go is how to display the employees who have made ALL the sandwiches, so matching not any SID to the employee_made table, but ALL of them.
First, never use commas in the FROM clause. Always use proper, explicit JOIN syntax.
You can approach this by counting the number of sandwiches mades by employees and then comparing to the total count of sandwiches:
select em.pid
from employee_made em
group by em.pid
having count(distinct em.sid) = (select count(*) from sandwich);
This gives the pid of the employee. I'll let you figure out how to bring in the employee name (hint: in, exists, and join could all be used).

3 Tables, JOIN query and alphabetic order

I am currently working with three tables where I am trying to figure out how to use a join to display once the title_id of any book with Dennis McCann as an editor. The tables have in common title_id and editor_id. Cant find a way to piece it all together. How to display once the title_id of any book with Dennis McCann as an editor?
SELECT * FROM title_editors;
EDITOR_ID TITLE_ EDITOR_ORDER
----------- ------ ------------
826-11-9034 Bu2075 2
826-11-9034 PS2091 2
826-11-9034 Ps2106 2
826-11-9034 PS3333 2
826-11-9034 PS7777 2
826-11-9034 pS1372 2
885-23-9140 MC2222 2
885-23-9140 MC3021 2
885-23-9140 Tc3281 2
885-23-9140 TC4203 2
885-23-9140 TC7777 2
321-55-8906 bU1032 2
321-55-8906 BU1111 2
321-55-8906 BU7832 2
321-55-8906 PC1035 2
321-55-8906 PC8888 2
321-55-8906 BU2075 3
777-02-9831 pc1035 3
777-02-9831 PC8888 3
943-88-7920 BU1032 1
943-88-7920 bu1111 1
943-88-7920 BU2075 1
943-88-7920 BU7832 1
943-88-7920 PC1035 1
943-88-7920 pc8888 1
993-86-0420 PS1372 1
993-86-0420 PS2091 1
993-86-0420 PS2106 1
993-86-0420 PS3333 1
993-86-0420 pS7777 1
993-86-0420 MC2222 1
993-86-0420 MC3021 1
993-86-0420 Tc3218 1
993-86-0420 TC4203 1
993-86-0420 TC7777 1
35 rows selected.
SQL> SELECT * FROM title_authors;
AUTHOR_ID TITLE_ AUTHOR_ORDER ROYALTY_SHARE
----------- ------ ------------ -------------
409-56-7008 Bu1032 1 .6
486-29-1786 PS7777 1 1
486-29-1786 pC9999 1 1
712-45-1867 MC2222 1 1
172-32-1176 Ps3333 1 1
213-46-8915 BU1032 2 .4
238-95-7766 PC1035 1 1
213-46-8915 Bu2075 1 1
998-72-3567 pS2091 1 .5
899-46-2035 PS2091 2 .5
998-72-3567 PS2106 1 1
722-51-5454 mc3021 1 .75
899-46-2035 MC3021 2 .25
807-91-6654 tC3218 1 1
274-80-9391 BU7832 1 1
427-17-2319 pC8888 1 .5
846-92-7186 PC8888 2 .5
756-30-7391 PS1372 1 .75
724-80-9391 PS1372 2 .25
724-80-9391 bu1111 1 .6
267-41-2394 bU1111 2 .4
672-71-3249 TC7777 1 .4
267-41-2394 TC7777 2 .3
472-27-2349 Tc7777 3 .3
648-92-1872 TC4203 1 1
25 rows selected.
SQL> SELECT * FROM editors;
EDITOR_ID EDITOR_LNAME EDITOR_FNAME EDITOR_POSITION PHONE ADDRESS CITY ST ZIP
----------- ----------------- ------------- --------------- ------------ -------------------- ------------ -- ------
321-55-8906 DeLongue Martinella Project 415 843-2222 3000 6th St. BERKELEY Ca 94710
723-48-9010 Sparks MANfred cOPY 303 721-3388 15 Sail DENVER Co 80237
777-02-9831 Samuelson Bernard proJect 415 843-6990 27 Yosemite OAKLAND Ca 94609
777-66-9902 Almond Alfred copy 312 699-4177 1010 E. DeVON CHICAGO Il 60018
826-11-9034 Himmel Eleanore pRoject 617 423-0552 97 Bleaker BOSTON Ma 02210
885-23-9140 Rutherford-Hayes Hannah PROJECT 301 468-3909 32 Rockbill Pike ROCKBILL MD 20852
993-86-0420 McCann Dennis acQuisition 301 468-3909 32 Rockbill Pike ROCKBill MD 20852
943-88-7920 Kaspchek Christof acquisitiOn 415 549-3909 18 Severe Rd. BERKELEY CA 94710
234-88-9720 Hunter Amanda acquisition 617 432-5586 18 Dowdy Ln. BOSTON MA 02210
You can try join on the table Editors and Ttile_Editors using the Editor_ID that will give you the matching records and you can filter out Only for the ' Dennis McCann ' using either multiple conditions in join or the where clause as,
WITHOUT WHERE
SELECT DISTINCT te.title_id,ed.EDITOR_ID,ed.EDITOR_LNAME,ed.EDITOR_FNAME
FROM
title_editors te JOIN editors ed
ON te.EDITOR_ID = ed.EDITOR_ID
AND ed.EDITOR_LNAME = 'McCann'
AND ed.EDITOR_FNAME = 'Dennis'
ORDER BY te.title_id
USing WHERE
SELECT DISTINCT te.title_id,ed.EDITOR_ID,ed.EDITOR_LNAME,ed.EDITOR_FNAME
FROM
title_editors te JOIN editors ed
ON te.EDITOR_ID = ed.EDITOR_ID
WHERE
ed.EDITOR_LNAME = 'McCann'
AND ed.EDITOR_FNAME = 'Dennis'
ORDER BY te.title_id
It would be easier with the in operator:
SELECT DISTINCT title_id
FROM title_editors
WHERE editor_id IN (SELECT editor_id
FROM editors
WHERE editor_fname = 'Dennis' AND
editor_lname = 'McCann')
ORDER BY title_id ASC

How to list data when a count = 0 for last 3 years?

I am trying to produce a list of branches that haven't made any sales in the last 3 years. I have been able to produce a list of sales that are older than 3 years but not with the added condition of 0 sales in the 3 years prior.
My task is as follows: List all of the branches that have not rented out any tools for more than 3 years.
I think that I have to do a nested subquery but I cannot work out what should go where. Here are the two relevant tables with their descriptions and data values. The only value that should be returned is that for branch 70.
SQL> desc toolorder
Name Null? Type
--------------------------------------------------------- -------- ---
ORDERID NOT NULL VARCHAR2(6)
CUST NOT NULL VARCHAR2(6)
SNAME NOT NULL VARCHAR2(20)
BRANCHID NOT NULL VARCHAR2(6)
TYPE NOT NULL VARCHAR2(15)
TOOLID NOT NULL VARCHAR2(6)
DATEOUT NOT NULL DATE
DUEDATE NOT NULL DATE
SQL> desc branch
Name Null? Type
--------------------------------------------------------------
BRANCHID NOT NULL VARCHAR2(6)
BNAME NOT NULL VARCHAR2(15)
ADDRESS NOT NULL VARCHAR2(25)
TELEPHONE VARCHAR2(11)
MANAGERID VARCHAR2(6)
SQL> select * from toolorder;
ORDERI CUSTOM SNAME BRANCH TYPE TOOLID DATEOUT DUEDATE
------ ------ -------------------- ------ --------------- ------ --------- ---------
000001 000100 smith 10 Adhesive 00042 20-OCT-13 27-NOV-12
000002 000101 jones 10 Guage 00050 13-OCT-12 30-OCT-12
000003 000103 may 10 Generic 00023 21-NOV-12 28-NOV-12
000004 000100 smith 10 Generic 00023 19-NOV-13 28-NOV-13
000005 000104 circus 10 Generic 00023 05-JAN-09 28-JAN-09
000006 000106 hanks 10 Wood 00062 11-APR-10 01-MAY-10
000007 000102 bond 20 Cutting 00073 13-DEC-11 27-DEC-11
000008 000102 bond 20 Guage 00053 13-DEC-11 27-DEC-11
000009 000104 circus 30 Generic 00025 13-DEC-06 28-DEC-06
000010 000104 circus 30 Brickwork 00035 13-DEC-06 28-DEC-06
000011 000105 harris 30 Cutting 00075 13-OCT-13 25-OCT-13
000012 000105 harris 40 Brickwork 00036 13-DEC-11 27-DEC-11
000013 000105 harris 40 Generic 00027 13-DEC-11 27-DEC-11
000014 000105 harris 40 Electric 00006 13-DEC-11 27-DEC-11
000015 000106 hanks 40 Adhesive 00046 13-MAY-11 27-MAY-11
000016 000107 head 50 Adhesive 00047 13-MAR-13 27-MAR-13
000017 000107 head 50 Wood 00018 13-MAR-13 27-MAR-13
000018 000101 jones 50 Guage 00055 06-JAN-13 20-JAN-13
000019 000103 may 60 Brickwork 00039 06-APR-13 20-APR-13
000020 000101 jones 60 Cutting 00080 24-DEC-12 07-JAN-13
000021 000101 circus 70 Cutting 00081 13-AUG-08 27-AUG-08
21 rows selected.
SQL> select * from branch;
BRANCH BNAME ADDRESS TELEPHONE MANAGE
------ --------------- ------------------------- ----------- ------
10 Oxford 18 Oxford Estate 08456325312
20 Greenwood 21 Greenwood Lane 02380282185
30 Weston 36 Weston Road 02380282635
40 Highstreet 12-15 Stafford Highstreet 02380865963
50 Meadow 16 The Meadow Yard 07974296353
60 Port Down 168 Port Down Av 08953164826
70 Red Rd 12-15 Red Road 07948247384
7 rows selected.
Now, running the following query returns the orders that are 3 years old. I need to adjust it (I think) using a nested subqueries, so that it checks there are no sales in the 3 years, but cannot work out how.
SQL> select count(toolorder.orderid) as rentalcount, branch.branchid, branch.bname,
branch.address from toolorder left outer join branch on toolorder.branchid =
branch.branchid where MONTHS_BETWEEN(sysdate, dateout) > 36 group by branch.branchid,
branch.bname, branch.address order by 1 desc;
RENTALCOUNT BRANCH BNAME ADDRESS
----------- ------ --------------- -------------------------
2 30 Weston 36 Weston Road
2 10 Oxford 18 Oxford Estate
1 70 Red Rd 12-15 Red Road
The easiest way to do this is to get the maximum dateout for each branchid and check that it is more than 36 months in the bast:
select b.*
from branch b join
(select branchid, max(dateout) as maxd
from toolorder
group by branchid
) tob
on b.branchid = tob.branchid
where MONTHS_BETWEEN(sysdate, tob.maxd) > 36;
You can use NOT EXISTS to check if something does not match in a correlated sub-query:
SELECT *
FROM branch b
WHERE NOT EXISTS ( SELECT 1
FROM toolorder r
WHERE r.branchid = b.branchid
AND MONTHS_BETWEEN(sysdate, dateout) <= 36 );
From your comment on #Gordon Linoff's answer, it looks like you want to delete matching rows; in which case you can do:
DELETE FROM branch b
WHERE NOT EXISTS ( SELECT 1
FROM toolorder r
WHERE r.branchid = b.branchid
AND MONTHS_BETWEEN(sysdate, dateout) <= 36 );