How to use MAX function in MySQL with the having clause - mysql

I am working with the employees table in SQL and I would like to fetch the data for max count of employees
SELECT (COUNT(emp_no)) AS emp_count, dept_no
FROM
dept_emp
GROUP BY dept_no
HAVING COUNT(emp_no) = (SELECT MAX(COUNT(emp_no)) FROM dept_emp)
ORDER BY emp_count DESC
So far this is what I have got but this results in an error saying 'Invalid use of group function'. There is another approach I followed by making a table first and then using the having clause but what would be the correct code in the above approach?

You don't have to use the having at all. The query as it is without the having will bring you all the departments with the number of employees at each one. The one with the most employees in the first row. If you want only that one you can add limit 1 at the end of the query.

You can't not use an aggregation over an aggretation, MAX(COUNT()) is invalid
SELECT (COUNT(emp_no)) AS emp_count, dept_no
FROM dept_emp
GROUP BY dept_no
HAVING COUNT(emp_no) = (
SELECT MAX(count_result) FROM (SELECT COUNT(emp_no) as count_result FROM dept_emp) as count_table
)
ORDER BY emp_count DESC
Side notes:
I think there is a missing WHERE in the subquery as the result will be always the same, as we are getting the MAX of a COUNT to a unfiltered table dept_emp
I think the MAX(COUNT()) is irrelevant in the subquery, since you can just order by the count and limit by one, for example SELECT COUNT(id) FROM foo ORDER BY COUNT(id) DESC LIMIT 1
If you can avoid the subqueries, databases are incredible slow understanding subqueries, if you are curious prepend EXPLAIN to the sql statement, and see what mysql does for it
Edit: If you provide the output of SHOW CREATE TABLE table_name for the tables that are involved in employee counting, I can give you the WHERE that you have to write in the subquery

Related

MySQL Subquery from ASC order to DESC order

select *, sum(sales_qty) as total_qty
from sales_details
left join sales on sales.salesid=sales_details.salesid
where month(sales.sales_date)='$m'
group by productid order by total_qty asc limit 2, 4
I have that sql that is sorted in ascending order. Now I want the results to be sorted in descending order. I have tried using subquery but doesn't work for me. Any help or suggestion is appreciated.
This is the sql that I have tried:
select * from (
select *, sum(sales_qty) as total_qty
from sales_details
left join sales on sales.salesid=sales_details.salesid
where month(sales.sales_date)='$m'
group by productid
order by total_qty asc
limit 2, 4
) as sub
order by sum(sales_qty) desc
Your query with the subquery should end
... ORDER BY sales_qty DESC
Why? When you say ... ORDER BY SUM(sales_qty) DESC you're converting the outer query into an aggregate query. Because that outer aggregate query has no GROUP BY clause, it necessarily has a one row result set.
The outer query treats the inner query as if it were a table, a virtual table. That virtual table has a column named sales_qty. It is the value by which you want to order.
Pro tip: Don't use SELECT * in aggregate queries. You're misusing the notorious nonstandard MySQL extension to GROUP BY, which means your result set contains unpredictable values. Read this. https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html

MySQL 5.7 How to do GROUP BY with sorting?

Similar to this issue: MySQL 5.7 group by latest record
I'm not sure how to do this properly in 5.7. Also with possibility of 2nd sort column. Working query in 5.6 that I'm trying to replicate in 5.7:
SELECT id FROM test
GROUP BY category
ORDER BY sort1 DESC, sort2 DESC
id is not always the highest, so MAX(id) does not work.
Looking into the link above, the solution for single sort should be:
SELECT t1.*
FROM test t1
INNER JOIN (
SELECT category, max(sort) AS sort FROM test GROUP BY category
) t2 ON t2.category = t1.category AND t2.sort = t1.sort
But how will it work with 2 sorting?
You are using GROUP BY the wrong way.
Think of group by as a way to separate data row into different groups. Each group has multiple rows, based on the value of group by column.
Once you get those groups, selecting table columns (as in: select *) is like picking any row from that group randomly. This is not helpful nor useful.
Usually once we group records (or rows), we need to find meta information about those records. For example: get us the count of records in that group (as in: select count(*)), or the sum of values of a specific column in that group (as in: select sum(price)), or get the min, max or avg values.
So in a nutshell, when you use group by you should use on of the aggregation functions with it, otherwise it's not going to do you any good.
Why don't you have the ORDER BY at your outer query, instead?
SELECT *
FROM (
SELECT 100 AS id, 1 AS category, NULL AS sort
UNION
SELECT 200 AS id, 1 AS category, 2 AS sort
) dt
GROUP BY category
ORDER BY sort DESC;
It seems that what happened to the data when it was grouped, it took the first data while neglecting the ORDER BY DESC. On your first query, it ordered descending first then group by took the first record which is 200. And yes, this shouldn't be the way you should use GROUP BY. It is used in conjunction with aggregate functions.
when you select a column in a group by query that is not one of the columns you are grouping by, (ie, your id) you have no control over the value unless you use another aggregate function. If you want to sort, use MIN or MAX:
SELECT MAX(id), category, FROM `test2`
GROUP BY category; -- always returns 200
SELECT MIN(id), category, FROM `test2`
GROUP BY category; -- always returns 100

GROUP BY in subquery to get accurate ranking

