【问题标题】:Create index to optimize slow query创建索引以优化慢查询
【发布时间】:2011-03-29 12:38:36
【问题描述】:

有一个查询在 250,000 行表上耗时过长。我需要加快速度:

create table occurrence (
occurrence_id int(11) primary key auto_increment,
client_id varchar(16) not null,
occurrence_cod varchar(50) not null,
entry_date datetime not null,
zone varchar(8) null default null
)
;

insert into occurrence (client_id, occurrence_cod, entry_date, zone)
values
('1116', 'E401', '2011-03-28 18:44', '004'),
('1116', 'R401', '2011-03-28 17:44', '004'),
('1116', 'E401', '2011-03-28 16:44', '004'),
('1338', 'R401', '2011-03-28 14:32', '001')
;

select client_id, occurrence_cod, entry_date, zone
from occurrence o
where
    occurrence_cod = 'E401'
    and
    entry_date = (
        select max(entry_date)
        from occurrence
        where client_id = o.client_id
    )
;
+-----------+----------------+---------------------+------+
| client_id | occurrence_cod | entry_date          | zone |
+-----------+----------------+---------------------+------+
| 1116      | E401           | 2011-03-28 16:44:00 | 004  |
+-----------+----------------+---------------------+------+
1 row in set (0.00 sec)

表格结构来自商业应用,不能更改。

优化它的最佳索引是什么?还是更好的查询?

编辑:

它是每个客户端的 E401 代码的最后一次出现,并且仅当最后一次出现是该代码时。

【问题讨论】:

  • 解释计划是什么样的?
  • @gnuchu 我无权访问生产环境来发布解释。我正在创建对外部客户端的查询。

标签: mysql sql optimization indexing


【解决方案1】:

此类查询的理想索引是:

index #1: [client_id] + [entry_date]
index #2: [occurence_cod] + [entry_date]

但是,如果数据具有某些特征,则可以简化这些索引。这将节省文件空间,以及更新数据(插入/删除/更新)的时间。

如果每个 [client_id] 的“出现”记录很少超过一个,则索引 #1 只能是 [client_id]。

同样,如果每个 [occurence_cod] 的“出现”记录很少超过一个,则索引 #1 只能是 [occurence_cod]。

将索引 #2 转换为 [entry_date] + [occurence_cod] 可能更有用。这将使您能够将索引用于仅在 [entry_date] 上的条件。

问候,

【讨论】:

    【解决方案2】:

    除非您真的试图获取具有最大日期的行,当且仅当occurrence_cod 匹配时,这应该有效:

    select client_id, occurrence_cod, entry_date, zone 
    from occurrence o 
    where occurrence_cod = 'E401'
    ORDER BY entry_date DESC
    LIMIT 1;
    

    它会返回最近的行,其出现_cod='E401'

    【讨论】:

    • 根据题主的内容,好像只是套路了。显示occurrence_cod ='E401'的最后一个条目。
    • @cairnz @dnagirl 是该代码的最后一次出现,但对于每个客户
    • @Clodoaldo:那么你想要 GROUP BY
    【解决方案3】:
    select 
      a.client_id, 
      a.occurrence_cod, 
      a.entry_date, 
      a.zone
    from occurrence a 
      inner join (
        select client_id, occurence_cod, max(entry_date) as entry_date 
        from occurence
      ) as b
    on 
      a.client_id = b.client_id and 
      a.occurence_cod = b.occurence_cod and 
      a.entry_date = b.entry_date
    
    where
        a.occurrence_cod = 'E401'
    

    使用这种方法,您可以避免每行的子选择,并且比较两组大数据应该比比较一组大数据集的每一行更快。

    【讨论】:

    • (使用此查询作为解释计划的基线应该为您提供有关将要使用的索引的信息,因为其他人已经指出了潜在的候选者)
    • 它有效。我会试试。只需将occurence的出现更改为occurrence
    • 性能与我发布的相同,但对我来说更清晰。这就是我投赞成票的原因。
    • 使用 EXPLAIN 计划和上面的索引(如果 mysql 显示它会使用它们),它在大数据集上的执行速度应该比您的子选择快得多。
    【解决方案4】:

    我会重写查询:

    select client_id, occurrence_cod, max(entry_date), zone
    from occurrence 
    group by client_id, occurrence_cod, zone;
    

    (假设其他行确实相同,并且输入日期是唯一变化的)。

    【讨论】:

      【解决方案5】:

      您是否尝试在occurrence_cod 上建立索引?

      【讨论】:

        【解决方案6】:

        如果其他方法不可用,请尝试此方法。

        1. 创建一个新表:last_occurrence。
        2. 每次用户发生,更新此 last_occurrence 表中的相应行。

        通过这样做,您只需要使用以下 sql 即可获得结果 :)

        从 last_occurrence 中选择 *

        【讨论】:

          猜你喜欢
          • 2018-01-05
          • 2012-02-23
          • 1970-01-01
          • 1970-01-01
          • 2023-03-15
          • 1970-01-01
          • 1970-01-01
          • 2012-08-05
          • 1970-01-01
          相关资源
          最近更新 更多