Query where another row's datetime is over 1 hour ago - mysql

I am trying to run this query against tickets table. ticket_updates table contains rows matching tickets.ticketnumber = ticket_updates.ticketnumber
I want to check for rows in tickets where the last row in ticket_updates.datetime is >= 1 hour ago.
The problem with the below is that it's picking up rows from ticket_updates where datetime is over 1 hour ago, because its in my WHERE clause, so it's completely ignoring the most recent row which in fact is only 10 minutes ago.
So I think I need to remote the datetime from my WHERE clause, but I'm not sure what to add to make it work.
SELECT * FROM tickets WHERE
(
status = 'Pending Response' AND
ticketnumber IN
(
SELECT ticketnumber FROM ticket_updates WHERE
type = 'customer_reminder_flag' AND
datetime < NOW() - INTERVAL 2 DAY
)
) OR
(
status = 'Pending Completion' AND
ticketnumber IN (
SELECT ticketnumber FROM ticket_updates WHERE
type = 'update' AND
datetime < NOW() - INTERVAL 1 HOUR
ORDER BY datetime DESC
)
)

You can re-write your query using EXISTS as follows:
SELECT t.*
FROM tickets t join ticket_updates tu on t.ticketnumber = tu.ticketnumber
WHERE t.status = 'Pending Completion'
AND tu.type = 'update'
AND tu.datetime < NOW() - INTERVAL 1 HOUR
AND NOT EXISTS
(SELECT 1 FROM ticket_updates tuu
WHERE tu.ticketnumber = tuu.ticketnumber
AND tuu.type = 'update'
AND tuu.datetime < NOW() - INTERVAL 1 HOUR
AND tuu.datetime > tu.datetime
)
If you are running on mysql 8.0+ then you can use analytical function as follows:
SELECT * FROM
(SELECT t.*, row_number() over (partition by tu.ticketnumber order by tu.datetime) as rn
FROM tickets t join ticket_updates tu on t.ticketnumber = tu.ticketnumber
WHERE t.status = 'Pending Completion'
AND tu.type = 'update'
AND tu.datetime < NOW() - INTERVAL 1 HOUR) t
WHERE RN = 1

I want to check for rows in tickets where the last row in ticket_updates.datetime is >= 1 hour ago.
For this problem statement, the code would use not exists:
select t.*
from tickets t
where not exists (select 1
from ticket_updates tu
where tu.ticketnumber = t.ticketnumber and
tu.datetime > now() - interval 1 hour
);
This returns tickets that have had more than one hour since the last update.
It is unclear to me what this problem statement has to do with the code you have shown.

Related

How to change format of the MySQL result?

I have a complex mysql query language, including several sub queries and my final result is as below. There is something that I am dealing with it and I can't solve it and this is a way result is being presented. I am wondering to know how can i change the structure of the result in a way that the result is being presented only in one row and I don't want to see NULL fields. I mean something like below
This is mysql query
select count(*) as userRetentionSameDay, null as 'userRetentionDiffDay' from (SELECT date(`timestamp`), `user_xmpp_login`
FROM table1
WHERE DATE(`timestamp` ) = CURDATE() - INTERVAL 1 DAY) as res1
right join (select date(ts), user
from table2
WHERE DATE(ts ) = CURDATE() - INTERVAL 1 DAY
and product_id REGEXP ("^(europe+$" )) as lej1
on lej1.user = res1.`user_xmpp_login`
where res1.`user_xmpp_login` IS not NULL
union all
select null as 'userRetentionSameDay', count(*) as userRetentionDiffDay from (SELECT date(`timestamp`), `user_xmpp_login`
FROM table1
WHERE DATE(`timestamp` ) = CURDATE() - INTERVAL 1 DAY) as res1
right join (select date(ts), user
from table2
WHERE DATE(ts ) = CURDATE() - INTERVAL 1 DAY
and product_id REGEXP ("^(europe+$" )) as lej2
on lej2.user = res1.`user_xmpp_login`
where res1.`user_xmpp_login` IS NULL;
What are the recommended solutions to doing that?
try this.
SELECT A.userRetentionSameDay,B.userRetentionDiffDay FROM (
SELECT COUNT() AS userRetentionSameDay FROM
(
SELECT DATE(timestamp), user_xmpp_login
FROM table1
WHERE DATE(timestamp ) = CURDATE() - INTERVAL 1 DAY) AS res1
RIGHT JOIN (SELECT DATE(ts), USER
FROM table2
WHERE DATE(ts ) = CURDATE() - INTERVAL 1 DAY
AND product_id REGEXP ("^(europe+$" )) AS lej1
ON lej1.user = res1.user_xmpp_login
WHERE res1.user_xmpp_login IS NOT NULL
) A,
(
SELECT COUNT() AS userRetentionDiffDay FROM (
SELECT DATE(timestamp), user_xmpp_login
FROM table1
WHERE DATE(timestamp ) = CURDATE() - INTERVAL 1 DAY
) AS res1
RIGHT JOIN (SELECT DATE(ts), USER
FROM table2
WHERE DATE(ts ) = CURDATE() - INTERVAL 1 DAY
AND product_id REGEXP ("^(europe+$" )
) AS lej2
ON lej2.user = res1.user_xmpp_login
WHERE res1.user_xmpp_login IS NULL
) B;

