How to fetch average rows per day? - mysql

I have a table
id | created
---------------
1 | 2014-07-01
2 | 2014-07-01
3 | 2014-07-01
4 | 2014-07-01
5 | 2014-07-02
6 | 2014-07-03
7 | 2014-07-04
8 | 2014-07-05
9 | 2014-07-05
10 | 2014-07-05
and I want to fetch the average of rows per day. So if it's
2014-07-01: 4 rows
2014-07-02: 1 row
2014-07-03: 1 row
2014-07-04: 1 row
2014-07-05: 3 rows
The average would be
sum 4 + 1 + 1 + 1 + 3 = 10 items
10 items / 5 days = 2 items/day
My SQL query is
SELECT AVG(COUNT(*))
FROM `mytable`
GROUP BY `created`
But I get this error
#1111 - Invalid use of group function
How do I have to modify my query in order to get a correct result?

You can do this without subquery also.It is much fatser.
SELECT COUNT(*)/COUNT(DISTINCT `created`) FROM `mytable`;
Here is the example link: http://sqlfiddle.com/#!2/2c284df/2

You need to put it in a subquery.
SELECT AVG(my_count) FROM (
SELECT COUNT(*) as my_count
FROM
your_table
GROUP BY created
) sq
see it working live in an sqlfiddle

Have a sub query to get the count per day, and then use that to get the average:-
SELECT AVG(day_count)
FROM
(
SELECT COUNT(*) AS day_count
FROM `mytable`
GROUP BY `created`
) sub0

Related

How to group latest DATETIME record per day by userId?

For the purpose of this question, I have three fields in a MySQL table: id, userId, and loginDate (DATETIME). I would like to return the latest record for each userId per day. How can I create a query to do the following in section B below?
A. Records
id
userId
loginDate
1
5
2021-01-01 00:05:50
2
7
2021-01-01 06:06:50
3
5
2021-01-01 06:34:50
4
3
2021-01-02 06:56:76
5
3
2021-01-02 15:46:52
B. What I would expect to be returned from the query
id
userId
loginDate
2
7
2021-01-01 06:06:50
3
5
2021-01-01 06:34:50
5
3
2021-01-02 15:46:52
For MySQL 8 you can use row_number analytic function to order your timestamps inside the day in descending order, and then select the first item per group. No tricky aggregation, limiting and joins, it just assigns the number according to the order and grouping, the rest of the row is unchanged.
with a as (
select 1 as id, 5 as userid, timestamp '2021-01-01 00:05:50' as logindate union all
select 2, 7, timestamp '2021-01-01 06:06:50' union all
select 3, 5, timestamp '2021-01-01 06:34:50' union all
select 4, 3, timestamp '2021-01-02 06:56:56' union all
select 5, 3, timestamp '2021-01-02 15:46:52'
)
, rn as (
select a.*,
row_number() over(
partition by
userid,
date(logindate)
order by
logindate desc
) as __rn
from a
)
select *
from rn
where __rn = 1
order by userid desc
id | userid | logindate | __rn
-: | -----: | :------------------ | ---:
2 | 7 | 2021-01-01 06:06:50 | 1
3 | 5 | 2021-01-01 06:34:50 | 1
5 | 3 | 2021-01-02 15:46:52 | 1
db<>fiddle here
You should use GROUP BY with the help of some aggregate functions.
SELECT max(id) AS id, userId, max(loginDate) AS loginDate FROM table GROUP BY userId ORDER BY id ASC;
return:
id userId loginDate
2 7 2021-01-01 06:06:50
3 5 2021-01-01 06:34:50
5 3 2021-01-02 15:46:52
Explanation: GROUP BY get's confused by your query because multiple rows may match. Using the max() aggregate function allows us to return only the newest rows data.

Select by a limited number of distinct values

