获取特定标识符的最新行

时间:2018-03-26 21:03:20

标签: mysql greatest-n-per-group

我有一张看起来像这样的表

ID | identifier | data  | created_at
------------------------------------
1  |     500    | test1 | 2011-08-30 15:27:29
2  |     501    | test1 | 2011-08-30 15:27:29
3  |     500    | test2 | 2011-08-30 15:28:29
4  |     865    | test3 | 2011-08-30 15:29:29
5  |     501    | test2 | 2011-08-30 15:31:29
6  |     500    | test3 | 2011-08-30 15:31:29

我需要的是每个标识符的最新条目,可以通过ID或created_at中的日期来决定。由于索引,我认为ID是更好的选择。

我希望这个结果集:

4  |     865    | test3 | 2011-08-30 15:29:29
5  |     501    | test2 | 2011-08-30 15:31:29
6  |     500    | test3 | 2011-08-30 15:31:29

结果应按日期或ID按升序排序。

这是一个包含大约8百万行的表格,这很重要。

我现在尝试了一些自我加入和子查询的方法。不幸的是,所有这些都是错误的结果或运行时间的五十年。

举个例子:

SELECT lo1.* 
FROM table lo1
INNER JOIN
(
    SELECT MAX(id) MaxID, identifier, id
    FROM table 
    GROUP BY identifier
) lo2
  ON lo1.identifier= lo2.identifier
  AND lo1.id = lo2.MaxID
ORDER BY lo1.id DESC
LIMIT 10

以上查询需要很长时间,有时不会返回标识符的最新结果,但不太确定原因。

有没有人能够获取所需的结果集,最好不需要十年?

如所询问的,这是创建代码:

CREATE TABLE `table` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `identifier` int(11) NOT NULL,
  `data` varchar(200) COLLATE latin1_bin NOT NULL,
  `created_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `identifier` (`identifier`),
  KEY `created_at` (`created_at`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin

1 个答案:

答案 0 :(得分:2)

提供正确结果的正确查询,但不会在较大的表上进行缩放。

<强>查询

SELECT
  `table`.*
FROM
  `table`
INNER JOIN
(
    SELECT
        MAX(id) AS MaxID
      , identifier
    FROM
      `table` 
    GROUP BY
      identifier
    #disables GROUP BY Sorting might make the query faster.
    ORDER BY
      NULL  
) `table_group`
ON
 `table`.ID = `table_group`.MaxID
ORDER BY
  `table`.ID DESC
LIMIT 10

<强>结果

| id | identifier |  data |           created_at |
|----|------------|-------|----------------------|
|  6 |        500 | test3 | 2011-08-30T15:31:29Z |
|  5 |        501 | test2 | 2011-08-30T15:31:29Z |
|  4 |        865 | test3 | 2011-08-30T15:29:29Z |

参见演示http://www.sqlfiddle.com/#!9/7f4401/4

但是当您检查“查看执行计划”时,您可以在额外列中看到“使用where;使用临时;使用filesort”意味着MySQL需要使用快速排序算法“使用临时”;表示快速排序算法首先在内存临时表上运行 如果内存临时表变大,它将转换为磁盘临时表上的MyISAM。
这意味着快速排序将需要基于磁盘的随机i / o来排序磁盘上的速度很慢。
所以这个方法不会在表上扩展到大约8百万行。

下面的查询也会给出相同的结果,但应该进行更优化

<强>查询

SELECT 
 `table`.*
FROM
 `table` 
INNER JOIN ( 

  SELECT
    `table`.ID
  FROM
    `table`
  INNER JOIN
  (
      SELECT
          MAX(id) AS MaxID
        , identifier
      FROM
        `table` 
      GROUP BY
        identifier
      #disables GROUP BY Sorting might make the query faster.
      ORDER BY
        NULL  
  )
   AS `table_group`
  ON
   `table`.ID = `table_group`.MaxID
)
  AS `table_group_max`
ON
 `table`.ID = `table_group_max`.ID
ORDER BY
 `table`.ID DESC
LIMIT 10

<强>结果

| id | identifier |  data |           created_at |
|----|------------|-------|----------------------|
|  6 |        500 | test3 | 2011-08-30T15:31:29Z |
|  5 |        501 | test2 | 2011-08-30T15:31:29Z |
|  4 |        865 | test3 | 2011-08-30T15:29:29Z |

参见演示http://www.sqlfiddle.com/#!9/7f4401/21

当您选中“查看执行计划”时,不再有“使用临时;使用文件”,这意味着查询应该比上一个查询更优化,理论上应该执行得更快。
因为组合“使用临时;使用filesort”确实可以像解释一样成为性能杀手。