MySQL查询优化:正确索引

时间:2015-07-20 07:42:44

标签: mysql sql optimization indexing

我需要帮助才能在一个LEFT JOIN的SQL查询中选择正确的索引。

我有两个表(对象和月度报告):

CREATE TABLE some_objects (
    id int(11) NOT NULL AUTO_INCREMENT,
    ...,
    PRIMARY KEY (id),
);
CREATE TABLE month_reports (
    id int(11) NOT NULL AUTO_INCREMENT,
    some_object_id int(11) DEFAULT NULL,
    month int(11) NOT NULL,
    year int(11) NOT NULL,
    ...,
    PRIMARY KEY (id),
    INDEX IDX_123 (month, year),
    INDEX IDX_456 (some_object_id),
    INDEX IDX_789 (some_object_id, month, year),
    CONSTRAINT FK_123 FOREIGN KEY (some_object_id)
    REFERENCES some_objects (id) ON DELETE RESTRICT ON UPDATE RESTRICT
)

我需要在特定月份选择所有带报告的对象,或者不选择它们。我试过这个问题:

SELECT * FROM some_objects so 
LEFT JOIN month_reports mr ON mr.some_object_id = so.id
WHERE
    mr.month = :month
    AND mr.year = :year
    OR mr.id IS NULL

EXPLAIN结果:

id  select_type  table  type  possible_keys
1   SIMPLE       so     ALL
1   SIMPLE       mr     ALL   IDX_456,IDX_789

EXPLAIN结果没有OR mr.id IS NULL

id  select_type  table  type    possible_keys            key      ref
1   SIMPLE       mr     ref     IDX_123,IDX_456,IDX_789  IDX_123  const,const
1   SIMPLE       so     eq_ref  PRIMARY

但如果没有OR mr.id IS NULL,我就不会在没有报告的情况下获得对象!

我应该添加哪个其他索引?我需要更改查询吗?

P.S。抱歉我的英文。

UPD。 ANALYZE TABLE返回"状态正常"。 EXPLAIN结果为FORCE INDEX(10个对象,只有一个有报告):

id  select_type  table  type  possible_keys  key      rows
1   SIMPLE       so     ALL                           10
1   SIMPLE       mr     ref   IDX_789        IDX_789  1

2 个答案:

答案 0 :(得分:0)

好吧,我没有得到我需要的东西。我会考虑如何以另一种方式解决我的问题。

不幸的是,我无法在我的框架中使用FORCE INDEX。但即使他们都没有报告,我也需要获得所有对象。

我只想优化我的SQL查询。在单个查询中执行此操作是不成功的尝试:)

答案 1 :(得分:0)

这是可能做你想做的重新制定:

SELECT  so.*
    FROM  some_objects so
    LEFT JOIN  month_reports mr
          ON mr.some_object_id = so.id
          AND ( mr.month = :month  AND  mr.year = :year )

请注意,由于IS NULL的工作方式,它可以避免处理LEFT。但是,它会将WHERE移动到ON

,从而稍微改变语义

使用UNION代替OR进行以下重新填充也可能有效(假设框架允许您这样做):

      ( SELECT  so.*
            FROM  some_objects so
            JOIN  month_reports mr ON mr.some_object_id = so.id
            WHERE  mr.month = :month
              AND  mr.year = :year         
      )
UNION
      ( SELECT  so.*
            FROM  some_objects so
            LEFT JOIN  month_reports mr ON mr.some_object_id = so.id
            WHERE  mr.id IS NULL 
      )