I am currently struggling on how to aggregate my daily data in other time aggregations (weeks, months, quarters etc).
Here is how my raw data type looks like:
| date | traffic_type | visits |
|----------|--------------|---------|
| 20180101 | 1 | 1221650 |
| 20180101 | 2 | 411424 |
| 20180101 | 4 | 108407 |
| 20180101 | 5 | 298117 |
| 20180101 | 6 | 26806 |
| 20180101 | 7 | 12033 |
| 20180101 | 8 | 80368 |
| 20180101 | 9 | 69544 |
| 20180101 | 10 | 39919 |
| 20180101 | 11 | 26291 |
| 20180102 | 1 | 1218490 |
| 20180102 | 2 | 410965 |
| 20180102 | 4 | 108037 |
| 20180102 | 5 | 297727 |
| 20180102 | 6 | 26719 |
| 20180102 | 7 | 12019 |
| 20180102 | 8 | 80074 |
First, I would like to check the sum of visits regardless of traffic_type:
SELECT date, SUM(visits) as visits_per_day
FROM visits_tbl
GROUP BY date
Here is the outcome:
| ymd | visits_per_day |
|:--------:|:--------------:|
| 20180101 | 2294563 |
| 20180102 | 2289145 |
| 20180103 | 2300367 |
| 20180104 | 2310256 |
| 20180105 | 2368098 |
| 20180106 | 2372257 |
| 20180107 | 2373863 |
| 20180108 | 2364236 |
However, if I want to check the specific day which the visits_per_day was the highest for each time aggregation (eg.: Month), I am struggling to retrieve the right output.
Here is what I did:
SELECT
(date div 100) as y_month, MAX(visits_per_day) as max_visit_per_day
FROM
(SELECT date, SUM(visits) as visits_per_day
FROM visits_tbl
GROUP BY date) as t1
GROUP BY
y_month
And here is the output of my query:
| y_month | max_visit_per_day |
|:-------:|:-----------------:|
| 201801 | 2435845 |
| 201802 | 2519000 |
| 201803 | 2528097 |
| 201804 | 2550645 |
However, I cannot know what was the exact day where the visits_per_day was the highest.
Desired output:
| y_month | max_visit_per_day | ymd |
|:-------:|:-----------------:|:--------:|
| 201801 | 2435845 | 20180130 |
| 201802 | 2519000 | 20180220 |
| 201803 | 2528097 | 20180325 |
| 201804 | 2550645 | 20180406 |
ymd would represent the day in which the visits_per_day was the highest.
This logic would be used in a dashboard with the help of programming in order to automatically select the time aggregation.
Can someone please help me?
This is a job for the structured part of structured query language. That is, you will write some subqueries and treat them as tables.
You already know how to find the number of visits per day. Let's add the month for each day to that query (http://sqlfiddle.com/#!9/a8455e/13/0).
SELECT date DIV 100 as month, date,
SUM(visits) as visits
FROM visits_tbl
GROUP BY date
Next you need to find the largest number of daily visits in each month. (http://sqlfiddle.com/#!9/a8455e/12/0)
SELECT month, MAX(visits) max_daily_visits
FROM (
SELECT date DIV 100 as month, date,
SUM(visits) as visits
FROM visits_tbl
GROUP BY date
) dayvisits
GROUP BY month
Then, the trick is retrieving the date on which that maximum occurred in each month. That requires a join. Without common table expressions (which MySQL lacks) you need to repeat the first subquery. (http://sqlfiddle.com/#!9/a8455e/11/0)
SELECT detail.*
FROM (
SELECT month, MAX(visits) max_daily_visits
FROM (
SELECT date DIV 100 as month, date,
SUM(visits) as visits
FROM visits_tbl
GROUP BY date
) dayvisits
GROUP BY month
) maxvisits
JOIN (
SELECT date DIV 100 as month, date,
SUM(visits) as visits
FROM visits_tbl
GROUP BY date
) detail ON detail.visits = maxvisits.max_daily_visits
AND detail.month = maxvisits.month
The outline of this rather complex query helps explain it. Instead of that subquery, we'll use an imaginary table called dayvisits.
SELECT detail.*
FROM (
SELECT month, MAX(visits) max_daily_visits
FROM dayvisits
GROUP BY date DIV 100
) maxvisits
JOIN dayvisits detail ON detail.visits = maxvisits.max_daily_visits
AND detail.month = maxvisits.month
You're seeking an extreme value for each month in the subquery. (This is a fairly standard sort of SQL operation.) To do that you find that value with a MAX() ... GROUP BY query. Then you join that to the subquery itself to find the other values corresponding to the extreme value.
If you did have common table expressions, the query would look like this. YOu might consider adopting the MySQL fork called MariaDB, which has CTEs.
WITH dayvisits AS (
SELECT date DIV 100 as month, date,
SUM(visits) as visits
FROM visits_tbl
GROUP BY date
)
SELECT dayvisits.*
FROM (
SELECT month, MAX(visits) max_daily_visits
FROM dayvisits
GROUP BY month
) maxvisits
JOIN dayvisits ON dayvisits.visits = maxvisits.max_daily_visits
AND dayvisits.month = maxvisits.month
[Query Check on MSSQL] its quick and efficient.
select visit_sum_day_wise.date
, visit_sum_day_wise.Max_Visits
, visit_sum_day_wise.traffic_type
, LAST_VALUE(visit_sum_day_wise.visits) OVER(PARTITION BY
visit_sum_day_wise.date ORDER BY visit_sum_day_wise.date ROWS BETWEEN
UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS max_visit_per_day
from (
select visits_tbl.date , visits_tbl.visits , visits_tbl.traffic_type
,max(visits_tbl.visits ) OVER ( PARTITION BY visits_tbl.date ORDER
BY visits_tbl.date ROWS BETWEEN UNBOUNDED PRECEDING AND 0
PRECEDING) Max_visits
from visits_tbl
) as visit_sum_day_wise
where visit_sum_day_wise.visits = (select max(visits_B.visits ) from
visits_tbl visits_B where visits_B.Date = visit_sum_day_wise.date )
enter image description here
Related
I have a table with 'ON' and 'OFF' values in column activity and another column datetime.
id(AUTOINCREMENT) id_device activity datetime
1 a ON 2017-05-26 22:00:00
2 b ON 2017-05-26 05:00:00
3 a OFF 2017-05-27 04:00:00
4 b OFF 2017-05-26 08:00:00
5 a ON 2017-05-28 12:00:00
6 a OFF 2017-05-28 15:00:00
I need to get total ON time by day
day id_device total_minutes_on
2017-05-26 a 120
2017-05-26 b 180
2017-05-27 a 240
2017-05-27 b 0
2017-05-28 a 180
2017-05-28 b 0
i have searched and tried answers for another posts, i tried TimeDifference and i get correct total time.
I don't find the way to get total time grouped by date
i appreciate your help
I'm not posting this as a definite answer rather it's an experiment for me and hopefully you'll find is useful in your case. Also I would like to mention that the MySQL database version I'm working with is quite old so the method I'm using is also very manual to say the least.
First of all lets extract your expected output:
The date value in day need to be repeated twice fro each of id_device a and b.
Minutes are calculated based on the activity; if activity is 'ON' until tomorrow, it needs to be calculated until the day end at 24:00:00 while the next day will calculate minutes until the activity is OFF.
What I come up with is this:
Creating condition (1):
SELECT * FROM
(SELECT DATE(datetime) dtt FROM mytable GROUP BY DATE(datetime)) a,
(SELECT id_device FROM mytable GROUP BY id_device) b
ORDER BY dtt,id_device;
The query above will return the following result:
+------------+-----------+
| dtt | id_device |
+------------+-----------+
| 2017-05-26 | a |
| 2017-05-26 | b |
| 2017-05-27 | a |
| 2017-05-27 | b |
| 2017-05-28 | a |
| 2017-05-28 | b |
+------------+-----------+
*Above will only work with all the dates you have in the table. If you want all date regardless if there's activity or not, I suggest you create a calendar table (refer: Generating a series of dates).
So this become the base query. Then I've added an outer query to left join the query above with the original data table:
SELECT v.*,
GROUP_CONCAT(w.activity ORDER BY w.datetime SEPARATOR ' ') activity,
GROUP_CONCAT(TIME_TO_SEC(TIME(w.datetime)) ORDER BY w.datetime SEPARATOR ' ') tr
FROM
-- this was the first query
(SELECT * FROM
(SELECT DATE(datetime) dtt FROM mytable GROUP BY DATE(datetime)) a,
(SELECT id_device FROM mytable GROUP BY id_device) b
ORDER BY a.dtt,b.id_device) v
--
LEFT JOIN
mytable w
ON v.dtt=DATE(w.datetime) AND v.id_device=w.id_device
GROUP BY DATE(v.dtt),v.id_device
What's new in the query is the addition of GROUP_CONCAT operation on both activity and time value extracted from datetime column which is converted into seconds value. You notice that in both of the GROUP_CONCAT there's a similar ORDER BY condition which is important in order to get the exact corresponding value.
The query above will return the following result:
+------------+-----------+----------+-------------+
| dtt | id_device | activity | tr |
+------------+-----------+----------+-------------+
| 2017-05-26 | a | ON | 79200 |
| 2017-05-26 | b | ON OFF | 18000 28800 |
| 2017-05-27 | a | OFF | 14400 |
| 2017-05-27 | b | (NULL) | (NULL) |
| 2017-05-28 | a | ON OFF | 43200 54000 |
| 2017-05-28 | b | (NULL) | (NULL) |
+------------+-----------+----------+-------------+
From here, I've added another query outside to calculate how many minutes and attempt to get the expected result:
SELECT dtt,id_device,
CASE
WHEN SUBSTRING_INDEX(activity,' ',1)='ON' AND SUBSTRING_INDEX(activity,' ',-1)='OFF'
THEN (SUBSTRING_INDEX(tr,' ',-1)-SUBSTRING_INDEX(tr,' ',1))/60
WHEN activity='ON' THEN 1440-(tr/60)
WHEN activity='OFF' THEN tr/60
WHEN activity IS NULL AND tr IS NULL THEN 0
END AS 'total_minutes_on'
FROM
-- from the last query
(SELECT v.*,
GROUP_CONCAT(w.activity ORDER BY w.datetime SEPARATOR ' ') activity,
GROUP_CONCAT(TIME_TO_SEC(TIME(w.datetime)) ORDER BY w.datetime SEPARATOR ' ') tr
FROM
-- this was the first query
(SELECT * FROM
(SELECT DATE(datetime) dtt FROM mytable GROUP BY DATE(datetime)) a,
(SELECT id_device FROM mytable GROUP BY id_device) b
ORDER BY a.dtt,b.id_device) v
--
LEFT JOIN
mytable w
ON v.dtt=DATE(w.datetime) AND v.id_device=w.id_device
GROUP BY DATE(v.dtt),v.id_device
--
) z
The last part I do is if the activity value have both ON and OFF on the same day then (OFF-ON)/60secs=total minutes. If activity value is only ON then minutes value for '24:00:00' > 24 hr*60 min= 1440-(ON/60secs)= total minutes, and if activity only OFF, I just convert seconds to minutes because the day starts at 00:00:00 anyhow.
+------------+-----------+------------------+
| dtt | id_device | total_minutes_on |
+------------+-----------+------------------+
| 2017-05-26 | a | 120 |
| 2017-05-26 | b | 180 |
| 2017-05-27 | a | 240 |
| 2017-05-27 | b | 0 |
| 2017-05-28 | a | 180 |
| 2017-05-28 | b | 0 |
+------------+-----------+------------------+
Hopefully this will give you some ideas. ;)
How do I find the oldest date from the most recent set of records in MySQL?
Consider the below
+--------+-----------+------------+
| ID | PRODUCTID | DATEACTIVE |
+--------+-----------+------------+
| 546502 | 23405 | 2017-07-20 |
| 545075 | 23405 | 2017-07-19 |
| 543651 | 23405 | 2017-07-18 |
| 456783 | 23405 | 2017-01-04 |
| 456782 | 23405 | 2017-01-03 |
| 456781 | 23405 | 2017-01-02 |
| 456780 | 23405 | 2017-01-01 |
| 65453 | 23405 | 2016-07-19 |
| 65452 | 23405 | 2016-07-18 |
+--------+-----------+------------+
I want to be able to find the most recent time a product was activated (18th July 2017)
Pls next time follow what Sloan said in the comments.
If I understood your question, you could use something like this.
The query, in the inner part, "groups" the consecutive dates, giving a consecutive number for each "group".
Then select only the "first" group (the more recent) and numbers the rows in ascending order by date.
Finally, the row with the first "row number" is selected.
SELECT *
FROM (
SELECT #rn:=#rn+1 AS RN,
A.*
FROM (
SELECT #gr:= IF(#prev_date=DATE_ADD(dateactive, INTERVAL +1 DAY), #gr, #gr+1) AS GR
,TN.*
,#prev_date:=dateactive AS PD
FROM TN
CROSS JOIN (SELECT #gr:=0, #prev_date:=(SELECT MAX(dateactive) FROM TN)) R
ORDER BY DATEACTIVE DESC
) A
CROSS JOIN (SELECT #rn:=0) R2
WHERE GR=1
ORDER BY DATEACTIVE
)B
WHERE RN=1;
Output:
RN GR id productid dateactive PD
1 1 543651 23405 18.07.2017 00:00:00 2017-07-18
SELECT MAX(DATEACTIVE) as most_recent_time
FROM `YOUR_TABLE_NAME`
WHERE PRODUCTID = 'YOUR_ID'
You could use ORDER BY to sort the data by date, See code below:
SELECT *
FROM `YOUR_TABLE_NAME`
WHERE `PRODUCTID` = 'YOUR_ID'
ORDER BY `DATEACTIVE` DESC;
Hi everybody,
I have this visitors table:
ID | Date | Purpose
1 | 20/10/2016 | Purpose1
2 | 22/10/2016 | Purpose1
3 | 25/10/2016 | Purpose2
4 | 12/11/2016 | Purpose1
5 | 14/11/2016 | Purpose2
6 | 16/11/2016 | Purpose2
Currently I'm using this query:
select case
when date like '%/10/2016' then '10/2016'
when date like '%/11/2016' then '11/2016'
end as month, count(*) as total
from visitors
where
date like '%/10/2016'
or date like '%/11/2016'
GROUP by month
I can only get month and total column count from query above. How can I achieve this output?
Month | Total | Purpose1 | Purpose2
10/2016 | 3 | 2 | 1
11/2016 | 3 | 1 | 2
Thanks!
Consider the following...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,Date DATE NOT NULL
,Purpose VARCHAR(12) NOT NULL
);
INSERT INTO my_table VALUES
(1,'2016-10-20','Purpose1'),
(2,'2016-10-22','Purpose1'),
(3,'2016-10-25','Purpose2'),
(4,'2016-11-12','Purpose1'),
(5,'2016-11-14','Purpose2'),
(6,'2016-11-16','Purpose2');
SELECT DATE_FORMAT(date,'%Y-%m') month
, SUM(purpose = 'purpose1') purpose1
, SUM(purpose = 'purpose2') purpose2
, COUNT(*) total
FROM my_table
GROUP
BY month;
+---------+----------+----------+-------+
| month | purpose1 | purpose2 | total |
+---------+----------+----------+-------+
| 2016-10 | 2 | 1 | 3 |
| 2016-11 | 1 | 2 | 3 |
+---------+----------+----------+-------+
..or (and in my view, better, provided you have access to application code)...
SELECT DATE_FORMAT(date,'%Y-%m') month
, purpose
, COUNT(*) total
FROM my_table
GROUP
BY month
, purpose;
+---------+----------+-------+
| month | purpose | total |
+---------+----------+-------+
| 2016-10 | Purpose1 | 2 |
| 2016-10 | Purpose2 | 1 |
| 2016-11 | Purpose1 | 1 |
| 2016-11 | Purpose2 | 2 |
+---------+----------+-------+
Transposing tables isn't very fast. It is better to do so in some small program.
If you do a
select case
when date like '%/10/2016' then '10/2016'
when date like '%/11/2016' then '11/2016'
end as month, count(*) as total, Purpose
from visitors
where
date like '%/10/2016'
or date like '%/11/2016'
GROUP by month, Purpose
You'll have a good starting point.
You might need to add an ORDER BY clause (depending on your DBMS).
If (and only if) you only have two purposes in your table and the table isn't huge in size, you can create two views and join them.
I have a table like this:
| id | date | user_id |
----------------------------------------------------
| 1 | 2008-01-01 | 10 |
| 2 | 2009-03-20 | 15 |
| 3 | 2008-06-11 | 10 |
| 4 | 2009-01-21 | 15 |
| 5 | 2010-01-01 | 10 |
| 6 | 2011-06-01 | 10 |
| 7 | 2012-01-01 | 10 |
| 8 | 2008-05-01 | 15 |
I’m looking for a solution how to select user_id where the difference between MIN and MAX dates is more than 3 yrs. For the above data I should get:
| user_id |
-----------------------
| 10 |
Anyone can help?
SELECT user_id
FROM mytable
GROUP BY user_id
HAVING MAX(`date`) > (MIN(`date`) + INTERVAL '3' YEAR);
Tested here: http://sqlize.com/MC0618Yg58
Similar to bernie's approach, I'd keep date formats native. I'd also probably list the MAX first as to avoid an ABS call (secure a positive number is always returned).
SELECT user_id
FROM my_table
WHERE DATEDIFF(MAX(date),MIN(date)) > 365
DATEDIFF just returns delta (in days) between two given date fields.
SELECT user_id
FROM (SELECT user_id, MIN(date) m0, MAX(date) m1
FROM table
GROUP by user_id)
HAVING EXTRACT(YEAR FROM m1) - EXTRACT(YEAR FROM m0) > 3
SELECT A.USER_ID FROM TABLE AS A
JOIN TABLE AS B
ON A.USER_ID = B.USER_ID
WHERE DATEDIFF(A.DATE,B.DATE) > 365
I have a table that defines time intervals.
_______________________________
| id | description | start_date |
|____|_____________|____________|
| 1 | First | NULL |
| 3 | Third | 2009-12-18 |
| 5 | Second | 2009-10-02 |
| 4 | Last | 2009-12-31 |
|____|_____________|____________|
It stores only the start date, the end date is a day before next date that follows.
I would like to have the next result:
____________________________________________
| id | description | start_date | end_date |
|____|_____________|____________|____________|
| 1 | First | NULL | 2009-10-01 |
| 5 | Second | 2009-10-02 | 2009-12-17 |
| 3 | Third | 2009-12-18 | 2009-12-30 |
| 4 | Last | 2009-12-31 | NULL |
|____|_____________|____________|____________|
How should I write this query, since a row contains values from other rows?
(I think MySQL function DATE_SUB could be useful.)
SELECT d.id, d.description, MIN(d.start_date), MIN(d2.start_date) - INTERVAL 1
DAY AS end_date
FROM start_dates d
LEFT OUTER JOIN start_dates d2 ON ( d2.start_date > d.start_date OR d.start_date IS NULL )
GROUP BY d.id, d.description
ORDER BY d.start_date ASC
try
select id, description, start_date, end_date from
(
select #rownum_start:=#rownum_start+1 rank, id, description, start_date
from inter, (select #rownum_start:=0) p
order by start_date
) start_dates
left join
(
select #rownum_end:=#rownum_end+1 rank, start_date - interval 1 day as end_date
from inter, (select #rownum_end:=0) p
where start_date is not null
order by start_date
) end_dates
using (rank)
where inter is your table
This actually returns:
mysql> select id, description, start_date, end_date from ...
+----+-------------+------------+------------+
| id | description | start_date | end_date |
+----+-------------+------------+------------+
| 1 | First | NULL | 2009-10-01 |
| 5 | Second | 2009-10-02 | 2009-12-17 |
| 3 | Third | 2009-12-18 | 2009-12-30 |
| 4 | Last | 2009-12-31 | NULL |
+----+-------------+------------+------------+
4 rows in set (0.00 sec)
if you are using PHP, just calculate the previous date in the script. that's easier.
if not, would something like:
SELECT ID,description,start_date,start_date-1 AS end_date FROM ...
work?
UPDATE: it works partially, as it returns 20091224 for startdate 2009-12-25 but won't work for dates like 2009-12-01 etc.
try
Select d.Id, d.Description, d.Start_Date,
n.Start_Date - 1 EndDate
From Table d
Left Join Table n
On n.Start_Date =
(Select Min(Start_date)
From Table
Where Start_Date > Coalesce(d.Start_Date, '1/1/1900')