Using left join and inner join to get data from three tables - mysql

I have three tables.
managersTbl
ID | NAME |
1 | Ana |
2 | Elsa |
3 | Olaf |
4 | Belle|
gigsTbl
ID | GIG | EARNING | MANAGER | ARTIST |
1 | sing | 500 | 2 | 1 |
2 | act | 100 | 2 | 3 |
3 | modelling | 250 | 3 | 4 |
4 | dance | 10 | 1 | 1 |
artistsTbl
ID | NAME |
1 | Haley |
2 | Aw |
3 | Fire |
4 | Finn |
What I want to accomplish is get all the managers, and get the sum of their gigs earning.
SELECT id, name
ifnull(b.earning, 0) AS earning
FROM doctorsTbl
LEFT JOIN(
SELECT id, name
SUM(earning) AS earning
FROM gigsTbl
GROUP BY manager_id
) b on (a.id = b.manager_id)
I have already achieved getting all the managers, and the sum of their gigs earnings with the above query. Now, I want to get all the artists, without getting repeated, from the gigsTbl grouped by their manager.
I've tried using inner join like this
SELECT id, name
ifnull(b.earning, 0) AS earning
FROM doctorsTbl
LEFT JOIN(
SELECT id, name
SUM(earning) AS earning
FROM gigsTbl
INNER JOIN artistsTbl d
on b.artist = d.id
GROUP BY manager_id
) b on (a.id = b.manager_id)
I want to do this without creating a new query for the artists because I'd like to be able to search the tables with using the artist identification.

Related

query to get customer list using JOIN with sum () of the amounts spent in orders

I have the following tables
table anag (customer registry)
id | surname | name | phone |
----------------------------------------------
1 | Brown | Jack | +3989265781 |
2 | Smith | Bill | +3954872358 |
3 | Rogers | Stan | +3912568453 |
4 | Pickford | Eric | +3948521358 |
----------------------------------------------
table levels (table that connects each customer to his salesperson. For database registration reasons, the link between customer and seller is given by the customer's telephone number)
id | client_phone | id_seller |
--------------------------------------
1 | +3989265781 | 4 |
2 | +3954872358 | 7 |
3 | +3912568453 | 7 |
4 | +3948521358 | 8 |
--------------------------------------
table orders (contains all purchases made by customers, of course)
id | id_client | id_item | id_seller | price | status |
--------------------------------------------------------------------
1 | 1 | 2 | 4 | 12.50 | 2 |
2 | 2 | 2 | 7 | 12.50 | 2 |
3 | 2 | 3 | 7 | 10.00 | 3 |
4 | 2 | 3 | 7 | 10.00 | 3 |
5 | 2 | 4 | 7 | 20.50 | 1 |
6 | 3 | 2 | 7 | 12.50 | 1 |
7 | 3 | 5 | 7 | 19.00 | 3 |
8 | 3 | 7 | 7 | 31.00 | 2 |
9 | 4 | 1 | 8 | 5.00 | 1 |
--------------------------------------------------------------------
What I'm trying to do is get from the JOIN of these tables a complete list by seller of his customers sorted in descending order by the amount spent on orders as long as the order status is 2 or 3
Something like this (example seller id 7):
id | surname | name | amaount |
----------------------------------------
3 | Rogers | Stan | 50.00 |
2 | Smith | Bill | 32.50 |
----------------------------------------
I have tried with this query which seems correct to me, but unfortunately it returns me error in fetch_assoc()
SELECT a.id, a.surname, a.name, o.amount FROM levels AS l
JOIN anag AS a ON a.phone = l.client_phone
JOIN {
SELECT id_client, SUM(price) AS amount FROM orders
WHERE id_seller = '7' AND (status = '2' OR status = '3') GROUP BY id_client
} AS o ON o.id_client = a.id
WHERE l.id_seller = '7'
ORDER BY o.amount DESC
If I separate the subquery from the main query, both return the data I expect and it seems strange to me the JOIN between the two does not work properly
I think the only real error is using curly braces instead of parentheses:
SELECT a.id, a.surname, a.name, o.amount
FROM levels l JOIN
anag a
ON a.phone = l.client_phone JOIN
(SELECT id_client, SUM(price) AS amount
FROM orders
WHERE id_seller = '7' AND status IN ('2', '3'))
GROUP BY id_client
) o
ON o.id_client = a.id
WHERE l.id_seller = '7'
ORDER BY o.amount DESC;
In addition:
You can use IN to shorten an equality comparison to multiple values.
Although I left them in, status and id_seller look like numbers. If so, drop the single quotes. Don't mix data types.
Your question is ambiguous on what to do if the seller in orders differs from the seller in anag for a customer. This keeps your logic (the sellers need to match).
SELECT a.id, a.surname, a.name, sum(o.price) 'amount'
FROM anag a
LEFT JOIN levels l ON l.id =a.id
LEFT JOIN orders of ON o.id_seller = l.id_seller AND o.id_client = l.id
GROUP BY o.id_seller
ORDER BY amount DESC

