mysql - 这个查询复杂到无法简化吗?

标签 mysql sql join self-join

我有两个 MySQL 数据库表,如下所述。一张表保存设备信息,另一张表是关于每个设备的一对多日志。

CREATE TABLE  `device` (
  `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `name` VARCHAR(255) NOT NULL,
  `active` INT NOT NULL DEFAULT 1,
  INDEX (`active`)
);

CREATE TABLE  `log` (
  `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `device_id` INT NOT NULL,
  `message` VARCHAR(255) NOT NULL,
  `when` DATETIME NOT NULL,
  INDEX (`device_id`)
);

我想做的是在单个查询中获取设备信息以及每个设备的最新日志条目(如果可能)。到目前为止,我所拥有的是:

SELECT d.id, d.name, l.message
FROM device AS d
LEFT JOIN (
  SELECT l1.device_id, l1.message
  FROM log AS l1
  LEFT JOIN log AS l2 ON (l1.device_id = l2.device_id AND l1.when < l2.when)
  WHERE l2.device_id IS NULL
) AS l ON (d.id = l.device_id)
WHERE d.active = 1
GROUP BY d.id
ORDER BY d.id ASC;

这些查询是我实际设置的简化复制,我的日志表超过 10 万行(实际上我查看了几个日志表)。查询确实运行,但是非常非常慢(比如超过两分钟)。我相信有一种更简洁/优雅/“SQL”的方式来形成此查询以获取我需要的数据,但我还没有找到它。

如果没有丑陋的 sub-SELECT 和 self-JOIN,我想做的事情是否可能实现?我可以用不同的策略完成工作吗?或者,查询的本质是否复杂得无法简化?

同样,应用程序逻辑是这样的,如果这不起作用,我可以“手动加入”表,但我觉得 MySQL 应该能够处理这样的事情而不会窒息 - 但我承认我是绿色的说到这种复杂的集合代数。

编辑:因为这是一个人为的例子,我忘记将索引添加到 device.active

最佳答案

这里有一个稍微不同的查询方法,它避免了自连接:

SELECT d.id, d.name, l.message
FROM device AS d
LEFT JOIN (
  SELECT l1.device_id, l1.message
  FROM log AS l1
  WHERE l1.when = (
        SELECT MAX(l2.when)
        FROM log AS l2
        WHERE l2.device_id = l1.device_id
  ) l ON l.device_id = d.id
WHERE d.active = 1
ORDER BY d.id ASC;

因为 100k 不是一个很大的表,即使没有适当的索引,我也不希望这个查询花费超过几秒钟的时间。但是,正如评论所建议的那样,您可以考虑根据您的 explain plan 的结果添加额外的索引。

关于mysql - 这个查询复杂到无法简化吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11729820/

相关文章:

mysql - 我想根据其他三个表将该列设置为空

sql - 在同一字段上选择升序和降序

java - 如何在hibernate中连接ManyToMany映射中的中间表

mysql - 具有多个连接的计数

sql - PostgreSQL 中多个表的多个总和/计数

php - 在 PHP 中绘制来自 MySQL 的时间戳

python - 在Django shell中输出MySQL查询结果

MySQL 外键已创建,但其引用不存在

mysql - 优化内连接 SQL 查询

sql - 列的 SUM 和分组依据的问题