7

我有以下查询:

SELECT DISTINCT f1.match_static_id,
                f2.comments_no,
                f2.maxtimestamp,
                users.username,
                users.id,
                matches_of_comments.localteam_name,
                matches_of_comments.visitorteam_name,
                matches_of_comments.localteam_goals,       
                matches_of_comments.visitorteam_goals,
                matches_of_comments.match_status,
                new_iddaa.iddaa_code
FROM comments AS f1
INNER JOIN (
             SELECT match_static_id,
                    MAX( TIMESTAMP ) maxtimestamp,
                    COUNT( match_static_id ) AS comments_no
             FROM comments
             GROUP BY match_static_id
          ) AS f2 ON f1.match_static_id = f2.match_static_id 
                  AND f1.timestamp = f2.maxtimestamp
INNER JOIN users ON users.id = f1.user_id
INNER JOIN matches_of_comments ON matches_of_comments.match_id = f2.match_static_id
LEFT JOIN new_iddaa ON new_iddaa.match_id = matches_of_comments.match_id
WHERE matches_of_comments.flag =1
ORDER BY f2.maxtimestamp DESC

这是该查询的 EXPLAIN 计划:

+----+-------------+---------------------+--------+-----------------------------------+-----------+---------+------------------------------------------+-------+------------------------------------------------+
| id | select_type |        table        |  type  |           possible_keys           |    key    | key_len |                   ref                    | rows  |                     extra                      |
+----+-------------+---------------------+--------+-----------------------------------+-----------+---------+------------------------------------------+-------+------------------------------------------------+
|  1 | PRIMARY     | <derived2>          | ALL    | NULL                              | NULL      | NULL    | NULL                                     |   542 | Using temporary; Using filesort                |
|  1 | PRIMARY     | f1                  | ref    | timestamp,match_static_id,user_id | timestamp | 4       | f2.maxtimestamp                          |     1 | Using where                                    |
|  1 | PRIMARY     | users               | eq_ref | PRIMARY                           | PRIMARY   | 4       | skormix_db1.f1.user_id                   |     1 |                                                |
|  1 | PRIMARY     | matches_of_comments | ALL    | match_id                          | NULL      | NULL    | NULL                                     | 20873 | Range checked for each record (index map: 0x8) |
|  1 | PRIMARY     | new_iddaa           | ref    | match_id                          | match_id  | 4       | skormix_db1.matches_of_comments.match_id |     1 |                                                |
|  2 | DERIVED     | comments            | ALL    | NULL                              | NULL      | NULL    | NULL                                     |   933 | Using temporary; Using filesort                |
+----+-------------+---------------------+--------+-----------------------------------+-----------+---------+------------------------------------------+-------+------------------------------------------------+

如果此匹配项至少包含一条评论,我将使用此查询来获取匹配信息。
我得到了团队的名称、代码(iddaa 代码)、评论数量、最后一个评论的时间戳、最后一个评论的作者。
我有一个大数据库,预计在接下来的几个月里它会更大,我对 MySQL 查询非常陌生,我想确保我从一开始就使用优化查询,所以我想知道如何阅读这个解释信息使查询更好更快。

我看到表中有很多地方没有使用索引,即使我构建了它们。
我还在表列中看到了派生,我不知道如何使这个查询更快以及如何摆脱文件排序,因为我无法为派生查询创建索引??

我用索引(键)写下查询中使用表的结构,希望能对我的问题得到一些提示或简单的答案,在此先感谢。

评论(f1)表结构是:

