【问题标题】:Taming a monster MySQL query驯服一个怪物 MySQL 查询
【发布时间】:2012-10-12 11:27:17
【问题描述】:

因此,在好心的 SO 用户的一点点帮助下,我最终得到了一个逻辑上正确的 MySQL 查询,用于我正在处理的一项任务:检索一个按时间顺序排列的 id 列表,以获得允许的新闻项目用户,将某些类型的分组项目过滤到该组的单个代表。 (呼!)

剩下的明显问题是这个查询非常笨重而且速度很慢 - 根据 CakePHP 的数据库调用调试打印输出,大约 145000 毫秒,哎呀。

有没有一种明智的方法来驯服像这样的野兽,或者我应该承认我在这里咬得比我能咀嚼的更多,并寻找一种不那么笨重的方法来获得或多或少相似的结果?所有建议都表示赞赏。

    SELECT DISTINCT Uid.id, Uid.type
    FROM (SELECT uids.id id, uids_uids.parent_id parent_id, uids.created date,
                 uids.type type
          FROM uids 
          JOIN uids_uids ON uids_uids.uid_id = uids.id
          JOIN aros_uids ON uids.id = aros_uids.uid_id
          JOIN uids_uids ParentUids ON uids_uids.parent_id = ParentUids.uid_id
          WHERE uids.type IN ('Document','Photo','Release','PreRelease',
                              'ArtworkResource','Event') 
            AND (uids.start_date IS NULL OR uids.start_date <= NOW())
            AND (uids.end_date IS NULL OR uids.end_date <= NOW())
            AND aros_uids.aro_id IN (3,2,86,1448)
          ) Uid
    JOIN (SELECT uids_uids.parent_id parent_id, MAX(uids.created) maxdate
          FROM uids JOIN uids_uids
          ON uids_uids.uid_id = uids.id
          GROUP BY uids_uids.parent_id, uids.type) T2
    ON Uid.parent_id = T2.parent_id AND Uid.date = T2.maxdate
    ORDER BY Uid.date DESC
    LIMIT 100

预计到达时间:

好的,作为第一遍,我将这些子选择转换为视图,所以现在查询看起来更易于管理

    SELECT DISTINCT Uid.id, Uid.type
    FROM UidView Uid
    JOIN UidView2 T2
    ON Uid.parent_id = T2.parent_id AND Uid.date = T2.maxdate
    WHERE Uid.aro_id IN (3,2,86,1448)
    ORDER BY Uid.date DESC
    LIMIT 100

这肯定有帮助,将 Cake 的估计查询时间(毫秒)从六位数减少到 2500 左右。绝对是一个好的开始!

【问题讨论】:

  • 我会给你一件事 - 在一个查询中查看很多“uid”并让你了解......
  • @nickhar 告诉我!在我的辩护中,我没有设置任何这些,我只是试图慢慢地将它摔跤到一个可控的状态。站点上的每个项目都有一个 uid,它通过 uids_uids 表连接到其他 uid。有很多“WHERE uids_uids.uid_id = uid.id”类型的东西在发生:)
  • 你有我的同情。一个 uid_uid 表!?好的。我会捕获该结构并将其提交给编码恐怖。
  • 首先,您是否对查询运行了 MYSQL 解释以确保最大限度地提高索引效率?
  • 索引 101:在引用另一个表(外键)的任何字段上放置索引,但在 where 子句中的任何字段上放置索引。如果 where 子句使用多个字段,则在所有字段上放置一个组合索引。

标签: mysql cakephp


【解决方案1】:

这是我会尝试的:

获取每个派生查询并分别针对每个查询运行EXPLAIN。正如 cmets 建议的那样,检查任何缺少索引的行并在需要时添加。发布您的EXPLAIN 结果以获得任何帮助。所以

EXPLAIN SELECT uids.id id, uids_uids.parent_id parent_id, uids.created date, ....
EXPLAIN SELECT uids_uids.parent_id parent_id, MAX(uids.created) maxdate ....

如果添加索引没有帮助或帮助很大,那么首先将每个子查询放入临时表并对其应用索引:

CREATE TABLE temp_uid
SELECT uids.id id, uids_uids.parent_id parent_id, uids.created date,
             uids.type type
      FROM uids 
      JOIN uids_uids ON uids_uids.uid_id = uids.id
      JOIN aros_uids ON uids.id = aros_uids.uid_id
      JOIN uids_uids ParentUids ON uids_uids.parent_id = ParentUids.uid_id
      WHERE uids.type IN ('Document','Photo','Release','PreRelease',
                          'ArtworkResource','Event') 
        AND (uids.start_date IS NULL OR uids.start_date <= NOW())
        AND (uids.end_date IS NULL OR uids.end_date <= NOW())
        AND aros_uids.aro_id IN (3,2,86,1448);

CREATE TABLE temp_t2
SELECT uids_uids.parent_id parent_id, MAX(uids.created) maxdate
      FROM uids JOIN uids_uids
      ON uids_uids.uid_id = uids.id
      GROUP BY uids_uids.parent_id, uids.type;

还有JOIN 在这些桌子上:

SELECT DISTINCT Uid.id, Uid.type
FROM temp_uid AS Uid
JOIN temp_t2 AS T2 ON Uid.parent_id = T2.parent_id AND Uid.date = T2.maxdate
ORDER BY Uid.date DESC
LIMIT 100;

正如我所提到的,您可能必须添加索引,并且可能需要添加到临时表中的这些列:

ALTER TABLE temp_uid ADD INDEX parentDateIdx (parent_id, Uid.date);
ALTER TABLE temp_t2 ADD INDEX parentMaxDateIdx (parent_id, maxdate);

如果您需要刷新临时表,只需截断它们并对其执行INSERT INTO temp_uid...SELECTINSERT INTO temp_t2...SELECT,而不是CREATE...SELECT。存储过程非常适用于此。

顺便说一句,执行CREATE TABLE temp_t2...SELECT,就像我对每个临时表所做的那样,可能无法创建最佳的表结构,因此最好稍后修改创建或自己从头开始。

【讨论】:

  • 干杯,这看起来是一种明智的做法。我尝试了类似的东西,但使用视图而不是表格。 CREATE TABLE 是否可能产生更好的结果?
  • 我相信应该这样做,尤其是当您向这些临时表的列添加索引时。您确实需要对其进行测试才能确定。并且不要忽略向所有这些 uid 表添加索引。这是我的创建表示例 link 和 insert...select link 中的文档
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-06
  • 1970-01-01
  • 1970-01-01
  • 2021-12-22
  • 2014-04-12
  • 1970-01-01
相关资源
最近更新 更多