【问题标题】:how to rewrite query replacing of (or),(and) conditions如何重写(或),(和)条件的查询替换
【发布时间】:2012-12-13 10:43:43
【问题描述】:
  1. 查询执行时间过长,大约需要 130 分钟。
  2. 具有索引但查询未使用索引的表。
  3. 表大小约为 3000 万条记录。
  4. 请帮助我通过更改(或)、(和)条件来重写查询。
  5. 查询未在正常执行中使用,因此我们尝试使用强制索引但没有成功。

查询:

SELECT customer_history.customer_id,
        customer_history.row_mod,
        customer_history.row_create,
        customer_history.event_id,
        customer_history.event_type,
        customer_history.new_value,
        customer_history.old_value
FROM customer_history FORCE INDEX (customer_history_n2)
WHERE customer_id >= 1
        AND customer_id < 5000000
        AND (customer_history.row_mod >= '2012-10-01')
        OR (
                customer_history.row_create >= '2012-10-01'
                AND customer_history.row_create < '2012-10-13'
                );


Table structure

    CREATE TABLE `customer_history` (
      `customer_id` int(11) NOT NULL,
      `row_mod` datetime DEFAULT NULL,
      `row_create` datetime DEFAULT NULL,
      `event_id` int(11) DEFAULT NULL,
      `event_type` varchar(45) DEFAULT NULL,
      `new_value` varchar(255) DEFAULT NULL,
      `old_value` varchar(255) DEFAULT NULL,
      KEY `customer_id1` (`customer_id`),
      KEY `new_value_n1` (`new_value`),
      KEY `customer_history_n1` (`row_create`),
      KEY `customer_history_n2` (`row_mod`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8

解释计划:

+----+-------------+------------------+------+---------------------+------+---------+------+----------+-------------+
| id | select_type | table            | type | possible_keys       | key  | key_len | ref  | rows     | Extra       |
+----+-------------+------------------+------+---------------------+------+---------+------+----------+-------------+
|  1 | SIMPLE      | customer_history | ALL  | customer_history_n2 | NULL | NULL    | NULL | 18490530 | Using where |
+----+-------------+------------------+------+---------------------+------+---------+------+----------+-------------+
1 row in set (0

【问题讨论】:

  • 您必须将您的键组合成一个多列索引,以便为此类查询利用适当的索引
  • 如果在查询前面加上 EXPLAIN 会得到什么?您是否绝对肯定确定这是您正在运行的查询,并且您不会为了方便而遗漏任何 JOIN 语句?另外,为什么要使用 FORCE INDEX 选项?
  • 您可以为该表运行SHOW TABLE STATUSSHOW INDEXES 吗?索引选择取决于基数,可能是在该表上运行 OPTIMIZE 的简单案例
  • 发生了什么?你为什么取消我的答案是正确的?您是否找到了与解决您的问题的 FORCE INDEX 问题无关的其他内容?
  • 对不起,你的答案是正确的。当我使用力指数时。它工作正常。

标签: mysql query-performance sql-execution-plan


【解决方案1】:

您可以尝试将此查询分成两部分:

SELECT customer_history.customer_id,
        customer_history.row_mod,
        customer_history.row_create,
        customer_history.event_id,
        customer_history.event_type,
        customer_history.new_value,
        customer_history.old_value
FROM customer_history
WHERE customer_id >= 1
        AND customer_id < 5000000
        AND (customer_history.row_mod >= '2012-10-01')

还有

SELECT customer_history.customer_id,
        customer_history.row_mod,
        customer_history.row_create,
        customer_history.event_id,
        customer_history.event_type,
        customer_history.new_value,
        customer_history.old_value
FROM customer_history
WHERE  customer_history.row_create >= '2012-10-01'
       AND customer_history.row_create < '2012-10-13';

并查看其中哪一个需要更长的时间来执行和优化它们。为了获得与第一个查询相同的结果集,您可以在 MySQL 中UNION 这两个查询,或者简单地从您的应用程序层单独执行它们并在那里加入结果集。

【讨论】:

    【解决方案2】:

    啊,我想我知道你的问题是什么。根据MySQL documentation

    通过指定 USE INDEX (index_list),你可以告诉 MySQL 只使用 用于在表中查找行的命名索引之一。替代方案 语法 IGNORE INDEX (index_list) 可用于告诉 MySQL 不要使用 一些特定的索引或索引。如果 EXPLAIN,这些提示很有用 表明 MySQL 使用了可能列表中的错误索引 索引。

    您也可以使用 FORCE INDEX,其作用类似于 USE INDEX (index_list) 但另外假设表扫描非常 昂贵的。换句话说,只有在没有 使用给定索引之一在表中查找行的方法。

    换句话说,通过使用 FORCE INDEX,您告诉 MySQL 使用customer_history_n2 索引并跳过所有其他可用索引。您需要从查询中删除 FORCE INDEX 子句,或者将其指定为 this 以便它将使用其他可用列的索引:

    SELECT customer_history.customer_id,
        customer_history.row_mod,
        customer_history.row_create,
        customer_history.event_id,
        customer_history.event_type,
        customer_history.new_value,
        customer_history.old_value
    FROM customer_history
        FORCE INDEX (customer_history_n2, customer_history_n1, customer_id1)
    WHERE customer_id >= 1
        AND customer_id < 5000000
        AND (customer_history.row_mod >= '2012-10-01')
        OR (
            customer_history.row_create >= '2012-10-01'
            AND customer_history.row_create < '2012-10-13' );
    

    【讨论】:

    • 顺便说一句,有 3000 万条记录,您可能需要认真考虑对数据库进行一些水平分区。如果您经常查询数据库以获取例如一个月内的所有日期,则可以按范围对其进行分区。我建议阅读这篇文章以获取更多信息,因为它会对您的数据库性能产生非常积极的影响。 dev.mysql.com/tech-resources/articles/partitioning.html
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-01
    • 2020-12-22
    • 2023-04-02
    相关资源
    最近更新 更多