MySQL How do I get the sum of multiple rows from multiple tables and then order the results by total descending?

I am trying to build a leaderboard based on the points (money) a user has. The points are stored in multiple tables and some users may not have points in a given table.
Table: account
--------------------------
| uid | name | locker |
|-----|---------|--------|
| 1 | Bob | 15 |
| 2 | Dave | 2 |
| 3 | Jim | 5 |
--------------------------
Table: container
------------------------
| account_uid | money |
|-------------|--------|
| 1 | 4 |
| 3 | 1 |
| 3 | 2 |
| 3 | 4 |
------------------------
Table: vehicle
------------------------
| account_uid | money |
|-------------|--------|
| 2 | 2 |
| 2 | 1 |
| 3 | 2 |
------------------------
I would like to see the results ouput as -
Bob 19
Jim 14
Dave 5
Note that some tables do not have points for some people.
This code did not work for me. It seems to have duplicated the points somehow.
SELECT
act.name,
act.uid,
SUM(COALESCE(act.locker,0) + COALESCE(con.money,0) + COALESCE(veh.money,0)) AS total
FROM account as act
LEFT JOIN container as con
ON act.uid = con.account_uid
LEFT JOIN vehicle as veh
ON act.uid = veh.account_uid
Group By act.name
ORDER BY total DESC
How about calculating the totals one at a time and then combining them into one?
select account.uid, account.name, sum(tot.Money) as TotalMoney from
(
(select uid, sum(Money) as Money from container
group by uid)
union all
(select uid, sum(Money) as Money from vehicle
group by uid)
) tot
inner join account on
tot.uid = account.uid
group by account.uid

MySQL return distinct accounts from one table, average rating from another, all based on service area in another table

as the title states, I'm trying to return a query which gets account details from the accounts table, gets the average rating for the account from the reviews table, and limits the rows to the service location associated to the account.
Here are the simplified tables:
accounts
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
| 1 | John | Smith |
| 2 | Bob | Doe |
| 3 | Alice | McLovin |
| 4 | Bruce | Wayne |
+----+------------+-----------+
reviews
+----+-------------+-----+--------+
| id | acccount_id | ... | rating |
+----+-------------+-----+--------+
| 1 | 1 | ... | 9 |
| 2 | 1 | ... | 10 |
| 3 | 2 | ... | 7 |
| 4 | 1 | ... | 2 |
| 5 | 4 | ... | 6 |
+----+-------------+-----+--------+
service_area
+----+-------------+---------+
| id | acccount_id | city_id |
+----+-------------+---------+
| 1 | 1 | 1140 |
| 2 | 1 | 1001 |
| 3 | 2 | 1140 |
| 4 | 1 | 1086 |
| 5 | 4 | 1001 |
+----+-------------+---------+
For example, the user may request to view all accounts which have a service area of city_id 1140. The query should then return the first_name, last_name, and average rating for each account within the specified service area. Note that accounts can have multiple service areas (see service_area table).
Thanks in advance!
UPDATE:
The following QUERY did the trick! I needed a LEFT JOIN for the reviews table!
SELECT a.first_name, a.last_name, AVG(r.rating) avg_rating
FROM accounts a
JOIN service_area sa
ON a.id = sa.account_id AND sa.city_id = 1140
LEFT JOIN reviews r
ON a.id = r.account_id
GROUP BY a.id
You can use joins and simple aggregation with group by
SELECT a.*,
AVG(r.rating) avg_rating
FROM accounts a
JOIN reviews r ON a.id = r.acccount_id
JOIN service_area s ON a.id = s.acccount_id
WHERE s.city_id = 1140
GROUP BY a.id
Result set will be like
id first_name last_name avg_rating
------ ---------- --------- ------------
1 John Smith 7.0000
2 Bob Doe 7.0000
Use LEFT join for when there are no reviews available
SELECT a.*,
COALESCE(AVG(r.rating),0) avg_rating
FROM accounts a
LEFT JOIN reviews r ON a.id = r.acccount_id
JOIN service_area s ON a.id = s.acccount_id
WHERE s.city_id = 1140
GROUP BY a.id
DEMO

Mysql nested left joins with counts

