Mysql return 0 when i use count - mysql

I have this table:
Notifications
+-------+--------+--------+
| id |category| notify |
+-------+--------+--------+
| 1 |products| yes|
| 2 |web | no |
| 3 |clients | yes|
| 4 |clients | yes|
+-------+--------+--------+
I need this result:
Category | Count |
products | 1|
web | 0|
clients | 2|
I tried
SELECT count(*), category
FROM Notifications
WHERE notify = "yes"
GROUP BY category
but I get
Category | Count |
products | 1|
clients | 2|
Thanks for any help.

One method to get all categories in Notifications is to use conditional aggregation:
SELECT n.category, sum(n.notify = 'no')
FROM Notifications n
GROUP BY n.category;
If you have a separate table of categories, then you can get all categories (even those without notifications) using left join:
select c.category, count(n.category)
from categories c left join
notifications n
on c.category = n.category and n.notify = 'no'
group by c.category;

And, you can also use the ubiquitous case statement to perform conditional aggregation.
Select category,
Sum(case when Notify = 'Yes' then 1 else 0 end) count,
FROM Notifications
GROUP BY category

SELECT count(*) AS no_category FROM Notifications
WHERE notify="no" GROUP BY category;
With this the count value will be stored inside no_category. Using PHP, you can
echo $row["no_category"];
And on the MySQL console, the query will generate a table in the form
no_category
1

Related

How to exclude rows when a certain condition is met in MySQL

I have two datatables. One table contains information of participants (Participants). And a table which contains all registrations for the event (Registrars).
+----+-------+
| id | name |
+----+-------+
| 1 | Peter |
+----+-------+
| 2 | John |
+----+-------+
+-----------+----------+
| person_id | event_id |
+-----------+----------+
| 1 | 1 |
+-----------+----------+
| 2 | 2 |
+-----------+----------+
| 2 | 1 |
+-----------+----------+
I run a MySQL query to get the information of registrars of an event. The query looks like this:
SELECT name
FROM Participants
INNER JOIN Registrars
ON Participants.id=Registrars.person_id
WHERE
event_id = 1
Now I want to build a query where if the participant also registered for event 2 it will not be returned in the result of the query the reg. How can I achieve this in one query?
You can group by participant and use conditional aggregation in the HAVING clause to set the conditions:
SELECT p.id, p.name
FROM Participants p INNER JOIN Registrars r
ON p.id = r.person_id
WHERE r.event_id IN (1, 2)
GROUP BY p.id, p.name
HAVING MAX(r.event_id = 1) = 1
AND MAX(r.event_id = 2) = 0
You can extend the code by adding more conditions in a similar way.
SELECT name
FROM Participants as p
INNER JOIN Registrars as reg
ON P.id=Reg.person_id
WHERE
reg.event_id = 1
and not exists (select 1 from Registrars as strr where strr.event_id=2 and p.id=strr.person_id )

MySQL GROUP_CONCAT with SUM() and multiple JOINs inside subquery

