Conditional INNER JOIN or LEFT JOIN based on the joining condition - mysql

I have a query
SELECT
users.email AS email,
addons.sku AS sku,
addons.quantity as quantity,
invoices.total as total
FROM addons
INNER JOIN users ON 1=1
and users.id = addons.user_id
LEFT JOIN invoices ON 1=1
AND invoices.user_id = users.id
AND invoices.status != 3
Here is what I need to happen:
if user doesn't have an invoice at all we should include them with NULL being returned in the total
if user has an invoice in status != 3 we should include them
if invoices exists and status = 3 we should exclude them.
So it's like I need both INNER JOIN and LEFT JOIN at the same time
How can I achieve that?

This is what you need:
SELECT
users.email AS email,
addons.sku AS sku,
addons.quantity as quantity,
invoices.total as total
FROM addons
INNER JOIN users
ON users.id = addons.user_id
LEFT JOIN invoices
ON invoices.user_id = users.id
WHERE invoices.status IS NULL OR invoices.status != 3
Explanation:
Users without invoices are included in the result, with "empty" invoice total. That's what the LEFT JOIN is for
Users with invoice status != 3 are included, and so are their invoices. So, add that to the where clause (remember the status could be NULL because of the above LEFT JOIN)
Users with invoice status = 3 are excluded. Do that with the WHERE clause

Related

Count elements from a table with differents conditions in mySql?

I wanna count all the orders a user has and all the complete orders a user has. I came with this but it´s not working
select
count(a.id) as total,
count(b.id) as complete
from
user
join
orders a on user.id = a.user_id
join
orders b on user.id = b.user_id
where
a.id = 1
and
(b.id = 1 and b.complete = 'yes');
Any idea?
you could sum the order with yes and count the distinct id group by user
select user.id, sum(if(a.complete ='yes',1,0)), count(distinct a.id)
from user
INNER join orders a on user.id = a.user_id
group by user.id
I believe you are searching for grouping (MySQL GROUP BY) by the differents users, and then count all the orders related to each user plus the completed ones. For this approach, you will need to:
(1) Join users with they orders.
(2) Use GROUP BY clause on user.id column.
(3) Count all orders related to each user with COUNT()
(4) Sum all orders related to each user having some specific condition with SUM(CASE WHEN <specific_condition> THEN 1 ELSE 0 END).
In summary, a query like next one should work:
SELECT
u.id,
COUNT(o.id) AS total_orders,
SUM(CASE WHEN o.complete = "yes" THEN 1 ELSE 0 END) AS complete_orders
FROM
user AS u
INNER JOIN
orders AS o ON o.user_id = u.id
GROUP BY
u.id

Left outer join + SUM with group by

I have a table called Purchases which belongs to a Users (that is a Purchase has a foreign key to User).
The Purchases table has a column called quantity and a state_id column, both are integers.
I want to be able to order the Users by their completed purchases (state_id = 10) where the SUM of their quantities is bigger than > 100. That is, all the users which have a total of completed purchases > 100, should appear first, and the rest right after.
This is what I have tried:
SELECT users.id as user_id,
SUM(CASE WHEN purchases.state_id = 5
THEN purchases.quantity
ELSE 0
END) as quantity
FROM users
LEFT OUTER JOIN purchases ON purchases.user_id = users.id
GROUP BY purchases.user_id
But this is just returning me one User, not all of them. What am I missing?
You can JOIN only completed purchases using JOIN condition. And you should group by users.id not purchases.user_id as soon you can have users in a table without purchases at all:
SELECT users.id as user_id,
SUM(purchases.quantity) as quantity
FROM users
LEFT JOIN purchases ON (users.id=purchases.user_id)
AND (purchases.state_id = 10)
GROUP BY users.id
ORDER BY quantity DESC

Select Row for Max Sum Value In Multiple Tables MYSQL