I am trying to chunkage selects by distinct values of one column. Like give me all rows for the first five distinct values. Or give me all rows for next five distinct values of one column.
I have a table like this VBS_DOCUMENT:
PK T_DOCUMENT
1 1
2 1
3 1
4 3
5 3
6 3
7 5
8 5
9 6
10 7
SELECT * FROM VBT_DOCUMENT
WHERE T_DOCUMENT IN (SELECT DISTINCT T_DOCUMENT FROM VBT_DOCUMENT LIMIT 2 OFFSET 2);
But then I get this error:
1235 - This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
PK T_DOCUMENT
7 5
8 5
9 6
Use your subquery in the FROM clause and join it with the base table:
SELECT t.*
FROM (
SELECT DISTINCT T_DOCUMENT
FROM VBT_DOCUMENT
ORDER BY T_DOCUMENT
LIMIT 2 OFFSET 2
) x
JOIN VBT_DOCUMENT t on t.T_DOCUMENT = x.T_DOCUMENT
Result:
| PK | T_DOCUMENT |
| --- | ---------- |
| 7 | 5 |
| 8 | 5 |
| 9 | 6 |
View on DB Fiddle
Note: When you use LIMIT you should also define a deterministic ORDER BY clause. Otherwise you might get an unexpected result depending on the execution plan.
WITH can be used in MySQL8.0 (,or MariaDB 10.2.1 )
WITH abc as (SELECT DISTINCT T_DOCUMENT FROM VBS_DOCUMENT LIMIT 2 OFFSET 2)
SELECT * FROM VBS_DOCUMENT WHERE T_DOCUMENT IN (select * from abc);
output:
# pk, t_document
'7', '5'
'8', '5'
'9', '6'

Update with SUM and LIMIT, rolling SUM

