Count elements from a table with differents conditions in mySql? - 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

Related

Conditional INNER JOIN or LEFT JOIN based on the joining condition

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

Merge query with two different WHERE clauses/conditions in one

I have three tables: users, accounts and scores. Each query is actually giving me the desired results:
-- This will return all user ids with a count of "calculated" scores
SELECT u.id AS user_id, count(1) AS total FROM scores s
INNER JOIN accounts a ON s.account_id = a.id
INNER JOIN user u ON a.user_id = u.id
WHERE s.status = 'CALCULATED'
GROUP BY user_id;
-- This will return all user ids with a count of non-calculated scores
SELECT u.id AS user_id, count(1) AS failures FROM scores s
INNER JOIN accounts a ON s.account_id = a.id
INNER JOIN user u ON a.user_id = u.id
WHERE s.status <> 'CALCULATED'
GROUP BY user_id;
But I would like to return something like this: user id, total, failures...all in one query!
This can be done with conditional aggregation. Conditions in SUM return 1 or 0 depending on the condition being satisfied.
SELECT u.id AS user_id,
SUM(s.status='CALCULATED'),
SUM(s.status<>'CALCULATED') AS total
FROM scores s
INNER JOIN accounts a ON s.account_id = a.id
INNER JOIN user u ON a.user_id = u.id
GROUP BY u.id;
As a note, you can simplify your query, because the user table is (presumably) not needed:
SELECT a.user_id,
SUM(s.status = 'CALCULATED') as num_calc,
SUM(s.status <> 'CALCULATED') AS num_notcalc
FROM scores s INNER JOIN
accounts a
ON s.account_id = a.id
GROUP BY a.user_id;
Your queries are giving the right answer, but you might also need to be careful about NULL values. If that's a concern, use <=> the NULL-safe equality operator:
SELECT a.user_id,
SUM(s.status = 'CALCULATED') as num_calc,
SUM(NOT s.status <=> 'CALCULATED') AS num_notcalc
FROM scores s INNER JOIN
accounts a
ON s.account_id = a.id
GROUP BY a.user_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

Creating a new column in an SQL query

I have one table to store users and one table to store member fee payments:
Users
Id (int)
Name (string)
MemberFeePayments
Id (int)
User (int)
Year (int)
The "User" column in MemberFeePayment is a reference (foreign key) to the user that have payed the fee. The "Year" column tells which year the user have payed the fee for. If a user has been a member for several years, there will be multiple rows for the user in the MemberFeePayments table.
Now I would like to write an sql query that fetches ALL users. The resulting table should have a column that tells if the users are members in a specific year (which will be provided to the query):
Result
Id (int)
Name (string)
IsMember (bool)
What is a good way to accomplish this?
I'm using MySql (5.6).
This should work if there is only one Fee Payment record per year:
SELECT
U.Id, U.Name,
CASE WHEN M.ID IS NULL THEN 0 ELSE 1 END AS `IsMember`
FROM Users U
LEFT JOIN MemberFeePayments M ON (U.Id = M.User AND M.Year = 2013)
This will work if you have multiple payments in year:
SELECT
U.Id, U.Name,
CASE WHEN COUNT(M.ID) > 0 THEN 1 ELSE 0 END AS `IsMember`
FROM Users U
LEFT JOIN MemberFeePayments M ON (U.Id = M.User AND M.Year = 2013)
GROUP BY U.Id, U.Name;
Here's a link to a demo
Try this:
SELECT a.Id,a.Name,if(b.id>0,TRUE,FALSE) as ismember FROM USERS a LEFT JOIN MemberFeepayments b ON a.id=b.id
check column fields and table name
Here is a way you can do it. So it will lit all the users no matter they are in the MemberFeePayments table or not and will show Yes if they are member for a particular year else No.
select
x.Id,
x.Name,
case when x.member is not null then 'Yes' else 'No' end as IsMember
from
(
select
u.Id,
u.Name,
m.User as member
from Users u
left join MemberFeePayments m on m.User = u.Id AND m.year = 2014
)x
OR simply as below without doing the outer select from derived table.
select
u.Id,
u.Name,
case when m.User IS NOT NULL THEN 'Yes' else 'No' end as IsMember
from Users u
left join MemberFeePayments m on m.User = u.Id AND m.year = 2014
DEMO
Use left join
SELECT t1.id,t1.Name,t2.id
FROM Users t1
LEFT JOIN MemberFeePayments t2 ON t1.id = t2.user and t2.year = 2014
t2.id will be null if there are no data in MemberFeePayments for that user and year

Adding a Subquery to a Query in SQL

I have a query that works very well. Let me start with it:
Edit: The SQL has been updated. I get 0 in every row.
SELECT i.item, i.user_id, u.username,
(COALESCE(r.ratetotal, 0)) AS total,
(COALESCE(c.commtotal, 0)) AS comments,
(COALESCE(r.rateav, '50%')) AS rate,
(COALESCE(x.wasRated, '0')) AS wasRated
FROM items AS i
LEFT JOIN master_cat AS c
ON (c.cat_id = i.cat_id)
LEFT JOIN users AS u
ON u.user_id = i.user_id
LEFT JOIN
(SELECT item_id,
COUNT(item_id) AS ratetotal,
AVG(rating) AS rateav
FROM ratings GROUP BY item_id) AS r
ON r.item_id = i.item_id
LEFT JOIN
(SELECT item_id,
COUNT(item_id) AS commtotal
FROM reviews GROUP BY item_id) AS c
ON c.item_id = i.item_id
LEFT JOIN
(SELECT xu.user_id, ra.item_id, '1' AS wasRated
FROM users AS xu
LEFT JOIN ratings AS ra
ON ra.user_id = xu.user_id
WHERE xu.user_id = '1') AS x
ON x.user_id = u.user_id
AND x.item_id = r.item_id
WHERE c.category = 'Movies'
ORDER by i.item ASC;
I need to add one more function to it, where you see AS x
Basically, there are three tables here that are important. items, reviews and ratings. In the top portion you see there are subqueries that are taking statistics such as averages and totals for each item.
I need a final query that is tied to user_id, item_id and rate_id (in ratings). In the end result, where it list each item and the stats with it, I want one more column, a simple true or false if logged in user has rated it. So I need something like this:
SELECT ???
FROM ratings AS r
WHERE r.user_id = '{$user_id}'
(user_id of logged in user is passed in from PHP.`)
How can I make a subquery that gives me that last bit of info, but puts it in each row of items in the parent query?
Add this to the parent query.
, coalesce(x.WasRated, 'false') as WasRated
Your x subquery is:
(select users.user_id
, ratings.item_id
, 'true' WasRated
from users join ratings on user.user_id = ratings.user_id
where users.user_id = the one for the logged in user
) x on x.user_id = users.user_id
and x.item_id = ratings.item_id
or something like it.