MySQL HAVING Clause return empty set? - mysql

I am having a table below, and I need to write code that extract the rows with budget greater than the average budget.
+------+-----------------+--------+
| Code | Name | Budget |
+------+-----------------+--------+
| 14 | IT | 65000 |
| 37 | Accounting | 15000 |
| 59 | Human Resources | 240000 |
| 77 | Research | 55000 |
+------+-----------------+--------+
I know this works:
SELECT * FROM Departments WHERE Budget > (SELECT AVG(Budget) FROM Departments);
but this looks ugly. This post seems to suggest having clause can simplify the query into:
SELECT * FROM Departments HAVING Budget > AVG(Budget);
but it returns empty set. Any ideas?
Thanks

This is because AVG() is aggregation function which should be used GROUP BY or with other Aggregation functions.
If not, SELECT would returns single row. for example:
mysql> SELECT * FROM test;
+------+--------+
| code | budget |
+------+--------+
| 14 | 65000 |
| 37 | 15000 |
| 59 | 240000 |
| 77 | 55000 |
+------+--------+
4 rows in set (0.00 sec)
mysql> SELECT code, budget, AVG(budget) FROM test;
+------+--------+-------------+
| code | budget | AVG(budget) |
+------+--------+-------------+
| 14 | 65000 | 93750.0000 | we got one row.
+------+--------+-------------+
1 row in set (0.00 sec)
In this case, HAVING budget > AVG(budget) means 65000 > 93750 which is false, so that returns empty list.
Your first attampt does not look like 'ugly' ;)

