MySQL - Date Difference and Flags - mysql

I am very new to MySQL and currently working on a table with three columns: trx_id, user_id, last_activity. (Churn Analysis)
tbl_activity:
The table capture activity of users. I am finding it difficulty in performing two tasks.
1) I would like to see two new columns through SQL query
date difference between subsequent transactions.
flag based on condition > 30 days.
Desired table:
2) One of the objectives of this study is to identify when (date) a customer churned. Ideally in my case it would be the 31st day since last activity. Any way to arrive at this date?
I am new to SQL learning and finding it difficult to address SQL queries for the above tasks.

Try this:
For SQL Server:
CREATE TABLE #tbl_activity(Trx_ID INT, User_Id INT, Last_Activity DATETIME)
INSERT INTO #tbl_activity VALUES(1,1100,'2015-06-08')
INSERT INTO #tbl_activity VALUES(2,1100,'2015-06-10')
INSERT INTO #tbl_activity VALUES(3,1100,'2015-06-10')
INSERT INTO #tbl_activity VALUES(4,1100,'2015-06-12')
INSERT INTO #tbl_activity VALUES(5,1100,'2015-06-13')
INSERT INTO #tbl_activity VALUES(6,1100,'2015-06-14')
INSERT INTO #tbl_activity VALUES(7,1100,'2015-09-25')
SELECT T1.Trx_ID, T1.User_Id, T1.Last_Activity
,DATEDIFF(DAY, T1.Last_Activity, T2.Last_Activity) days_Diff
,CASE WHEN DATEDIFF(DAY, T1.Last_Activity, T2.Last_Activity) >30 THEN 1 ELSE 0 END Flag
FROM #tbl_activity T1
LEFT JOIN #tbl_activity T2 ON T1.Trx_ID = T2.Trx_ID-1
DROP TABLE #tbl_activity
For MySQL:
CREATE TABLE tbl_activity(Trx_ID INT, User_Id INT, Last_Activity DATETIME)
INSERT INTO tbl_activity VALUES(1,1100,'2015-06-08')
INSERT INTO tbl_activity VALUES(2,1100,'2015-06-10')
INSERT INTO tbl_activity VALUES(3,1100,'2015-06-10')
INSERT INTO tbl_activity VALUES(4,1100,'2015-06-12')
INSERT INTO tbl_activity VALUES(5,1100,'2015-06-13')
INSERT INTO tbl_activity VALUES(6,1100,'2015-06-14')
INSERT INTO tbl_activity VALUES(7,1100,'2015-09-25')
SELECT T1.Trx_ID, T1.User_Id, T1.Last_Activity
,DATEDIFF(T2.Last_Activity, T1.Last_Activity) days_Diff
,CASE WHEN DATEDIFF(T2.Last_Activity, T1.Last_Activity) >30 THEN 1 ELSE 0 END Flag
FROM tbl_activity T1
LEFT JOIN tbl_activity T2 ON T1.Trx_ID = T2.Trx_ID-1
DROP TABLE tbl_activity
Try this in #SQL Fiddle
Output:
Trx_ID User_Id Last_Activity days_Diff Flag
1 1100 2015-06-08 00:00:00.000 2 0
2 1100 2015-06-10 00:00:00.000 0 0
3 1100 2015-06-10 00:00:00.000 2 0
4 1100 2015-06-12 00:00:00.000 1 0
5 1100 2015-06-13 00:00:00.000 1 0
6 1100 2015-06-14 00:00:00.000 103 1
7 1100 2015-09-25 00:00:00.000 NULL 0

Related

Delete all SQL rows except one for a Group

I have a table like this:
Schema (MySQL v5.7)
CREATE TABLE likethis
(`id` int, `userid` int, `date` DATE)
;
INSERT INTO likethis
(`id`, `userid`, `date`)
VALUES
(1, 1, "2021-11-15"),
(2, 2, "2021-11-15"),
(3, 1, "2021-11-13"),
(4, 3, "2021-10-13"),
(5, 3, "2021-09-13"),
(6, 2, "2021-09-13");
id
userid
date
1
1
2021-11-15
2
2
2021-11-15
3
1
2021-11-13
4
3
2021-10-13
5
3
2021-09-13
6
2
2021-09-13
View on DB Fiddle
I want to delete all records which are older than 14 days, EXCEPT if the user only has records which are older - than keep the "newest" (biggest "id") row for this user.
Desired target after that action shall be:
id
userid
date
1
1
2021-11-15
2
2
2021-11-15
3
1
2021-11-13
4
3
2021-10-13
i.e.: User ID 1 only has records within the last 14 days: Keep all of them. User ID has a record within the last 14 days, so delete ALL his records which are older than 14 days. User ID 3 has only "old" records, i.e. older than 14 days - so keep only the one newest of those records, even though it's older than 14 days.
I thought of something like a self join with a subquery where I group by user-id ... but can't really get to it ...
This query could work
DELETE b
FROM likethis a
JOIN likethis b ON a.`userid` = b.`userid` AND a.`date` > b.`date`
WHERE b.`date` < NOW() - INTERVAL 14 DAY
I believe you can use case function in MySql
For Example -
SELECT TableID, TableCol,
CASE
WHEN Date > 30 THEN "Delete statement"
ELSE "Dont Delete (Record is not 30"
END
FROM TableName;
Suggested link:
https://www.w3schools.com/sql/func_mysql_case.asp
https://dev.mysql.com/doc/refman/5.7/en/case.html
Hope this helps...