I have 2 tables, SVISE and OVERW
Inside OVERW I have some scores with person ids and the date of that score.
E.g
p_id degrees mo_date
5 10.2 2013-10-09
5 9.85 2013-03-10
8 14.75 2013-04-25
8 11.00 2013-02-22
5 5.45 2013-08-11
5 6.2 2013-06-10
SVISE.ofh field must be updated with the sum of the last three records
(for a specific person, ordered by date descending), so for person with id 5, the sum would result from the rows
5 10.2 2013-10-09
5 5.45 2013-08-11
5 6.2 2013-06-10
sum=21.85.
Desired final result on SVISE, based on the values above:
HID OFH START
5 21.85 October, 16 2013 ##(10.2 + 5.45 + 6.2)
5 21.5 September, 07 2013 ##(5.45 + 6.2 + 9.85)
5 0 March, 05 2013 ##(no rows)
8 25.75 October, 14 2013 ##(14.75 + 11)
3 0 October, 14 2013 ##(no rows)
5 0 March, 05 2012 ##(no rows)
OFHwas 0 initially
I can get the total sum for a specific person, but I can't use limit to get the last 3 rows. It gets ignored.
This is the query I use to retrieve the sum of all degrees per person for a given date:
UPDATE SVISE SV
SET
SV.ofh=(SELECT sum(degrees) FROM OVERW WHERE p_id =SV.hid
AND date(mo_date)<date(SV.start)
AND year(mo_date)=year(SV.start))
I cannot just use limit with sum:
UPDATE SVISE SV
SET
SV.ofh=(SELECT sum(degrees) FROM OVERW WHERE p_id =SV.hid
AND date(mo_date)<date(SV.start)
AND year(mo_date)=year(SV.start)
ORDER BY mo_date DESC
LIMIT 3)
This does not work.
I have tried with multi-table updates and with nested queries to achieve this.
Every scenario has known limitations that block me from accomplishing the desired result.
Nested queries cant see the parent table. Unknown column 'SV.hid'in 'where clause'
Multi-table update cant be use with limit. Incorrect usage of UPDATE and LIMIT
Any solution will do. There is no need to do it in a single query. If anyone wants to try even with an intermediate table.
An SQL fiddle is also available.
Thanks in advance for your help.
--Update--
Here is the solution from Akash: http://sqlfiddle.com/#!2/4cf1a/1
This should work,
UPDATED to have a join on svice
UPDATE
svice SV
JOIN (
SELECT
hid,
start,
sum(degrees) as degrees
FROM
(
SELECT
*,
IF(#prev_row <> unix_timestamp(start)+P_ID, #row_number:=0,NULL),
#prev_row:=unix_timestamp(start)+P_ID,
#row_number:=#row_number+1 as row_number
FROM
(
SELECT
mo_date,
p_id,
hid,
start,
degrees
FROM
OVERW
JOIN svice sv ON ( p_id = hid
AND date(mo_date)<date(SV.start)
AND year(mo_date)=year(SV.start) )
ORDER BY
hid,
start,
mo_date desc
) sub_query1
JOIN ( select #row_number:=0, #prev_row:=0 ) sub_query2
) sub_query
where
row_number <= 3
GROUP BY
hid,
start
) sub_query ON ( sub_query.hid = sv.hid AND sub_query.start = sv.start )
SET
SV.ofh = sub_query.degrees
Note: Check this with your updated data, the test data provided could not yield the results you expected due to the date conditions
Try
UPDATE svice SV
JOIN (SELECT SUM(degrees)sumdeg,p_id FROM(SELECT DISTINCT degrees,p_id FROM OVERW,svice WHERE OVERW.p_id IN (SELECT svice.hid FROM svice)
AND date(mo_date)<date(svice.start)
AND year(mo_date)=year(svice.start)ORDER BY mo_date DESC )deg group by p_id)bbc
ON bbc.p_id=SV.hid
SET
SV.ofh=bbc.sumdeg where p_id =SV.hid
http://sqlfiddle.com/#!2/95b42/42
Getting closer,now it "only" needs a limit in GROUP BY.
Two assumptions:
You can figure out how to turn this into an update, and
A PK exists on (id,mo_date)
Then you can do this -
SELECT p_id
, SUM(degrees) ttl
FROM
( SELECT x.*
FROM overw x
JOIN overw y
ON y.p_id = x.p_id
AND y.mo_date >= x.mo_date
GROUP
BY p_id
, mo_date HAVING COUNT(*) <= 3
) a
GROUP
BY p_id;
Maybe I'm slow, but let's ignore svice for now.
Can you show the correct result and the working for each row below...
+------+---------+------------+--------+
| p_id | degrees | mo_date | result |
+------+---------+------------+--------+
| 5 | 6.20 | 2013-06-10 | ? |
| 5 | 5.45 | 2013-08-11 | ? |
| 5 | 10.20 | 2013-10-09 | 21.85 | <- = 10.2+5.45+6.2 = 21.85
| 8 | 14.75 | 2013-04-25 | ? |
| 5 | 9.85 | 2013-03-10 | ? |
| 8 | 11.00 | 2013-02-22 | ? |
+------+---------+------------+--------+

Select all rows with multiple occurrences

Ok, I have a single MySQL table with the name 'test' and 3 columns.
ID | playername | lastloginip
-----------------------------
1 | user 1 | 1
2 | user 2 | 2
3 | user 3 | 3
4 | user 4 | 4
5 | user 5 | 5
6 | user 6 | 1
7 | user 7 | 1
8 | user 8 | 2
Now, I would like to select ALL the rows where the lastloginip is found multiple times in the table, and then give the count of those rows.
In this case, it should return the number 5
as user 1, 2, 6, 7 and 8 have a lastloginip that is found multiple times.
I already tried using
SELECT COUNT(*)
FROM (
SELECT *
FROM test
GROUP BY lastloginip
HAVING COUNT(*) > 1
) t
But that gave back the number 2 instead of 5.
I am not sure how to set up this query correctly. Most of my findings on the internet keep showing only 2 rows or giving the number 2 instead of 5.
First COUNT(), then SUM():
SELECT SUM(occurences)
FROM
(
SELECT COUNT(*) AS occurences
FROM test
GROUP BY lastloginip
HAVING COUNT(*)>1
) t
Try this query.
SELECT SUM(loginip)
FROM(
SELECT
lastloginip,
COUNT(lastloginip)
as loginip
FROM test
GROUP BY lastloginip
HAVING COUNT(ID)>1
)t
You can fetch the sum of occurrences using the above code and if you want to view the records with multiple occurences, refer to the query below-
Select *
from test
where lastloginip in (
select *
from
(select lastloginip
from test
group by lastloginip
having count(lastloginip)>1
)
as a)

Averaging an average in mySQL

I have a table
car
id | person_id | mpg
------------------------
4 | 1 | 50
5 | 1 | 15
6 | 2 | 10
7 | 2 | 28
8 | 3 | 33
I need to get an average of each person's mpg and then an average for the group.
person 1 avg = (50 + 15) / 2 = 32.5
person 2 avg = (10 + 28) / 2 = 19
person 3 avg = 33
group average = 32.5 + 19 + 33 / 3 = 28.1
Is there a query that will do what I need?
SELECT person_id, AVG(mpg) from car group by person_id;
If you want to get an average for the group, you should probably do this:
SELECT AVG(mpg) from car;
Unless you really want to average the averages, which seems a bit dubious to me:
SELECT AVG(average) from (SELECT person_id, AVG(mpg) as average from car group by person_id);
you cannot solve this in 1 query, but you have to use 2 queries or 1 query en solve the overal average in your code
select person, avg(mpg) from cat group by person
SELECT person_id, AVG(mpg) AS mpg_avg FROM car GROUP BY person_id WITH ROLLUP
The WITH ROLLUP-modifier will add a line to the result set where persion_id is NULL and mpg_avg is the average over the whole result set (MySQL >= 4.1.1):
person_id | mpg
------------------
1 | 32.5
2 | 19.0
3 | 33.0
NULL | 27.2