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

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 );

Related

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).

SQL, joining two tables and selecting their max

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

Stored Procedure Select from 3 tables

I have three tables in my database Sales, SalesPeople and Appliances.
Sales
SaleDate EmployeeID AppID Qty
---------- ---------- ----- -----------
2010-01-01 1412 150 1
2010-01-05 3231 110 1
2010-01-03 2920 110 2
2010-01-13 1412 100 1
2010-01-25 1235 150 2
2010-01-22 1235 100 2
2010-01-12 2920 150 3
2010-01-14 3231 100 1
2010-01-15 1235 300 1
2010-01-03 2920 200 2
2010-01-31 2920 310 1
2010-01-05 1412 420 1
2010-01-15 3231 400 2
SalesPeople
EmployeeID EmployeeName CommRate BaseSalary SupervisorID
---------- ------------------------------ ----------- ----------- ------------
1235 Linda Smith 15 1200 1412
1412 Anne Green 12 1800 NULL
2920 Charles Brown 10 1150 1412
3231 Harry Purple 18 1700 1412
Appliances
ID AppType StoreID Cost Price
---- -------------------- ------- ------------- -------------
100 Refrigerator 22 150 250
110 Refrigerator 20 175 300
150 Television 27 225 340
200 Microwave Oven 22 120 180
300 Washer 27 200 325
310 Washer 22 280 400
400 Dryer 20 150 220
420 Dryer 22 240 360
How can I obtain this result? (That displays the profitability of each of the salespeople ordered from the most profitable to the least. Gross is simply the sum of the quantity of items sold multiplied by the price. Commission is calculated from the gross minus the cost of those items (i.e. from
qty*(price-cost)). Net profit is the total profit minus commission.)
Name Gross Commission Net Profit
------------- ----- ---------- ---------
Charles Brown 2380 83.5 751.5
Linda Smith 1505 83.25 471.75
Harry Purple 990 65.7 299.3
Anne Green 950 40.2 294.8
My attempt:
CREATE PROC Profitability AS
SELECT
sp.EmployeeName, (sum(s.Qty) * a.Price) as [Gross],
[Gross] - a.Cost, as [Commision],
SOMETHING as [Net Profit]
FROM
Salespeople sp, Appliances a, Sales s
WHERE
s.AppID = a.ID
AND sp.EmployeeID = s.EmployeeID
GROUP BY
sp.EmployeeName
GO
EXEC Profitability
Simple rule: Never use commas in the FROM clause. Always use explicit JOIN syntax.
In addition to fixing the JOIN syntax, your query needs a few other enhancements for the aggregation functions:
SELECT sp.EmployeeName, sum(s.Qty * a.Price) as Gross,
SUM(s.Qty * (a.Price - a.Cost)) * sp.CommRate / 100.0 as Commission,
SUM(s.Qty * (a.Price - a.Cost)) * (1 - sp.CommRate / 100.0) as NetProfit
FROM Sales s JOIN
Salespeople sp
ON sp.EmployeeID = s.EmployeeID JOIN
Appliances a
ON s.AppID = a.ID
GROUP BY sp.EmployeeName sp.CommRate
ORDER BY NetProfit DESC;

Using multiple inner joins and sorting result