self join providing wrong answers

Hypothetical data - tbl1:
orderID
SupplierID
Status
Reason
Created At
29
1
22-01-2021 22:08
29
2
22-01-2021 22:10
29
265
3
23-01-2021 06:25
29
2
sometext
23-01-2021 12:25
29
1605
3
24-01-2021 10:21
29
1605
4
anothertext
24-01-2021 11:03
29
324
3
26-01-2021 06:43
29
2
sometext
26-01-2021 12:43
29
1564
3
26-01-2021 16:09
Desired result:
orderID
SupplierID
Status
Reason
Created At
29
265
3
23-01-2021 06:25
29
324
3
26-01-2021 06:43
My query -
select distinct tbl1.orderID, tbl1.created_at, tbl2.supplierID
from tblxyz as tbl1 left join tblxyz as tbl2
on tbl1.orderID = tbl2.orderID
where tbl1.status=2 and tbl1.reason='sometext' and tbl2.status=3 and tbl1.created_at < (tbl2.created_at + INTERVAL 1 DAY)
group by tbl2.supplierID
I am unable to figure out where is my query wrong.
You can try to use LAG window function to get previous status and reason, then do your judgment.
Schema (MySQL v8.0)
CREATE TABLE tblxyz(
orderID int,
SupplierID INT,
Status INT,
Reason VARCHAR(50),
CreatedAt DATETIME
);
INSERT INTO tblxyz VALUES (29,NULL, 1,'','2021-01-22 22:08');
INSERT INTO tblxyz VALUES (29,NULL, 2,'','2021-01-22 22:10');
INSERT INTO tblxyz VALUES (29,265 , 3,'','2021-01-23 06:25');
INSERT INTO tblxyz VALUES (29,NULL, 2,'sometext','2021-01-23 12:25');
INSERT INTO tblxyz VALUES (29,1605, 3,'','2021-01-24 10:21');
INSERT INTO tblxyz VALUES (29,1605, 4,'anothertext','2021-01-24 11:03');
INSERT INTO tblxyz VALUES (29,324 , 3,'','2021-01-26 06:43');
INSERT INTO tblxyz VALUES (29,NULL, 2,'sometext','2021-01-26 12:43');
INSERT INTO tblxyz VALUES (29,1564, 3,'','2021-01-26 16:09');
Query #1
SELECT t1.orderID,t1.SupplierID,t1.Status,t1.Reason,t1.PreviewCreatedAt
FROM (
select *,
LAG(Status) OVER(PARTITION BY orderID ORDER BY CreatedAt) PreviewStatus,
LAG(Reason) OVER(PARTITION BY orderID ORDER BY CreatedAt) PreviewReason,
LAG(CreatedAt) OVER(PARTITION BY orderID ORDER BY CreatedAt) PreviewCreatedAt
from tblxyz
) t1
WHERE PreviewStatus = 2 AND Status = 3 AND PreviewReason='sometext';
orderID
SupplierID
Status
Reason
PreviewCreatedAt
29
1605
3
2021-01-23 12:25:00
29
1564
3
2021-01-26 12:43:00
View on DB Fiddle
Do you need in this:
SELECT t2.*
FROM tbl1 t1
JOIN tbl1 t2 USING (orderID)
WHERE t1.Status = 2
AND t2.Status = 3
AND t1.Reason = 'sometext'
AND t2.Created_At BETWEEN t1.Created_At - INTERVAL 1 DAY AND t1.Created_At
ORDER BY t1.Created_At;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=543d9150d1b23a01df01e0223f3fb3f2

SQL query which selects the last row from each day

