How to use SUM with INNER JOIN from two different tables - mysql

I am stuck on a query where i have to show list of manufacturers and the Amount of Loan they took and Amount of Refund they returned. i have generated the query but i want to sum total no: of refunds of a manufacturer and total no: of Loans manufacturer has taken. and the difference , that is LoanTaken - RefundAmount = Remaining Amount. If its possible in the query.
SELECT MM.*, ML.*, MR.* FROM microfinance_manufacturers AS MM
INNER JOIN manufacturer_loans AS ML ON MM.ManufacturerId = Ml.ManufacturerId
INNER JOIN manufacturer_refunds AS MR ON MM.ManufacturerId = MR.manufacturerId
WHERE 1 = 1
ManufacturerId FirstName LastName Gender Religion PhoneNumber EmailAddress Notes CustomerAddedDateTime manufacturerTypeId manufacturerRoles LoanID ManufacturerId LoanAmount LoanDate RefundId manufacturerId RefundAmount RefundDate
5 Saud Jibran 0 0 8475983748G HFDKJFH VGHJXGVHJD 2015-04-29 14:12:20 21 O:16:"clsEmployeeRoles":68:{s:56:"aEmployeeRole_Organization_RegionalHierarchy_RegionTypes";a:4:{i:0;i:0;i:1;i:0;i:2;i:0;i:3;i:0;}s:52:"aEmployeeRole_Organization_RegionalHierarchy_Regions";a:4:{i:0;i:0;i:1;i:0;i:2;i:0;i:3;i:0;}s:69:"aEmployeeRole_Organization_RegionalHierarchy_Regions_RegionStatistics";a:2:{i:0;i:0;i:2;i:0;}s:82:"aEmployeeRole_Organization_RegionalHierarchy_Regions_ 6 5 65644343:1:"2015-05-06Em 6 5 77744s_Station2015-05-06:{
5 Saud Jibran 0 0 8475983748G HFDKJFH VGHJXGVHJD 2015-04-29 14:12:20 21 O:16:"clsEmployeeRoles":68:{s:56:"aEmployeeRole_Organization_RegionalHierarchy_RegionTypes";a:4:{i:0;i:0;i:1;i:0;i:2;i:0;i:3;i:0;}s:52:"aEmployeeRole_Organization_RegionalHierarchy_Regions";a:4:{i:0;i:0;i:1;i:0;i:2;i:0;i:3;i:0;}s:69:"aEmployeeRole_Organization_RegionalHierarchy_Regions_RegionStatistics";a:2:{i:0;i:0;i:2;i:0;}s:82:"aEmployeeRole_Organization_RegionalHierarchy_Regions_ 13 5 543543;s:1:"2015-05-07Em 6 5 77744s_Station2015-05-06:{
7 Naveed Ahmed 0 0 847893 hfkjhfskj fjksddshkfjdshfkj 2015-04-29 14:22:16 19 O:16:"clsEmployeeRoles":68:{s:56:"aEmployeeRole_Organization_RegionalHierarchy_RegionTypes";a:4:{i:0;i:0;i:1;i:0;i:2;i:0;i:3;i:0;}s:52:"aEmployeeRole_Organization_RegionalHierarchy_Regions";a:4:{i:0;i:0;i:1;i:0;i:2;i:0;i:3;i:0;}s:69:"aEmployeeRole_Organization_RegionalHierarchy_Regions_RegionStatistics";a:2:{i:0;i:0;i:2;i:0;}s:82:"aEmployeeRole_Organization_RegionalHierarchy_Regions_ 16 7 8798u656:1:"2015-05-07Em 9 7 4354334Station2015-05-07:{
7 Naveed Ahmed 0 0 847893 hfkjhfskj fjksddshkfjdshfkj 2015-04-29 14:22:16 19 O:16:"clsEmployeeRoles":68:{s:56:"aEmployeeRole_Organization_RegionalHierarchy_RegionTypes";a:4:{i:0;i:0;i:1;i:0;i:2;i:0;i:3;i:0;}s:52:"aEmployeeRole_Organization_RegionalHierarchy_Regions";a:4:{i:0;i:0;i:1;i:0;i:2;i:0;i:3;i:0;}s:69:"aEmployeeRole_Organization_RegionalHierarchy_Regions_RegionStatistics";a:2:{i:0;i:0;i:2;i:0;}s:82:"aEmployeeRole_Organization_RegionalHierarchy_Regions_ 16 7 8798u656:1:"2015-05-07Em 10 7 896789798ation2015-05-07:{
7 Naveed Ahmed 0 0 847893 hfkjhfskj fjksddshkfjdshfkj 2015-04-29 14:22:16 19 O:16:"clsEmployeeRoles":68:{s:56:"aEmployeeRole_Organization_RegionalHierarchy_RegionTypes";a:4:{i:0;i:0;i:1;i:0;i:2;i:0;i:3;i:0;}s:52:"aEmployeeRole_Organization_RegionalHierarchy_Regions";a:4:{i:0;i:0;i:1;i:0;i:2;i:0;i:3;i:0;}s:69:"aEmployeeRole_Organization_RegionalHierarchy_Regions_RegionStatistics";a:2:{i:0;i:0;i:2;i:0;}s:82:"aEmployeeRole_Organization_RegionalHierarchy_Regions_ 17 7 87987687:1:"2015-05-07Em 9 7 4354334Station2015-05-07:{
7 Naveed Ahmed 0 0 847893 hfkjhfskj fjksddshkfjdshfkj 2015-04-29 14:22:16 19 O:16:"clsEmployeeRoles":68:{s:56:"aEmployeeRole_Organization_RegionalHierarchy_RegionTypes";a:4:{i:0;i:0;i:1;i:0;i:2;i:0;i:3;i:0;}s:52:"aEmployeeRole_Organization_RegionalHierarchy_Regions";a:4:{i:0;i:0;i:1;i:0;i:2;i:0;i:3;i:0;}s:69:"aEmployeeRole_Organization_RegionalHierarchy_Regions_RegionStatistics";a:2:{i:0;i:0;i:2;i:0;}s:82:"aEmployeeRole_Organization_RegionalHierarchy_Regions_ 17 7 87987687:1:"2015-05-07Em 10 7 896789798ation2015-05-07:{
I have attached the pic of the output i am getting...
tables are manufacturer,loan,refund, manufacutrerId is common in all tables.
now in the pic it shows repeated record as the manufacturer took loan 2 times but return amount 1 time.. but it repeats in refund column. Please Help !!!

This query should work:
SELECT ManufacturerId,FirstName,LA,RA, LA - RA as RemainingAmount FROM (SELECT MM.ManufacturerId,MM. FirstName,SUM(LoanAmount) as LA, SUM(RefundAmount) as RA FROM microfinance_manufacturers AS MM
INNER JOIN manufacturer_loans AS ML ON MM.ManufacturerId = Ml.ManufacturerId
INNER JOIN manufacturer_refunds AS MR ON MM.ManufacturerId = MR.manufacturerId
GROUP BY MM.ManufacturerId) as a

You need to do the aggregations before the join. Otherwise, you end up with a cartesian product that throws off the values:
SELECT MM.*, ML.*, MR.*
FROM microfinance_manufacturers MM left join
(select Ml.ManufacturerId, count(*) as numloans, sum(ml.amount) as loanamount
from manufacturer_loans ML
group by ManufacturerId
) ml
on MM.ManufacturerId = Ml.ManufacturerId left join
(select Mr.ManufacturerId, count(*) as numrefunds, sum(ml.amount) as refundamount
from manufacturer_refunds mr
group by ManufacturerId
) mr
on MM.ManufacturerId = MR.manufacturerId;
Your question is unclear both on the data layout and on the expected results. But this should be basically what you need.

Related

MYSQL query to display max values of column grouped by inner join column

I have two tables
PILOTS
pilot_id
first_name
last_name
status
hub
1
fname1
lname1
1
YBBN
2
fname2
lname2
0
YSSY
3
fname3
lname3
1
YMML
4
fname4
lname4
1
YBBN
5
fname5
lname5
1
EGLL
6
fname6
lname6
1
EGLL
7
fname7
lname7
1
EGLL
8
fname8
lname8
1
YPAD
PIREPS
pirep_id
pilot_id
date
landing_rate
accepted
1
1
2021-04-01
-113
1
2
1
2021-04-02
-110
1
3
1
2021-04-03
-200
1
4
2
2021-04-04
-20
1
5
2
2021-04-05
-120
1
6
3
2021-04-06
-130
1
7
3
2021-04-07
-132
1
8
4
2021-04-08
-91
1
9
5
2021-04-09
-64
1
10
6
2021-04-10
-47
0
11
6
2021-04-11
-112
1
12
7
2021-04-12
-113
1
13
7
2021-04-13
-201
1
14
1
2021-04-14
-300
0
15
1
2021-04-15
-301
1
EXPECTED REUSULT
pilot_id
first_name
last_name
hub
landing_rate
date
pirep_id
2
fname2
lname2
YSSY
-20
2021-04-04
4
5
fname5
lname5
EGLL
-64
2021-04-09
9
4
fname4
lname4
YBBN
-91
2021-04-08
8
3
fname3
lname3
YMML
-130
2021-04-06
6
8
fname8
lname8
YPAD
-301
2021-04-15
15
The below code gives me the expected output if I only group by pilot ID and displays the best landing rate with the associated pilot and the date they completed the flight
SELECT pi.first_name,pi.last_name,p.pirep_id,p.pilot_id,p.date,p.landing_rate
FROM qvi_pireps p
LEFT JOIN qvi_pilots pi on p.pilot_id=pi.pilot_id
INNER JOIN
(SELECT pilot_id as pil,date as da,MAX(landing_rate) AS max_landing_rate
FROM qvi_pireps
where landing_rate<0 GROUP BY pilot_id) grouppedp
ON p.pilot_id = grouppedp.pil
AND p.landing_rate = grouppedp.max_landing_rate
where pi.status=1 and
accepted=1
group by p.pilot_id ORDER BY `grouppedp`.`max_landing_rate` DESC,p.date asc limit 20
ABOVE QUERY OUTPUT
first_name
last_name
pirep_id
pilot_id
date
landing_rate
fname2
lname2
4
2
2021-04-04
-20
fname5
lname5
9
5
2021-04-09
-64
fname4
lname4
8
4
2021-04-08
-91
fname1
lname1
2
1
2021-04-02
-110
fname6
lname6
11
6
2021-04-11
-112
fname7
lname7
12
7
2021-04-12
-113
fname3
lname3
6
3
2021-04-06
-130
fname8
lname8
15
8
2021-04-15
-301
when I change group by p.pilot_id to group by pi.hub I receive the unique hubs however the pilot,landing_rate and date do not match what should be the best
Basically, what I'm trying to achieve is getting the MAX(landing_rate) of each pilot where pilot.status=1 & flight.accepted=1 then group by pilot.hub to display the best hub with the highest landing rate with the pilot and date they achieved that landing rate
Any help would be greatly appreciated!
This:
SELECT pilot_id, date, MAX(landing_rate)
FROM qvi_pireps
GROUP BY pilot_id
is invalid SQL. You group by pilot and select a date. Which date? There are many dates per pilot in that table. You'd have to apply some aggregation function on date to get this valid. MySQL should raise an exception here (and I am sure it would, did you change from cheating mode to SET sql_mode = 'ONLY_FULL_GROUP_BY';. This should be set by default and as this doesn't seem to be the case, I surmise you are working with a old version of MySQL.
Apart from this and the inappropriate outer join and the missing accepted check in your subquery (which may be the main reason you are seeing incorrect dates), your query looks rather fine. Only that in your main query you group again by pilot, which makes no sense at all. Maybe you left that in by mistake when rewriting your query at some point. Here is your query corrected:
SELECT pil.first_name, pil.last_name, pir.pirep_id, pil.pilot_id, pir.date, pir.landing_rate
FROM qvi_pilots pil
JOIN qvi_pireps pir ON pir.pilot_id = pil.pilot_id AND pir.accepted = 1
JOIN
(
SELECT pilot_id, MAX(landing_rate) AS max_landing_rate
FROM qvi_pireps
WHERE accepted = 1
GROUP BY pilot_id
) grouppedp ON grouppedp.pilot_id = pir.pilot_id AND grouppedp.max_landing_rate = pir.landing_rate
WHERE pil.status = 1
ORDER BY pir.landing_rate DESC, pir.date ASC
LIMIT 20;
For readability, though I prefer an IN clause over a join:
SELECT pil.first_name, pil.last_name, pir.pirep_id, pil.pilot_id, pir.date, pir.landing_rate
FROM qvi_pilots pil
JOIN qvi_pireps pir ON pir.pilot_id = pil.pilot_id
AND pir.accepted = 1
AND (pir.pilot_id, pir.landing_rate) IN
(
SELECT pilot_id, MAX(landing_rate)
FROM qvi_pireps
WHERE accepted = 1
GROUP BY pilot_id
)
WHERE pil.status = 1
ORDER BY pir.landing_rate DESC, pir.date ASC
LIMIT 20;
(The same can be achieved with NOT EXISTS (<a greater landing rate for the pilot>) by the way.)
In current MySQL versions we would rather use a window function in order to access the qvi_pireps table only once:
SELECT first_name, last_name, pirep_id, pilot_id, date, landing_rate
FROM
(
SELECT
pil.first_name, pil.last_name, pir.pirep_id, pil.pilot_id, pir.date, pir.landing_rate,
MAX(pir.landing_rate) OVER (PARTITION BY pil.pilot_id) AS max_landing_rate
FROM qvi_pilots pil
JOIN qvi_pireps pir ON pir.pilot_id = pil.pilot_id AND pir.accepted = 1
WHERE pil.status = 1
) with_max_landing_rate
WHERE landing_rate = max_landing_rate
ORDER BY landing_rate DESC, date ASC
LIMIT 20;
UPDATE: Same query for old MySQL versions, but for best rate per hub instead of best rate per pilot.
After all, this simply means we must look for the MAX(landing_rate) per hub instead of per pilot:
SELECT pil.first_name, pil.last_name, pir.pirep_id, pil.pilot_id, pir.date, pir.landing_rate
FROM qvi_pilots pil
JOIN qvi_pireps pir ON pir.pilot_id = pil.pilot_id
AND pir.accepted = 1
AND (pil.hub, pir.landing_rate) IN
(
SELECT pl.hub, MAX(pr.landing_rate)
FROM qvi_pireps pr
JOIN qvi_pilots pl USING (pilot_id)
WHERE pr.accepted = 1 AND pl.status = 1
GROUP BY pl.hub
)
WHERE pil.status = 1
ORDER BY pir.landing_rate DESC, pir.date ASC
LIMIT 20;

MySQL: join three tables and get counts

I am so totally lost.
Working on a database for my co-rec team. Players, Matches, Available players for a match, chosen players for a match, etc.....
The first major step I'd like is to be able to combine Players, Matches and Available to get a list of matches with the number of Women and number of Men available.
Here are my tables:
Players (id, Gender, Name, ....)
id Gender Name
1 M David
2 M Alberto
3 F Alison
4 F Karen
5 F Callie
6 M Stephan
Matches (id, ...)
id
1
2
3
Available (id, matchID, playerID)
id matchID PlayerID
1 1 1
2 1 8
3 1 11
... ... ...
16 2 1
17 2 2
18 2 15
... ... ...
26 3 6
27 3 7
28 3 18
Desired Result
Match Women Men Total
1 5 10 15
2 4 6 10
3 6 10 16
... ... ... ...
Here's the closest I've got (just this morning):
select m.id, p.gender
from matches m
inner join available a on m.id = a.matchid
inner join players p on p.id = a.playerid
Morning clarity:
select m.id,
sum(case when p.gender="Male" then 1 else 0 end) "Males",
sum(case when p.gender="Female" then 1 else 0 end) "Females",
count(p.gender) "Total"
from matches m
inner join available a on m.id = a.matchid
inner join players p on p.id = a.playerid
group by m.id

How can I get the id, min and max in the same My SQL query?

I have 2 tables: stops and bus_route_details.
stop_id stop_name
1 ā€˜Cā€™ CROSS ROAD JUNCTION
2 10TH ROAD
3 16TH ROAD
4 4TH ROAD (GOLIBAR)
5 600TENAMENTGATE
6 A.D.MODI INSTITUTE
7 AHANSARI CHOWK
8 A.H.SCHOOL
9 A.P.M.COMPLEX
10 A.T.I.
11 AAI TULJABHAVANI CHOWK/LOKHANDWALA COMPLEX
12 AAKASH GANGA SOCIETY (DHARAVI)
The table stops simply stores the id and name of each stop. The bus_route_details table stores the bus_number, the stop_id of the stop from the stops table and the order in which the stop appears on that route. The first stop has the order 1 whereas the last stop can be a number like 44 if the route has 44 stops in total.
bus_number stop_id stop_order
8 2139 30
8 351 31
8 1791 32
8 19 33
8 2 34
8 497 35
8 2024 36
8 20 37
8 404 38
8 1787 39
8 621 40
8 1937 41
8 1941 42
7 509 1
7 788 2
7 996 3
7 1340 4
7 1161 5
7 335 6
7 2296 7
7 891 8
As per the above, I would like to get a table that shows where bus number 7 and 8 start and end.
In simple words, get the distinct list of bus numbers, find the stop which minimum stop order and maximum stop order for each bus.
7, start, end [This is how I want the bus numbers and stop names]
My current query only gives me 2 columns: either the starting or the ending stop_name. How can I retrieve all 3 columns in the same query?
SELECT bus_number, stop_name from bus_route_details, stops `WHERE(bus_number, stop_order) IN (SELECT bus_number, MAX(stop_order)`
FROM bus_route_details
GROUP BY bus_number)
AND stops.stop_id = bus_route_details.stop_id
Any suggestions? I did the UNION and got all 4 results successfully in 2 columns but I would like 3 columns for this. Thank you
One way of doing this is to find the min and max in a derived table and join the stops table and finally use conditional aggregation to flatten the result, like this:
select
b.Bus_number,
max(case when b.stop_order = x.mio then b.stop_id end) min_stop_id,
max(case when b.stop_order = x.mio then s.stop_name end) min_stop_name,
max(case when b.stop_order = x.mao then b.stop_id end) max_stop_id,
max(case when b.stop_order = x.mao then s.stop_name end) max_stop_name
from bus_route_details b
join (
select Bus_number, min(stop_order) mio, max(stop_order) mao
from bus_route_details
group by Bus_number
) x on b.Bus_number = x.Bus_number and (b.stop_order = x.mio or b.stop_order = x.mao)
join stops s on b.stop_id = s.stop_id or b.stop_id = s.stop_id
group by b.Bus_number;
This would give you a result like:
Bus_number min_stop_id min_stop_name max_stop_id max_stop_name
----------- ----------- -------------------- ----------- --------------------
7 509 stop 1 891 stop 2
8 351 stop 3 1941 stop 4
Use double inner join on stops
select b.stop_name as name_start, c.stop_name as finish_name
from
( SELECT bus_number, min(stop_order) as start_id, max(stop_order) as finish_id
from bus_route_details
group by bus_number) as t
inner join bus_route_details as b on t.start_id = b.stop_id
inner join bus_route_details as c on t.finish_id = c.stop_id

MySQL join next lower value from other table

I've the two tables orders
id article amount
1 1 1
2 2 50
and prices
id article min_amount price
1 1 1 42.99
2 2 1 5.06
3 2 5 4.55
4 2 10 4.3
5 2 25 4.05
6 2 100 2.66
The prices tables contains IDs of articles and a minimum amount you would have to buy to get a bulk discount (which would change the price for the order). I would like to join prices into orders, so that the result looks like:
id article amount price
1 1 1 42.99
2 2 50 4.05
The order id 2 is above the minimum (25) to get the article for 4.05ā‚¬, but still below 100 at which you would get a bigger discount, so the query would to have pick the next-lower value.
I've tried this query so far
SELECT
orders.id AS id,
orders.article,
orders.amount,
prices.price,
(orders.amount - prices.min_amount) AS discount_diff
FROM orders
LEFT JOIN prices ON (prices.article = orders.article) AND (prices.min_amount <= orders.amount)
which gives this result
id article amount price discount_diff
1 1 1 42.99 0
2 2 50 5.06 49
2 2 50 4.55 45
2 2 50 4.3 40
2 2 50 4.05 25
You can find this example on "js"fiddle: http://sqlfiddle.com/#!9/1b2bf/8
The query you need is this:
SELECT orders.id AS id,
orders.article,
orders.amount,
prices.price
FROM orders
INNER JOIN prices ON ( prices.article = orders.article
and prices.min_amount <= orders.amount)
INNER JOIN ( SELECT orders.article,
orders.amount,
min(prices.price) minprince
FROM orders
INNER JOIN prices ON (prices.article = orders.article
AND prices.min_amount <= orders.amount)
GROUP BY orders.article,
orders.amount) b
ON ( prices.article = b.article
AND orders.amount = b.amount
AND prices.price = b.minprince)
See it here: http://sqlfiddle.com/#!9/1b2bf/27

having trouble writing a query that list all salesmen that did not sell to a particular customer

I was successful in writing the query that lists salesmen that did sell to a particular customer, but not those that have not. I suspect it is because the same salesmen that sold to the specific customer, also sold to other customers.
select a.name from salesperson a inner join orders b on
a.salesperson_id = b.salesperson_id where cust_id="4";
I was thinking that modifying the same query like this would do the trick:
.... a.salesperson_id <> b.salesperson_id where cust_id="4";
But the result lists all the salesmen. This is most likely due to the fact that the same salesmen that were returned in the original query, also sold to other customers
The 3 tables look like this:
Salesperson table
salesperson_ID, Name, Age, Salary
1 Abe 61 140000
2 Bob 34 44000
5 Chris 34 40000
7 Dan 41 52000
8 Ken 57 115000
11 Joe 38 38000
Customer table
cust_ID, Name, City Industry Type
4 faralon sacramento H
6 Apple cupertino S
7 Honda NY B
9 Kolb Oshkosh B
Orders table
Number, Order_date, cust_id, salesperson_id, Amount
10 8/2/1996 4 2 540
20 1/30/1999 4 8 1800
30 7/14/1995 9 1 460
40 1/29/1998 7 2 2400
50 2/3/1998 6 7 600
60 3/2/1998 6 7 720
70 5/6/1998 9 7 150
Any help would be greatly appreciated. ~Alpinehyker
You can do something like this:
select a.name from salesperson a
left join orders b on a.salesperson_id = b.salesperson_id and b.cust_id="4"
where b.Number is null
So, get all salepersons, left join to orders for customer 4, and return only rows where there is no such order.
I am assuming that Number is the primary key for Orders, or at least not null.
All salespeople who have NOT sold to customer_ID 4:
SELECT s.Name FROM Salesperson AS s
LEFT JOIN Orders AS o
ON s.salesperson_ID = o.salesperson_ID
WHERE o.customer_ID <> 4
GROUP BY o.salesperson_ID;
Perhaps this will work
SELECT
s.*
FROM `Salesperson` AS s
LEFT JOIN `Orders` AS o ON o.`salesperson_id` = s.`salesperson_ID`
WHERE
o.`cust_id` NOT IN (4)
GROUP BY s.`salesperson_ID`;
Answer to your 2nd question:
SELECT
COUNT(*) AS num_of_orders
,s.`Name`
FROM `Salesperson` AS s
LEFT JOIN `Orders` AS o ON o.`salesperson_id` = s.`salesperson_ID`
GROUP BY s.`salesperson_ID`
HAVING num_of_orders >= 2;
...and 3rd question. (assuming you have your highAchiever table ready)
INSERT INTO `highAchiever`
(`Name`,`Age`)
SELECT
`Name`
,`Age`
FROM `Salesperson`
WHERE
`Salary` >= 100000;