MySQL:如何在特定日期之前选择最近日期的记录

MySQL:如何在特定日期之前选择最近日期的记录

问题描述:

假设一个简单的表格,其中包含两个用户的状态事件。 status_id为1表示使其处于“活动状态”,其他任何因素都会使其停用。 我需要找出所有那些在一年内变为的用户,例如2015-05-01(不包括该日期)。MySQL:如何在特定日期之前选择最近日期的记录

CREATE TABLE user_status(
     user_id INT, 
     status_id INT, 
     date_assigned VARCHAR(10)); 

INSERT INTO user_status(user_id, status_id, date_assigned) 
VALUES 
(1234, 1, '2015-01-01'), -- 1234 becomes active (status id = 1) 
(1234, 2, '2015-07-01'), -- 1234 de-activated for reason 2 

(5678, 1, '2015-02-01'), -- 5678 becomes active (status id = 1) 
(5678, 3, '2015-04-01'), -- 5678 de-activated for reason 3 
(5678, 5, '2015-06-01'); -- 5678 de-activated for reason 5 

使用查询

SELECT t1.* 
FROM user_status t1 
WHERE t1.date_assigned = (SELECT MIN(t2.date_assigned)  -- the first occurrence 
          FROM user_status t2 
          WHERE t2.user_id = t1.user_id  -- for this user 
          AND t2.status_id <> 1    -- where status not active 
          AND t2.date_assigned BETWEEN  -- within 1 yr of given date 
           '2015-05-01' + INTERVAL 1 DAY -- (not including that date) 
          AND 
           '2015-05-01' + INTERVAL 1 YEAR 
         ) 

我能得到的结果

user_id status_id date_assigned 
1234  2   2015-07-01 
5678  5   2015-06-01 

这有点正确的,但用户5678不应该存在,因为虽然他们中的一个闲置事件日期范围内,它们在期望的日期范围开始之前已经不活动,因此在该范围内没有变为不活动。

我需要添加一点到我的查询中,'只显示那些有非活动事件的用户,其中该用户的以前的status_id是1,即他们在非活动状态时处于活动状态事件发生了。

任何人都可以帮助我得到正确的语法?
SQL fiddle

您可以添加NOT EXISTS到您的查询:

SELECT t1.* 
FROM user_status t1 
WHERE t1.date_assigned = (SELECT MIN(t2.date_assigned)  -- the first occurance 
          FROM user_status t2 
          WHERE t2.user_id = t1.user_id  -- for this user 
          AND t2.status_id <> 1    -- where status not active 
          AND t2.date_assigned BETWEEN  -- within 1 yr of given date 
           '2015-05-01' + INTERVAL 1 DAY -- (not including that date) 
          AND 
           '2015-05-01' + INTERVAL 1 YEAR 
         ) 
     AND NOT EXISTS (SELECT 1       -- such a record should not exist 
         FROM user_status t3 
         WHERE t3.user_id = t1.user_id -- for this user 
          AND t3.status_id <> 1  -- where status is not active 
          AND t3.date_assigned < -- before the examined period 
           '2015-05-01' + INTERVAL 1 DAY) 

Demo here

编辑:

您可以使用下面的查询也认为有多个的情况下,激活日期:

你的约会标准中找到最低(第一次改变状态):
SELECT * 
FROM user_status 
WHERE (user_id, date_assigned) IN (
    -- get last de-activation date 
    SELECT t1.user_id, MAX(t1.date_assigned) 
    FROM user_status AS t1 
    JOIN (
    -- get last activation date 
    SELECT user_id, MAX(date_assigned) AS activation_date 
    FROM user_status 
    WHERE status_id = 1 
    GROUP BY user_id 
) AS t2 ON t1.user_id = t2.user_id AND t1.date_assigned > t2.activation_date 
    GROUP BY user_id 
    HAVING MAX(date_assigned) BETWEEN '2015-05-01' + INTERVAL 1 DAY AND '2015-05-01' + INTERVAL 1 YEAR AND 
     MIN(date_assigned) > '2015-05-01' + INTERVAL 1 DAY) 
+0

很抱歉,但不起作用。如果您添加另一个记录(5678,1,'2015-04-20'),以便5678在日期范围之前再次处于活动状态(之前处于取消激活状态之前),那么它们不会被取消为取消激活第二次在2015-06-01 – user3209752

+0

啊,这似乎工作正常,谢谢。我已经接受了编辑的答案。 – user3209752

自连接解决方​​案

select a.user_id,b.status_id,max(b.date_assigned) 
from user_status a 
inner join user_status b 
on a.user_id=b.user_id 
and a.date_assigned <b.date_assigned 
where b.status_id >1 and a.status_id=1 
group by a.user_id,b.status_id 
having max(b.date_assigned)> '2015-05-01' 
and max(b.date_assigned) <='2016-05-01' 
+0

同样的问题,我很害怕从Giorogos那里得到答案。如果添加了另一条记录,以便2015-04-20(在日期范围开始之前)用户5678再次变为活动状态,那么他们应该在2015-06-01第二次被取消激活,但此代码不会“ t显示该记录。顺便提一下,我使用INTERVAL作为期间的开始日期作为实际系统中的参数传递,尽管这不会影响您的解决方案。 – user3209752

+0

它使用这两个额外的记录在我的虚拟桌上得到拾取... – kostas

+0

仍然只给我用户1234的记录,而不是5678以及使用。 CREATE TABLE user_status( user_id INT, status_id INT, date_assigned VARCHAR(10)); INSERT INTO user_status(user_id,status_id,date_assigned) VALUES (1234,1',2015-01-01'), - 1234变为活动状态(状态id = 1) (1234,2',2015-07- 01'), - 1234由于原因而失效2 (5678,1',2015-02-01'), - 5678变为活动的(状态id = 1) (5678,3,'2015-04 (5678,1,'2015-04-20'), - 5678再次激活 (5678,5,'2015-06-01'); - 5678由于原因3 - 5678失去原因5 – user3209752