Query to select intervals from a table - mysql

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')

Related

MySql Delete Data older than 1 month but leave one entry per week and id (the one with the lowest value)

This is the table I am working with:
+---------------------+-----------
| Field | Type |
+---------------------+--------------+
| ID | binary(17) |
| MiscSensor_ID | binary(17) |
| rawValue | varchar(100) |
| RawValueUnitType_ID | int |
| timestamp | timestamp |
+---------------------+--------------+
Now my goal is to implement an event which deletes all entries older than a month BUT for each week I want to leave one entry per MiscSensor_ID (the one with the lowest rawValue).
I am this far:
CREATE EVENT delete_old_miscsensordatahistory
ON SCHEDULE EVERY 1 DAY
STARTS CURRENT_DATE + INTERVAL 1 DAY
DO
DELETE
FROM history
WHERE TIMESTAMPDIFF(DAY, timestamp,NOW()) > 31;
I need to do something like: delete if (value > minvalue) and group it in by MiscSensor_ID and 7 day periods but i am stuck right now on how to do that.
Any help would be much appreciated.
You can try using the ROW_NUMBER window function to match the rows which you don't want to delete. Records having row number equal to 1 will be those rows with the minimum "rawValue" for each combination of (week, sensorId).
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(
PARTITION BY MiscSensorId, WEEK(timestamp)
ORDER BY rawValue ) AS rn
FROM history
WHERE TIMESTAMPDIFF(DAY, timestamp,NOW()) > 31
)
DELETE
FROM history
INNER JOIN cte
ON history.ID = cte.ID
WHERE rn > 1;
This is how i implemented the event right now:
CREATE EVENT delete_old_miscsensordatahistory
ON SCHEDULE EVERY 1 DAY
STARTS CURRENT_DATE + INTERVAL 1 DAY
DO
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(
PARTITION BY MiscSensor_ID, WEEK(timestamp)
ORDER BY CAST(rawValue AS SIGNED) ) AS rn
FROM MiscSensorDataHistory
WHERE TIMESTAMPDIFF(DAY, timestamp,NOW()) > 31
)
DELETE MiscSensorDataHistory
FROM MiscSensorDataHistory
INNER JOIN cte
ON cte.ID = MiscSensorDataHistory.ID
WHERE rn > 1
Testing my method I found out that there are still entries with the same MiscSensor_ID and less than 7 days apart:
| 0x3939333133303037343939353436393032 | 0x3439303031303031303730303030303535 | 554 | 30 | 2022-02-17 23:09:21 |
| 0x3939333133303037343939313631333039 | 0x3439303031303031303730303030303535 | 554 | 30 | 2022-02-06 16:52:48 |
| 0x3939333133303037343938383835353239 | 0x3439303031303031303730303030303535 | 553 | 30 | 2022-01-30 08:21:55 |
| 0x3939333133303037343938383639333436 | 0x3439303031303031303730303030303535 | 554 | 30 | 2022-01-29 22:48:06 |
| 0x3939333133303037343937303734353537 | 0x3439303031303031303730303030303535 | 444 | 30 | 2021-12-26 06:12:07 |
| 0x3939333133303037343937303530363738 | 0x3439303031303031303730303030303535 | 446 | 30 | 2021-12-25 21:53:03 |
| 0x3939333133303037343936333034343238 | 0x3439303031303031303730303030303535 | 0 | 30 | 2021-12-14 13:08:04 |
| 0x3939333133303037343935393934303832 | 0x3439303031303031303730303030303535 | 415 | 30 | 2021-12-08 12:56:43
Any suggestions would be much appreciated.

How can I retrieve all the columns on a timerange aggregation?

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

Oldest date from most recent set of records

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;

How to select all rows that share a maximum value in a column using SQL

I have a table like this:
+----+---------+---------------------+
| id | user_id | start_date |
+----+---------+---------------------+
| 1 | 1 | 2014-02-01 00:00:00 |
| 2 | 1 | 2014-01-01 00:00:00 |
| 3 | 2 | 2014-01-01 00:00:00 |
| 4 | 2 | 2014-01-01 00:00:00 |
| 5 | 3 | 2015-01-01 00:00:00 |
+----+---------+---------------------+
how can I select all rows that, for each user, have:
start date before NOW() and
maximum start_date
so for sample rows, the output should be:
+----+---------+---------------------+
| id | user_id | start_date |
+----+---------+---------------------+
| 1 | 1 | 2014-02-01 00:00:00 | // this is a single maximum date within that user
| 3 | 2 | 2014-01-01 00:00:00 | // these two share maximum start date
| 4 | 2 | 2014-01-01 00:00:00 |
+----+---------+---------------------+
what I have so far is something like this:
SELECT t.* FROM ticket t
JOIN (
SELECT start_date, MAX(start_date) FROM ticket /* GROUP BY user_id */
) highest
ON t.start_date = highest.start_date
WHERE t.start_date <= NOW();
but this doesn't work as desired. Am I on good path?
You're on the right track, sort of.
In your derived table, you need to get the max date for each user id, so:
SELECT user_id,
MAX(start_date) as MaxDate
FROM ticket
GROUP BY user_id
Then you can join to that on start date and user id:
SELECT t.* FROM ticket t
JOIN (
SELECT user_id,
MAX(start_date) as MaxDate
FROM ticket
GROUP BY user_id
) highest
ON t.start_date = highest.maxdate
and t.user_id = highest.user_id
WHERE t.start_date <= NOW();
SQL Fiddle
_try this:
SELECT T.* FROM ticket AS T
JOIN (SELECT
[User_Id]
,MAX([Start_Date]) AS Start_Date
FROM ticket
WHERE Start_Date <= GETDATE()
GROUP BY User_Id) AS Grouped ON T.User_Id = Grouped.User_Id AND T.Start_Date = Grouped.Start_Date
ORDER BY Id
DROP TABLE #This

MySQL - how to select id where min/max dates difference is more than 3 years

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