为什么这个 mysql 查询(带有 is null 检查)比另一个查询慢那么多?
Why this mysql query (with is null check) is so slower than this other one?
I getting in doubt with "IS NULL" MySQL check. I have this 2 queries. The first one 运行s in about 300 seconds. The second one 运行 less then 1 second!
Slow query:
SELECT count(distinct(u.id))
FROM ips_usuario AS u
JOIN ips_fatura AS f
ON ((u.id = f.ips_usuario_id) OR
(u.ips_usuario_id_titular IS NOT NULL AND
u.ips_usuario_id_titular = f.ips_usuario_id));
Fast query:
SELECT count(distinct(u.id))
FROM ips_usuario AS u
JOIN ips_fatura AS f
ON ((u.id = f.ips_usuario_id) OR
(u.ips_usuario_id_titular = f.ips_usuario_id));
All join conditions use foreign keys indexed columns. The table ips_usuario have about 20.000 records and the table ips_fatura have about 500.000 records.
令我惊讶的是两者都很快。我建议将它们替换为 exists
:
SELECT COUNT(*)
FROM ips_usuario u
WHERE EXISTS (SELECT 1 FROM ips_fatura f WHERE u.id = f.ips_usuario_id) OR
EXISTS (SELECT 1 FROM ips_fatura f WHERE u.ips_usuario_id_titular = f.ips_usuario_id);
第二个:
SELECT COUNT(*)
FROM ips_usuario u
WHERE EXISTS (SELECT 1 FROM ips_fatura f WHERE u.id = f.ips_usuario_id) OR
(u.ips_usuario_id_titular IS NOT NULL AND
EXISTS (SELECT 1 FROM ips_fatura f WHERE u.ips_usuario_id_titular = f.ips_usuario_id)
)
对于这两个,您需要两个索引:ips_fatura(ips_usuario_id)
和 ips_fatura(ips_usuario_id_titular)
。您可以检查说明以确保 EXISTS
正在使用索引。如果不是,较新版本的 MySQL 使用 IN
:
的索引
SELECT COUNT(*)
FROM ips_usuario u
WHERE u.id IN (SELECT f.ips_usuario_id FROM ips_fatura f) OR
u.ips_usuario_id_titular IN (SELECT f.ips_usuario_id FROM ips_fatura f);
无论哪种情况(EXISTS
或 IN
),目标都是 "semi-join"。也就是说,只对第一行进行匹配而不是所有匹配。这是一个重要的效率,因为它允许查询避免重复删除。
我推测问题出在 or
的优化上——通常这会导致 JOIN
算法效率低下。但是,也许 MySQL 在您的第一种情况下很聪明。但是将 IS NULL
添加到外部 table 会将其关闭。
I getting in doubt with "IS NULL" MySQL check. I have this 2 queries. The first one 运行s in about 300 seconds. The second one 运行 less then 1 second!
Slow query:
SELECT count(distinct(u.id))
FROM ips_usuario AS u
JOIN ips_fatura AS f
ON ((u.id = f.ips_usuario_id) OR
(u.ips_usuario_id_titular IS NOT NULL AND
u.ips_usuario_id_titular = f.ips_usuario_id));
Fast query:
SELECT count(distinct(u.id))
FROM ips_usuario AS u
JOIN ips_fatura AS f
ON ((u.id = f.ips_usuario_id) OR
(u.ips_usuario_id_titular = f.ips_usuario_id));
All join conditions use foreign keys indexed columns. The table ips_usuario have about 20.000 records and the table ips_fatura have about 500.000 records.
令我惊讶的是两者都很快。我建议将它们替换为 exists
:
SELECT COUNT(*)
FROM ips_usuario u
WHERE EXISTS (SELECT 1 FROM ips_fatura f WHERE u.id = f.ips_usuario_id) OR
EXISTS (SELECT 1 FROM ips_fatura f WHERE u.ips_usuario_id_titular = f.ips_usuario_id);
第二个:
SELECT COUNT(*)
FROM ips_usuario u
WHERE EXISTS (SELECT 1 FROM ips_fatura f WHERE u.id = f.ips_usuario_id) OR
(u.ips_usuario_id_titular IS NOT NULL AND
EXISTS (SELECT 1 FROM ips_fatura f WHERE u.ips_usuario_id_titular = f.ips_usuario_id)
)
对于这两个,您需要两个索引:ips_fatura(ips_usuario_id)
和 ips_fatura(ips_usuario_id_titular)
。您可以检查说明以确保 EXISTS
正在使用索引。如果不是,较新版本的 MySQL 使用 IN
:
SELECT COUNT(*)
FROM ips_usuario u
WHERE u.id IN (SELECT f.ips_usuario_id FROM ips_fatura f) OR
u.ips_usuario_id_titular IN (SELECT f.ips_usuario_id FROM ips_fatura f);
无论哪种情况(EXISTS
或 IN
),目标都是 "semi-join"。也就是说,只对第一行进行匹配而不是所有匹配。这是一个重要的效率,因为它允许查询避免重复删除。
我推测问题出在 or
的优化上——通常这会导致 JOIN
算法效率低下。但是,也许 MySQL 在您的第一种情况下很聪明。但是将 IS NULL
添加到外部 table 会将其关闭。