GROUP BY error - SQL - mysql

This is the SQL query I have written. It works until right before the group by statement but once I add that part, I get this error:
'reading_datetime' is neither present in the group by, nor is it an aggregate function. Add to group by or wrap in first() (or first_value) if you don't care which value you get
My query:
Select A.bill_account, hour(A.reading_datetime), A.reading_value
from (
Select cast(cast(bill_account as double) as int)bill_account, reading_datetime, cast(reading_value as double)reading_value, `interval`
from amerendataorc
WHERE cast(cast(`interval` as double)as int) = 3600 AND reading_datetime between '2015-03-15 00:00:00' and '2016-03-14 23:59:59'
) A
GROUP BY A.bill_account
HAVING (COUNT(A.bill_account)>= 8000) and (COUNT(A.bill_account) < 9500)")
Not sure exactly how the group by is messing up the query.

take the sum of reading date time and reading value
Select A.bill_account, sum(hour(A.reading_datetime)), sum(A.reading_value)
from (
Select cast(cast(bill_account as double) as int)bill_account, reading_datetime, cast(reading_value as double)reading_value, `interval`
from amerendataorc
WHERE cast(cast(`interval` as double)as int) = 3600 AND reading_datetime between '2015-03-15 00:00:00' and '2016-03-14 23:59:59'
) A
GROUP BY A.bill_account
HAVING (COUNT(A.bill_account)>= 8000) and (COUNT(A.bill_account) < 9500)")
---- explanation ------------
mysql> SELECT * FROM tt where user="user1";
+----------+-------+
| duration | user |
+----------+-------+
| 00:06:00 | user1 |
| 00:02:00 | user1 |
+----------+-------+
2 rows in set (0.00 sec)
mysql> SELECT * FROM tt where user="user1" group by user;
+----------+-------+
| duration | user |
+----------+-------+
| 00:06:00 | user1 |
+----------+-------+
1 row in set (0.00 sec)
once you add group by it will give only the summery after group by on that column in above example its giving 1st value
else you can get sum,max ... aggreagte values

SQL is trying to avoid an issue whereby you have multiple hour(A.reading_datetime) per A.Bill_Account. Grouping by Bill_account will give you a list of unique Bill_accounts. Then it has multiple hour(A.reading_datetime) per Bill_account and needs you to help it choose how to select one.
You need to group by each value that occurs or use aggregate functions on non-group by fields. If you group by reading_datetime and reading_value as well SQL will list all unique combinations of the three fields in the group by.
MySql suggests using first(); max() min() sum() etc are all aggregate functions what will help you get once value per Bill_account.
You will need to doing this for reading_value as well.

Standard SQL doesn't permit queries for which the select list refers to nonaggregated columns that are not named in the GROUP BY clause.
Therefore you have to add those columns to the GROUP BY clause, or you have to aggregate the columns in the SELECT clause, in your case:
Select A.bill_account, sum(hour(A.reading_datetime)), sum(A.reading_value)
But you have to evaluate if it is adequate for your data to sum those columns in that way, and if it isn't, add the columns as GROUP BY criteria.

Any field that is not included in the Group By Clause will require an aggregate function like SUM, COUNT, MIN or MAX to be included in the Selected fields.
http://www.w3schools.com/sql/sql_groupby.asp
To correct the issue you will need to use the following group by clause
GROUP BY A.bill_account, A.reading_datetime, A.reading_value

Related

Having Without Group By in MySQL

I have read multiple articles and now I am confused between 2 following statements.
If we use having without group by then whole table act as Single Group.
If we use having without group by then each table act as an individual Group.
Which One is Correct in MySQL?
For example I have a table named ABC as Follow:
| Wage |
_____________
| 4 |
| 8 |
| 28 |
| 90 |
If We Use Following Query
select wage
from ABC
having wage > 1
then all the records get printed. So each row works as indivisual group.
But If We Use:
select wage
from ABC
having wage = max(wage)
the no record get printed. So whole table works as a group.
So which one is correct and why this 2 queries shows different results.
Don't use having without group by. Although MySQL supports that, this is not valid standard SQL, and the behavior you get will most likely be counter-intuitive.
The first query should be just a where clause:
select wage from abc where wage > 1
The second query just makes no sense: you have both an aggregated and a non-aggregated wage in the having clause. If you want the row that has the maximum wage, then you can order by and limit:
select wage
from abc
order by wage desc limit 1
Or if you want to allow ties, use a correlated subquery:
select *
from abc a
where wage = (select max(a1.wage) from abc)

Is it possible to get sum without change sql_mode?