I have a set of MySQL three tables in a "has many" relationship: deals, orders, and coupons.
Deals
|----|--------------|
| id | title |
|----|--------------|
| 1 | Some deal |
| 2 | Another deal |
|----|--------------|
Orders
|----|---------|-----------|
| id | deal_id | state |
|----|---------|-----------|
| 1 | 1 | purchased |
| 2 | 1 | purchased |
| 3 | 1 | expired |
| 4 | 2 | purchased |
|----|---------|-----------|
Coupons
|----|----------|
| id | order_id |
|----|----------|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 2 |
| 6 | 4 |
|----|----------|
So, deals have many orders, which have many coupons.
What I'd like to do is select on the deals table while counting the number of purchased orders and coupons.
I know how to get a count on paid orders already:
SELECT deals.*, count(orders.id) AS orders_purchased_count FROM deals
LEFT JOIN orders ON deals.id=orders.deal_id AND orders.state='purchased'
WHERE deal_id < 3
GROUP BY deals.id
Deals
|----|--------------|------------------------|
| id | title | orders_purchased_count |
|----|--------------|------------------------|
| 1 | Some deal | 2 |
| 2 | Another deal | 1 |
|----|--------------|------------------------|
Similarly, I can get a count of coupons for orders:
SELECT orders.*, count(coupons.id) AS coupons_count FROM orders
LEFT JOIN coupons ON orders.id=couoons.orders_id
WHERE orders.state='purchased'
GROUP BY orders.id
Orders
|----|-----------|---------------|
| id | state | coupons_count |
|----|-----------|---------------|
| 1 | purchased | 3 |
| 2 | purchased | 2 |
| 4 | purchased | 1 |
|----|-----------|---------------|
My question is: How do I combine these so that I can add coupons_count next to orders_purchased_count?
Deals
|----|--------------|------------------------|---------------|
| id | title | orders_purchased_count | coupons_count |
|----|--------------|------------------------|---------------|
| 1 | Some deal | 2 | 5 |
| 2 | Another deal | 1 | 1 |
|----|--------------|------------------------|---------------|
The tricky thing, in my case, will be to run the WHERE deal_id < 3 filter when selecting from deals before I join on orders and to run the WHERE orders.state='purchased' filter when selecting from orders before I join on coupons. It's a large dataset and I don't want to load all my orders and coupons into memory for the purpose of joining.
At a loss for how to do this.
Does this work? I couldn't understand your concerns which part was going to be tricky.
SELECT deals.*, COUNT(DISTINCT(orders.id)) AS orders_purchased_count, COUNT(coupons.id) AS coupons_count
FROM deals
LEFT JOIN orders
ON deals.id=orders.deal_id
AND orders.state='purchased'
LEFT JOIN coupons
ON orders.id=coupons.orders_id
WHERE deal_id < 3
GROUP BY deals.id;
Try this one with a co related subquery and join
SELECT deals.*, count(orders.id) AS orders_purchased_count
(
SELECT count(id) FROM coupons WHERE orders_id = orders.id
) AS coupons_count
FROM deals
LEFT JOIN orders ON deals.id=orders.deal_id AND orders.state='purchased'
WHERE deal_id < 3
GROUP BY deals.id

Join top 3 interest fields along with each user row

I'm trying to get the top 3 interests of each user, probably as a LEFT JOIN query.
The way the app is designed, each user has a set of interests which are no other than 'childs' (rows without parent) of the categories table.
Here are some simplified table schemas w/mock data (see SQL Fiddle demo)
-- Users table
| ID | NAME |
--------------
| 1 | John |
| 2 | Mary |
| 3 | Chris |
-- Categories table -- Interests table
| ID | NAME | PARENT | | ID | USER_ID | CATEGORY_ID |
-------------------------------------- ------------------------------
| 1 | Web Development | (null) | | 1 | 1 | 1 |
| 2 | Mobile Apps | (null) | | 2 | 1 | 1 |
| 3 | Software Development | (null) | | 3 | 1 | 1 |
| 4 | Marketing & Sales | (null) | | 4 | 2 | 1 |
| 5 | Web Apps | 1 | | 5 | 2 | 1 |
| 6 | CSS | 1 | | 6 | 3 | 1 |
| 7 | iOS | 2 | | 7 | 3 | 1 |
| 8 | Streaming Media | 3 | | 8 | 3 | 1 |
| 9 | SEO | 4 |
| 10 | SEM | 4 |
To get the top 3 interests of a given user, I've usually performed this query:
SELECT `c`.`parent` as `category_id`
FROM `interests` `i` LEFT JOIN `categories` `c` ON `c`.`id` = `i`.`category_id`
WHERE `i`.`user_id` = '2'
GROUP BY `c`.`parent`
ORDER BY count(`c`.`parent`) DESC LIMIT 3
This query returns the top 3 categories (parents) of user with id = 2
I would like to find out how I can query the users table and get their top 3 categories either in 3 different fields (preferred) or as a group_concat(..) in one field
SELECT id, name, top_categories FROM users, (...) WHERE id IN ('1', '2', '3');
Any ideas how I should go about doing this?
Thanks!
First build a groped query that lists on distinct rows, the top three skills for each user. Then pivot that into to pull the three skills for eah user out to the right. You will need to use the Max(isnull(skill,'')) expression on the skills in each skill column.
It is very crude way of doing it in MYSQL to get top 3 records for each user
SELECT u.id, c.name
FROM
users u,
categories c,
(SELECT i.id,
i.user_id,
i.category_id,
#running:=if(#previous=i.user_id,#running,0) + 1 as rId,
#previous:=i.user_id
FROM
(SELECT * FROM intersect ORDER BY user_id) i JOIN
(SELECT #running=0, #previous=0 ) r) i
WHERE
u.id = i.USER_ID AND
i.CATEGORY_ID = c.id AND
i.rId <= 3
group by u.id, c.name ;
Hope it helps
FIDDLE