Invalid use of group function in mysql - mysql

I've a table like this:
+----+------+--------+
| id | name | salary |
+----+------+--------+
| 1 | Ajay | 20000 |
| 2 | Aja | 2000 |
| 3 | Aj | 200 |
| 4 | A | 3000 |
| 5 | q | 30000 |
+----+------+--------+
I want to write a query that can print highest salary, medium salary and lowest salary. So I wrote this query:
select salary
from parent
where max(sal)
&& salary < ( SELECT MAX( salary )
FROM parent )
&& min(salary);
And mysql returned an error:
ERROR 1111 (HY000): Invalid use of group function
what is the correct query?

MySQL doesn't offer an aggregate function to grab a median value, sorry to say. Hopefully you can go with the average (the arithmetic mean) value.
Stuff like MAX(), MIN(), and AVG() (called aggregate functions in the jargon of SQL) can't appear in WHERE clauses. They can show up in SELECT and HAVING clauses. That's why you got an error.
You'll be wanting
SELECT MAX(salary) max_salary,
AVG(salary) avg_salary,
MIN(salary) min_salary
FROM parent
If you control your own MySQL server and you have a few MySQL dba chops, you can install a user-defined function to compute the median value. (If you're using a hosting service with a multitenant MySQL server, forget about this.) Read this.
http://mysql-udf.sourceforge.net/

select 'min' as k, min(salary) as v from parent
union
select 'avg' as k, avg(salary) as v from parent
union
select 'max' as k, max(salary) as v from parent

Related

how to find the nth highest number when values of the numbers are the same

SQL Question:
Write a SQL query to get the nth highest salary from the Employee table (SQL Server)
| Id | Salary |
+----+--------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
For this example, the nth highest salary where n = 2 is 200. If there is no nth highest salary, then the query should return null.
| getNthHighestSalary(2) |
+------------------------+
| 200 |
My code is the following:
CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
RETURN (
select distinct a.salary from Employee a, Employee b where
a.id =b.id-n+1 order by a.id desc
limit 1
);
END
My question is: The code works for most of the cases except for the situation when there are multiple same values before the nth place. For example, whe n is 2 and the table is the following. (The expected result should be NULL and my code returns 100. ) So, how can I change my code so that it can work for all situations? Thank you!
+----+--------+
| id | salary |
+----+--------+
| 1 | 100 |
| 2 | 100 |
| 3 | 100 |
+----+--------+
Try this
Select distinct salary
From (
Select salary, dense_rank() over
(order by salary desc) r
from employee
) where r=n
1st 2nd and 3rd highest salary query
SELECT MAX(salary) AS ThirdHighestSalary
FROM emp
WHERE (salary <
(SELECT MAX(salary) AS SecondHighestSalary
FROM emp
WHERE (salary <
(SELECT MAX(salary) AS HighestSalary
FROM emp))))

MySQL: Error: Operand should contain 1 column(s). What's wrong with my use of WHERE...NOT IN(SELECT...)?

Credit:Leetcode_1355. Activity Participants
Question:
Write an SQL query to find the names of all the activities with neither maximum, nor minimum number of participants.
Return the result table in any order. Each activity in table Activities is performed by any person in the table Friends.
Friends table:
+------+--------------+---------------+
| id | name | activity |
+------+--------------+---------------+
| 1 | Jonathan D. | Eating |
| 2 | Jade W. | Singing |
| 3 | Victor J. | Singing |
| 4 | Elvis Q. | Eating |
| 5 | Daniel A. | Eating |
| 6 | Bob B. | Horse Riding |
+------+--------------+---------------+
Activities table:
+------+--------------+
| id | name |
+------+--------------+
| 1 | Eating |
| 2 | Singing |
| 3 | Horse Riding |
+------+--------------+
Result table:
+--------------+
| activity |
+--------------+
| Singing |
+--------------+
My code is as follows:
WITH a AS(
SELECT activity, COUNT(1) AS n
FROM Friends
GROUP BY activity
)
SELECT activity
FROM a
WHERE n NOT IN (SELECT MAX(n),MIN(n) FROM a)
I have seen the success of using n != (select min(n) from a) and n != (select max(n) from a), but I did not know why my code went wrong. My guess is that it's because 'SELECT MAX(n), MIN(n) FROM a' will generate two columns, rather than two rows. While I still don't know the exact reason.
Hope someone can help me out! Thank you so much!
You are close. But NOT IN does work that way -- because the subquery returns multiple columns. And you are comparing to only one value. Instead, use two separate comparisons:
SELECT activity
FROM a
WHERE n <> (SELECT MAX(n) FROM a) AND
n <> (SELECT MIN(n) FROM a) ;
My guess is that it's because SELECT MAX(n), MIN(n) FROM a will generate two columns, rather than two rows.
Yes, that's the point. Other than using two subqueries (which you already found out by yourself), you can also take advantage of window functions here (the fact that you use a with clause indicates that you are running MySQL 8.0, which supports window functions):
select activity
from (
select
activity,
row_number() over(order by count(*) asc) rn_asc,
row_number() over(order by count(*) desc) rn_desc
from friends
group by activity
) t
where 1 not in (rn_asc, rn_desc)
I suspect that this performs better than a with clause and two subqueries.
Instead of using the subquery in WHERE, you can join with the subquery.
WITH a AS(
SELECT activity, COUNT(1) AS n
FROM Friends
GROUP BY activity
)
SELECT activity
FROM a AS a1
JOIN (SELECT MAX(n) AS maxn, MIN(n) AS minn) AS a2
ON a1.n NOT IN (a2.maxn, a2.minn)
You can use MIN() and MAX() window functions:
WITH cte AS (
SELECT activity,
COUNT(*) AS n,
MIN(COUNT(*)) OVER () min_n,
MAX(COUNT(*)) OVER () max_n
FROM Friends
GROUP BY activity
)
SELECT activity, n
FROM cte
WHERE n NOT IN (min_n, max_n)
See the demo.
Results:
| activity | n |
| -------- | --- |
| Singing | 2 |

Combining MySQL querys

This SQL tells me how much when the maximum occurred in the last hour, and is easily modified to show the same for the minimum.
SELECT
mt.mB as Hr_mB_Max,
mt.UTC as Hr_mB_Max_when
FROM
thundersense mt
WHERE
mt.mB =(
SELECT
MAX(mB)
FROM
thundersense mt2
WHERE
mt2.UTC >(UNIX_TIMESTAMP() -3600))
ORDER BY
utc
DESC
LIMIT 1
How do I modify it so it returns both maximum & minimum and their respective times?
Yours Simon M.
Based on my understanding of your question, you are looking to create a 4 column and 1 row answer where it looks like:
+-------+-----------------+----------+-----------------+
| event | time_it_occured | event | time_it_occured |
+-------+-----------------+----------+-----------------+
| fun | 90000 | homework | 12000 |
+-------+-----------------+----------+-----------------+
Below is a similar situation/queries you can adapt for your situation.
So, given a table called 'people' that looks like:
+----+------+--------+
| ID | name | salary |
+----+------+--------+
| 1 | bob | 40000 |
| 2 | cat | 12000 |
| 3 | dude | 50000 |
+----+------+--------+
You can use this query:
SELECT * FROM
(SELECT name, salary FROM people WHERE salary = (SELECT MAX(salary) FROM people)) t JOIN
(SELECT name, salary FROM people WHERE salary = (SELECT MIN(salary) FROM people)) a;
to generate:
+------+--------+------+--------+
| name | salary | name | salary |
+------+--------+------+--------+
| bob | 40000 | cat | 12000 |
+------+--------+------+--------+
Some things to note:
you can change the WHERE clauses to be the ones you have mentioned in question (for MAX and MIN).
Please be careful with the above query, here I am using a cartesian join (cross join in MYSQL) in order to get the 4 columns. To be honest, it doesn't make sense for me to get back data in one row but you said that's what you're looking for.
Here is what I would work with instead, getting two tuples/rows back:
+----------+--------+
| name | salary |
+----------+--------+
| dude | 95000 |
| Cat | 12000 |
+----------+--------+
And to generate this, you would use:
(SELECT name, salary FROM instructor WHERE salary = (SELECT MAX(salary) FROM instructor))
UNION
(SELECT name, salary FROM instructor WHERE salary = (SELECT MIN(salary) FROM instructor));
Also: A JOIN without a ON clause is just a CROSS JOIN.
How to use mysql JOIN without ON condition?
One method uses a join:
SELECT mt.mB as Hr_mB_Max, mt.UTC as Hr_mB_Max_when
FROM thundersense mt JOIN
(SELECT MAX(mB) as max_mb, MIN(mb) as min_mb
FROM thundersense mt2
WHERE mt2.UTC >(UNIX_TIMESTAMP() - 3600)
) mm
ON mt.mB IN (mm.max_mb, mm.min_mb)
ORDER BY utc DESC;
My only concern is your limit 1. Presumably, the mBs should be unique. If not, there is a bit of a challenge. One possibility would be to use an auto-incremented id rather than mB.

Using only agregate function on determined row

I have to use only aggregate function (so without checking exact values in columns, for determined row), when I select the determined IT worker's salary.
It isn't as simple as SELECT SUM(salary) FROM table WHERE occupation='IT worker' LIMIT <here stays which place the person has on the list>, 1, is it? I think that by using SUM command, we don't check exact values.
If this question isn't understandable, I can translate the whole exercise.
EDIT: the exercise
Using only agregate functions (without checking exact values in columns, for determined row) create a query, which lets us know about determined IT worker's salary. table contains:
id int
occupation varchar(50)
salary int
I am not going to write the queries, this is meant to get you started:
The table seems to have 3 fields: id, occupation, and salary.
Each row seems to correspond to a specific worker
Then you can use agregate functions to calculate stuff regarding different occupations (for example).
The structure for a query that uses agregate functions is like this:
Select <fields and agregate functions>
from table
where <filter records to consider for the query before aggregation>
group by <field having the same value, hence that can be grouped by>
having <optional condition evaluated after doing the aggregation>
Examples of agregate functions are Sum, Count, Avg.
The question is not clear but with the informatin you have you can calculate:
Number of workers with same occupaton.
Average salary.
Total salary income by occupation.
If we ignore department for now you could do something like this
MariaDB [sandbox]> select emp_no, salary from employees ;
+--------+--------+
| emp_no | salary |
+--------+--------+
| 1 | 20000 |
| 2 | 39500 |
| 3 | 50000 |
| 4 | 19500 |
| 5 | 10000 |
| 6 | 19500 |
| 7 | 40000 |
| 9 | NULL |
+--------+--------+
8 rows in set (0.00 sec)
MariaDB [sandbox]> select emp_no,salary,
-> concat(rank,' of ' ,obs) as Rank,
-> Position,
-> reltoavg realtivetoavg
-> from
-> (
-> select emp_no,salary ,
-> #rn:=#rn+1 as Rank,
-> (select count(*) from employees) as obs,
-> concat('There are ',
-> (Select count(*) from employees e1 where e1.SALARY > e.salary) , ' employees who earn more and ',
-> (Select count(*) from employees e1 where e1.SALARY < e.salary and salary is not null) , ' who earn less') Position,
-> (select avg(salary) from employees) avgsalary,
-> if (salary > (select avg(salary) from employees), 'Salary is above average', 'salary is below average') reltoavg
-> from employees e,(Select #rn:=0) r
-> where salary is not null
-> order by salary
-> ) s
-> where s.emp_no = 1
-> ;
+--------+--------+--------+---------------------------------------------------------+-------------------------+
| emp_no | salary | Rank | Position | realtivetoavg |
+--------+--------+--------+---------------------------------------------------------+-------------------------+
| 1 | 20000 | 4 of 8 | There are 3 employees who earn more and 3 who earn less | salary is below average |
+--------+--------+--------+---------------------------------------------------------+-------------------------+
1 row in set (0.00 sec)

sql - Why doesn't MAX() of SUM() work?

I am trying to understand why the SQL command of MAX(SUM(col)) gives the a syntax error. I have the two tables as below-:
+--------+--------+---------+-------+
| pname | rollno | address | score |
+--------+--------+---------+-------+
| A | 1 | CCU | 1234 |
| B | 2 | CCU | 2134 |
| C | 3 | MMA | 4321 |
| D | 4 | MMA | 1122 |
| E | 5 | CCU | 1212 |
+--------+--------+---------+-------+
Personnel Table
+--------+-------+----------+
| rollno | marks | sub |
+--------+-------+----------+
| 1 | 90 | SUB1 |
| 1 | 88 | SUB2 |
| 2 | 89 | SUB1 |
| 2 | 95 | SUB2 |
| 3 | 99 | SUB1 |
| 3 | 99 | SUB2 |
| 4 | 82 | SUB1 |
| 4 | 79 | SUB2 |
| 5 | 92 | SUB1 |
| 5 | 75 | SUB2 |
+--------+-------+----------+
Results Table
Essentially I have a details table and a results table. I want to find the name and marks of the candidate who has got the highest score in SUB1 and SUB2 combined. Basically the person with the highest aggregate marks.
I can find the summation of SUB1 and SUB2 for all candidates using the following query-:
select p.pname, sum(r.marks) from personel p,
result r where p.rollno=r.rollno group by p.pname;
It gives the following output-:
+--------+--------------+
| pname | sum(r.marks) |
+--------+--------------+
| A | 178 |
| B | 167 |
| C | 184 |
| D | 198 |
| E | 161 |
+--------+--------------+
This is fine but I need the output to be only D | 198 as he is the highest scorer. Now when I modify query like the following it fails-:
select p.pname, max(sum(r.marks)) from personel p,
result r where p.rollno=r.rollno group by p.pname;
In MySQL I get the error of Invaild Group Function.
Now searching on SO I did get my correct answer which uses derived tables. I get my answer by using the following query-:
SELECT
pname, MAX(max_sum)
FROM
(SELECT
p.pname AS pname, SUM(r.marks) AS max_sum
FROM
personel p, result r
WHERE
p.rollno = r.rollno
GROUP BY p.pname) a;
But my question is Why doesn't MAX(SUM(col)) work ?
I don't understand why max can't compute the value returned by SUM(). Now an answer on SO stated that since SUM() returns only a single value so MAX() find its meaningless to compute the value of one value, but I have tested the following query -:
select max(foo) from a;
on the Table "a" which has only one row with only one column called foo that holds an integer value. So if MAX() can't compute single values then how did this work ?
Can someone explain to me how the query processor executes the query and why I get the error of invalid group function ? From the readability point of view using MAX(SUM(col)) is perfect but it doesn't work out that way. I want to know why.
Are MAX and SUM never to be used together? I am asking because I have seen queries like MAX(COUNT(col)). I don't understand how that works and not this.
Aggregate functions require an argument that provides a value for each row in the group. Other aggregate functions don't do that.
It's not very sensical anyway. Suppose MySQL accepted MAX(SUM(col)) -- what would it mean? Well, the SUM(col) yields the sum of all non-NULL values of column col over all rows in the relevant group, which is a single number. You could take the MAX() of that to be that same number, but what would be the point?
Your approach using a subquery is different, at least in principle, because it aggregates twice. The inner aggregation, in which you perform the SUM(), computes a separate sum for each value of p.pname. The outer query then computes the maximum across all rows returned by the subquery (because you do not specify a GROUP BY in the outer query). If that's what you want, that's how you need to specify it.
The error is 1111: invalid use of group function. As for why specifically MySQL has this problem I can really only say it is part of the underlying engine itself. SELECT MAX(2) does work (in spite of a lack of a GROUP BY) but SELECT MAX(SUM(2)) does not work.
This error will occur when grouping/aggregating functions such as MAX are used in the wrong spot such as in a WHERE clause. SELECT SUM(MAX(2)) also does not work.
You can imagine that MySQL attempts to aggregate both simultaneously rather than doing things in an order of operations, i.e. it does not SUM first and then get the MAX. This is why you need to do the queries as separate steps.
Try something like this:
select max(rs.marksums) maxsum from
(
select p.pname, sum(r.marks) marksums from personel p,
result r where p.rollno=r.rollno group by p.pname
) rs
with temp_table (name, max_marks) as
(select name, sum(marks) from personel p,result r, where p.rollno = r.rollno group by p.name)
select *from temp_table where max_marks = (select max(max_marks) from temp_table);
I didn't run this. But try this one. Hope it will work :)