I have a problem to get SUM value from multiple table using join statement. The error is:
this is incompatible with sql_mode=only_full_group_by
Is it possible to get sum without change sql_mode? If possible, how to make a SQL statement to?
Table fuel:
vehicle_id | liter
-----------+-----------
2 | 43.5
4 | 78.3
8 | 20.5
Table usage:
date_usage | vehicle_id
-----------+-----------
2019-10-01 | 8
2019-10-15 | 2
2019-10-20 | 8
2019-10-20 | 4
2019-11-02 | 8
The SQL statement is below:
SELECT fuel.vehicle_id, SUM(fuel.liter), usage.date_usage
FROM fuel
LEFT JOIN usage ON fuel.vehicle_id = usage.vehicle_id
WHERE fuel.vehicle_id='8'
AND usage.date_usage >='2019-10-01' AND usage.date_usage <='2019-10-31'
GROUP BY fuel.vehicle_id
You have two possibilities to solve this:
Remove column usage.date_usage from SELECT.
Use a aggregate function on column usage.date_usage too:
MAX to get the highest value of the column.
MIN to get the lowest value of the column.
ANY_VALUE to get any value of the column.
So your query can look like the following (using MAX on column usage.date_usage):
SELECT fuel.vehicle_id, SUM(fuel.liter), MAX(`usage`.date_usage)
FROM fuel LEFT JOIN `usage` ON fuel.vehicle_id = `usage`.vehicle_id
WHERE fuel.vehicle_id = 8
AND `usage`.date_usage BETWEEN '2019-10-01' AND '2019-10-31'
GROUP BY fuel.vehicle_id
demo on dbfiddle.uk
Note: be careful using words like usage as identifiers (e.g. table and column names) in MySQL. There are keywords and reserved words you should avoid or quote with backticks.

NULL value count in group by

