Performance at finding all foreigns with 3 one-to-many relation
-
04-03-2021 - |
سؤال
Basically, I have 2 tables : user and object. One user can be associated to one object by three ways (roles). I want to list all users associated at least to one business object by any role.
This query works, but it takes 5 seconds !
SELECT u.id
FROM user u
LEFT JOIN translation_revision t1
ON u.id = t1.translator_id
LEFT JOIN translation_revision t2
ON u.id = t2.proofreader_id
LEFT JOIN translation_revision t3
ON u.id = t3.reviewer_id
wHERE t1.id IS NOT NULL
OR t2.id IS NOT NULL
OR t3.id IS NOT NULL
GROUP BY u.id
Complete fiddle with creation and data here : https://www.db-fiddle.com/f/nojYaUsWicKJDuRQk1Nqd2/7
Any idea on how to improve perf here ?
المحلول
Mine is fast, at 1-2 milliseconds (tested across various versions between 5.5 and 8.0, plus MariaDB):
SELECT u.id
FROM user u
WHERE EXISTS ( SELECT 1 FROM translation_revision t1
WHERE u.id = t1.translator_id )
OR EXISTS ( SELECT 1 FROM translation_revision t2
WHERE u.id = t2.proofreader_id )
OR EXISTS ( SELECT 1 FROM translation_revision t3
WHERE u.id = t3.reviewer_id )
GROUP BY u.id
LIMIT 30;
(I would prefer to use DISTINCT
instead of GROUP BY
, but probably no performance diff.)
نصائح أخرى
Instead of doing 3 separate joins, you could accomplish the same thing by simply using an OR clause (the execution time seems to be ~1-2ms):
SELECT DISTINCT u.id
FROM user u
JOIN translation_revision t1
ON u.id = t1.translator_id OR u.id = t1.proofreader_id OR u.id = t1.reviewer_id
GROUP BY u.id
LIMIT 30
Other ideas would be to denormalize your data and keep some additional info about your users roles.
Maybe simply
SELECT DISTINCT u.id
FROM user u
JOIN translation_revision t
ON u.id IN (t.translator_id, t.proofreader_id, t.reviewer_id)
?
You may use union all to avoid sorting
mysql> select id from (
-> SELECT u.id
-> FROM user u
-> LEFT JOIN translation_revision t1
-> ON u.id = t1.translator_id
->union all
-> SELECT u.id
-> FROM user u
-> LEFT JOIN translation_revision t2
-> ON u.id = t2.proofreader_id
->union all
-> SELECT u.id
-> FROM user u
-> LEFT JOIN translation_revision t3
-> ON u.id = t3.reviewer_id
-> ) t
-> group by id
-> ;
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
+----+
3 rows in set (0.01 sec)