How to update table after a certain time interval - mysql

How can I update a table after some time interval when a condtion is matched?
tb_contest
id contest_id name is_expire
1 101 new 0
2 102 old 0
tb_answer
contest_id answer_id date
101 1 2012-02-02
101 2 2012-09-14
102 5 2012-06-01
I need to update tb_contest after some condition was met and make is_expire=1 after 2 days on basis of the last answer received i:e 2012-03-14, so the tb_contest should be updated on 2012-09-16.

You could use MySQL's event scheduler:
CREATE EVENT expire_contests
ON SCHEDULE EVERY DAY
STARTS CURRENT_DATE
DO UPDATE tb_contest JOIN (
SELECT contest_id, MAX(date) AS latest
FROM tb_answer
GROUP BY contest_id
) t USING (contest_id)
SET tb_contest.is_expire = 1
WHERE tb_contest.is_expire <> 1
AND t.latest <= CURRENT_DATE - INTERVAL 2 DAY

Try this one,
UPDATE tb_contest a INNER JOIN
(
SELECT contest_ID, MAX(`date`) maxDate
FROM tb_answer
GROUP BY contest_ID
) b ON a.contest_ID = b.contest_ID
SET a.is_expire = 1
WHERE DATEDIFF(CURDATE(), b.maxDate) >= 2 AND
a.is_expire = 0
So here it goes, the two tables were joined by contest_ID and having the lastest answered date on tb_answer. By using DATEDIFF() we can know the difference between today's date and the date the contest has been answered.

You can JOIN the contest and an inner-query on the answer table in the UPDATE clause and use MySQL's DATEDIFF to count the number-of-days since the answer was, well, answered:
UPDATE
tb_contest c
JOIN (SELECT contest_id, MAX(date) AS date FROM tb_answer GROUP BY contest_id) AS a
ON a.contest_id = c.id
SET
c.is_expire = 1
WHERE
DATEDIFF(NOW(), a.date) >= 2

Related

Find occurrences of a record over the last 10 days in mysql

Consider the mysql table,
user_event| created_at
1 2021-12-03
2 2021-12-03
3 2021-12-03
3 2021-12-01
1 2021-11-28
2 2021-11-10
2 2021-11-01
1 2021-11-01
I am trying to find count of users grouped by created_at & count of repeat users if they had an event in the last 10 days
The expected result would be:
created_at | total_users | repeat_users
2021-12-03 3 2
2021-12-02 0 0
2021-12-01 1 0
.
.
2021-11-28 1 0
.
.
2021-11-10 1 1
2021-11-01 2 0
Basically, user_event 1,2,3 had activities on 2021-12-03, so total_users are 3. 2 of them had activity in the last 10 days so, repeat_users should be 2.
Similarly, user_event 3 was present on 2021-12-01 but he has no activity in last 10 days, hence repeat_users would be 0 and so on.
Given your table of data
CREATE TABLE MyData
(
user_event INT,
created_at DATE
);
INSERT INTO MyData
VALUES
(1,'2021-12-03'),
(2,'2021-12-03'),
(3,'2021-12-03'),
(3,'2021-12-01'),
(1,'2021-11-28'),
(2,'2021-11-10'),
(2,'2021-11-01'),
(1,'2021-11-01')
You can solve this using a calendar table which can be created as follows (you probably should populate with dates well into the future)
delimiter //
CREATE PROCEDURE CreateDateTable ()
BEGIN
SET #StartDate = CAST('2021-01-01' AS DATE);
SET #EndDate = CAST('2021-12-31' AS DATE);
CREATE TABLE Calendar
(
DateValue DATE
);
WHILE #StartDate <= #EndDate DO
INSERT INTO Calendar
(
DateValue
)
VALUES
(
#StartDate
);
SET #StartDate = DATE_ADD(#StartDate, INTERVAL 1 DAY);
END WHILE;
END//
CALL CreateDateTable
DROP PROCEDURE CreateDateTable
Once you have the calendar table, you can select the dates you are interested in and use a combination of COUNT / GROUP BY to count the number of user events for the day and then join to a subquery which uses a self join to find the number of repeat events:
SELECT c.DateValue,
COUNT(d.user_event) AS total_users,
IFNULL(r.NumRepeats,0) AS repeat_users
FROM Calendar c
LEFT JOIN MyData d
ON c.DateValue = d.created_at
LEFT JOIN (
/* repeats */
SELECT a.created_at,
COUNT(a.user_event) AS NumRepeats
FROM MyData a
LEFT JOIN MyData b
ON b.created_at > DATE_ADD(a.created_at, INTERVAL -10 DAY) AND
b.created_at < a.created_at AND
a.user_event = b.user_event
WHERE b.user_event IS NOT NULL
GROUP BY a.created_at
) r
ON r.created_at = c.DateValue
WHERE c.DateValue >= '2021-11-01' AND
c.DateValue <= '2021-12-03'
GROUP BY c.DateValue,
r.NumRepeats
ORDER BY c.DateValue DESC