I need to query for the users with highest amount of sales by all projects, where the users are in users table, sales in units table, projects in projects table.
Projects Top Agent Total Sales for Project
Project A User A 100000
Project B User B 20000
Project C User A 1000
Project D - 0
The Projects column is list all the projects regardless it has sales or not.
The Top Agent column is list the user with the highest sales in the project.
The Total Sales for Project is the total sales for a projects.
The agent column i got is incorrect because there is someone else has the highest sales, the query seems to return the first row of the result
SELECT projects, pid, CASE WHEN agent is null THEN '-' ELSE agent END as agent,
CASE WHEN FORMAT(topagent,0) > 0 THEN FORMAT(topagent,0) ELSE 0 END as salesvolume
FROM (
SELECT projects.name as projects, projects.id as pid,
concat(users.f_name, ' ', users.l_name) as agent,
SUM(units.price) AS topagent
FROM users inner join bookings on bookings.agent_id = users.id
inner join units on bookings.unit = units.id
inner join types on types.id = units.types_id
inner join projects on projects.id = types.project_id
WHERE units.status = 'Sold'
GROUP BY pid
union
select projects.name as projects, projects.id as pid,
concat(users.f_name, ' ', users.l_name) as agent,
SUM(units.price) AS topagent
from projects left outer join types on projects.id = types.project_id
left outer join units on types.id = units.types_id and units.status = 'Sold'
left outer join bookings on units.id = bookings.unit and units.status = 'Sold'
left outer join users on bookings.agent_id = users.id and units.status = 'Sold'
group by pid
) a
GROUP BY pid
order by topagent desc
Try it if helps you-
SELECT a.prjname, IFNULL(usr.name,'-') AS Top_Agent, SUM(a.sale) AS Total_Sales_for_Project
FROM
(
SELECT prj.id AS prjid,prj.name AS prjname,usr.id,usr.name AS usrname,IFNULL(SUM(unit.price),0) AS sale
FROM projects AS prj
LEFT JOIN `types` AS typ ON typ.project_id=prj.id
LEFT JOIN units AS unt ON unt.type_id=typ.id AND unt.status='sold'
LEFT JOIN bookings bkg ON bkg.unit=unt.id
LEFT JOIN users usr ON usr.id=bkg.agent_it
GROUP BY prj.id,usr.id
ORDER BY prj.id,usr.id,sale DESC
) a
GROUP BY a.prjid
Your column aliases are confusing to read. In English, it seems what you mean by topagent is "sum of sales by a human". But in SQL, your GROUP BY pid means that the SUM(units.price) really means "sum of sales in a project".
Then the UNION adds a list of projects to a list of users. The agent names are basically random at this point.
If I decipher the requirements as "a list of projects ranked by the sales values of each project's top sales agent", then you'd have SQL as below:
SELECT
pid,
projects.name as project_name,
IFNULL(a.top_agent_name,'-') as top_agent_name,
CASE WHEN FORMAT(top_agent_sales,0) > 0 THEN FORMAT(top_agent_sales,0) ELSE 0 END as top_agent_salesvolume
FROM
projects
JOIN
SELECT
a.pid,
a.agent_name as top_agent_name,
a.agent_sales as top_agent_sales
FROM
(SELECT
projects.id as pid,
concat(users.f_name, ' ', users.l_name) as agent_name,
SUM(units.price) AS agent_sales
FROM users
inner join bookings on bookings.agent_id = users.id
inner join units on bookings.unit = units.id
inner join types on types.id = units.types_id
inner join projects on projects.id = types.project_id
WHERE units.status = 'Sold'
GROUP BY pid, users.id
) a # get all agents for all projects
JOIN
(SELECT
MAX(agent_sales) as max_project_agent_sales
FROM
(SELECT
projects.id as pid,
SUM(units.price) AS agent_sales
FROM users
inner join bookings on bookings.agent_id = users.id
inner join units on bookings.unit = units.id
inner join types on types.id = units.types_id
inner join projects on projects.id = types.project_id
WHERE units.status = 'Sold'
GROUP BY pid, users.id
)
GROUP BY pid) b ON a.pid = b.pid
WHERE
a.agent_sales = b.max_project_agent_sales
ORDER BY a.agent_sales desc
Old answer below:
There are 2 topagents for each pid in the inner query since it's a union of 2 group bys. There isn't a reducing function in the outer group by pid so the topagent returned in the select is the first one that came up in the inner query.

