我的查询的对象是从表a中获取性别= f的所有行,而表b中的用户名不存在于campid = xxxx中。以下是我成功使用的查询:
SELECT `id`
FROM pool
LEFT JOIN sent
ON pool.username = sent.username
AND sent.campid = 'YA1LGfh9'
WHERE sent.username IS NULL
AND pool.gender = 'f'
问题是查询需要9分钟才能完成,池表包含超过1000万行,并且发送的表最终会比这更大。我为许多列创建了索引,包括用户名和性别。但是,MySQL拒绝使用我的任何索引来进行此查询。我甚至尝试过使用FORCE INDEX。以下是来自池的索引和EXPLAIN输出的查询:
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| pool | 0 | PRIMARY | 1 | id | A | 9326880 | NULL | NULL | | BTREE | |
| pool | 1 | username | 1 | username | A | 9326880 | NULL | NULL | | BTREE | |
| pool | 1 | source | 1 | source | A | 6 | NULL | NULL | | BTREE | |
| pool | 1 | gender | 1 | gender | A | 9 | NULL | NULL | | BTREE | |
| pool | 1 | location | 1 | location | A | 59030 | NULL | NULL | | BTREE | |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
6 rows in set (0.00 sec)
mysql> explain SELECT `id` FROM pool FORCE INDEX (username) LEFT JOIN sent ON pool.username = sent.username AND sent.campid = 'YA1LGfh9' WHERE sent.username IS NULL AND pool.gender = 'f';
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------------------+
| 1 | SIMPLE | pool | ALL | NULL | NULL | NULL | NULL | 9326881 | Using where |
| 1 | SIMPLE | sent | ALL | NULL | NULL | NULL | NULL | 351 | Using where; Not exists |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------------------+
2 rows in set (0.00 sec)
另外,这是我发送表的索引:
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| sent | 0 | PRIMARY | 1 | primary_key | A | 351 | NULL | NULL | | BTREE | |
| sent | 1 | username | 1 | username | A | 351 | NULL | NULL | | BTREE | |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
2 rows in set (0.00 sec)
您可以看到没有使用任何索引,因此我的查询花费的时间太长。如果有人有一个涉及重新处理查询的解决方案,请给我一个如何使用我的数据结构来做这个的例子,这样我就不会对如何实现和测试有任何困惑。谢谢。
答案 0 :(得分:4)
首先,您的原始查询在您的所有内容中都是正确的...包括阵营。通过使用从Pool到Sent的LEFT JOIN,然后将所需的相等性(如“CAMP”)拉入WHERE子句中,如前所述最终将其转换为INNER JOIN,因此需要双方进入。保持原样。
您已发送的表上已有用户名索引,但我会执行以下操作。
在(CampID,UserName)上的“已发送”表上构建索引作为复合(即:多键)索引。这样,左连接将针对BOTH条目进行优化。
在“池”表中,尝试3个字段(性别,用户名,ID)的综合索引。
通过这样做,您可以利用NOT不必浏览包含1000多万条记录的所有实际数据页面。由于索引是用于比较的列,因此它不必查找实际记录并查看列,它可以直接使用索引。
另外,对于笑话,我添加了关键字“STRAIGHT_JOIN”,它告诉MySQL完全按照我显示的方式进行查询,不要试图为我思考。很多次,我发现这可以显着提高查询性能...很少有人得到反馈,但它没有帮助。
SELECT STRAIGHT_JOIN
p.id
FROM
pool p
LEFT JOIN sent s
ON s.campid = 'YA1LGfh9'
AND p.username = s.username
WHERE
p.gender = 'f'
AND s.username IS NULL
所有这一切,你仍然要回报10万多人中的记录......如果游泳池有1000多万,而单一营地只有5,000。你几乎可以回到整个场景。