mysql query - date subtraction - mysql

I am trying to query users who has been active in the last 3 years.
I am adjusting a query I have used for years that I know works fine. The only part of the query that I am trying to add on is the part below that is outside the grey code. I can't get this part correct.
The column that I am querying the date for (lastactivity) is stored in int(10) format and I don't think I will be changing it because it is a VBulletin column and I don't want that change to affect so many other queries.
$query = "SELECT user.userid AS id3, user.lastactivity AS lastactivity1, DATE_FORMAT(FROM_UNIXTIME(user.lastactivity), '%Y %m %d') AS 'lastactivity2', socialgroup.groupid AS id1, socialgroupicon.groupid AS id2, socialgroupicon.dateline AS picdate, name, zip1, city1, state1, socialgroup.dateline AS groupdate, creatoruserid, members, picturecount, visible, paid
FROM socialgroup
LEFT JOIN socialgroupicon
ON socialgroupicon.groupid=socialgroup.groupid
LEFT JOIN user
ON user.userid=socialgroup.creatoruserid
WHERE type != 'inviteonly' AND state1 = '$region' AND lastactivity2 < UNIX_TIMESTAMP(DATE_SUB(now(), INTERVAL 3 YEAR))
ORDER BY paid DESC, lastactivity2 DESC, id1 ASC
LIMIT 100
";

if you are trying to get entries that were active within the last 3 years, your comparison should be a greater than:
lastactivity2 > UNIX_TIMESTAMP(DATE_SUB(now(), INTERVAL 3 YEAR))
Time moves forward, newer times are a bigger number than older times.
What you probably thought was that you wanted to compare 3 years to the time interval between today and the last activity, but you are comparing absolute timestamps, not differences.

