在子查询中,快速查询会变慢

时间:2014-02-07 16:56:06

标签: mysql sql subquery

我有这个问题:

SELECT timestmp
    FROM devicelog 
    WHERE device_id = 5
    ORDER BY id DESC LIMIT 1

获取时间不到0.001秒,但是一旦我将它放入子查询中,它就会减慢到大约3.05秒。有什么理由为什么会这样做,或者我如何解决它?

这是第二个查询(我想要优化的那个):

SELECT device.id,
    (SELECT timestmp
    FROM devicelog 
    WHERE device_id = device.id
    ORDER BY id DESC LIMIT 1) as timestmp
FROM device

表“设备”只有10-15条记录(devicelog有几百万条记录),所以我假设每条记录逐一进行,然后执行子查询,但显然它正在做其他事情。 devicelog的PK是id,设备中的PK也是id。对于timestmp(这是一个日期时间)和device_id的devicelog有一个索引,它也是一个返回到devicelog的FK。还有其他指数,但它们无关紧要(如名称,描述等)。

我只需要循环浏览设备,然后显示最后一个时间戳记录。

如果我在PHP中列出每个设备,然后单独执行第一个查询,它将表现得非常好,但我想在一个完整的查询中执行此操作。就像,我可以做一些像(伪代码):

foreach($row in <devicelog>)
   query('<first query> where id = $row[id]')

在devicelog上进行整个连接太昂贵了,因为计数很高。

2 个答案:

答案 0 :(得分:1)

使用内部联接

尝试此操作
  SELECT d.id, dl.timestamp
  FROM device d
  INNER JOIN  devicelog  dl
  ON  dl.device_id = d.id
  ORDER BY d.id DESC LIMIT 1

列出您可能认为是GROUP BY的所有设备

  SELECT d.id, dl.timestamp
  FROM device d
  INNER JOIN  devicelog  dl
  ON  dl.device_id = d.id
  GROUP BY d.id
  ORDER BY d.id DESC

编辑:

如果你有很多索引,最好索引你的列id

试试这个

  ALTER TABLE `device` ADD INDEX `id` (`id`)

答案 1 :(得分:1)

根据提供的第一个答案的评论,您的问题和查询与您要查找的内容不符。

您可以做的是在每台设备上进行预聚合以获取最大ID日志,然后将其加入您的主设备列表......

SELECT 
      d.name,
      d.id,
      DeviceMax.lastTime
   from
      device d
         LEFT JOIN ( select dl.device_id,
                            max( dl.timestamp ) lastTime
                        from
                           devicelog dl
                        group by
                           dl.device_id ) as DeviceMax
            ON d.id = DeviceMax.device_id

现在,如果您需要从设备日志中获取该特定条目的其他内容,我们可以添加到该...

LEFT JOIN devicelog dl2
   on DeviceMax.Device_id = dl2.Device_id
   AND DeviceMax.lastTime = dl2.timestamp

然后您可以将“dl2”别名中的任何其他列添加到您的查询中。

另外,对于你的设备日志表,我会有一个覆盖索引(device_id,timestamp)

来自反馈的评论

然后我会提出这个作为对你的建议,当有人需要“最高数量”或“大多数”某事或“最近”等等时,这在网络开发领域也很常见。

仅使用一个方面对您的Device表进行非规范化...为LastDeviceLogID添加一列。然后,只要您的DeviceLog添加了一个条目,您就可以使用后插入触发器来执行...

update Device 
   set LastDeviceLogID = newRecord.DeviceLogID
   where ID = newRecord.Device_ID

列可能不准确,但原则就在那里。通过这种方式,您永远不需要执行LIMIT 1,MAX()等操作并查看数百万条记录,您可以像执行一样简单地进行操作

SELECT 
      d.name,
      d.id,
      dl.timestamp,
      dl.othercolumns
   from
      device d
         LEFT JOIN devicelog dl
            on d.LastDeviceLogID = dl.DeviceLogID