I have a query where I'm selecting the number of users that have submitted to each application.
SELECT c.*, count(p.id) people
FROM users p
LEFT JOIN apps c
ON c.id = p.app
WHERE c.status='1'
GROUP BY p.app
ORDER BY c.id DESC
However, I only want to select the ones that have less than 50 people. How can I do this?
I tried WHERE c.status='1' AND people < 50 but it didn't work it said it's an invalid column. How can I only select the values where people is less than 50?
Thanks
use HAVING. It behaves like WHERE but its use is for aggregated columns.
SELECT c.*, count(p.id) people
FROM users p
LEFT JOIN apps c
ON c.id = p.app
WHERE c.status='1'
GROUP BY p.app
HAVING count(p.id) < 50
ORDER BY c.id DESC
Related
I want to group order's count to show how many clients have that number of orders.
I have come up with:
select count(*) as quantidade_pedidos, clientes.id
from pedidos
inner join clientes
on pedidos.cliente_id = clientes.id
where pedidos.aprovado = 1
group by quantidade_pedidos
but I just can't group by 'quantidade_pedidos' anyway.
Is there any way to group by a temporary column? Another way of doing this? show how many clients (number) have that number of orders placed?
Example
8 orders placed -> 3 clients have 8 orders placed
etc
Your original query is wrong. You need to group by clientes.id:
select count(*) as quantidade_pedidos, c.id
from pedidos p inner join
clientes c
on p.cliente_id = c.id
where p.aprovado = 1
group by c.id;
In an aggregation query, the unaggregated columns go in the group by, not the aggregated ones.
Also note that table aliases make the query easier to write and to read.
As for the question in the first line, use a subquery:
select quantidade_pedidos, count(*)
from (select count(*) as quantidade_pedidos, c.id
from pedidos p inner join
clientes c
on p.cliente_id = c.id
where p.aprovado = 1
group by c.id
) x
group by quantidade_pedidos;
But given that the query in the question doesn't work, I'm not sure this is what you really want to do.
I've already read every post with the similarly title but didn't find right answer.
What I really need to do is to count some data from MySQL table and then do group_concat because I got more than one row.
My table looks like this
and here is how I tried to run the query
SELECT
count(cal.day) * 8,
w.name
FROM claim as c
RIGHT JOIN calendar as cal ON c.id = cal.claim_id
RIGHT JOIN worker as w ON c.worker_id = w.id
GROUP BY c.id
ORDER BY w.name asc
But I get for some workers more than one row and I can't group_concat them because of count(). I need this for mysql procedure I've making so please help me if you can.
I hope I've gived you enough informations
Edit for Dylan:
See the difference in output
GROUP BY w.id
GROUP BY c.id
MySQL does'not allow two aggregate functions used together, like GROUP_CONCAT(COUNT(...)).
Therefore, we can use a sub-query to work around as below.
SELECT
GROUP_CONCAT(t.cnt_cal_day) as cnt_days,
t.name
FROM
(
SELECT
count(cal.day) * 8 as cnt_cal_day,
w.name
FROM claim as c
RIGHT JOIN calendar as cal ON c.id = cal.claim_id
RIGHT JOIN worker as w ON c.worker_id = w.id
GROUP BY c.id
ORDER BY w.name asc
) t
While the question is still not clear for me, I try to guess what you need.
This query:
SELECT
w.name,
COUNT(cal.day) * 8 AS nb_hours
FROM worker w
LEFT JOIN claim c ON w.id = c.worker_id
INNER JOIN calendar cal ON c.id = cal.claim_id
GROUP BY w.id
ORDER BY w.name ASC
returns the names of all workers and, for each one, the number of hours of vacation approved for them.
If you use LEFT JOIN calendar instead you will get the number of hours of vacation claimed by each worker (approved and not approved). In order to separate them you should make the query like this:
SELECT
w.name,
c.approved, # <---- I assumed the name of this field
COUNT(cal.day) * 8 AS nb_hours
FROM worker w
LEFT JOIN claim c ON w.id = c.worker_id
LEFT JOIN calendar cal ON c.id = cal.claim_id
GROUP BY w.id, c.approved
ORDER BY w.name ASC
This query should return 1 or 2 rows for each worker, depending on the types of vacation claims they have (none, approved only, not approved only, both). For workers that don't have any vacation claim, the query returns NULL in column approved and 0 in column nb_hours.
I have .6 million data set. Now I am trying to sort the data set by timestamp, and also due to one to many relationship I have to do some Inner JOIN and use distinct.
My Query is like below,
SELECT DISTINCT p.id, s.subject, p.joining_time
FROM profile p
INNER JOIN profile_subject ps ON p.id=ps.profile_id
LEFT JOIN subject s ON ps.subject_id=s.id
ORDER BY p.joining_time LIMIT 20;
Now this query is taking almost 28 sec
But without order by clause its taking only 0.11 sec
How to improve this query with desired result?
My simplest suggestion is to put an index on profile(joining_time). Then select a certain number of the most recent in a subquery. For instance, if you are pretty confident that the top 20 rows you want are within the most recent 100 records in profile, then you can try this:
SELECT DISTINCT p.id, s.subject, p.joining_time
FROM (SELECT p.id, p.joining_join
FROM profile p
ORDER BY p.joining_time
LIMIT 100
) p INNER JOIN
profile_subject ps
ON p.id = ps.profile_id LEFT JOIN
subject s
ON ps.subject_id = s.id
ORDER BY p.joining_time
LIMIT 20;
I would also suggest that you remove the DISTINCT keyword. Unless you have duplicate subjects for one profile, then this is not necessary. Similarly, it is hard to believe that the LEFT JOIN is necessary. In a well-structured database, there would be no subject_id values in profile_subject that are not in subject. So, try this:
SELECT p.id, s.subject, p.joining_time
FROM (SELECT p.id, p.joining_join
FROM profile p
ORDER BY p.joining_time
LIMIT 100
) p INNER JOIN
profile_subject ps
ON p.id = ps.profile_id JOIN
subject s
ON ps.subject_id = s.id
ORDER BY p.joining_time
LIMIT 20;
I have this php/sql Query:
$result = mysql_query("
SELECT r.item_id, AVG(rating) AS avgrating, count(rating) AS count, i.item, c.category
FROM ratings AS r
LEFT JOIN items AS i
ON r.item_id = i.items_id
INNER JOIN master_cat c
ON c.cat_id = i.cat_id
GROUP BY item_id
ORDER BY avgrating DESC
LIMIT 25;");
When I output this, count is correct, it shows how much votes certain items have received.
I simply want to add a WHERE count >= 10 clause but everything breaks. Obviously, when there are thousands of items, some will get one vote and have 100%. But that is not a good indicator. I want to print out items that have at least 10 votes (or count >= 10)
You should to use having instead where
SELECT
r.item_id, AVG(rating) AS avgrating,
count(rating) AS count, i.item, c.category
FROM
ratings AS r
LEFT JOIN items AS i
ON r.item_id = i.items_id
INNER JOIN master_cat c
ON c.cat_id = i.cat_id
GROUP BY
item_id
HAVING
count >= 10
ORDER BY
avgrating DESC
LIMIT 25;
You can't use a where filter on the results of an aggregate function (count()). where is applied at the row-level, as the DB is deciding whether to include the row or not in the result set - at this point the results of the count aren't available yet.
What you want is a having clause, which is applied as one of the last steps before results are sent to the client, after all the aggregate results have been calculated.
...
GROUP BY item_id
HAVING count > 10
ORDER BY ...
you need to tell it what you want to count
having count(*) > 10
I have three tables tl_log, tl_geo_countries,tl_purpose. I am trying to get the count of number of days spent in each country in table 'tl_log' for each purpose in table 'tl_purpose'.
I tried below mysql query
SELECT t.country_id AS countryID,t.reason_id AS reasonID,count(t.reason_id) AS
days,c.name AS country, p.purpose AS purpose
FROM `tl_log` AS t
LEFT JOIN tl_geo_countries AS c ON t.country_id=c.id
LEFT JOIN tl_purpose AS p ON t.reason_id=p.id
GROUP BY t.reason_id,t.country_id ORDER BY days DESC
But landed up with.
I am not able to get the count for purpose for each country in 'tl_log' that is not present in table 'tl_log'. Any help is greatly appreciated. Also, Please let me know if the question is difficult to understand.
Expected Output:
Below is the structure of these three tables
tl_log
tl_geo_countries
tl_purpose
If you want all possible combination of countries and purposes, even those that do not appear on the log table (these will be shown with a count of 0), you can do first a cartesian product of the two tables (a CROSS join) and then LEFT join to the log table:
SELECT
c.id AS countryID,
p.id AS reasonID,
COUNT(t.reason_id) AS days,
c.name AS country,
p.purpose AS purpose
FROM
tl_geo_countries AS c
CROSS JOIN
tl_purpose AS p
LEFT JOIN
tl_log AS t
ON t.country_id = c.id
AND t.reason_id = p.id
GROUP BY
p.id,
c.id
ORDER BY
days DESC ;
If you want the records for only the countries that are present in the log table (but still all possible reason/purposes), a slight modification is needed:
SELECT
c.id AS countryID,
p.id AS reasonID,
COUNT(t.reason_id) AS days,
c.name AS country,
p.purpose AS purpose
FROM
( SELECT DISTINCT
country_id
FROM
tl_log
) AS dc
JOIN
tl_geo_countries AS c
ON c.id = dc.country_id
CROSS JOIN
tl_purpose AS p
LEFT JOIN
tl_log AS t
ON t.country_id = c.id
AND t.reason_id = p.id
GROUP BY
p.id,
c.id
ORDER BY
days DESC ;
LEFT JOIN should be replaced by RIGHT JOIN