The solution was that I need to change the LEFT JOIN on the user table to an INNER JOIN since the lastactivity column, which I am adding to there WHERE clause, is in that table. (I also need to change it to "lastactivity" instead of "lastactivity2).
$query = "SELECT user.userid AS id3, user.lastactivity AS lastactivity1, DATE_FORMAT(FROM_UNIXTIME(user.lastactivity), '%Y %m %d') AS 'lastactivity2', socialgroup.groupid AS id1, socialgroupicon.groupid AS id2, socialgroupicon.dateline AS picdate, name, zip1, city1, state1, socialgroup.dateline AS groupdate, creatoruserid, members, picturecount, visible, paid
FROM socialgroup
INNER JOIN user
ON user.userid=socialgroup.creatoruserid
LEFT JOIN socialgroupicon
ON socialgroupicon.groupid=socialgroup.groupid
WHERE type != 'inviteonly' AND state1 = '$region' AND lastactivity > UNIX_TIMESTAMP(DATE_SUB(now(), INTERVAL 3 YEAR))
ORDER BY paid DESC, lastactivity2 DESC, id1 ASC
LIMIT 100
";

Related

Using SQL to get recent n days activity of every user in the table [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I have a table of game activity of users which looks something like this
So,for simplicity just consider account_id and date column. By now you might've understood that each record represents a player playing some game on a specific day. What I want to extract is the recent 15 days activity of every user counting backwards from his last game played. For eg,we have data that ranges from 4th April 2020 to 24 Sep 2020, Let's just say that a user played his last game on 20th Sep 2020 and hasn't played any game since then, so for that user I want his playing activity for the date ranging from 5th to 20th Sep(15 day back from his last game played) and I want to extract the same data for every user.
I've initially thought to implement this like ..... sort the table in descending order based on date and match the date with that specific account when that account_id appears for the first time(to create a dictionary in which key is account_id and value is last date he played) so that I can subtract 15 days from the value and filter the data for every account_id, but my co-worker isn't satisfied with that and is expecting to do all this within a single shot(using SQL query). Can someone guide me on how to do that. Thanks in advance :)
If I understood this correctly, you are basically looking for the MAX(Date) Grouped BY User as your starting (actually end) point.
It's easiest to put this in a subquery or CTE.
Then you can simply query your table again using the last date by user as your end date and calculate that date - 15 days as your start point.
This will retrieve all entries for users in the given period.
Example:
WITH BASE AS(
SELECT
MAX(Date) AS LastDate,
UserID
FROM GameActivity
GROUP BY UserID
)
SELECT
ga.UserID,
ga.Date
FROM GameActivity GA
JOIN BASE B ON b.UserID = ga.UserID
WHERE ga.Date >= DATE_SUB(b.LastDate, INTERVAL 15 DAY)
AND ga.Date <= b.LastDate
EDIT:
For getting the last 15 days regardless of actual dates, I would personally use a Window Function to count back
I split this into 2 CTEs to highlight the logic
WITH DistinctDates AS (
SELECT DISTINCT
user_id,
active_date
FROM userdata
),
DAYCount AS (
SELECT
user_id,
active_date,
COUNT(active_date) OVER (PARTITION BY user_id ORDER BY active_date DESC) AS ActiveDays
FROM DistinctDates
)
SELECT
dc.user_id,
ud.active_date,
dc.ActiveDays
FROM DayCount DC
JOIN userdata UD ON ud.user_id = dc.user_id AND ud.active_date = dc.active_date
WHERE ActiveDays BETWEEN 1 AND 15
ORDER BY dc.user_id, dc.ActiveDays ;
I tried this on MS SQL Server but MySQL should work the same
If you are running MySQL 8.0, you can do this with window functions:
select *
from (
select t.*, max(date) over(partition by account_id) max_date
from mytable t
) t
where date >= max_date - interval 15 day
In earlier versions, an alternative is a correlated subquery:
select *
from mytable t
where date >= (select max(t1.date) from mytable t1 where t1.account_id = t.account_id) - interval 15 day
Or using a join:
select *
from mytable t
inner join (select account_id, max(date) max_date from mytable group by account_id) m
on t.date >= m.max_date - interval 15 day

How to transform a NOT IN into a LEFT JOIN (or a NOT EXISTS)

I have the following query that is quite complex and even though I tried to understand how to do using various sources online, all the examples uses simple queries where mine is more complex, and for that, I don't find the solution.
Here's my current query :
SELECT id, category_id, name
FROM orders AS u1
WHERE added < (UTC_TIMESTAMP() - INTERVAL 60 SECOND)
AND (executed IS NULL OR executed < (UTC_DATE() - INTERVAL 1 MONTH))
AND category_id NOT IN (SELECT category_id
FROM orders AS u2
WHERE executed > (UTC_TIMESTAMP() - INTERVAL 5 SECOND)
GROUP BY category_id)
GROUP BY category_id
ORDER BY added ASC
LIMIT 10;
The table orders is like this:
id
category_id
name
added
executed
The purpose of the query is to list n orders (here, 10) that belong in different categories (I have hundreds of categories), so 10 category_id different. The orders showed here must be older than a minute ago (INTERVAL 60 SECOND) and never executed (IS NULL) or executed more than a month ago.
The NOT IN query is to avoid treating a category_id that has already been treated less than 5 seconds ago. So in the result, I remove all the categories that have been treated less than 5 seconds ago.
I've tried to change the NOT IN in a LEFT JOIN clause or a NOT EXISTS but the switch results in a different set of entries so I believe it's not correct.
Here's what I have so far :
SELECT u1.id, u1.category_id, u1.name, u1.added
FROM orders AS u1
LEFT JOIN orders AS u2
ON u1.category_id = u2.category_id
AND u2.executed > (UTC_TIMESTAMP() - INTERVAL 5 SECOND)
WHERE u1.added < (UTC_TIMESTAMP() - INTERVAL 60 SECOND)
AND (u1.executed IS NULL OR u1.executed < (UTC_DATE() - INTERVAL 1 MONTH))
AND u2.category_id IS NULL
GROUP BY u1.category_id
LIMIT 10
Thank you for your help.
Here's a sample data to try. In that case, there is no "older than 5 seconds" since it's near impossible to get a correct value, but it gives you some data to help out :)
Your query is using a column which doesn't exist in the table as a join condition.
ON u1.domain = u2.category_id
There is no column in your example data called "domain"
Your query is also using the incorrect operator for your 2nd join condition.
AND u2.executed > (UTC_TIMESTAMP() - INTERVAL 5 SECOND)
should be
AND u2.executed < (UTC_TIMESTAMP() - INTERVAL 5 SECOND)
as is used in your first query

Selecting multiple values within a timestamp interval