I need your help. I have a table (senosrId, time, data), and I need to select the latest data from each day for one of the sensors for the latest 10 days.
For MS SQL, tested, compiled:
Test table:
CREATE TABLE [dbo].[DataTable](
[SensorId] [int] NULL,
[SensorTime] [datetime] NULL,
[SensorData] [int] NULL
)
Run several times to insert demo data:
insert into DataTable (SensorId, SensorTime, SensorData) select 1, getdate() - 15*rand(), convert(int, rand()*100)
Get last value for each of the last 10 days (actual answer):
select top 10 *
from DataTable
inner join ( -- max time for each day
select SensorId, max(SensorTime) as maxtime, convert(varchar(10), SensorTime, 112) as notneededcolumn
from DataTable
group by SensorId, convert(varchar(10), SensorTime, 112)
) lastvalues on lastvalues.maxtime=DataTable.SensorTime and lastvalues.SensorId=DataTable.SensorId
where DataTable.SensorId=1
order by DataTable.SensorTime desc
Example output:
1 2017-05-17 21:07:14.840 54 1 2017-05-17 21:07:14.840 20170517
1 2017-05-16 23:35:37.220 94 1 2017-05-16 23:35:37.220 20170516
1 2017-05-14 22:35:48.970 8 1 2017-05-14 22:35:48.970 20170514
1 2017-05-13 14:56:34.557 94 1 2017-05-13 14:56:34.557 20170513
1 2017-05-12 22:28:55.400 89 1 2017-05-12 22:28:55.400 20170512

Self join table current row and next row horizontally in MySQL

I am implementing a functionality wherein I have to delete current row based on values present in next row of same table.
I have records with columns: id, created_at and mark.
I need delete all records,
WHERE currentrow.mark != nextrow.mark or (currentrow.mark = nextrow.mark and currentrow.created_at= '2000-01-01 00:00:00.000')
i.e. only records with next rows have not same mark or records with next row have same mark and created_at = '2000-01-01 00:00:00.000'
id created_at mark
235 1990-01-01 00:00:00.000 5 /delete
236 1990-01-01 00:00:00.000 5 /delete
237 1990-01-01 00:00:00.000 5
238 2016-10-10 23:45:40.000 5
id created_at mark
312 1990-01-01 00:00:00.000 8 /delete
313 2016-01-09 18:00:00.000 6
314 1990-01-01 00:00:00.000 4 /delete
315 1990-01-01 00:00:00.000 7
316 2016-10-10 23:45:40.000 7
Kindly help to retrieve table every row joined next rows of same table horizontally in result set.
One way to join the next row is
INNER JOIN `tablename` AS `next` ON `next`.`id` = (
SELECT MIN(id) FROM `tablename` WHERE `tablename`.`id` > `current`.`id`
)
AND (`next`.`mark` != `current`.`mark`
OR `next`.`created_at` = '2000-01-01 00:00:00.000') // maybe 1990?
I did not understand the delete conditions completely. But I will give answer to your last question
"Kindly help to retrieve table every row joined next rows of same table horizontally in result set."
SELECT * FROM
(SELECT (#rowid1 := #rowid1 + 1) firstSeq, id firsttableid, created_at firsttablecreated_at, mark firsttablemark FROM `mytable`
JOIN (SELECT #rowid1 := 0) a) table1
LEFT JOIN (SELECT (#rowid2 := #rowid2 + 1) secondSeq, id secondtableid, created_at secondtablecreated_at, mark secondtablemark FROM `mytable`
JOIN (SELECT #rowid2 := 0) a) table2 ON table1.firstSeq = table2.secondSeq - 1 and **(your conditions)**
This will give you the result set as you need. Now you may add your required conditions to convert to delete

Convert frequency table into a schedule view in mySQL

I have a mySQL table with the next contents:
ID START FREQUENCY REPETITIONS RESOURCE
--------------------------------------------------
1 24/02/2014 daily 5 10
2 24/02/2014 yearly 2 11
Is there any easy way to transform this into a view such as:
ID DATE RESOURCE
-------------------------
1 24/02/2014 10
1 25/02/2014 10
1 26/02/2014 10
1 27/02/2014 10
1 28/02/2014 10
2 24/02/2014 11
2 24/02/2015 11
Thanks
If you can have a limited number of repetitions, you can create a Numbers table like this:
CREATE TABLE numbers (
num INT,
i INT
);
INSERT INTO numbers VALUES
(1,1),
(2,1),
(2,2),
(3,1),
(3,2),
(3,3),
(4,1),
(4,2),
(4,3),
(4,4),
...
then you can use a JOIN:
SELECT
ID,
`START` + INTERVAL CASE WHEN FREQUENCY='daily' THEN i-1 ELSE 0 END DAY
+ INTERVAL CASE WHEN FREQUENCY='yearly' THEN i-1 ELSE 0 END YEAR
as `Date`,
RESOURCE
FROM
yourtable INNER JOIN numbers
ON yourtable.REPETITIONS = numbers.num
ORDER BY
ID, numbers.i
Please see fiddle here.