Description:
A query becomes much slower after adding a semantically redundant duplicate EXISTS predicate.
The original query contains a predicate of the following form:
P AND EXISTS (subquery)
The mutated query rewrites it into the equivalent form:
EXISTS (subquery) AND (P AND EXISTS (subquery))
This transformation is logically equivalent because the same EXISTS predicate is duplicated under AND. However, MySQL does not eliminate or reuse the duplicated EXISTS predicate. Instead, the optimizer expands the duplicated subquery into the execution plan again, producing a much larger plan and significantly slower execution.
In the observed case, the original query has median runtime around 50 ms, while the mutated equivalent query has median runtime around 3094 ms.
Observed timing:
original median: 50.658 ms
mutated median: 3094.383 ms
ratio original/mutated: 0.0164
The mutated query is about 61x slower.
Compared with #Bug120899,the redundant predicate is different.This is the exists subquery, additionally,#Bug120899' is in subquery
How to repeat:
The simplest SQL used to reproduced:
DROP DATABASE IF EXISTS rift_exists_min;
CREATE DATABASE rift_exists_min;
USE rift_exists_min;
CREATE TABLE outer_t (
id INT PRIMARY KEY,
v INT
);
CREATE TABLE a (
x INT
);
CREATE TABLE b (
y INT
);
INSERT INTO outer_t VALUES (1, 1);
INSERT INTO a
WITH RECURSIVE seq(n) AS (
SELECT 1
UNION ALL
SELECT n + 1 FROM seq WHERE n < 1000
)
SELECT n FROM seq;
INSERT INTO b
WITH RECURSIVE seq(n) AS (
SELECT 1
UNION ALL
SELECT n + 1 FROM seq WHERE n < 1000
)
SELECT n FROM seq;
-- Original query: one EXISTS predicate.
EXPLAIN FORMAT=TREE
SELECT COUNT(*)
FROM outer_t
WHERE v = 1
AND EXISTS (
SELECT 1
FROM a CROSS JOIN b
WHERE a.x + b.y = 2000
);
SELECT COUNT(*)
FROM outer_t
WHERE v = 1
AND EXISTS (
SELECT 1
FROM a CROSS JOIN b
WHERE a.x + b.y = 2000
);
-- Mutated query: the same EXISTS predicate is duplicated under AND.
-- This is semantically equivalent to the original query.
EXPLAIN FORMAT=TREE
SELECT COUNT(*)
FROM outer_t
WHERE EXISTS (
SELECT 1
FROM a CROSS JOIN b
WHERE a.x + b.y = 2000
)
AND (
v = 1
AND EXISTS (
SELECT 1
FROM a CROSS JOIN b
WHERE a.x + b.y = 2000
)
);
SELECT COUNT(*)
FROM outer_t
WHERE EXISTS (
SELECT 1
FROM a CROSS JOIN b
WHERE a.x + b.y = 2000
)
AND (
v = 1
AND EXISTS (
SELECT 1
FROM a CROSS JOIN b
WHERE a.x + b.y = 2000
)
);
Description: A query becomes much slower after adding a semantically redundant duplicate EXISTS predicate. The original query contains a predicate of the following form: P AND EXISTS (subquery) The mutated query rewrites it into the equivalent form: EXISTS (subquery) AND (P AND EXISTS (subquery)) This transformation is logically equivalent because the same EXISTS predicate is duplicated under AND. However, MySQL does not eliminate or reuse the duplicated EXISTS predicate. Instead, the optimizer expands the duplicated subquery into the execution plan again, producing a much larger plan and significantly slower execution. In the observed case, the original query has median runtime around 50 ms, while the mutated equivalent query has median runtime around 3094 ms. Observed timing: original median: 50.658 ms mutated median: 3094.383 ms ratio original/mutated: 0.0164 The mutated query is about 61x slower. Compared with #Bug120899,the redundant predicate is different.This is the exists subquery, additionally,#Bug120899' is in subquery How to repeat: The simplest SQL used to reproduced: DROP DATABASE IF EXISTS rift_exists_min; CREATE DATABASE rift_exists_min; USE rift_exists_min; CREATE TABLE outer_t ( id INT PRIMARY KEY, v INT ); CREATE TABLE a ( x INT ); CREATE TABLE b ( y INT ); INSERT INTO outer_t VALUES (1, 1); INSERT INTO a WITH RECURSIVE seq(n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM seq WHERE n < 1000 ) SELECT n FROM seq; INSERT INTO b WITH RECURSIVE seq(n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM seq WHERE n < 1000 ) SELECT n FROM seq; -- Original query: one EXISTS predicate. EXPLAIN FORMAT=TREE SELECT COUNT(*) FROM outer_t WHERE v = 1 AND EXISTS ( SELECT 1 FROM a CROSS JOIN b WHERE a.x + b.y = 2000 ); SELECT COUNT(*) FROM outer_t WHERE v = 1 AND EXISTS ( SELECT 1 FROM a CROSS JOIN b WHERE a.x + b.y = 2000 ); -- Mutated query: the same EXISTS predicate is duplicated under AND. -- This is semantically equivalent to the original query. EXPLAIN FORMAT=TREE SELECT COUNT(*) FROM outer_t WHERE EXISTS ( SELECT 1 FROM a CROSS JOIN b WHERE a.x + b.y = 2000 ) AND ( v = 1 AND EXISTS ( SELECT 1 FROM a CROSS JOIN b WHERE a.x + b.y = 2000 ) ); SELECT COUNT(*) FROM outer_t WHERE EXISTS ( SELECT 1 FROM a CROSS JOIN b WHERE a.x + b.y = 2000 ) AND ( v = 1 AND EXISTS ( SELECT 1 FROM a CROSS JOIN b WHERE a.x + b.y = 2000 ) );