Output Name Field is DIfferent With the Output without Name Field MYSQL

SELECT users.id as uid, projects.id as pid
FROM users
inner join usergroup on usergroup.id = users.user_group
inner join bookings on bookings.agent_id = users.id
inner join units on bookings.unit = units.id
inner join types on types.id = units.types_id
inner join projects on projects.id = types.project_id
WHERE bookings.status = 'Accepted' AND units.status = 'Sold'
GROUP BY pid
with the query above, i get the correct and logic output:
pid | uid
1 1
2 1
9 12
10 14
then i want to show the user's name, so i added 1 field in query as shown below:
SELECT users.id as uid, users.f_name, projects.id as pid
FROM users
inner join usergroup on usergroup.id = users.user_group
inner join bookings on bookings.agent_id = users.id
inner join units on bookings.unit = units.id
inner join types on types.id = units.types_id
inner join projects on projects.id = types.project_id
WHERE bookings.status = 'Accepted' AND units.status = 'Sold'
GROUP BY pid
but i got a different output for uid:
pid | uid
1 1
2 1
9 9
10 11
This is not logic and anyone know why? Let's assume both are correct but why will output different result?
Your 2nd query you are filtering data with join condition before where condition while in 1st query you are filtering data just in where clause...keep you 2nd query same as 1st to get same results....
try below query-
SELECT projects.id as pid, users.id as uid, users.f_name
FROM users
inner join usergroup on usergroup.id = users.user_group
inner join bookings on bookings.agent_id = users.id
inner join units on bookings.unit = units.id
inner join types on types.id = units.types_id
inner join projects on projects.id = types.project_id
WHERE bookings.status = 'Accepted' AND units.status = 'Sold'
GROUP BY pid
One project ID can be associated to many units and each unit with many bookings and hence with many users. You group by project ID, but you don't specify which of all associated users you want to see for a project ID. You would do this with an aggregate function such as MIN(users.id), MAX(users.id), etc. But you don't use such function, thus telling MySQL: "just give me randomly one of the matching users".
As long as you keep your query as is, it seems that MySQL always gives you the same users, maybe the first it finds. This is by no way guaranteed; you could just as well get different users with the same query.
Now that you changed your query, MySQL goes another route and picks different matching users.

Mysql simulating FULL OUTER JOIN

I have 2 tables users and orders, I want get users and his orders count
SELECT `users`.*, `orders`.*,count(*) FROM `users` LEFT JOIN orders ON
`users`.`id` = `orders`.`user_id`
UNION SELECT `users`.*, `orders`.*,count(*) FROM users
RIGHT JOIN orders ON `users`.`id` = `orders`.`user_id`
This query select users and order count of users which have order, but not select users who not have orders .
What I want get
user orders
John 5
Thomas 0
Mike 8
What I get
user orders
John 5
Mike 8
How to get also users who not have orders ?
You don't need a full outer join for this. A left outer join should be fine, assuming that all the users in the orders table have a valid reference to the users table:
SELECT u.*, count(o.user_id)
FROM `users` u LEFT JOIN
orders o
ON u.`id` = o.`user_id`
group by u.id
The following query will give you a list of all users and the count of their orders, including 0 if that user has no orders.
Also, are you sure that ORDER_ID is the FK to the user table? That seems counter-intuative to me...
SELECT U.NAME
,COUNT(O.ORDER_ID)
FROM USERS U
LEFT OUTER JOIN
ORDERS O
ON U.ID = O.ORDER_ID
GROUP BY
U.NAME