I have a (MySQL) table that looks like this:
`radiostation_id` varchar(36) NOT NULL,
`song_id` varchar(36) NOT NULL,
`date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`length` int(4) NOT NULL
What I want to do is to find every same song_id that has been played within an interval of 15 minutes. It could be that the song was played for example today 15:10 and then again 15:20. So it shouldnt be that i need to set the interval myself, it should check for any interval through the table and list all the songs and timestamps it happened.
It seems to me that you want a song that is played within a certain 15 minutes regardless of the day. There are date and time functions you can use to parse the information you want. For example, you can use TIME() to extract the time portion of your column. For example, you can search for any song played between 12:00 and 12:15 like this:
SELECT DISTINCT id
FROM myTable
WHERE TIME(timeColumn) BETWEEN '12:00:00' AND '12:15:00';
Here is a little SQL Fiddle I used to try it.
EDIT
Based on our discussions in the comments, you can use the following query. It will self join the table on the condition that the song_id matches (so you can compare individual songs), the condition that the time is not exactly the same (so no occurrence is counted twice) and on the condition that the interval in the second table is within +/- 15 minutes of the first like this:
SELECT m.song_id, m.timecolumn
FROM myTable m
JOIN myTable mt
ON m.song_id = mt.song_id
AND m.timeColumn != mt.timeColumn
AND mt.timeColumn BETWEEN DATE_SUB(m.timeColumn, INTERVAL 15 MINUTE) AND DATE_ADD(m.timeColumn, INTERVAL 15 MINUTE);
Here is an updated Fiddle.
It's not entirely clear what you are asking.
If you want to find occurrences of the same song_id, where a second row with the same song_id has a date value within 15 minutes of the first row, one way to get that would be a query like this:
SELECT t.song_id
FROM looks_like_this t
WHERE EXISTS ( SELECT 1
FROM looks_like_this s
WHERE s.song_id = t.song_id
AND s.date >= t.date
AND s.date <= t.date + INTERVAL 15 MINUTE
AND NOT (s.radiostation_id = t.radiostation_id AND s.date = t.date)
If you want to return a unique list of song_id, then add either DISTINCT keyword or a GROUP BY t.song_id clause.
Another way to get the result would be use a JOIN operation:
SELECT t.song_id
FROM looks_like_this t
JOIN looks_like_this s
ON s.song_id = t.song_id
AND s.date >= t.date
AND s.date <= t.date + INTERVAL 15 MINUTE
AND NOT (s.radiostation_id = t.radiostation_id AND s.date = t.date)
GROUP BY t.song_id
The following will show you all the rows where the last played is in the last 15 minutes.
SELECT * FROM myTable
WHERE date>= NOW() - INTERVAL 15 MINUTE
Note that using date in a table column is bad practice as it is a reserved mysql word.
You just need to list out all the songs, when they were played and connect to the same song if it was played again within 15 minutes later.
select pt1.ID, pt1.StartDate, pt2.StartDate StartDate2
from PlayTimes pt1
join PlayTimes pt2
on pt2.ID = pt1.ID
and pt2.StartDate > pt1.StartDate
and pt2.StartDate <= Date_Add( pt1.StartDate, interval 15 minute );
You didn't mention if the radio station played any role. The query above will return a song if it was play again within 15 minutes even on a different station. This makes sense. If you need only those songs played again within 15 minutes on the same station, then add the station id to the join criteria. Would a station ever do that (play the same song twice within 15 minutes)? Or are you checking to make sure a station never does that?

Looping over a single column in mysql

I have a table that looks like this and i want to know the number of entries that are registered over a six hour time period and display that period which has max number of entries.
Time
09:42:29
10:37:28
15:18:49
15:28:34
16:43:51
18:14:10
18:26:06
18:26:14
So for each element in Time column, i will include a 6 hour period starting from that element and count how many entries in that column will fall in that period.
Ex 09:42:29 will have the end period has 15:42:29 and it should have count as 4 (09:42:29,10:37:28
15:18:49,15:28:34).
So do this for each element in Time Column and whichever element has max count, that will be the starting time of the period and display the start and end period accordingly.
Help me with writing a mysql query for this. Thank You!!!
Hope it helps
select
T.TimeStart,
T.TimeEnd,
COUNT(*)
from (
select
T.Time TimeStart,
date_add(T.Time,INTERVAL 6 HOUR) TimeEnd
from TimeTable T
) T
inner join TimeTable T2 on
T2.Time between T.TimeStart and T.TimeEnd
group by
T.TimeStart,
T.TimeEnd
The code below is for MSSQL but it works as expected and should give you some guidelines how the example above could be used
WITH TimeTable([Time]) AS (
select
CONVERT(DATETIME,a.a)
from (
values
('09:42:29'),
('10:37:28'),
('15:18:49'),
('15:28:34'),
('16:43:51'),
('18:14:10'),
('18:26:06'),
('18:26:14'))a(a)
)
select
convert(time(7),T.TimeStart)TimeStart,
convert(time(7),T.TimeEnd)TimeEnd,
COUNT(*) [Ocorrences]
from (
select
T.Time TimeStart,
DATEADD(HOUR,6,T.Time) TimeEnd
from TimeTable T
) T
inner join TimeTable T2 on
T2.Time between T.TimeStart and T.TimeEnd
group by
T.TimeStart,
T.TimeEnd

Getting the newest record from a database Multiple queries

I have a mysql database with vehicles records. I need a fast query that will return the newest records of those records that were updated within the last 4 minutes. For example vehicle "A" may be updated several times a minute so it will appear many times within the last 4min. Same with vehicle B C etc. I need only the most recent entries for each vehicle within a 4 min window. I have tried like this
SELECT *
FROM yourtable AS a
WHERE a.ts =
(SELECT MAX(ts)
FROM yourtable AS b
WHERE b.ts > NOW() - INTERVAL 5 MINUTE
AND b.name = a.name)
but it takes too long to produce results >10seconds.
You don't need the self-join.
select max(ts), name from Table1
where ts > NOW() - INTERVAL 5 MINUTE
group by name
To get all the rows for the latest updates and not only the name and timestamp:
SELECT t.*
FROM
TableX AS t
JOIN
( SELECT name
, MAX(ts) AS maxts
FROM TableX
WHERE ts > NOW() - INTERVAL 4 MINUTE
GROUP BY name
) AS grp
ON grp.name = t.name
AND grp.maxts = t.ts
You'll need at least an index on the timestamp column for this query.