In mySQL, having an aggregation column with SELECT * will return the first row only.
This SQL Fiddle shows that:
SELECT *, AVG(BUDGET) FROM Departments;
will return only the first row and the average of budget of all rows.
Then, as in your first row, the budget is smaller than the average of budgets, it will return no rows.
I believe your UGLY (I don't think it is ugly) query is a good solution for this.

Related

How to reuse variables in the select statement of mysql

I would like to use mysql variables to prevent same statements. In the following example i would like to sum the salary of an each employee and also sum it twice times. Of course the second column is wrong.
MariaDB [Messdaten]> select * from t;
+----+----------+--------+
| id | employee | salery |
+----+----------+--------+
| 1 | 10 | 1000 |
| 2 | 10 | 2000 |
| 3 | 20 | 3000 |
| 4 | 20 | 4000 |
+----+----------+--------+
4 rows in set (0.000 sec)
MariaDB [Messdaten]> select employee, #x:=sum(salery), 2*#x from t group by employee;
+----------+-----------------+-------+
| employee | #x:=sum(salery) | 2*#x |
+----------+-----------------+-------+
| 10 | 3000 | 14000 |
| 20 | 7000 | 14000 |
+----------+-----------------+-------+
2 rows in set (0.001 sec)
Of course i could use select employee, sum(salery), 2*sum(salery) but in my real use case the statements are very big and therefore bad readable.
What ist going wrong and if this is a gap of mysql are there some workarounds?
You can use a subquery like so to get the correct result while only summing (or executing a more complex statement) once
SELECT
employee,
totalSalary,
totalSalary*2 AS doubleSalary
FROM (
SELECT
employee,
sum(salary) AS totalSalary
FROM employees
GROUP BY employee
) AS employeeSalaries;
The unexpected variable behaviour is described in the MySQL docs here.
HAVING, GROUP BY, and ORDER BY, when referring to a variable that is assigned a value in the select expression list do not work as expected because the expression is evaluated on the client and thus can use stale column values from a previous row.

MySQL query sum result divide by number of row (Average)

I have following data in MySQL and i want to sum (total) and then divide by number for row.
Example:
I want to sum all number in AcctSessionTime column and divide that with number of items so in our case 6+4+3+31=44 divide them with 4 number of row.
How do i craft query to do all in single query?
mysql> select AcctStartTime,AcctStopTime,AcctSessionTime
from radacct
where UserName='spatel' AND SipResponseCode='200';
+---------------------+---------------------+-----------------+
| AcctStartTime | AcctStopTime | AcctSessionTime |
+---------------------+---------------------+-----------------+
| 2015-06-04 15:32:03 | 2015-06-04 15:32:09 | 6 |
| 2015-06-04 16:11:27 | 2015-06-04 16:11:31 | 4 |
| 2015-06-04 16:13:37 | 2015-06-04 16:13:40 | 3 |
| 2015-06-05 11:44:31 | 2015-06-05 11:45:02 | 31 |
+---------------------+---------------------+-----------------+
4 rows in set (0.00 sec)
Notes: above is example data we have actual data in thousands of rows.
The calculation SUM(RowData) / #Rows is an Average of RowData - this is already supported natively as the AVG aggregate function:
select AVG(AcctSessionTime)
from radacct
where UserName='spatel' AND SipResponseCode='200';
You can also do grouped averages with a GROUP BY statement, if required
mysql> select sum(AcctSessionTime) div count(AcctSessionTime) as result from radacct where UserName='spatel' AND SipResponseCode='200';
| result |
+--------+
| 11 |
You can try this.
You can try this:
Select AVG(AcctSessionTime) from radacct where UserName='spatel' AND SipResponseCode='200';

How to select row which a certain text format?

Sorry for an unclear question coz I don't know how to ask correctly. Let me explain this. I'd like to search for some rows in mySQL. Which is only in this format - "product1" (text and number). While in my table. There're some other rows begins with "product" as well. But they're not followed by number.
Here's my table.
**product_db**
+----------+-------+
| key | value |
+----------+-------+
| product1 | 100 |
+----------+-------+
| product2 | 184 |
+----------+-------+
| product3 | 170 |
+----------+-------+
| productA | 210 |
+----------+-------+
| productB | 100 |
+----------+-------+
This is mySQL:
select * from product_db where key like 'product%'
After run the code. Every row shows up. Because they're all begins with "product". I expect only the first 3 rows that followed by number to be displayed. How can I write a command in mySQL.
you should use regex:
select * from product_db where key REGEXP '^product[0-9]+$';
SELECT 'product1' REGEXP '[product][0-9]';
+------------------------------------+
| 'product1' REGEXP '[product][0-9]' |
+------------------------------------+
| 1 |
+------------------------------------+
1 row in set (0.01 sec)
SELECT 'productA' REGEXP '[product][0-9]';
+------------------------------------+
| 'productA' REGEXP '[product][0-9]' |
+------------------------------------+
| 0 |
+------------------------------------+
1 row in set (0.00 sec)

How return only one column as output in MySQL?

I want to return only one column as output in MySQL.
This works:
select COALESCE(sum(debit_amt),0) as credit from client_debit_bal where mob_no=id
but when I try the following query it does not:
select COALESCE(sum(debit_amt),0) as credit from client_debit_bal where mob_no=id
and cmy_code='001'
I only need credit to be outputted. What am I missing?
Are you looking for LIMIT?
LIMIT clause constrains the number of result.
You can fetch the first N rows using LIMIT [N].
Suppose there is a table like following,
tbl_t
| idx | name | age |
| 0 | Tom | 30 |
| 1 | Jerry | 25 |
| 2 | Bob | 30 |
| 3 | Ken | 45 |
Then query following statement,
SELECT name FROM tbl_t WHERE age=30
result would be {'Tom', 'Bob'}.
But if you query SELECT name FROM tbl_t WHERE age=30 LIMIT 1 then
result would be {'Tom'}.
If you want to get further information, visit http://www.mysqltutorial.org/mysql-limit.aspx .

MySQL: optimize query for scoring calculation

I have a data table that I use to do some calculations. The resulting data set after calculations looks like:
+------------+-----------+------+----------+
| id_process | id_region | type | result |
+------------+-----------+------+----------+
| 1 | 4 | 1 | 65.2174 |
| 1 | 5 | 1 | 78.7419 |
| 1 | 6 | 1 | 95.2308 |
| 1 | 4 | 1 | 25.0000 |
| 1 | 7 | 1 | 100.0000 |
+------------+-----------+------+----------+
By other hand I have other table that contains a set of ranges that are used to classify the calculations results. The range tables looks like:
+----------+--------------+---------+
| id_level | start | end | status |
+----------+--------------+---------+
| 1 | 0 | 75 | Danger |
| 2 | 76 | 90 | Alert |
| 3 | 91 | 100 | Good |
+----------+--------------+---------+
I need to do a query that add the corresponding 'status' column to each value when do calculations. Currently, I can do that adding the following field to calculation query:
select
...,
...,
[math formula] as result,
(select status
from ranges r
where result between r.start and r.end) status
from ...
where ...
It works ok. But when I have a lot of rows (more than 200K), calculation query become slow.
My question is: there is some way to find that 'status' value without do that subquery?
Some one have worked on something similar before?
Thanks
Yes, you are looking for a subquery and join:
select s.*, r.status
from (select s.*
from <your query here>
) s left outer join
ranges r
on s.result between r.start and r.end
Explicit joins often optimize better than nested select. In this case, though, the ranges table seems pretty small, so this may not be the performance issue.