I'm very average with MySQL, but usually I can write all the needed queries after reading documentation and searching for examples. Now, I'm in the situation where I spent 3 days re-searching and re-writing queries, but I can't get it to work the exact way I need. Here's the deal:
1st table (mpt_companies) contains companies:
| company_id | company_title |
------------------------------
| 1 | Company A |
| 2 | Company B |
2nd table (mpt_payment_methods) contains payment methods:
| payment_method_id | payment_method_title |
--------------------------------------------
| 1 | Cash |
| 2 | PayPal |
| 3 | Wire |
3rd table (mpt_payments) contains payments for each company:
| payment_id | company_id | payment_method_id | payment_amount |
----------------------------------------------------------------
| 1 | 1 | 1 | 10.00 |
| 2 | 2 | 3 | 15.00 |
| 3 | 1 | 1 | 20.00 |
| 4 | 1 | 2 | 10.00 |
I need to list each company along with many stats. One of stats is the sum of payments in each payment method. In other words, the result should be:
| company_id | company_title | payment_data |
--------------------------------------------------------
| 1 | Company A | Cash:30.00,PayPal:10.00 |
| 2 | Company B | Wire:15.00 |
Obviously, I need to:
Select all the companies;
Join payments for each company;
Join payment methods for each payment;
Calculate sum of payments in each method;
GROUP_CONCAT payment methods and sums;
Unfortunately, SUM() doesn't work with GROUP_CONCAT. Some solutions I found on this site suggest using CONCAT, but that doesn't produce the list I need. Other solutions suggest using CAST(), but maybe I do something wrong because it doesn't work too. This is the closest query I wrote, which returns each company, and unique list of payment methods used by each company, but doesn't return the sum of payments:
SELECT *,
(some other sub-queries I need...),
(SELECT GROUP_CONCAT(DISTINCT(mpt_payment_methods.payment_method_title))
FROM mpt_payments
JOIN mpt_payment_methods
ON mpt_payments.payment_method_id=mpt_payment_methods.payment_method_id
WHERE mpt_payments.company_id=mpt_companies.company_id
ORDER BY mpt_payment_methods.payment_method_title) AS payment_data
FROM mpt_companies
Then I tried:
SELECT *,
(some other sub-queries I need...),
(SELECT GROUP_CONCAT(DISTINCT(mpt_payment_methods.payment_method_title), ':', CAST(SUM(mpt_payments.payment_amount) AS CHAR))
FROM mpt_payments
JOIN mpt_payment_methods
ON mpt_payments.payment_method_id=mpt_payment_methods.payment_method_id
WHERE mpt_payments.company_id=mpt_companies.company_id
ORDER BY mpt_payment_methods.payment_method_title) AS payment_data
FROM mpt_companies
...and many other variations, but all of them either returned query errors, either didn't return/format data I need.
The closest answer I could find was MySQL one to many relationship: GROUP_CONCAT or JOIN or both? but after spending 2 hours re-writing the provided query to work with my data, I couldn't do it.
Could anyone give me a suggestion, please?
You can do that by aggregating twice. First for the sum of payments per method and company and then to concatenate the sums for each company.
SELECT x.company_id,
x.company_title,
group_concat(payment_amount_and_method) payment_data
FROM (SELECT c.company_id,
c.company_title,
concat(pm.payment_method_title, ':', sum(p.payment_amount)) payment_amount_and_method
FROM mpt_companies c
INNER JOIN mpt_payments p
ON p.company_id = c.company_id
INNER JOIN mpt_payment_methods pm
ON pm.payment_method_id = p.payment_method_id
GROUP BY c.company_id,
c.company_title,
pm.payment_method_id,
pm.payment_method_title) x
GROUP BY x.company_id,
x.company_title;
db<>fiddle
Here you go
SELECT company_id,
company_title,
GROUP_CONCAT(
CONCAT(payment_method_title, ':', payment_amount)
) AS payment_data
FROM (
SELECT c.company_id, c.company_title, pm.payment_method_id, pm.payment_method_title, SUM(p.payment_amount) AS payment_amount
FROM mpt_payments p
JOIN mpt_companies c ON p.company_id = c.company_id
JOIN mpt_payment_methods pm ON pm.payment_method_id = p.payment_method_id
GROUP BY p.company_id, p.payment_method_id
) distinct_company_payments
GROUP BY distinct_company_payments.company_id
;

SQL left join: how to return the newest from tableB and grouped by another field

I've been trying for two days, without luck.
I have the following simplified tables in my database:
customers:
| id | name |
| 1 | andrea |
| 2 | marco |
| 3 | giovanni |
access:
| id | name_id | date |
| 1 | 1 | 5000 |
| 2 | 1 | 4000 |
| 3 | 2 | 1500 |
| 4 | 2 | 3000 |
| 5 | 2 | 1000 |
| 6 | 3 | 6000 |
| 7 | 3 | 2000 |
I want to return all the names with their last access date.
At first I tried simply with
SELECT * FROM customers LEFT JOIN access ON customers.id =
access.name_id
But I got 7 rows instead of 3 as expected. So I understood I need to use GROUP BY statemet as the following:
SELECT * FROM customers LEFT JOIN access ON customers.id =
access.name_id GROUP BY customers.id
As far I know, GROUP BY combines using a random row. In fact I got unordered access dates with several tests.
Instead I need to group every customer id with its corresponding latest access! How this can be done?
You have to get the latest date from the access table with a group by on the the name_id, then join this result with the customer table. Here is the query:
select c.id, c.name, a.last_access_date from customers c left join
(select id, name_id, max(access_date) last_access_date from access group by name_id) a
on c.id=a.name_id;
Here is a DEMO on sqlfiddle.
I think this is what you'd like to achieve:
SELECT c.id, c.name, max(a.date) last_access
FROM customers c
LEFT JOIN access a ON c.id = a.name_id
GROUP BY c.id, c.name
The LEFT join will return all entries in table customers regardless if the join criteria (c.id = a.name_id) is satisfied. This means that you might get some NULL entries.
Example:
Simply add a new row in the customers table (id: 4, name: manuela). The output will have 4 rows and the newest row will be (id: 4, last_access: null)
I would do this using a correlated subquery in the ON clause:
SELECT a.*, c.*
FROM customers c LEFT JOIN
access a
ON c.id = a.name_id AND
a.DATE = (SELECT MAX(a2.date) FROM access a2 WHERE a2.name_id = a.name_id);
If this statement is true:
I need to group every customer id with its corresponding latest access! How this can be done?
Then you can simply do:
select a.name_id, max(a2.date)
from access a
group by a.name_id;
You do not need the customers table because:
All customers are in access, so the left join is not necessary.
You need no columns from customers.