CREATE TABLE `comments` (
 `id` int(25) NOT NULL AUTO_INCREMENT,
 `comments` text COLLATE utf8_unicode_ci NOT NULL,
 `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 `date` date NOT NULL,
 `time` time NOT NULL,
 `match_static_id` int(25) NOT NULL,
 `ip` varchar(255) CHARACTER SET latin1 NOT NULL,
 `comments_yes_or_no` int(25) NOT NULL,
 `user_id` int(25) NOT NULL,
 PRIMARY KEY (`id`),
 KEY `timestamp` (`timestamp`),
 KEY `match_static_id` (`match_static_id`),
 KEY `user_id` (`user_id`)
) ENGINE=MyISAM AUTO_INCREMENT=935 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

用户表结构为:

CREATE TABLE `users` (
 `id` int(25) NOT NULL AUTO_INCREMENT,
 `username` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `password` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `gender` int(25) NOT NULL,
 `first_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `last_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `avatar` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `alert` int(25) NOT NULL,
 `daily_tahmin` int(25) NOT NULL,
 `monthly_tahmin` int(25) NOT NULL,
 `admin` int(25) NOT NULL,
 PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=995 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

match_of_comments_ 结构是:

CREATE TABLE `matches_of_comments` (
 `id` int(25) NOT NULL AUTO_INCREMENT,
 `en_tournament_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `tournament_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `country_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `match_status` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `match_time` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `match_date` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `static_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `fix_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `match_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `localteam_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `localteam_goals` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `localteam_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `visitorteam_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `visitorteam_goals` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `visitorteam_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `ht_score` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `flag` int(25) NOT NULL,
 PRIMARY KEY (`id`),
 KEY `match_status` (`match_status`),
 KEY `match_date` (`match_date`),
 KEY `match_id` (`match_id`),
 KEY `localteam_id` (`localteam_id`),
 KEY `visitorteam_id` (`visitorteam_id`),
 KEY `flag` (`flag`)
) ENGINE=MyISAM AUTO_INCREMENT=237790 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

new_iddaa 表结构为:

CREATE TABLE `new_iddaa` (
 `id` int(25) NOT NULL AUTO_INCREMENT,
 `match_id` int(25) NOT NULL,
 `iddaa_code` int(25) NOT NULL,
 `tv_channel` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
 `skormix_tahmin` varchar(255) CHARACTER SET utf8 NOT NULL,
 PRIMARY KEY (`id`),
 KEY `match_id` (`match_id`)
) ENGINE=MyISAM AUTO_INCREMENT=8191 DEFAULT CHARSET=latin1
4

5 回答 5

2

此查询的主要问题是连接数。我会建议:

  1. 取出嵌套查询f2并将其输出插入表或临时表,最好使用索引。(使用索引它会将全表扫描更改为索引,从而加快速度。此类表可重用的可能性很高)。

  2. 替代 1 或同时交换f1f2。您需要尽早消除行。因此,首先获取您需要的匹配和时间戳列表,然后只加入必要的日期。此查询需要巨大的评论表并过滤掉不必要的行。对于相同的结果,取 10% 比取走 90% 更容易。

  3. 删除Distinct,因为它在这里什么都不做:所有连接都是平等的。如果有任何重复,则从所有表中获取数据并找到差异。然后添加必要JOIN条件来选择您需要的行。

  4. 考虑这些PRIMARY KEY: 评论:PRIMARY KEY (match_static_id) matches_of_comments:PRIMARY KEY (match_id) new_iddaa:PRIMARY KEY (match_id)

    原因:主键应该在一个经常使用的列上做有意义的。将它放在技术AUTOINCREMENTing 列上不会为表格增加价值。

  5. 您可以考虑将flag第一列添加到PRIMARY KEY. 更改时会增加开销flag,但会加速此查询,因为所有flag=1记录都将在单个范围内并在 hdd 上按顺序排列。希望这可以帮助。

于 2013-11-21T00:03:09.970 回答
1

如果你把这件作品转过来怎么办:

     SELECT match_static_id,
            MAX( TIMESTAMP ) maxtimestamp,
            COUNT( match_static_id ) AS comments_no
     FROM comments
     GROUP BY match_static_id

到一个表中,你用触发器填充了这个?当新记录保存在评论中时,它会触发“comments_counter”表中的更新或类似的东西?这应该会给您一些性能,因为您不需要 Max、Counts、group by 操作。

此外,matches_of_comments.match_id = f2.match_static_id 具有不同的数据类型,第一个是 varchar(255),第二个是 int(25) - 两者都是 int(25) 应该有助于整体性能。

最后,我会让评论中的 user_id 也成为 user.id 的外键。

于 2013-11-22T00:31:31.793 回答
1

在讨论选项之前,先从更紧迫的问题开始。


第一个直接的问题是:

SELECT DISTINCT …

Aselect distinct很慢。非常非常慢:它基本上比较您的集合返回的每一行的每个字段。当其中有一个保证每行唯一的元素时,自然有优化的空间ID,但是您自己的查询看起来并不提供任何这种可能性:充其量是一个来自matches_of_commentsand的元组new_iddaa

要解决此问题,请将查询分成两个或更多部分,并仅获取您正在执行的操作实际需要的内容。这似乎是matches_of_comments按他们的最新评论日期排序,然后从users和获取额外的化妆品数据new_iddaa

下一个是最大的问题恕我直言:

INNER JOIN (
         SELECT match_static_id,
                MAX( TIMESTAMP ) maxtimestamp,
                COUNT( match_static_id ) AS comments_no
         FROM comments
         GROUP BY match_static_id
      ) AS f2 ON f1.match_static_id = f2.match_static_id 
              AND f1.timestamp = f2.maxtimestamp

您正在将一个聚合与一个(match_static_id, timestamp)没有索引的元组上的表连接起来,并在那里获取一个巨大的集合。你有一个保证的合并加入——不是你想要的。

最后一个让人大跌眼镜的问题是:

ORDER BY f2.maxtimestamp DESC

首先,你没有限制。这意味着您将构建、排序并返回一个巨大的集合。当然,您正在对这些数据进行分页,因此请在查询中添加限制子句。

添加限制后,您需要考虑添加额外行的内容以及应如何对它们进行排序。根据您的架构,我想new_iddaa可以。您是否以这样一种方式对事物进行分页,即后者的信息需要成为该查询的一部分以及它返回的行数?我想不会,因为您显然对这些行的排序方式不感兴趣。

扫描您的架构后,会弹出这个额外的架构:

`match_id` varchar(255)

引用它的行是整数,对吗?所以它也应该是一个整数,以避免将 varchars 转换为 ints 或反之亦然的开销,并允许在任何一种情况下使用索引。

虽然与此特定查询无关,但以下两个字段也需要注意并正确转换:

`tournament_id` varchar(255)
`match_time` varchar(255)
`match_date` varchar(255)
`static_id` varchar(255)
`fix_id` varchar(255)
`localteam_id` varchar(255)
`visitorteam_id` varchar(255)

继续改进查询……</p>

当我读到它时,您正在matches_of_comments按最新评论订购。您还需要评论的数量,所以我们从这样做开始。假设您要对前 10 个进行分页,查询如下所示:

SELECT match_static_id,
       MAX( TIMESTAMP ) maxtimestamp,
       COUNT( match_static_id ) AS comments_no
FROM comments
GROUP BY match_static_id
ORDER BY maxtimestamp DESC
LIMIT 10 OFFSET 0

就这样。

它为您提供 10 个 ID — 如果您增加限制,则更多。在您的应用程序中循环它们并构建一个in (…)子句,允许您根据需要从其他表中获取每个单独的数据位;您可以通过一个或多个查询来做到这一点,这无关紧要。关键是要避免加入该聚合,以便索引可用于后续查询。


通过完全删除上述查询,您可以更显着地改进事情。

为此,向 中添加三个字段matches_of_comments,即last_comment_timestamplast_comment_user_idnum_comments。使用触发器维护它们,并在(flag, last_comment_timestamp). 这将允许您改为运行以下高效查询:

SELECT matches_of_comments.static_id,
       matches_of_comments.num_comments,
       matches_of_comments.last_comment_timestamp,
       matches_of_comments.last_comment_user_id,
       matches_of_comments.localteam_name,
       matches_of_comments.visitorteam_name,
       matches_of_comments.localteam_goals,       
       matches_of_comments.visitorteam_goals,
       matches_of_comments.match_status
FROM matches_of_comments
WHERE matches_of_comments.flag = 1
ORDER BY matches_of_comments.last_comment_timestamp DESC
LIMIT 10 OFFSET 0

然后,您只需要从usersand中选择所需的数据new_iddaa— 使用已经讨论过的带有in (…)子句的单独查询。

于 2013-11-22T17:31:08.763 回答
0

distinct 总是会对性能产生影响,验证查询的哪一部分正在获取多行,如左外连接,目标是删除 distinct。使您的列更小,只占用减少索引空间所需的空间。

验证是否按子查询中的日期的顺序使用索引,如果不使用表中的等效日期。

于 2013-11-16T19:57:35.783 回答
0

我使用了分析功能。但由于没有数据可供我测试,我不太确定这是否是最优的

SELECT * 
FROM
(
    SELECT DISTINCT f1.match_static_id,
                    users.username,
                    users.id,
                    matches_of_comments.localteam_name,
                    matches_of_comments.visitorteam_name,
                    matches_of_comments.localteam_goals,       
                    matches_of_comments.visitorteam_goals,
                    matches_of_comments.match_status,
                    new_iddaa.iddaa_code,
                    @MAX_TIMESTAMP AS `FIRST_VALUE(MATCH_STATIC_ID) OVER(partition by f1.match_static_id ORDER BY F1.TIMESTAMP DESC)`,
                    @COMMENTS_NO AS `COUNT(1) OVER(partition by f1.match_static_id)`,
                    F1.TIMESTAMP
    FROM comments AS f1
    INNER JOIN users ON users.id = f1.user_id
    INNER JOIN matches_of_comments 
    ON matches_of_comments.match_id = f1.match_static_id
    AND matches_of_comments.flag =1
    LEFT JOIN new_iddaa 
    ON new_iddaa.match_id = matches_of_comments.match_id
) A
WHERE @MAX_TIMESTAMP = TIMESTAMP
ORDER BY @MAX_TIMESTAMP DESC
于 2013-11-22T03:22:48.643 回答