How to reuse variables in the select statement of mysql - 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.

Related

How do I check whether the result row of given mysql query exceeds a certain number without using count()

Now my problem is to know a mysql query will fetch result which exceeds a certain row count (like 5000 rows). I know it can use select * ... limit 5001 to replace count() for performance optimization in terms of time effeciency, but it still return 5001 row of records which is totally useless in my scenario, becasue all I want is a sample 'yes/no' answer. Is there any better approach? big thanks ! ^_^
The accepted answer in the link provided by Devsi Odedra
is substantially correct but if you don't want a big result set select a column into a user defined variable and limit 1
for example
MariaDB [sandbox]> select * from dates limit 7;
+----+------------+
| id | dte |
+----+------------+
| 1 | 2018-01-02 |
| 2 | 2018-01-03 |
| 3 | 2018-01-04 |
| 4 | 2018-01-05 |
| 5 | 2018-01-06 |
| 6 | 2018-01-07 |
| 7 | 2018-01-08 |
+----+------------+
SELECT SQL_CALC_FOUND_ROWS ID INTO #ID FROM DATES WHERE ID < 5 LIMIT 1;
SELECT FOUND_ROWS();
+--------------+
| FOUND_ROWS() |
+--------------+
| 4 |
+--------------+
1 row in set (0.001 sec)
SELECT 1 FROM tbl
WHERE ... ORDER BY ...
LIMIT 5000, 1;
will give you either a row or no row -- This indicates whether there are more than 5000 row or not. Wrapping it in EXISTS( ... ) turns that into "true" or "false" -- essentially the same effort, but perhaps clearer syntax.
Caution: If the WHERE and ORDER BY are used but cannot handled by an INDEX, the query may still read the entire table before getting to the 5000 and 1.
When paginating, I recommend
LIMIT 11, 1
to fetch 10 rows, plus an indication that there are more rows.

mysql returns wrong results with random duplicate values

i need to return the best 5 scores in each category from a table.so far i have tried query below following an example from this site: selecting top n records per group
query:
select
subject_name,substring_index(substring_index
(group_concat(exams_scores.admission_no order by exams_scores.score desc),',',value),',',-1) as names,
substring_index(substring_index(group_concat(score order by score desc),',',value),',',-1)
as orderedscore
from exams_scores,students,subjects,tinyint_asc
where tinyint_asc.value >=1 and tinyint_asc.value <=5 and exam_id=2
and exams_scores.admission_no=students.admission_no and students.form_id=1 and
exams_scores.subject_code=subjects.subject_code group by exams_scores.subject_code,value;
i get the top n as i need but my problem is that its returning duplicates at random which i dont know where they are coming from
As you can see English and Math have duplicates which should not be there
+------------------+-------+--------------+
| subject_name | names | orderedscore |
+------------------+-------+--------------+
| English | 1500 | 100 |
| English | 1500 | 100 |
| English | 2491 | 100 |
| English | 1501 | 99 |
| English | 1111 | 99 |
|Mathematics | 1004 | 100 |
| Mathematics | 1004 | 100 |
| Mathematics | 2722 | 99 |
| Mathematics | 2734 | 99 |
| Mathematics | 2712 | 99 |
+-----------------------------------------+
I have checked table and no duplicates exist
to confirm there are no duplicates in the table:
select * from exams_scores
having(exam_id=2) and (subject_code=121) and (admission_no=1004);
result :
+------+--------------+---------+--------------+-------+
| id | admission_no | exam_id | subject_code | score |
+------+--------------+---------+--------------+-------+
| 4919 | 1004 | 2 | 121 | 100 |
+------+--------------+---------+--------------+-------+
1 row in set (0.00 sec)
same result for English.
If i run the query like 5 times i sometimes end up with another field having duplicate values.
can anyone tell me why my query is behaving this way..i tried adding distinct inside
group_concat(ditinct(exams_scores.admission_no))
but that didnt work ??
You're grouping by exams_scores.subject_code, value. If you add them to your selected columns (...as orderedscore, exams_scores.subject_code, value from...), you should see that all rows are distinct with respect to these two columns you grouped by. Which is the correct semantics of GROUP BY.
Edit, to clarify:
First, the SQL server removes some rows according to your WHERE clause.
Afterwards, it groups the remaining rows according to your GROUP BY clause.
Finally, it selects the colums you specified, either by directly returning a column's value or performing a GROUP_CONCAT on some of the columns and returning their accumulated value.
If you select columns not included in the GROUP BY clause, the returned results for these columns are arbitrary, since the SQL server reduces all rows equal with respect to the columns specified in the GROUP BY clause to one single row - as for the remaining columns, the results are pretty much undefined (hence the "randomness" you're experiencing), because - what should the server choose as a value for this column? It can only pick one randomly from all the reduced rows.
In fact, some SQL servers won't perform such a query and return an SQL error, since the result for those columns would be undefined, which is something you don't want to have in general. With these servers (I believe MSSQL is one of them), you more or less can only have columns in you SELECT clause which are part of your GROUP BY clause.
Edit 2: Which, finally, means that you have to refine your GROUP BY clause to obtain the grouping that you want.

MySQL HAVING Clause return empty set?

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.

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 .

SQL (mysql) - If a given row on a given column as a certain value, don't list that column

I have a query that retrieves some data, among those data I have some that are returned with a value like 0. I would like the query to NOT return the columns when that's the case.
How can we do such a thing?
Regards,
MEM
select <column_name> from <table_name> where <column_name> <> 0.0
Here is all the data in a sample database. Notice how there are 3 rows with one having a zero value for the num column.
mysql> select * from test_tbl;
+------+----------+
| num | some_str |
+------+----------+
| 0 | matt |
| 2 | todd |
| 3 | Paul |
+------+----------+
3 rows in set (0.00 sec)
Now lets use the where clause to specify the rows we want to ignore (it's a little bit of reverse logic because we are actually specifying what rows we want).
mysql> select * from test_tbl where num <> 0.0;
+------+----------+
| num | some_str |
+------+----------+
| 2 | todd |
| 3 | Paul |
+------+----------+
2 rows in set (0.00 sec)
Note: This will only work without getting messy if 0 is the only value you are worried about. A better way would be to allow nulls in your column and then you can check to see if they are non-null in the where clause.