mysql - 使用多个 JOIN 且没有 WHERE 子句的 SQL 语句在 1 秒内检索 8000 行是否现实?

标签 mysql sql mariadb query-optimization

我正在创建一个页面,该页面总结了有关万智牌的牌组、胜率和其他数据,网址为 https://pennydreadfulmagic.com/seasons/all/cards/

主要 SQL 查询在生产中经常需要 10 秒以上(在我的笔记本电脑上较少,但仍然 > 5 秒)。服务器是运行 openSUSE 的标准 Linode 机器,数据库是 MariaDB (MySQL)。

我正在尝试弄清楚在请求页面时在用户时间运行此查询是否是一个愚蠢的想法,或者我是否只需要正确的优化以使其在不到一秒的时间内运行。

查询的简单版本是:

SELECT
    card,
    COUNT(*) AS num_decks,
    SUM(CASE WHEN dm.games > IFNULL(odm.games, 0) THEN 1 ELSE 0 END) AS wins,
    SUM(CASE WHEN dm.games < odm.games THEN 1 ELSE 0 END) AS losses,
    SUM(CASE WHEN dm.games = odm.games THEN 1 ELSE 0 END) AS draws
FROM
    deck_card AS dc
INNER JOIN
    deck AS d ON dc.deck_id = d.id
INNER JOIN
    deck_match AS dm ON d.id = dm.deck_id
INNER JOIN
    deck_match AS odm ON dm.match_id = odm.match_id AND dm.deck_id <> odm.deck_id
GROUP BY
    dc.card
ORDER BY
    num_decks DESC,
    card

每个表中大约有这么多行:

deck_card - 470,000 (DISTINCT card = 8,500)
deck - 20,000
match - 35,000
deck_match - 70,000

这些看起来对于 SQL 来说并不是很大的数字,因此我想知道让这个查询运行得更快是否现实。

解释说:

+--------+-----+-------+------------------------+-------------------------------+-----+----------------------+-------+----------------------------------------------+
| id   | type   | tbl | type  | possible_keys          | key                    | len | ref                  | rows  | Extra                                        |
+------+--------+-----+-------+------------------------+------------------------+-----+----------------------+-------+----------------------------------------------+
|    1 | SIMPLE | d   | index | PRIMARY                | person_id              | 4   | NULL                 | 18888 | Using index; Using temporary; Using filesort |
|    1 | SIMPLE | dm  | ref   | match_id,deck_id       | deck_id                | 4   | decksite.d.id        |     1 |                                              |
|    1 | SIMPLE | odm | ref   | match_id               | match_id               | 4   | decksite.dm.match_id |     1 | Using where                                  |
|    1 | SIMPLE | dc  | ref   | deck_id_card_sideboard | deck_id_card_sideboard | 4   | decksite.d.id        |    10 | Using index                                  |
+------+--------+-----+-------+------------------------+------------------------+-----+----------------------+-------+----------------------------------------------+
4 rows in set (0.00 sec)

我有一个更快的查询版本(仍然太慢),我将 match/deck_card 内容拉出到子查询中,然后将其连接到主查询,我在这里省略了它,因为它更难理解。这确实使速度逐渐加快,但远未接近理想速度。

我不一定希望得到这里所需的优化(尽管这也很好!),而是想了解在用户时间上通过正确的优化运行这样的查询是否现实?或者我应该花时间寻找正确的缓存策略或对数据库进行非规范化?

<小时/>

创建表如下:

mysql> SHOW CREATE TABLE deck_card;
+-----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table     | Create Table                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
+-----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| deck_card | CREATE TABLE `deck_card` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `deck_id` int(11) NOT NULL,
  `card` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `n` int(11) NOT NULL,
  `sideboard` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `deck_card_deck_id_card_sideboard` (`deck_id`,`card`,`sideboard`),
  KEY `idx_card` (`card`),
  KEY `idx_card_deck_id_sideboard_n` (`card`,`deck_id`,`sideboard`,`n`),
  CONSTRAINT `deck_card_ibfk_1` FOREIGN KEY (`deck_id`) REFERENCES `deck` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=39407094 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci |
+-----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> SHOW CREATE TABLE deck;
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| deck  | CREATE TABLE `deck` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `person_id` int(11) NOT NULL,
  `source_id` int(11) NOT NULL,
  `identifier` varchar(190) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `name` text COLLATE utf8mb4_unicode_ci,
  `created_date` int(11) NOT NULL,
  `updated_date` int(11) NOT NULL,
  `competition_id` int(11) DEFAULT NULL,
  `url` text COLLATE utf8mb4_unicode_ci,
  `archetype_id` int(11) DEFAULT NULL,
  `resource_uri` text COLLATE utf8mb4_unicode_ci,
  `featured_card` text COLLATE utf8mb4_unicode_ci,
  `score` int(11) DEFAULT NULL,
  `thumbnail_url` text COLLATE utf8mb4_unicode_ci,
  `small_thumbnail_url` text COLLATE utf8mb4_unicode_ci,
  `finish` int(11) DEFAULT NULL,
  `decklist_hash` char(40) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `retired` tinyint(1) DEFAULT '0',
  `reviewed` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `deck_source_id_identifier` (`source_id`,`identifier`),
  KEY `person_id` (`person_id`),
  KEY `competition_id` (`competition_id`),
  KEY `archetype_id` (`archetype_id`),
  KEY `deck_hash` (`decklist_hash`),
  CONSTRAINT `deck_ibfk_1` FOREIGN KEY (`person_id`) REFERENCES `person` (`id`),
  CONSTRAINT `deck_ibfk_2` FOREIGN KEY (`source_id`) REFERENCES `source` (`id`),
  CONSTRAINT `deck_ibfk_3` FOREIGN KEY (`competition_id`) REFERENCES `competition` (`id`),
  CONSTRAINT `deck_ibfk_4` FOREIGN KEY (`archetype_id`) REFERENCES `archetype` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21460 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> SHOW CREATE TABLE deck_match;
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table      | Create Table                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| deck_match | CREATE TABLE `deck_match` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `match_id` int(11) NOT NULL,
  `deck_id` int(11) NOT NULL,
  `games` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `match_id` (`match_id`),
  KEY `deck_id` (`deck_id`),
  CONSTRAINT `deck_match_ibfk_2` FOREIGN KEY (`deck_id`) REFERENCES `deck` (`id`),
  CONSTRAINT `deck_match_ibfk_3` FOREIGN KEY (`match_id`) REFERENCES `match` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=73857 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci |
+------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

最佳答案

你得到“正确”的答案吗?或者数字是否比应有的大?后者发生在执行 JOINGROUP BY 时发生的“爆炸-内爆”。

看看如果你改变会发生什么

FROM deck_card AS dc

FROM ( SELECT DISTINCT card FROM deck_card ) AS dc

关于mysql - 使用多个 JOIN 且没有 WHERE 子句的 SQL 语句在 1 秒内检索 8000 行是否现实?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51618379/

相关文章:

php - mysql select 转换为 zend Framework select

mysql - 如何划分单个列中的值并将结果存储在 MySQL 中的另一列中

php - 防止时段被重复预订

mysql - 在我的 Rails 项目中使用 MariaDB 而不是 MySQL

mysql - 将一个表的行值附加到另一个表的列

mysql - 删除 Google Cloud SQL 上的二进制日志

sql - 在 Oracle 中添加冗余连接条件会导致不同的计划

mysql - 如何在 Java 应用程序中使用 Hibernate 和 MySQL 清除或删除数据库

mysql - 加速本地MySql对大表发起规范化查询

python - 使用Python从MySQL数据库访问特定表