In my DB there are three tables. I am trying to figure out how to List the school name, class name and teacher's name. Sort the records by class name within school name. I have a query but it is not working. How can I list the school name, class name and teacher's name and sort record by class name within school name?
SELECT c.class_name FROM class c INNER JOIN teacher t WHERE t.teacher_id = c.teacher_id INNER JOIN school s WHERE t.school_id = s.school_id;
TABLES
SQL> select * from class;
CLASS_ID CLASS_NAME TEACHER_ID MAX_SEATS_AVAILABLE
---------- ------------------- ---------- -------------------
1 Intro to ALGEBRA 11 12
2 Basic CALCULUS 2 10
3 ABC and 123 1 15
4 Sharing 101 8 10
5 Good Talk, Bad Talk 9 20
6 Nap Time 1 21
7 WRITing 101 5 10
8 Finger Painting 9 14
9 Physics 230 2 20
10 Gym 5 25
10 rows selected.
SQL> select * from teacher;
TEACHER_ID FIRST_NAME LAST_NAME T HOME_ROOM_NUM PHONE_NUM START_DAT HO SCHOOL_ID
---------- ---------------- ---------------- - ------------- ---------- --------- -- ----------
1 FRanK JOHNSON k 10C 22-OCT-97 In 11090
2 LISA JONES h 11Bc 317-587-90 19-JAN-15 iN 123134
87
3 Jeff Dafferty C W8CZ 12-DEC-96 OH 11546
4 Frank MARTIN g 12aA 212-098-98 19-JAN-15 IN 11090
76
5 John Smith H 34C 10-OCT-93 In 123134
6 John Smith G 34C 10-OCT-93 in 11090
7 Lisa Jones G 11E 317-587-90 19-JAN-15 IN 123134
87
8 Trevor Horse k x 19-JAN-15 Oh 11090
9 Gregor Ivan K 12A 317-987-09 10-NOV-96 KY 11090
87
10 Gregor Ivan g 12A 317-987-09 10-NOV-96 Ky 11090
87
11 Pat Francis H 1z1a 123-317-09 19-JAN-15 Il 11546
12
12 Brad Smith G 13A 18-NOV-94 IN 11546
12 rows selected.
SQL> select * from school;
SCHOOL_ID SCHOOL_NAME SCHOOL_TYPE
---------- ----------------------------- ------------
11546 Ivy Tech College COLLegE
11090 LAWRENCE Central Grade School GRADE SCHOOL
11111 Lawrence NORTH High School HIGH SCHooL
19283 Howe High SCHOOL High SchooL
123134 Lawrence Central High School HIGH SCHOOL
192 Little Big Horn Grade School GRADE SCHOOL
You can use the join to get what you want. This will give the all schools with the teachers and there classes.
SELECT DISTINCT
sc.SCHOOL_NAME,
teach.FIRST_NAME,
teach.LAST_NAME ,
cs.CLASS_NAME
FROM
school sc JOIN teacher teach
ON sc.SCHOOL_ID = teach.SCHOOL_ID
JOIN class cs ON cs.TEACHER_ID = teach.TEACHER_ID
ORDER BY
sc.SCHOOL_NAME,cs.CLASS_NAME
SELECT t.FIRST_NAME +' '+t.LAST_NAME, s.SCHOOL_NAME, c.class_name
FROM class c
INNER JOIN teacher t
ON t.teacher_id = c.teacher_id
INNER JOIN school s
ON t.school_id = s.school_id
ORDER BY s.SCHOOL_NAME,c.CLASS_NAME

Retrieve top 48 unique records from database based on a sorted Field

I have database table that I am after some SQL for (Which is defeating me so far!)
Imagine there are 192 Athletic Clubs who all take part in 12 Track Meets per season.
So that is 2304 individual performances per season (for example in the 100Metres)
I would like to find the top 48 (unique) individual performances from the table, these 48 athletes are then going to take part in the end of season World Championships.
So imagine the 2 fastest times are both set by "John Smith", but he can only be entered once in the world champs. So i would then look for the next fastest time not set by "John Smith"... so on and so until I have 48 unique athletes..
hope that makes sense.
thanks in advance if anyone can help
PS
I did have a nice screen shot created that would explain it much better. but as a newish user i cannot post images.
I'll try a copy and paste version instead...
ID AthleteName AthleteID Time
1 Josh Lewis 3 11.99
2 Joe Dundee 4 11.31
3 Mark Danes 5 13.44
4 Josh Lewis 3 13.12
5 John Smith 1 11.12
6 John Smith 1 12.18
7 John Smith 1 11.22
8 Adam Bennett 6 11.33
9 Ronny Bower 7 12.88
10 John Smith 1 13.49
11 Adam Bennett 6 12.55
12 Mark Danes 5 12.12
13 Carl Tompkins 2 13.11
14 Joe Dundee 4 11.28
15 Ronny Bower 7 12.14
16 Carl Tompkin 2 11.88
17 Nigel Downs 8 14.14
18 Nigel Downs 8 12.19
Top 4 unique individual performances
1 John Smith 1 11.12
3 Joe Dundee 4 11.28
5 Adam Bennett 6 11.33
6 Carl Tompkins 2 11.88
Basically something like this:
select top 48 *
from (
select athleteId,min(time) as bestTime
from theRaces
where raceId = '123' -- e.g., 123=100 meters
group by athleteId
) x
order by bestTime
try this --
select x.ID, x.AthleteName , x.AthleteID , x.Time
(
select rownum tr_count,v.AthleteID AthleteID, v.AthleteName AthleteName, v.Time Time,v.id id
from
(
select
tr1.AthleteName AthleteName, tr1.Time time,min(tr1.id) id, tr1.AthleteID AthleteID
from theRaces tr1
where time =
(select min(time) from theRaces tr2 where tr2.athleteId = tr1.athleteId)
group by tr1.AthleteName, tr1.AthleteID, tr1.Time
having tr1.Time = ( select min(tr2.time) from theRaces tr2 where tr1.AthleteID =tr2.AthleteID)
order by tr1.time
) v
) x
where x.tr_count < 48