How to get average sum for every weekday? (MySQL) - mysql

I'm struggling with one task - I need to get average number of users for every weekday. Unfortunately, I'm stuck at this point.
SELECT dayname(DAY) as week, SUM(VISITORS_NUMBER) as vis
FROM mytable
GROUP BY week
The result of code above looks like this Sum results
From this moment I want to get same weekday column but with average values.
What can I do? I've tried subqueries, but I'm still a beginner and can't use it properly
Edit 1:
AVG() is not working. I'm getting results like this: AVG() RESULTS
I checked in excel, average for friday should be 572, not 53.
That's how my dataset looks like: Data set
edit 2:
CREATE TABLE `mytable` (
`DAY` date NOT NULL,
`BROWSER` varchar(22) COLLATE utf8_unicode_ci DEFAULT NULL,
`PLATFORM` varchar(13) COLLATE utf8_unicode_ci DEFAULT NULL,
`VISITORS_NUMBER` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
INSERT INTO mytable(DAY, BROWSER, PLATFORM,VISITORS_NUMBER)
VALUES('2020-02-01','Android Webkit Browser','Android','9'),
('2020-02-01','Safari','iOs','5'),
('2020-02-01','Android Webkit Browser','Android','15');

Test
SELECT DAYNAME(`day`) as `week`, SUM(visitors_number) / COUNT(DISTINCT `day`) as avg_vis
FROM mytable
GROUP BY `week`

You need 2 levels of aggregation:
SELECT DAYNAME(t.day) AS day, AVG(t.visitors) AS vis
FROM (
SELECT day, SUM(VISITORS_NUMBER) AS visitors
FROM mytable
GROUP BY day
) t
GROUP BY DAYNAME(t.day)
See a simplified demo.

Related

'WHERE' clause does not work with both conditions

SHOW CREATE TABLE:
CREATE TABLE `calls` (
`fullname` varchar(201) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
`lastname` text COLLATE utf8_unicode_ci,
`firstname` text COLLATE utf8_unicode_ci,
`call` datetime DEFAULT NULL,
`phoneNumber` char(12) COLLATE utf8_unicode_ci DEFAULT NULL,
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
call stores information regarding the date and time of the call made by the user
I am trying to select all the persons that called at least three times between 8 P.M. and 10 P.M. in the last month.
My query:
SELECT fullname, call, COUNT(fullname) AS NumberOfCalls
FROM calls
WHERE HOUR(call) IN (20,21) AND datediff(CURDATE(), call) < 30
GROUP BY fullname
HAVING NumberOfCalls >= 3;
The query does not provide a result.
If i comment /* AND datediff(CURDATE(), call) < 30 */ i get all persons that called between 8 pm and 10 pm.
If i comment /* HOUR(call) IN (20,21) AND */ i get all persons that called in the last month.
The WHERE clause does not work with both conditions, and I do not understand why. How can i make it accept both conditions? Thank you!
Based on your description, I think your code should work. The call in the SELECT is inappropriate (because it is not part of the GROUP BY). And I recommend direct comparisons on the date/time values:
SELECT fullname, COUNT(*) AS NumberOfCalls
FROM calls c
WHERE HOUR(call) IN (20, 21) AND call >= curdate() - interval 30 day
GROUP BY fullname
HAVING NumberOfCalls >= 3;

MySQL subquery count with calendar table slow

I have a sales table in MySQL (InnoDB). It's +- 1 million records big. I would like to show some nice charts. Fetching the right data is not a problem. Fetching it fast is...
So I like to count the amount of sales in table A grouped per day (later on also month, and year) for PERIOD A till Z. Concrete; for the last 30 days I like to know for each day how many sales records we have in the DB.
So MySQL would have to return something like this:
I like to achieve that MySQL returns the data like this:
date, count
2017-04-01, 2482
2017-04-02, 1934
2017-04-03, 2701
...
The structure of the Sales basically like this:
CREATE TABLE `sales` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `contacts_created_at_index` (`created_at`),
KEY `contacts_deleted_at_index` (`deleted_at`),
KEY `ind_created_at_deleted_at` (`created_at`,`deleted_at`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Some days (datapoints) might not have any results, but I don't like to have gaps in the data. So I also have some 'calendar' table.
CREATE TABLE `time_dimension` (
`id` int(11) NOT NULL,
`db_date` date NOT NULL,
`year` int(11) NOT NULL,
`month` int(11) NOT NULL,
`day` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `td_ymd_idx` (`year`,`month`,`day`),
UNIQUE KEY `td_dbdate_idx` (`db_date`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Fetching 30 rows (30 days) with a count per day takes 30 secs...
This is the first query I tried:
SELECT
`db_date` AS `date`,
(SELECT
COUNT(1)
FROM
sales
WHERE
DATE(created_at) = db_date) AS count
FROM
`time_dimension`
WHERE
`db_date` >= '2017-04-11'
AND `db_date` <= '2017-04-25'
ORDER BY `db_date` ASC
But like I said it's really slow (11.9 secs). I tried al kinds of other approaches, but without luck. For example:
SELECT time_dimension.db_date AS DATE,
COUNT(1) AS count
FROM sales RIGHT JOIN time_dimension ON (DATE(sales.created_at) =
time_dimension.db_date)
WHERE
(time_dimension.db_date BETWEEN '2017-03-11' AND '2017-04-11')
GROUP BY
DATE
A query for just 1 datapoint takes only 5.4ms:
SELECT COUNT(1) FROM sales WHERE created_at BETWEEN '2017-04-11 00:00:00' AND '2017-04-25 23:59:59'
I haven't checked innodb_buffer_poolsize on my local machine. I will check that as well. Any ideas on how to make queries like this fast? In the future I would even need to where clauses and joins, to filter the set of sales records..
Thanks.
Nick
You could try to count sale data first, then join count result with your calendar table.
SELECT time_dimension.db_date AS date,
by_date.sale_count
FROM time_dimension
LEFT JOIN (SELECT DATE(sales.created_at) sale_date,
COUNT(1) AS sale_count
FROM sales
WHERE created_at BETWEEN '2017-03-11 00:00:00' AND
'2017-04-11 23:59:59'
GROUP BY DATE(sales.created_at)) by_date
ON time_dimension.db_date = by_date.sale_date
WHERE time_dimension.db_date BETWEEN '2017-03-11' AND '2017-04-11'
The problematic part of your query is the data type conversion DATE(created_at), which effectively prevents Mysql from using the index at created_at.
Your 1 datapoint query avoids that, and that is why it is working fast.
To fix this you should check if created_at is within a range of specific day, like that:
created_at BETWEEN db_date AND DATE_ADD(db_date,INTERVAL 1 DAY)
This way Mysql will be able to make use of index on it (do a range lookup), as appropriate.
WHERE DATE(created_at) = db_date)
-->
WHERE created_at >= db_date
AND created_at < db_date + INTERVAL 1 DAY
This avoids including midnight of second day (as BETWEEN does)
Work for all flavors: DATE, DATETIME, DATETIME(6)
Does not hid the created_at inside a function where the index cannot see it.
For time_dimension, get rid of PRIMARY KEY (id) and change UNIQUE(db_date) to the PK.
After making these changes, your original subquery may be competitive with the LEFT JOIN ( SELECT ... ). (It depends on which version of MySQL.)

Mysql Subquery between specific dates

I'm a newbie to MySQL querying and need some assistance with the subqueries.
I am using ASP .NET charting control that retrieves data from MySQL.I want to display a drill down chart and need some help on MySQL subquery.
Below is my table:
CREATE TABLE IF NOT EXISTS `data` (
`runtime` smallint(6) NOT NULL,
`app` varchar(60) NOT NULL,
`process` varchar(40) NOT NULL,
`username` varchar(51) NOT NULL,
`time` time NOT NULL,
`date` date NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Step 1 :
Showing a pie chart of Top 10 users with highest time between 2 dates.
I get the top 10 users used between 2 dates using the below query:
SELECT username ,SUM(runtime) as Runtime,
process,ROUND(SUM(runtime/201600),2) as 'Total Time',
role ,
date
FROM data
WHERE `date` BETWEEN 'date1' AND 'date2'
Group BY process LIMIT 10.
Step 2:
When user clicks on the individual user in chartArea, I wan to display the top 10 apps/process between specific dates.

MySQL Select next 2 rows greater than time

I have a database that has stored time values for a train schedule. This is my table:
CREATE TABLE IF NOT EXISTS `bahn_hausen` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`time` time NOT NULL,
`day` varchar(12) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=132 ;
Now I want to select the next two rows after now():
SELECT time FROM bahn_hausen WHERE time > now() LIMIT 2
The Problem is that when now is > than last time today (23:45:00), there is no row selected. However, I want to select the next 2 values of course (00:15:00 and 00:45:00). This only works correctly when now() is >= 0:00:00
*[edit]*For clarification: The problem I am having is that SQL doesn't recognize 00:15 to be greater than 23:45.
How do I do this?
Thanks for any help.
Your query is almost there. You just need an order by:
SELECT time
FROM bahn_hausen
ORDER BY time > now() desc, time
LIMIT 2;
Have you try to use the method CURTIME() or DATEDIFF(...) > 0
https://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_current-time

Return multiple copies slightly modified

In my events table there are records that have a daily flag that indicate that this event must be repeated each day:
CREATE TABLE IF NOT EXISTS `events` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`start` datetime DEFAULT NULL,
`title` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
`description` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
`daily` tinyint(1) NOT NULL,
) ENGINE=InnoDB;
When I try to fill a calendar with this data I need a row for each item, it is, if the calendar ask for all events this week, daily flaged events must return 7 events (one for each week day) with the same data (title, description, etc), but with a different start day.
Is it possible to do from MySQL?
Usually I would use a table that contains each of the single date.
Example
create table daily
(
/* FYI, date is not a reserved keyword */
date date
);
insert into daily values ('2011-09-11'), ('2011-09-12'),
('2011-09-13'), ('2011-09-14'), ('2011-09-15'),
('2011-09-16'), ('2011-09-17');
alter table daily add index (date);
select daily.date as daily_date,
weekly.start, weekly.id, weekly.title, weekly.description
from daily
inner join
(
select events.id, events.title,
events.description, events.start, events.daily
from events
where events.start between '2011-09-11' and '2011-09-17'
) as weekly
on date(weekly.start)=daily.date
union
/* query for recurr */
select daily.date as daily_date,
recurr.start, recurr.id, recurr.title, recurr.description
from daily
inner join
(
select events.id, events.title,
events.description, events.start, events.daily
from events
where events.daily=1 AND events.start between '2011-09-11' and '2011-09-17'
)as recurr
on date(recurr.start)<=daily.date
order by daily_date;
It would be more efficient if you break the start column to two column (date, time).
The drawback of this method is on the pre-create of table daily and loaded it with lots of day value.
This should be one time cost and can easily inserted via a for.
Alternatively, you can do a repeat of (from a week start to week end) on the query for recurr