Mysql Query to Merge Two Condition Into One Row

I just get confused. Already tried to search this whole site or google but didn't find the 'nearest' solution.
Ok let's say I have this table structure.
id date finger_id finger_time is_enter
1 2017-03-30 2 09:00 1
2 2017-03-30 2 17:13 0
3 2017-03-31 4 09:10 1
4 2017-03-31 3 09:01 1
5. 2017-03-31 3 17:00 0
I want to make the table to be like below.
date finger_id enter_time exit_time
2017-03-30 2 09:00 17:13
2017-03-30 4 09:10
2017-03-31 3 09:10 17:00
I have made sql statement but it turns like this.
date finger_id enter_time exit_time
2017-03-30 2 09:00
2017-03-30 2 17:13
2017-03-31 4 09:10
2017-03-31 3 09:01
2017-03-31 3 17:00
I just want to know how to merge the is_enter 1 with is_enter 0 on the same date by the finger_id column.
Here's my sql query for the reference.
SELECT *
FROM `tbl_fingerprint`
LEFT JOIN `tbl_employee` ON `tbl_employee`.`fingerprint_id`=`tbl_fingerprint`.`fingerprint_id`
LEFT JOIN `tbl_position` ON `tbl_position`.`position_id`=`tbl_employee`.`position_id`
WHERE `fingerprint_date` >= '2017-03-01'
AND `fingerprint_date` <= '2017-04-01'
GROUP BY `tbl_fingerprint`.`fingerprint_id`,
`tbl_fingerprint`.`fingerprint_date`,
`tbl_fingerprint`.`is_enter`
ORDER BY `fingerprint_date` ASC LIMIT 30
Thanks for your help guys.
You can do a group by date and finger_id fields and use conditional expression (case or if()) within an aggregate function to get the expected outcome. The conditional statements within the aggregate function make sure that they return value only if the right value is set in is_enter field. I leave out the employee details, since those do not form part of your question:
SELECT date, fingerprint_id, max(if(is_enter=1,finger_time,null) as enter_time, max(if(is_enter=0,finger_time,null) as exit_time
FROM `tbl_fingerprint`
WHERE `fingerprint_date` >= '2017-03-01'
AND `fingerprint_date` <= '2017-04-01'
GROUP BY `tbl_fingerprint`.`fingerprint_id`,
`tbl_fingerprint`.`fingerprint_date`,
ORDER BY `fingerprint_date` ASC LIMIT 30
SELECT * FROM `tbl_fingerprint`
LEFT JOIN `tbl_employee` ON `tbl_employee`.`fingerprint_id`=`tbl_fingerprint`.`fingerprint_id`
LEFT JOIN `tbl_position` ON `tbl_position`.`position_id`=`tbl_employee`.`position_id`
LEFT JOIN (SELECT * FROM tbl_fingerprint WHERE is_enter = 0) a
ON a.finger_id = tbl_fingerprint.finger_id AND a.date = tbl_fingerprint.date
WHERE `fingerprint_date` >= '2017-03-01' AND `fingerprint_date` <= '2017-04-01' AND tbl_fingerprint.is_enter = 1
GROUP BY `tbl_fingerprint`.`fingerprint_id`, `tbl_fingerprint`.`fingerprint_date`, `tbl_fingerprint`.`is_enter`
ORDER BY `fingerprint_date` ASC LIMIT 30
Try This (This will work if finger_time is of time type):-
SELECT date, finger_id, min(finger_time) enter_time, if (min(finger_time) = max(finger_time), null, max(finger_time)) exit_time FROM xyz group by finger_id, date
SELECT a1.*, a3.time as time_out FROM attendance as a1
INNER JOIN (SELECT MIN(id) as id FROM attendance where is_enter = '1' group by date, f_id ) as a2
ON a2.id = a1.id
LEFT JOIN attendance as a3 ON a3.date = a1.date AND a1.f_id = a3.f_id and a3.is_enter = '0'
you may need to cast the date to not include the time portion or to char with the yyyy-mm-dd format

Fetching count from database by days

I have the following structure:
id | some_foreign_id | date
1 5 2015-09-29 23:14:23
2 5 2015-09-29 14:13:21
3 8 2015-09-28 22:23:12
For the specified some_foreign_id I want to return the count of rows in this table for each day from last 2 weeks. I created this:
SELECT DATE(t.sent_at), COUNT(*)
FROM table t
INNER JOIN sometable st ON st.some_id = t.id
INNER JOIN someOtherTable sot ON sot.someother_id = st.id
WHERE t.sent_at >= DATE_ADD(CURDATE(), INTERVAL -14 DAY)
AND t.some_foreign_id = 5
GROUP BY DATE(t.sent_at);
It shows some results, but:
Doesn't show 0's if the day has 0 records.
Changing interval to -15 changes the count from the last day - don't know why.
How could I do this properly?
To solve 1., you'll need to left join to something like this and use IFNULL()
To solve 2. (or try to), try changing your query to this (I suggest you solve this first):
SELECT DATE(t.sent_at), COUNT(*)
FROM table t
INNER JOIN sometable st ON st.some_id = t.id
INNER JOIN someOtherTable sot ON sot.someother_id = st.id
WHERE DATE(t.sent_at) >= DATE(DATE_ADD(CURDATE(), INTERVAL -14 DAY))
AND t.some_foreign_id = 5
GROUP BY DATE(t.sent_at);

MySQL date related query

I am having problems understanding how best to tackle this query.
I have a table called user_files. A user can have many files. I want to get a list of users who have not modified any of their files within the last year. If a user has modified at least one of their files within the last year, then that user should be excluded.
Table: user_files
file_id | user_id | date_modified
----------------------------------
1 100 2010-10-01
2 100 2010-11-13
3 100 2011-01-01
4 101 2010-10-01
5 101 2010-06-13
6 101 2011-04-12
7 101 2012-04-01
The expected result would only list user_id 100.
Here is some bad sql I have been playing with. The idea is that I find all users who recently modified their files and then find users who are not included in that list.
select user_id from users where user_id not in
(
select user_id from
(
select user_id, story_id, max(date_modified) from user_files
where DATE_FORMAT(date_modified, '%Y-%m-%d') >= DATE_SUB(curdate(), INTERVAL 1 YEAR)
group by user_id
)x
)
Thanks
SELECT DISTINCT(f.user_id)
FROM user_files f
WHERE NOT EXISTS(SELECT 1
FROM user_files ff
WHERE ff.user_id = f.user_id
AND ff.date_modified >= DATE_SUB(curdate(), INTERVAL 1 YEAR))
http://sqlfiddle.com/#!2/64e7f/1
Or,
SELECT user_id
FROM user_files
GROUP BY user_id
HAVING MAX(date_modified) < DATE_SUB(curdate(), INTERVAL 1 YEAR)
http://sqlfiddle.com/#!2/64e7f/4
You can use this simple solution:
SELECT a.user_id
FROM users a
LEFT JOIN user_files b ON
a.user_id = b.user_id AND
b.date_modified >= CURDATE() - INTERVAL 1 YEAR
WHERE b.user_id IS NULL

How to select a column value that corresponds to a row returned by a MySQL aggregate function?

I have a table like
date user_id page_id
2010-06-19 16:00:00 1 4
2010-06-19 16:00:00 3 4
2010-06-20 07:10:00 1 1
2010-06-20 12:00:10 1 2
2010-06-20 12:00:10 1 3
2010-06-20 13:05:00 2 1
2010-06-20 14:10:00 3 1
2010-06-21 17:00:00 2 1
I want to write a query that will return the last page_id for those users who haven't visited in the last day.
So, I can find who hasn't visited in the last day with:
SELECT user_id, MAX(page_id)
FROM page_views GROUP BY user_id
HAVING MAX(date) < DATE_SUB(NOW(), INTERVAL 1 DAY);
However, how can I find the last viewed page_id for these users? i.e. I want to know which page_id corresponds to the value in the same row as MAX(date). In the case where there are multiple page views per date, I can just select the MAX(page_id).
The expected output from above should be (if NOW() returns 2010-06-21 18:00:00):
user_id page_id
1 3
3 1
user_id 1 last visited over a day ago
at 2010-06-20 12:00:10, and the
MAX(page_id) was 3.
user_id 2 last
visited less than a day ago, so they
are ignored.
user_id 3 last visited
over a day ago, and their most recent
page_id was 1.
How can I achieve this? I need to use only SQL. I'm using a MySQL derivative that requires all columns in the SELECT clause to be declared in the GROUP BY clause (it's a little more standards compliant).
Thanks.
I could see different approaches.
For example:
select a.user_id, a.page_id
from page_views a
inner join (SELECT user_id, MAX(date) as date
FROM page_views GROUP BY user_id
HAVING MAX(date) < DATE_SUB(NOW(), INTERVAL 1 DAY) ) b on a.user_id = b.user_id
and a.date = b.date
It could be implemented more effective in MS SQL or Oracle with windowed functions.
Another idea:
select a.user_id, a.page_id
from page_views a
where date < DATE_SUB(NOW(), INTERVAL 1 DAY)
and not exist(select 1 from page_views b
where a.user_id = b.user_id and b.date > a.date)