基于两个字段查找“最后”行的MySQL查询

基于两个字段查找“最后”行的MySQL查询,mysql,sql,Mysql,Sql,我有以下MySQL表记录学生注册状态的变化: CREATE TABLE `pupil_registration_statuses` ( `status_id` INT(11) NOT NULL AUTO_INCREMENT, `status_pupil_id` INT(10) UNSIGNED NOT NULL, `status_status_id` INT(10) UNSIGNED NOT NULL, `status_effectivedate` DATE NOT NULL, PRIMARY

我有以下MySQL表记录学生注册状态的变化:

CREATE TABLE `pupil_registration_statuses` (
`status_id` INT(11) NOT NULL AUTO_INCREMENT,
`status_pupil_id` INT(10) UNSIGNED NOT NULL,
`status_status_id` INT(10) UNSIGNED NOT NULL,
`status_effectivedate` DATE NOT NULL,
PRIMARY KEY (`status_id`),
INDEX `status_pupil_id` (`status_pupil_id`)
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM;
示例数据:

INSERT INTO `pupil_registration_statuses` (`status_id`, `status_pupil_id`, `status_status_id`, `status_effectivedate`) VALUES
    (1, 123, 1, '2013-05-06'),
    (2, 123, 2, '2014-03-15'),
    (3, 123, 5, '2013-03-15'),
    (4, 123, 6, '2013-05-06'),
    (5, 234, 2, '2013-02-02'),
    (6, 234, 4, '2013-04-17'),
    (7, 345, 2, '2014-02-01'),
    (8, 345, 3, '2013-06-01');
可以插入状态,因此日期序列不一定遵循相同的ID序列

例如:状态id 1的日期可能为2013-05-06,但状态id 3的日期可能为2013-03-15

然而,状态id值在任何特定日期内都是连续的。因此,如果学生的注册状态在一天内多次更改,则最后一行将反映该日期的状态

有必要在特定日期了解特定学生的注册状态。以下查询适用于单个学生:

SELECT * 
FROM pupil_registration_statuses 
WHERE status_pupil_id = 123
    AND status_effectivedate <= '2013-05-06'
ORDER BY status_effectivedate DESC, status_id DESC
LIMIT 1;
但是,此查询为第123行返回2行

编辑

为了澄清,如果输入是日期“2013-05-06”,我希望从查询中获得第4行和第6行


我更改了where子句,请尝试一下

SELECT *
    FROM pupil_registration_statuses prs
        INNER JOIN (SELECT status_pupil_id, MAX(status_effectivedate) last_date
            FROM pupil_registration_statuses
            WHERE Datediff(status_effectivedate, '2013-05-06') <= 0
            GROUP BY status_pupil_id) qprs ON prs.status_pupil_id = qprs.status_pupil_id AND prs.status_effectivedate = qprs.last_date;

如果我理解正确,你想

每人1排

2获取您手动输入的特定日期的状态更改

3获取特定日期内的最后状态更改

如果这是正确的,您需要查询您已经按日期排序,然后按id排序,只需要一个


这就是你想要的吗

SELECT a.*
  FROM pupil_registration_statuses a
  JOIN
     ( SELECT prs.status_pupil_id
            , MIN(prs.status_id) min_status_id
         FROM pupil_registration_statuses prs
         JOIN 
            ( SELECT status_pupil_id
                   , MAX(status_effectivedate) last_date
                FROM pupil_registration_statuses
               WHERE status_effectivedate <= '2013-05-06'
               GROUP
                  BY status_pupil_id
            ) qprs 
           ON prs.status_pupil_id = qprs.status_pupil_id 
          AND prs.status_effectivedate = qprs.last_date
        GROUP
           BY prs.status_pupil_id
     ) b
    ON b.min_status_id = a.status_id;
顺便说一句,对于这类问题,有一种丑陋的、未经记录的黑客行为,它是这样的:

SELECT x.* FROM (SELECT * FROM prs WHERE status_effectivedate <= '2013-05-06' ORDER BY status_pupil_id, status_effectivedate DESC, status_id)x GROUP BY status_pupil_id;

…但我没告诉你

我不明白你的意思。您指的是什么?这只返回与我的查询要求完全相反的结果。将>=0更改为哦!这意味着我错过了符号。我在上面编辑了我尝试的内容,但要澄清的是:它生成了一个等效的数据集。谢谢你的回答。我需要输入的最后一个状态。因此,例如,在2013年5月6日,234号学生的状态为4,于2013年4月17日生效。这也应该是返回数据的一部分。您是否尝试将状态_effectivedate更改为2013-04-17之前?该查询按日期过滤,然后进行区分,因此使用所有学生至少有一个状态的有效日期。但这并不能回答问题。我需要查看为某个特定学生输入的最后一个状态。这很好-只需对最后一个状态\u id进行一次修改,即可将MINprs.status\u id更改为MAXprs.status\u id。然后,它似乎可以完美地工作!只是附带说明一下——子查询虽然功能强大,但在性能上通常不如没有子查询的等价查询好。所以我的建议是:如果没有其他方法可以让它工作的话,那就使用它们吧。这完全是胡说八道
SELECT DISTINCT on status_pupil_id *
FROM pupil_registration_statuses 
WHERE status_pupil_id = 123
    AND status_effectivedate <= '2013-05-06'
ORDER BY status_effectivedate DESC, status_id DESC
SELECT a.*
  FROM pupil_registration_statuses a
  JOIN
     ( SELECT prs.status_pupil_id
            , MIN(prs.status_id) min_status_id
         FROM pupil_registration_statuses prs
         JOIN 
            ( SELECT status_pupil_id
                   , MAX(status_effectivedate) last_date
                FROM pupil_registration_statuses
               WHERE status_effectivedate <= '2013-05-06'
               GROUP
                  BY status_pupil_id
            ) qprs 
           ON prs.status_pupil_id = qprs.status_pupil_id 
          AND prs.status_effectivedate = qprs.last_date
        GROUP
           BY prs.status_pupil_id
     ) b
    ON b.min_status_id = a.status_id;
SELECT x.* FROM (SELECT * FROM prs WHERE status_effectivedate <= '2013-05-06' ORDER BY status_pupil_id, status_effectivedate DESC, status_id)x GROUP BY status_pupil_id;