【问题标题】:SQL LIMIT to get latest recordsSQL LIMIT 获取最新记录
【发布时间】:2012-02-12 01:25:15
【问题描述】:

我正在编写一个脚本,它将列出所有 12 个类别中的 25 个项目。数据库结构如下:

tbl_items
---------------------------------------------
item_id | item_name | item_value | timestamp 
---------------------------------------------

tbl_categories
-----------------------------
cat_id | item_id | timestamp
-----------------------------

tbl_items 表中有大约 600,000 行。我正在使用这个 SQL 查询:

SELECT e.item_id, e.item_value
  FROM tbl_items AS e
  JOIN tbl_categories AS cat WHERE e.item_id = cat.item_id AND cat.cat_id = 6001
  LIMIT 25

在循环中使用相同的查询 cat_id 从 6000 到 6012。但我想要每个类别的最新记录。如果我使用类似的东西:

SELECT e.item_id, e.item_value
  FROM tbl_items AS e
  JOIN tbl_categories AS cat WHERE e.item_id = cat.item_id AND cat.cat_id = 6001
  ORDER BY e.timestamp
  LIMIT 25

..查询进行大约 10 分钟的计算,这是不可接受的。我可以更好地使用LIMIT 来提供每个类别的最新 25 条记录吗?

没有ORDER BY,谁能帮我实现这个目标?任何想法或帮助将不胜感激。

编辑

tbl_items

+---------------------+--------------+------+-----+---------+-------+
| Field               | Type         | Null | Key | Default | Extra |
+---------------------+--------------+------+-----+---------+-------+
| item_id             | int(11)      | NO   | PRI | 0       |       |
| item_name           | longtext     | YES  |     | NULL    |       |
| item_value          | longtext     | YES  |     | NULL    |       |
| timestamp           | datetime     | YES  |     | NULL    |       |
+---------------------+--------------+------+-----+---------+-------+

tbl_categories

+----------------+------------+------+-----+---------+-------+
| Field          | Type       | Null | Key | Default | Extra |
+----------------+------------+------+-----+---------+-------+
| cat_id         | int(11)    | NO   | PRI | 0       |       |
| item_id        | int(11)    | NO   | PRI | 0       |       |
| timestamp      | datetime   | YES  |     | NULL    |       |
+----------------+------------+------+-----+---------+-------+

【问题讨论】:

  • 请显示您的表创建语句。您的索引是否正确?
  • @Astha - 请您确认您拥有哪些索引,以及是否可以请求/创建新索引?
  • 从问题中不清楚整个结果是按时间戳排序还是按组内数据排序?

标签: mysql sql sql-order-by limit greatest-n-per-group


【解决方案1】:

首先:

itemscategories 之间似乎是 N:M 关系:item 可能在多个 categories 中。我这样说是因为categoriesitem_id 外键。

如果不是 N:M 关系,那么您应该考虑更改设计。如果是 1:N 关系,一个类别有多个项目,那么item 必须包含category_id 外键。

使用 N:M:

我已重写您的查询以使内部联接代替交叉联接:

  SELECT e.item_id, e.item_value
  FROM 
     tbl_items AS e
  JOIN 
     tbl_categories AS cat 
        on e.item_id = cat.item_id
  WHERE  
     cat.cat_id = 6001
  ORDER BY 
     e.timestamp
  LIMIT 25

要优化性能所需的索引是:

create index idx_1 on tbl_categories( cat_id, item_id)

它不是强制性的项目索引,因为主键也被索引。 包含时间戳的索引无济于事。可以肯定的是,可以尝试使用 item_idtimestamp 对项目进行索引,以避免访问表并从索引中获取值:

create index idx_2 on tbl_items( item_id, timestamp)

为了提高性能,您可以通过单个查询更改类别循环:

  select T.cat_id, T.item_id, T.item_value from 
  (SELECT cat.cat_id, e.item_id, e.item_value
   FROM 
     tbl_items AS e
   JOIN 
     tbl_categories AS cat 
        on e.item_id = cat.item_id
   ORDER BY 
     e.timestamp
   LIMIT 25
  ) T
  WHERE  
     T.cat_id between 6001 and 6012
  ORDER BY
     T.cat_id, T.item_id

请尝试此查询并在必要时与您的 cmets 一起改进它。

【讨论】:

  • 感谢您的回复。我无法更改数据库的结构,因为它不是我的。但是是的,它是 1:N 关系表。一个 item_id 将仅属于一个类别。我尝试了这个查询,但它也花费了相同的时间。我正在为类别 ID 使用循环,我不能在 caluse 之间使用像这样的单个查询。希望你明白我的意思。
  • 如果不是您的数据库,它们不是一个简单的解决方案。看看这个相关的问题:Is cross-table indexing possible?
【解决方案2】:

你能添加索引吗?如果您在 timestamp 和其他适当的列上添加索引,ORDER BY 不会花费 10 分钟。

【讨论】:

  • 索引就像电话簿。如果电话簿中没有订单,则查找某人的电话号码需要几个小时。时间戳列上的索引告诉 MySQL 记录的位置,按时间戳,因此它可以像在电话簿中一样查找行。
  • 不幸的是,我不知道有什么像样的网站可以帮助您入门。 (有人吗?)
  • 好吧,官方文档总是有的,尽管它可能不是对该主题的最佳介绍:dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html
  • 是的,时间戳列上没有索引。在 items 表的时间戳列上添加索引将使您的 ORDER BY 加速数千倍(我并不夸张)。
  • 如果您刚刚开始(对我而言),这可能会让人望而生畏,但您可以学到很多东西。如果你运行一个看起来像 EXPLAIN {sql-query} 的查询,那么 MySQL 会告诉你它将如何执行 {sql-query}.dev.mysql.com/doc/refman/5.0/en/explain-output.html
【解决方案3】:

抛开所有其他因素,我可以告诉你,查询如此缓慢的主要原因是因为结果涉及longtext 列。

MySQL 中的BLOBTEXT 字段主要用于存储完整的文件,文本或二进制文件。它们与 InnoDB 表的行数据分开存储。每次查询涉及排序时(显式地或针对group by),MySQL 肯定会使用磁盘进行排序(因为它无法提前确定任何文件的大小)。

这可能是一个经验法则:如果您需要在查询中返回多于一行的列,则字段的类型几乎从不应该是TEXTBLOB,使用@987654328 @ 或 VARBINARY 代替。

UPD

如果您无法更新表,则使用当前索引和列类型查询将很难快速。但是,无论如何,这里有一个类似的问题和一个流行的解决方案:How to SELECT the newest four items per category?

【讨论】:

  • 感谢您的解释。真的很感激,但正如我所说,这个数据库不是我的。我只需要获取数据来显示。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-15
  • 2020-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-15
相关资源
最近更新 更多