【问题标题】:How can I optimize this query with subquery?如何使用子查询优化此查询?
【发布时间】:2012-01-27 21:32:40
【问题描述】:

我有以下 MySQL 查询:

SELECT value_1, (
    SELECT value_4 
    FROM table_1
    WHERE value_3 < value_1 
    ORDER BY value_3 DESC  
    LIMIT 1
)
AS result_value 
FROM table_2 
WHERE value_1 BETWEEN 1325372400000 AND 1328050800000  
ORDER BY value_1

返回 32 个结果,运行时间为 6.6 秒。这个想法是从 table_1 中获取条目,其中 value_3 与 table_2 中的 value_1“最接近”。

查询由两个查询组成,即

SELECT value_1
AS result_value 
FROM table_2 
WHERE value_1 BETWEEN 1325372400000 AND 1328050800000  
ORDER BY value_1

和(例如)

SELECT value_4 
FROM table_1
WHERE value_3 < 1328050800000 
ORDER BY value_3 DESC  
LIMIT 1

每次运行需要 0.03 秒。计算 32 个结果集的累积时间,复合查询应该不会超过 1 秒,甚至可能更短(因为单个查询的 I/O 开销没有考虑在内)。然而,在 6.6 秒时,它需要的时间要长得多。

为什么会这样,我该如何优化它?还是有其他/更好的方法来实现我的目标?

更新:

表定义:

table_1(MyISAM,700000 个条目):

'id', 'int(10) unsigned', 'NO', 'PRI', NULL, 'auto_increment'
'value_3', 'bigint(20) unsigned', 'NO', 'UNI', '0', ''
'value_4', 'bigint(20) unsigned', 'NO', '', '0', ''

table_2(MyISAM,4000 个条目):

'value_1', 'bigint(20) unsigned', 'NO', 'PRI', NULL, ''

解释[查询]:

'1', 'PRIMARY', 'table_2', 'range', 'PRIMARY,value_3_UNIQUE', 'PRIMARY', '8', NULL, '32', 'Using where; Using index'
'2', 'DEPENDENT SUBQUERY', 'table_1', 'index', 'value_3,value_3_value_4', 'value_3', '8', NULL, '1', 'Using where'

【问题讨论】:

  • 你的表有什么索引?
  • 你在table_1 上有一个(value3) 索引吗?还是(value3, value4)
  • 你能添加表格的定义吗?表格有多少行?
  • 您的通用“价值观”并没有为我们提供您想要的真实背景。答案可能是以完全不同的方式处理您的查询。如果一些显示上下文的示例数据(有限,并且只有那些列可以理解)有时也会有所帮助。
  • 我在 table_2 上有索引 value_1,在 table_1 上有 value_3 和 (value_3, value_4)。

标签: mysql sql subquery


【解决方案1】:

使用一点mysql功夫:

SELECT * from
(SELECT value_1, value_4
FROM table_2 
join table_1 on value_3 < value_1
WHERE value_1 BETWEEN 1325372400000 AND 1328050800000  
ORDER BY value_1, value_3 DESC) x
GROUP BY value_1 
ORDER BY value_1

“功夫”是当你没有分组的列没有聚合时(例如SUM() 等),使用 mysql,你会得到每个组遇到的 first 行。如果您在有序结果集上使用此技术,您可以获得所需的值。

这不仅更容易编码,而且您会注意到只对表进行了一次传递(而不是您尝试过的每行一次查询)。它应该表现良好。


编辑:

一些评论者猜测GROUP BY 的这种特殊形式是“不确定的”和/或“不受官方支持”等。documentation 指出在“不确定”中选择的行,但是我从来没有见过也没有听说过 mysql 选择任何行 other 而不是第一次遇到并在内部选择上使用 order by 在无数生产查询中使用和依赖。

FWIW,我很高兴将这种方法推荐为“可靠”且值得生产。

【讨论】:

  • 这可能会导致订购一个非常大的派生表。
  • 这是否保证在所有情况下都返回正确的行,因为您依赖 MySQL 来完全返回每个组中的第一行。据我了解,这不一定是这种情况,只要它们属于该组,基本上由 MySQL 来为每个属性返回任意值?!
  • @ypercube 是的。 OP 注意:因为你有一个范围(不是匹配),所以行数是无限的。如果这个查询很糟糕,那就糟糕了。你将不得不尝试另一种方式。这完全取决于行数和可用系统资源
  • @stryba:查询可以正常工作(给出正确的结果)。至少在您更新 MySQL 并且查询优化器代码已更改之前(关于 GROUP BY 的工作原理)。然后这个查询就会中断:)
  • The documentation could not be any clearer that results are indeterminate, not guaranteed 所以即使它现在确实有效,我也不会使用它,除非您计划在升级时重新访问它。
【解决方案2】:

如果您在table_2 中的value_1 上有一个索引,并且在table_1 上有一个复合(value3, value4) 索引,那么查询将只使用这些索引。


你也可以试试这个查询:

SELECT value_1
     , value_4 AS result_value 
FROM table_2 
  JOIN table_1
    ON table_1.value_3 =
       ( SELECT value_3
         FROM table_1
         WHERE value_3 < value_1 
         ORDER BY value_3 DESC  
         LIMIT 1
       )
WHERE value_1 BETWEEN 1325372400000 AND 1328050800000  
ORDER BY value_1

【讨论】:

  • 我一开始没有的复合索引;现在添加后,查询仍然以相同的 6.6 秒计时。
  • @Maximilian:你能在问题中添加表格定义和 EXPLAIN 输出吗?
  • 我已将表定义添加到我的问题中。
【解决方案3】:

这里我只是避免使用虚拟(派生)表。使用虚拟表的主要原因是排序,这是通过在 groupcconcat() 中应用 order by 子句实现的。

SELECT SUBSTRING_INDEX(group_concat(value_1 
                                    ORDER BY value_1, value_3 DESC),',',1) as value_1, 
       SUBSTRING_INDEX(group_concat(value_4
                                    ORDER BY value_1, value_3 DESC),',',1) as value_4 
FROM table_2 join table_1 on value_3 < value_1 

WHERE value_1 BETWEEN 1325372400000 AND 1328050800000  

GROUP BY value_1  
ORDER BY value_1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-01
    • 2023-04-02
    相关资源
    最近更新 更多