У меня есть сложный вопрос для экспертов MySQL.Как оптимизировать этот запрос MySQL? (CROSS JOIN, subquery)
У меня есть система прав пользователей с 4-мя столами:
users (id | email | created_at)
permissions (id | responsibility_id | key | weight)
permission_user (id | permission_id | user_id)
responsibilities (id | key | weight)
Пользователи могут иметь любое количество разрешений, назначенных и любого разрешения может быть предоставлено любому количеству использования rs (многие ко многим). Ответственность подобна группам для разрешений, каждое разрешение принадлежит только одной ответственности. Например, одно разрешение называется update
с ответственностью customers
. Еще один будет delete
с ответственностью orders
.
Мне нужно получить полную карту разрешений для каждого пользователя, но только для тех, у кого есть хотя бы одно разрешение. Результаты должны быть отсортированы по: номеру
- пользователя разрешений от большего к меньшей
created_at
колонка пользователя, сначала старый- ответственности в
weight
- разрешение в
weight
Примера результирующий набор:
user_id | responsibility | permission | granted
-----------------------------------------------
5 | customers | create | 1
5 | customers | update | 1
5 | orders | create | 1
5 | orders | update | 1
2 | customers | create | 0
2 | customers | delete | 0
2 | orders | create | 1
2 | orders | update | 0
Предположим, что у меня 10 пользователей в базе данных, но только у двух из них есть разрешения. Есть 4 разрешения в общей сложности:
create
изcustomers
ответственностиupdate
изcustomers
ответственностиcreate
изorders
ответственностиupdate
изorders
ответственности.
Вот почему у нас есть 8 записей в результатах (2 пользователя с разрешением × 4 разрешений). Сначала отображается пользователь с id = 5, потому что у него больше прав. Если бы были какие-то ничьи, то первые со старыми created_at
будут идти первым. Разрешения всегда сортируются по весу их ответственности, а затем по собственному весу.
Мой вопрос в том, как написать оптимальный запрос для этого случая? Я уже сделал один я, и он работает хорошо:
SELECT `users`.`id` AS `user_id`,
`responsibilities`.`key` AS `responsibility`,
`permissions`.`key` AS `permission`,
!ISNULL(`permission_user`.`id`) AS `granted`
FROM `users`
CROSS JOIN `permissions`
JOIN `responsibilities`
ON `responsibilities`.`id` = `permissions`.`responsibility_id`
LEFT JOIN `permission_user`
ON `permission_user`.`user_id` = `users`.`id`
AND `permission_user`.`permission_id` = `permissions`.`id`
WHERE (
SELECT COUNT(*)
FROM `permission_user`
WHERE `user_id` = `users`.`id`
) > 0
ORDER BY (
SELECT COUNT(*)
FROM `permission_user`
WHERE `user_id` = `users`.`id`
) DESC,
`users`.`created_at` ASC,
`responsibilities`.`weight` ASC,
`permissions`.`weight` ASC
Проблема заключается в том, что я использую тот же подзапрос дважды.
Могу ли я сделать лучше? Я рассчитываю на вас, эксперты MySQL!
--- EDIT ---
Благодаря комментарий Гордон Линофф, я сделал это использовать HAVING
пункт:
SELECT `users`.`email`,
`responsibilities`.`key`,
`permissions`.`key`,
!ISNULL(`permission_user`.`id`) as `granted`,
(
SELECT COUNT(*)
FROM `permission_user`
WHERE `user_id` = `users`.`id`
) AS `total_permissions`
FROM `users`
CROSS JOIN `permissions`
JOIN `responsibilities`
ON `responsibilities`.`id` = `permissions`.`responsibility_id`
LEFT JOIN `permission_user`
ON `permission_user`.`user_id` = `users`.`id`
AND `permission_user`.`permission_id` = `permissions`.`id`
HAVING `total_permissions` > 0
ORDER BY `total_permissions` DESC,
`users`.`created_at` ASC,
`responsibilities`.`weight` ASC,
`permissions`.`weight` ASC
Я был удивлен, обнаружив, что HAVING
может идти в одиночку без GROUP BY
.
Можно ли улучшить его улучшение для лучшей производительности?
Добавить подзапрос в предложение 'select'. Затем вы можете фильтровать, используя псевдоним в предложении 'having', а также использовать псевдоним в' order by'. –
Параметр where-where гарантирует, что есть строки разрешающего_ищика, т. Е. LEFT JOIN может быть регулярным JOIN. – jarlh
@jarlh Пожалуйста, внимательно прочитайте мой вопрос. Я сказал, что мне нужно отображать ВСЕ разрешения, а не только те, которые предоставляются. Нужно много нулей. –