for simplification purposes, I will use simple table attribute (meaning the table is bigger) to demonstrate the issue:
I have the following table test:
id | nbr
----+-----
1 | 0
2 |
3 |
4 | 1
5 | 1
(5 rows)
id and nbr are both numeric values
The following query
select nbr, count(nbr) from test group by nbr;
outputs:
nbr | count
-----+-------
| 0
1 | 2
0 | 1
(3 rows)
whereas the query:
select nbr, count(*) from test group by nbr;
outputs:
nbr | count
-----+------
| 2
1 | 2
0 | 1
(3 rows)
I find it hard to explain the difference between count(nbr) and count(*) regarding null values
can someone explain this to me like I'm five, thanks
It's pretty simple:
count(<expression>) counts the number of values. Like most aggregate functions, it removes null values before doing the actual aggregation.
count(*) is a special case that counts the number of rows (regardless of any null).
count (no matter if * or <expression>) never returns null (unlike most other aggregate functions). In case no rows are aggregated, the result is 0.
Now, you have done a group by on an nullable column. group by put's null values into the same group. That means, the group for nbr null has two rows. If you now apply count(nbr), the null values are removed before aggregation, giving you 0 as result.
If you would do count(id), there would be no null value to be removed, giving you 2.
This is standard SQL behavior and honored by pretty much every database.
One of the common use-cases is to emulate the filter clause in databases that don't support it natively: http://modern-sql.com/feature/filter#conforming-alternatives
The exceptions (aggregate functions that don't remove null prior to aggregation) are functions like json_arrayagg, json_objectagg, array_agg and the like.
MySQL explains it in the documentation of function COUNT():
COUNT(expr)
Returns a count of the number of non-NULL values of expr in the rows retrieved by a SELECT statement.
COUNT(*) is somewhat different in that it returns a count of the number of rows retrieved, whether or not they contain NULL values.
PostgreSQL also explains it in the documentation:
Most aggregate functions ignore null inputs, so that rows in which one or more of the expression(s) yield null are discarded. This can be assumed to be true, unless otherwise specified, for all built-in aggregates.
For example, count(*) yields the total number of input rows; count(f1) yields the number of input rows in which f1 is non-null, since count ignores nulls; and count(distinct f1) yields the number of distinct non-null values of f1.
count(*) count the number of rows related to the group by colums. Inpependntly of the fatc the the column in group by contain null or not null values
count(nbr) count the number of rows related to the group by column where nbr is not null
Count with null values:
SELECT nbr, COUNT(*) FROM mytables WHERE nbr IS NULL GROUP BY nbr
UNION
SELECT nbr, COUNT(nbr) FROM mytables WHERE nbr IS NOT NULL GROUP BY nbr

Explaining a SELECT statement script in detail for MySql

For one of the questions in my computing coursework, I was asked to explain the following SQL script in detail:
SELECT exam_board, COUNT(*)
FROM subjects
GROUP BY exam_board;
Below is what I have written in response to that question. I was just wondering if I forgot to include something, or if I incorrectly stated something.Any feedback at all would be greatly appreciated!
The script begins with a SELECT statement. A SELECT statement retrieves records from one or more tables or databases (, the data that is returned is then stored inside a result table, which is called a result-set). ‘COUNT ()’ is a function which returns (all (, as there is an asterisk)) the number of rows which match a specified criteria and it gives a total number of records fetched in a query. Therefore ‘SELECT exam_board, COUNT() FROM subjects’ means that the script will return all exam boards from the ‘exam_board’ column in the ‘subjects’ table with their count (of how many subjects are of that exam board). Finally the last line is ‘GROUP BY exam_board;’ the ‘GROUP BY’ clause is often used in SELECT statements to collect data from a number of records. Its purpose is to group the results in one or more columns. In this case it was grouped by ‘exam_board’, meaning that the result of the query will be grouped into a column of the exam boards.
You forgot the effect of GROUP BY is to reduce the result set to one row per distinct value in the grouping column (exam_board in this query).
So there might be 10,000 rows in the subjects table, but only four distinct values for exam_board. Using GROUP BY means you will only have four rows in the result set, exactly one row for each exam_board.
Then the COUNT(*) will be the count of rows that were "collapsed" for each respective group.
I request that you do not copy & paste my answer, but write your own answer in your own words. My writing style is pretty different from yours, so if you copy & paste, it'll be obvious to your teacher that you lifted this.
Actually this not the best answer.
SELECT can return not only data from the tables, but any result of any function, for example SELECT VERSION() returns a version of server software.
An asterisk as a parameter for COUNT(*) does not matter at all. You can put here any column or function, even COUNT(VERSION()), the result will be the same.
‘SELECT exam_board, COUNT() FROM subjects’ will return a single row with two columns: the total number of rows in table 'subjects' and the value of 'exam_board' column in the first row of the table.
Content of the table:
mysql> select exam_board from subjects;
+------------+
| exam_board |
+------------+
| 2 |
| 2 |
| 3 |
| 3 |
| 3 |
+------------+
5 rows in set (0.00 sec)
Mixing together column values and a function returning a single value like SUM(), MIN(), MAX() etc without grouping functions:
mysql> select exam_board, count(*) from subjects;
+------------+----------+
| exam_board | count(*) |
+------------+----------+
| 2 | 5 |
+------------+----------+
1 row in set (0.00 sec)
And only with grouping operator we will get the desired result: the count of records for each value of exam_board field.
mysql> select exam_board, count(*) from subjects group by exam_board;
+------------+----------+
| exam_board | count(*) |
+------------+----------+
| 2 | 2 |
| 3 | 3 |
+------------+----------+
2 rows in set (0.00 sec)

Limit number of results returned for each matching field in mysql

I'm trying to write a query that returns a fixed number of results in a group concat. I don't think it's possible with a group concat, but I'm having trouble figuring out what sort of subquery to add.
Here's what I would like to do:
Query
select id,
group_concat(concat(user,'-',time) order by time limit 5)
from table
where id in(1,2,3,4)
group by 1
When I remove the "limit 5" from the group concat, the query works but spits out way too much information.
I'm open to structuring the query differently. Specific ID numbers will be supplied by the user of the query, and for each ID specified, I would like to list a fixed number of results. Let me know if there is a better way to achieve this.
Not sure the exact result set you want, but check out this SO post:
How to hack MySQL GROUP_CONCAT to fetch a limited number of rows?
As another example, I tried out the query/solution provided in the link and came up with this:
SELECT user_id, SUBSTRING_INDEX(GROUP_CONCAT(DISTINCT date_of_entry),',',5) AS logged_dates FROM log GROUP BY user_id;
Which returns:
user_id | logged_dates
1 | "2014-09-29,2014-10-18,2014-10-05,2014-10-12,2014-10-19"
2 | "2014-09-12,2014-09-03,2014-09-23,2014-09-22,2014-10-13"
3 | "2014-09-10"
6 | "2014-09-29,2014-09-27,2014-09-26,2014-09-25"
8 | "2014-09-26,2014-09-30,2014-09-27"
9 | "2014-09-28"
13 | "2014-09-29"
22 | "2014-10-12"
The above query will return every user id that has logged something, and up to 5 dates that the user has logged. If you want more or less results form the group concat, just change the number 5 in my query.
Following up, and merging my query with yours, I get:
SELECT user_id, SUBSTRING_INDEX(GROUP_CONCAT(date_of_entry ORDER BY date_of_entry ASC),',',3) AS logged_dates FROM log WHERE user_id IN(1,2,3,4) GROUP BY user_id
Which would return (notice that I changed the number of results returned from the group_concat):
user_id | logged_dates
1 | "2014-09-16,2014-09-17,2014-09-18"
2 | "2014-09-02,2014-09-03,2014-09-04"
3 | "2014-09-10"