I'm trying to get the rank of a particular lap time of a specific track owned by a particular user.
There are multiple rows (laps) in this table for a specific user. So I'm trying to GROUP BY as seen in the subquery of FIND_IN_SET.
Right now MySQL (latest version) is complaining that my session_id,user_id,track_id,duration are not aggregated for the GROUP BY.
Which I don't understand why its complaining about this since the GROUP BY is in a subquery.
session_lap_times schema:
session_id, int
user_id, int
track_id, int
duration, decimal
This is what I've got so far.
SELECT
session_id
user_id,
track_id,
duration,
FIND_IN_SET( duration,
(SELECT GROUP_CONCAT( duration ORDER BY duration ASC ) FROM
(SELECT user_id,track_id,min(duration)
FROM session_lap_times GROUP BY user_id,track_id) AS aa WHERE track_id=s1.track_id)
) as ranking
FROM session_lap_times s1
WHERE user_id=1
It seems like its trying to enforce the group by rules on the parent queries as well.
For reference, this is the error I'm getting: http://imgur.com/a/ILufE
Any help is greatly appreciated.
If I'm not mistaken, the problem is here (broken out for clarity):
SELECT user_id,track_id,any_value(duration)
FROM session_lap_times
GROUP BY user_id
The query is probably barfing because track_id is in the select and not in the group by. That means the subselect doesn't stand on its own and makes the whole thing fail.
Try adding track_id to your group by and adjust from there.
You are grouping by user_id but you do not do any aggregation in select or having in the following sub-query
SELECT
user_id,any_value(track_id),any_value(duration)
FROM session_lap_times GROUP BY user_id
You are using GROUP_CONCAT in a wrong context in the following sub-query because you do not group any column in ranking temporary table.
(SELECT GROUP_CONCAT( duration ORDER BY duration ASC ) FROM
(SELECT user_id,track_id,any_value(duration)
FROM session_lap_times GROUP BY user_id,track_id) AS aa WHERE track_id=s1.track_id)
) as ranking

In MySQL, what does this simple nested SELECT clause mean exactly?

Here is the example from this thread:
select (
select distinct Salary from Employee order by Salary Desc limit 1 offset 1
)as second;
The select(...) as second looks confusing to me because I've never seen a query-set instead of column names can be used as the argument of SELECT..
Does anyone have ideas about how to understand nested select clause like this? Is there any tutorials about this feature?
That's a subquery in the SELECT list of a query.
To get there, let's look at some other examples
SELECT t.id
, 'bar' AS foo
FROM mytable
WHERE ...
LIMIT ...
'bar' is just a string literal that gets returned in every row (in a column named foo) in the resultset from the query.
Also, MySQL allows us to run a query without a FROM clause
SELECT 'fee' AS fum
We can also put a subquery in the SELECT list of a query. For example:
SELECT t.id
, (SELECT r.id FROM resorts r ORDER BY r.id ASC LIMIT 1) AS id
FROM mytable
WHERE ...
LIMIT ...
The query pattern you asked about is a SELECT statement without a FROM clause
And the only expression being returned is the result from a subquery.
For example:
SELECT e.salary
FROM Employee e
GROUP BY e.salary
ORDER BY e.salary DESC
LIMIT 4,1
If this query runs, it will return one column, and will return either one or zero rows. (No more than one.) This satisfies the requirements for a subquery used in a SELECT list of another query.
SELECT ( subquery ) AS alias
With that, the outer query executes. There's no FROM clause, so MySQL returns one row. The resultset is going to consist of one column, with a name of "alias".
For each row returned by the outer query, MySQL will execute the subquery in the SELECT list. If the subquery returns a row, the value of the expression in the SELECT list of the subquery is assigned to the "alias" column of the resultset. If the execution of the subquery doesn't return a row, then MySQL assigns a NULL to the "alias" column.

Nested SQL Query

I have problems with the following SQL Query:
SELECT job
FROM (SELECT job, COUNT(*) AS cnt
FROM Employee
GROUP BY job)
WHERE cnt=1
As Result it should only shows all jobs where cnt (count of jobs) equals 1.
When I test the select query above on Fiddle, I get following error :
Incorrect syntax near the keyword 'WHERE'.
SQLFiddle: http://sqlfiddle.com/#!6/d812a/7
No need to increase complexity by using sub-query when it is not require
SELECT job, count(job)
FROM Employee
GROUP BY job
having count(job)=1;
You need to provide alias name to the nested query
SELECT A.job
FROM (SELECT job, COUNT(*) AS cnt
FROM Employee
GROUP BY job)A
WHERE A.cnt=1
You forget to add the alias name.
Please change the query like this
SELECT job
FROM (SELECT job, COUNT(*) AS cnt
FROM Employee
GROUP BY job) As MyTable
WHERE cnt=1
You should have to give the alias name for the inner query when you are using the select and where clauses outside.
You should use the HAVING syntax :
SELECT job, COUNT(*) AS cnt
FROM Employee
GROUP BY job
HAVING cnt = 1;
You should use the HAVING clause which is done for that kind of thing. Your request will be simply :
SELECT job FROM Employee GROUP BY job
HAVING COUNT(id)=1
The documentation states that
The SQL standard requires that HAVING must reference only columns in
the GROUP BY clause or columns used in aggregate functions. However,
MySQL supports an extension to this behavior, and permits HAVING to
refer to columns in the SELECT list and columns in outer subqueries as
well.
The important thing to note is that contrary to the WHERE clause, you can use aggregate funcitons (like count, max, min ...) in the HAVING clause.