【问题标题】:MySQL Query Optimization: Correct IndexMySQL 查询优化:正确索引
【发布时间】:2015-07-20 07:42:44
【问题描述】:

我需要帮助以使用一个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,我不会得到没有报告的对象!

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

附:对不起我的英语。

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

【问题讨论】:

  • 您可以将some_object_id添加到现有的IDX_123
  • @Vatev 抱歉,我修复了 IDX_789。它有 some_object_id、month、year 列,并且 MySQL-server 不使用它:(
  • 当行数太少时,有时会出现这种情况(如果行数很少,请添加一些)。尝试运行ANALYZE TABLE。如果这不起作用,请将 FORCE KEY(IDX_789) 添加到 JOIN 并发布查询计划。同时发布行数估计值。
  • @Vatev 我将更新添加到问题中。结果正常吗?
  • 看起来很正常。第一个查询计划 (SIMPLE mr ALL) 的行估计值是多少?

标签: mysql sql optimization indexing


【解决方案1】:

这是一个可能做你想做的事的重新表述:

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 )

请注意,由于LEFT 的工作方式,它会避免处理IS NULL。但是,它通过将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 
      ) 

【讨论】:

    【解决方案2】:

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

    不幸的是,我不能在我的框架中使用FORCE INDEX。但我需要获取所有对象,即使它们都没有报告。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-24
      • 2012-01-27
      • 1970-01-01
      相关资源
      最近更新 更多