MySQL SELECT Multiple DISTINCT COUNT

Here is what I'm trying to do. I have a table with user assessments which may contain duplicate rows. I'm looking to only get DISTINCT values for each user.
In the example of the table below. If only user_id 1 and 50 belongs to the specific location, then only the unique video_id's for each user should be returned as the COUNT. User 1 passed video 1, 2, and 1. So that should only be 2 records, and user 50 passed video 2. So the total for this location would be 3. I think I need to have two DISTINCT's in the query, but am not sure how to do this.
+-----+----------+----------+
| id | video_id | user_id |
+-----+----------+----------+
| 1 | 1 | 1 |
| 2 | 2 | 50 |
| 3 | 1 | 115 |
| 4 | 2 | 25 |
| 5 | 2 | 1 |
| 6 | 6 | 98 |
| 7 | 1 | 1 |
+-----+----------+----------+
This is what my current query looks like.
$stmt2 = $dbConn->prepare("SELECT COUNT(DISTINCT user_assessment.id)
FROM user_assessment
LEFT JOIN user ON user_assessment.user_id = user.id
WHERE user.location = '$location'");
$stmt2->execute();
$stmt2->bind_result($video_count);
$stmt2->fetch();
$stmt2->close();
So my query returns all of the count for that specific location, but it doesn't omit the non-unique results from each specific user.
Hope this makes sense, thanks for the help.
SELECT COUNT(DISTINCT ua.video_id, ua.user_id)
FROM user_assessment ua
INNER JOIN user ON ua.user_id = user.id
WHERE user.location = '$location'
You can write a lot of things inside a COUNT so don't hesitate to put what you exactly want in it. This will give the number of different couple (video_id, user_id), which is what you wanted if I understood correctly.
The query below joins a sub-query that fetches the distinct videos per user. Then, the main query does a sum on those numbers to get the total of videos for the location.
SELECT
SUM(video_count)
FROM
user u
INNER JOIN
( SELECT
ua.user_id,
COUNT(DISTINCT video_id) as video_count
FROM
user_assessment ua
GROUP BY
ua.user_id) uav on uav.user_id = u.user_id
WHERE
u.location = '$location'
Note, that since you already use bindings, you can also pass $location in a bind parameter. I leave this to you, since it's not part of the question. ;-)

mysql select top unique values with inner join

I have 2 tables that look like this:
users (uid, name)
-------------------
| 1 | User 1 |
| 2 | User 2 |
| 3 | User 3 |
| 4 | User 4 |
| 5 | User 5 |
-------------------
highscores (user_id, time)
-------------------
| 3 | 12005 |
| 3 | 29505 |
| 3 | 17505 |
| 5 | 19505 |
-------------------
I want to query only for users that have a highscore and only the top highscore of each user. The result should look like:
------------------------
| User 3 | 29505 |
| User 5 | 19505 |
------------------------
My query looks like this:
SELECT user.name, highscores.time
FROM user
INNER JOIN highscores ON user.uid = highscores.user_id
ORDER BY time ASC
LIMIT 0 , 10
Actually this returns multiple highscores of the same user. I also tried to group them but it did not work since it did not return the best result but a random one (eg: for user id 3 it returned 17505 instead of 29505).
Many thanks!
You should use the aggregated function MAX() together with group by clause.
SELECT a.name, MAX(b.`time`) maxTime
FROM users a
INNER JOIN highscores b
on a.uid = b.user_id
GROUP BY a.name
SQLFiddle Demo
Your effort of grouping users was correct. You just needed to use MAX(time) aggregate function instead of selecting only time.
I think you wrote older query was like this:
SELECT name, time
FROM users
INNER JOIN highscores ON users.uid = highscores.user_id
GROUP BY name,time
But actual query should be:
SELECT user.name, MAX(`time`) AS topScore
FROM users
INNER JOIN highscores ON users.uid = highscores.user_id
GROUP BY user.name