how to find duplicate records in a table within a predefined time period in sql

For example, i have following table
Mobile number Timestamp
123456 17-09-2015 11:30
455677 17-09-2015 12:15
123456 17-09-2015 12:25
453377 17-09-2015 13:15
If now is 11:30, I want to scan my table and find rows with the same numbers within the past 1 hour.
That's my SQL statement:
select a.number, a.time
from mytable a inner join
(select number, time
from mytable b
where time>=now()-Interval 1 hour and time<=now ()
group by number
Having count(*) > 1
) b
on a.number = b.number and a.time = b.time
I want to find duplicate rown with the same numbers happening within 1 hour. I should output the number and timestamp.
How about just using exists?
select t.*
from mytable t
where t.time >= now() - Interval 1 hour and
t.time <= now() and
exists (select 1
from mytable t2
where t2.number = t.number and
t2.time >= now() - Interval 1 hour and
t2.time <= now () and
t2.time <> t.time
);
However, I suspect that the problem with your query is the join to time. Just remove the time from the subquery and the on clause and you will get all numbers. Alternatively, use group by:
select t.number, group_concat(time)
from mytable t
where t.time >= now() - Interval 1 hour and
t.time <= now()
group by t.number
having count(*) > 1;

Is this possible with mysql?

First of all: sorry for the title, but maybe I will find a better one later.
I asked this some minutes ago, but since I was not able to describe what I want I try it again :)
Here is my table structure:
http://sqlfiddle.com/#!2/b25f9/37
The table is used to store user sessions.
Out of this I would like to generate a stacked bar chart that should show how many active users I have. My idea was that I group the users based on their online-times of the last days like this
Lets say its friday:
Group B: Users that were online thursday (and today)
Group C: Users that were not online thursday but wednesday (and today)
Group D: Users that were not online thursday or wednesday but tuesday (and today)
Group E: Users that were not online thursday, wednesday or tuesday but last monday, sunday or saturday (and today)
Group A: Users that do not match the other groups (but were only today)
I only want to know the number of users in those groups (for a specific day)
a user can only be in ONE of these groups (for the same day)
Another Update: Accidently (by copy&paste) had starttime = ... or starttime = ... but it should be starttime = ... or endtime = ...
UPDATE:
To explain my query in more detail (in the final query there are even more comments):
First we simply got
SELECT
...
FROM gc_sessions s
WHERE DATE(starttime) = CURDATE() OR DATE(endtime) = CURDATE()
That's nothing more like saying "give me all users whose session started today or ended today". Having to consider those two times again and again makes the query a bit clumsy, but actually it's not that complicated.
So, usually we would use the COUNT() function to count something, obviously, but since we want "conditional counting", we simply use the SUM() function and tell it when to add 1 and when not.
SUM (CASE WHEN ... THEN 1 ELSE 0 END) AS a_column_name
The SUM() function examines now each row in the result set of sessions from today. So for each user in this result set we look if this user was online the date we specify. It doesn't matter how many times he/she was online, so for performance reasons we use EXISTS. With EXISTS you can specify a subquery which stops as soon as something is found, so it doesn't matter what it returns when something is found, as long as it's not NULL. So don't get confused why I selected 1. In the subquery we have to connect the user which is currently examined from the outer query with the user from the inner query (subquery) and specify the time window. If all criterias meet count 1 else 0 like explained before.
SUM(CASE WHEN
EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) = CURDATE() - INTERVAL 1 DAY)
OR (date(endtime) = CURDATE() - INTERVAL 1 DAY)))
THEN 1 ELSE 0 END) AS todayAndYesterday,
Then we make a column for each condition and voila, you have all you need in one query. So with your updated question your criteria has changed, we just have to add more rules:
SELECT
/*this is like before*/
SUM(CASE WHEN
EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) = CURDATE() - INTERVAL 1 DAY)
OR (date(endtime) = CURDATE() - INTERVAL 1 DAY)))
THEN 1 ELSE 0 END) AS FridayAndThursday,
SUM(CASE WHEN
EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) = CURDATE() - INTERVAL 2 DAY)
OR (date(endtime) = CURDATE() - INTERVAL 2 DAY)))
/*this one here is a new addition, since you don't want to count the users that were online yesterday*/
AND NOT EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) = CURDATE() - INTERVAL 1 DAY)
OR (date(endtime) = CURDATE() - INTERVAL 1 DAY)))
THEN 1 ELSE 0 END) AS FridayAndWednesdayButNotThursday,
SUM(CASE WHEN
EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) = CURDATE() - INTERVAL 3 DAY) /* minus 3 days to get tuesday*/
OR (date(endtime) = CURDATE() - INTERVAL 3 DAY)))
/*this is the same as before, we check again that the user was not online between today and tuesday, but this time we really use BETWEEN for convenience*/
AND NOT EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) BETWEEN CURDATE() - INTERVAL 2 DAY AND CURDATE() - INTERVAL 1 DAY)
OR (date(endtime) BETWEEN CURDATE() - INTERVAL 2 DAY AND CURDATE() - INTERVAL 1 DAY)))
THEN 1 ELSE 0 END) AS FridayAndTuesdayButNotThursdayAndNotWednesday,
.../*and so on*/
FROM gc_sessions s
WHERE DATE(starttime) = CURDATE() OR DATE(endtime) = CURDATE()
So, I hope you get the idea now. Any more questions? Feel free to ask.
end of update
Answer to previous version of question:
select
SUM(CASE WHEN EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) = CURDATE() - INTERVAL 1 DAY)
OR (date(starttime) = CURDATE() - INTERVAL 1 DAY)))
THEN 1 ELSE 0 END) AS todayAndYesterday,
SUM(CASE WHEN EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) BETWEEN CURDATE() - INTERVAL 2 DAY AND CURDATE() - INTERVAL 1 DAY)
OR (date(starttime) BETWEEN CURDATE() - INTERVAL 2 DAY AND CURDATE() - INTERVAL 1 DAY)))
THEN 1 ELSE 0 END) AS todayAndYesterdayOrTheDayBeforeYesterday,
SUM(CASE WHEN EXISTS (SELECT 1 FROM gc_sessions sub_s WHERE s.user = sub_s.user
AND ((date(starttime) BETWEEN CURDATE() - INTERVAL 7 DAY AND CURDATE() - INTERVAL 1 DAY)
OR (date(starttime) BETWEEN CURDATE() - INTERVAL 7 DAY AND CURDATE() - INTERVAL 1 DAY)))
THEN 1 ELSE 0 END) AS todayAndWithinTheLastWeek
from gc_sessions s
where date(starttime) = CURDATE()
or date(endtime) = CURDATE()
Instead of relying on session table, I suggest you to create separate table, which stores 2 fields, date and user_id.
Every time user logs-in you need to insert new entry into this table.
This way you will be able to retrieve all the 3 requirement of yours.
Example table:
CREATE TABLE `test`.`user_login_history` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`userid` INTEGER UNSIGNED NOT NULL,
`date` DATETIME NOT NULL,
PRIMARY KEY (`id`)
)
ENGINE = InnoDB;
Once a user login, check whether he/she has login today or not:
select count(*) from user_login_history where
userid = 1 and `date` = '2013-01-28 00:00:00';
If the returned value is 1, means he/she has login today. no changes needed.
but, if the returned value is 0, means he/she has not login today. So record it down.
insert into user_login_history(userid,`date`)values(1,'2013-01-28 00:00:00');
Q1. How many users were online TODAY that were also online YESTERDAY?
select count(*) from user_login_history u where
u.`date` = '2013-01-28 00:00:00' and
(
select count(*) from user_login_history v where
v.`date` = '2013-01-27 00:00:00' and
v.userid = u.userid
) = 1;
Q2. How many users were online TODAY that were also online within in the last TWO DAYS
select count(*) from user_login_history u where
u.`date` = '2013-01-28 00:00:00' and
(
select count(*) from user_login_history v where
v.`date` >= '2013-01-26 00:00:00' and
v.`date` <= '2013-01-27 00:00:00' and
v.userid = u.userid
) > 0;
Q3. How many users were online TODAY that were also online within the last 7 DAYS
select count(*) from user_login_history u where
u.`date` = '2013-01-28 00:00:00' and
(
select count(*) from user_login_history v where
v.`date` >= '2013-01-21 00:00:00' and
v.`date` <= '2013-01-27 00:00:00' and
v.userid = u.userid
) > 0;
For yesterday
select id from gc_sessions where id in
(
select id
from gc_sessions
where starttime > subdate(current_date, 2)
and endtime < subdate(current_date, 1)
)
and starttime > subdate(current_date, 1);
For 2 Days
select id from gc_sessions where id in
(
select id
from gc_sessions
where starttime > subdate(current_date, 3)
and endtime < subdate(current_date, 1)
)
and starttime > subdate(current_date, 1);
For 7 Days
select id from gc_sessions where id in
(
select id
from gc_sessions
where starttime > subdate(current_date, 8)
and endtime < subdate(current_date, 1)
)
and starttime > subdate(current_date, 1);
You need to add a subquery that loads the data from the specified range (eg, 1day/2day/7days) and compares it with the data for the current day.
set #range = 7;
select * from gc_sessions
WHERE user in (SELECT user from gc_sessions
where starttime between subdate(current_date, #range) AND subdate(current_date, 1))
AND starttime > subdate(current_date, 0)
Where #range holds information about the number of days. See your expanded sql fiddle at - http://sqlfiddle.com/#!2/9584b/24
SELECT today.user
, GROUP_CONCAT(DISTINCT today.ip) ip
FROM gc_sessions today
JOIN gc_sessions yesterday
ON DATE(yesterday.starttime) = DATE(today.starttime) - INTERVAL 1 DAY
AND today.user = yesterday.user
WHERE DATE(today.starttime) = '2013-01-10'
GROUP
BY today.user;

Looking to select a row from the database using DATETIME

I have the following query
SELECT * FROM ".TBL_FOOT_GAMES." ORDER BY id DESC LIMIT 1
I need to add a WHERE clause on the field date_confirmed.
date_confirmed is a DATETIME type.
I need to select only rows that are within 7 days of the current moment.
MORE CODE
SELECT g.home_user, g.away_user, g.home_score, g.away_score, g.id AS gameid, g.date_confirmed,
hu.username AS home_username, au.username AS away_username, ht.team AS home_team, at.team AS away_team
FROM tbl_foot_games g INNER JOIN tbl_users hu ON hu.id = g.home_user INNER JOIN tbl_users au ON au.id = g.away_user
INNER JOIN tbl_foot_teams ht ON ht.id = g.home_team INNER JOIN tbl_foot_teams at ON at.id = g.away_team
WHERE (g.type = '1' OR g.type = '2' OR g.type = '3' OR g.type = '4') AND g.status = '3' AND g.date_confirmed BETWEEN NOW() AND DATE_SUB(NOW(), INTERVAL 50 WEEK)
ORDER BY g.id DESC LIMIT 1
The statement works fine until I add the WHERE clause for the 50 week interval.
Presuming only seven days in the future (it looks like you're going to list upcoming football games):
SELECT *
FROM `tbl`
WHERE `date_confirmed` BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 1 WEEK)
ORDER BY `id` DESC
LIMIT 1
Please read the documentation first next time; the answers are all there.
... WHERE date_confirmed BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 7 DAY) ...
Have a look at the NOW() and DATE_SUB() functions.
These should let you create a date 7 days ago, then in your where clause you can check that the datetime column is greater than this.
You can use the date_sub function of MySQL to see if the diff is 7 days or less.
SELECT * FROM ".TBL_FOOT_GAMES."
WHERE DATE_ADD(DATE_CONFIRMED, INTERVAL '7 00:00:00' DAYS_SECOND) >= TIMESTAMP(CURDATE())
ORDER BY id DESC LIMIT 1
If you are interested in seeing only 7 days of difference from current date (ignoring the time value), then you can use DATEDIFF function like this:
SELECT * FROM ".TBL_FOOT_GAMES."
WHERE DATEDIFF(CURDATE(), DATE_CONFIRMED) <= 7
ORDER BY id DESC LIMIT 1

MySQL Specialists: Delete all rows older than x days but not the latest one

first of all, this is the query which creates the "player history"
it can be executed as often as you want and it will only create new history rows for the players if there is no history row for yesterday or if the values changed since the latest history entry in the past.
INSERT INTO `player_history` (`player_id`, `date`, `races`, `maps`, `playtime`, `points`)
SELECT `p`.`id`, DATE_SUB(NOW(), INTERVAL 1 DAY), `p`.`races`, `p`.`maps`, `p`.`playtime`, `p`.`points`
FROM `player` `p`
WHERE `p`.`playtime` IS NOT NULL
AND `p`.`playtime` > 0
AND (
SELECT `player_id`
FROM `player_history`^
WHERE `player_id` = `p`.`id`
AND (
`date` = DATE_SUB(NOW(), INTERVAL 1 DAY)
OR (
`date` < DATE_SUB(NOW(), INTERVAL 1 DAY)
AND `races` = `p`.`races`
AND `points` = `p`.`points`
AND `maps` = `p`.`maps`
AND `playtime` = `p`.`playtime`
)
)
ORDER BY `date` DESC
LIMIT 1
) IS NULL;
now the problem is i also want to cleanup the history table using a single query. this already selects all history entries older than 10 days but the latest. but i cant just like do DELETE instead of SELECT *.
SELECT *
FROM `player_history` `ph`
WHERE `date` < DATE_SUB(NOW(), INTERVAL 10 DAY)
AND `date` != (SELECT `date`
FROM `player_history`
WHERE `player_id` = `ph`.`player_id`
ORDER BY `date` DESC
LIMIT 1);
so is tehre a way to do what i want using a single delete query?
Your query looks right in my eyes but you don't have the interval in the subquery.
I would do this:
DELETE FROM player_history
WHERE date < DATE_SUB(NOW(), INTERVAL 10 DAY)
AND date != (
SELECT MAX(date) FROM player_history
WHERE date < DATE_SUB(NOW(), INTERVAL 10 DAY)
)
What's the error message from mysql?
Probably you can't do this in a single query because the documentation states:
Currently, you cannot delete from a table and select from the same table in a subquery.
As a workaround you could select the ids of the rows that have to be deleted into a temporary table and then use a multi-table delete statement to delete the records from the original table.