Как я могу оптимизировать запрос SQL? - PullRequest
0 голосов
/ 20 марта 2020
SELECT *
FROM account a1 
LEFT JOIN account a2 ON a1.group_id>0 AND a2.app_id=0 AND a1.group_id=a2.group_id AND a1.id!=a2.id 
JOIN user_account_ref uar ON uar.account_id=a1.id OR (uar.account_id=a2.id AND uar.role>0) 
WHERE uar.user_id IN ($some_user_ids);

Когда я заменяю предложение where на WHERE a1.id IN ($some_account_ids), запрос выполняется быстро, но он очень медленный для запроса идентификаторов пользователей. Я пытался использовать подзапросы, но не смог заставить его работать.

Схема таблицы, как показано ниже:

---account---
id
app_id
group_id

---user_account_ref---
id
user_id
account_id
role

Еще немного информации:

select * from user_account_ref where user_id IN (xxx)

быстрый и имеет 4 строки в результате.

SELECT * 
FROM account a1 
LEFT JOIN account a2 ON a1.group_id>0 AND a2.app_id=0 AND a1.group_id=a2.group_id AND a1.id!=a2.id

также быстрый и имеет более 2000 строк в результате.

Ответы [ 3 ]

1 голос
/ 20 марта 2020

Пожалуйста, попробуйте эти три индекса и посмотрите, решают ли они вашу проблему с производительностью. (Затем посмотрите план объяснения, чтобы увидеть, какие из этих индексов не используются, и отбросьте их.)

create index idx1 on account (group_id, id, app_id);
create index idx2 on user_account_ref (user_id, account_id);
create index idx3 on user_account_ref (account_id, user_id);

idx1 - для ускорения вашего account самостоятельного объединения. idx2 и idx3 - два предложения СУБД для быстрого присоединения к таблице user_account_ref. Надеемся, что СУБД выберет один из них и, таким образом, ускорит выполнение запроса.

Если эти индексы не помогают, вы можете сначала попытаться отбросить только idx3, чтобы вызвать принудительное действие idx2 так как кажется, что условие для пользователей сильно уменьшает количество строк, которые нужно просматривать, и СУБД может не знать об этом.

Наконец-то вы можете обновить статистику таблиц MySQLs, чтобы СУБД знала ваше таблицы лучше. Удачи.

0 голосов
/ 20 марта 2020

Если это быстро:

SELECT a1.*, a2.* 
FROM account a1 LEFT JOIN
     account a2
    ON a1.group_id > 0 AND 
       a2.app_id = 0 AND
       a1.group_id = a2.group_id AND
       a1.id <> a2.id;

Затем используйте два left join s:

    SELECT a1.*, a2.*, COALESCE(uar1.role, uar2.role) as role
    FROM account a1 LEFT JOIN
         account a2
         ON a1.group_id > 0 AND 
            a2.app_id = 0 AND
            a1.group_id = a2.group_id AND
            a1.id <> a2.id LEFT JOIN
         user_account_ref uar1 AND uar2.role > 0
         ON uar1.account_id = a1.id LEFT JOIN
         user_account_ref uar2
         ON uar2.account_id = a2.id AND uar2.role > 0
WHERE uar1.user_id IN ($some_user_ids) OR
      uar2.user_id IN ($some_user_ids) ;
0 голосов
/ 20 марта 2020

Я наконец-то использовал запрос UNION, предложенный @Akina. И я добавил индекс на group_id, который напоминает @Thorsten Kettner. Спасибо вам обоим.

Вот мой новый запрос:

SELECT a.id, uar.* 
FROM account a
JOIN user_account_ref uar ON uar.account_id=a.id AND uar.status=0
WHERE uar.user_id IN (xxx)

UNION

SELECT a.id, uar.* 
FROM account a,
(
    SELECT DISTINCT ra.id, ra.group_id
    FROM account ra
    JOIN user_account_ref uar ON ra.id=uar.account_id and ra.app_id=0
    WHERE uar.user_id in (xxx) AND ra.group_id>0
) ra
JOIN user_account_ref uar ON uar.role>0
WHERE a.group_id=ra.group_id AND uar.account_id=ra.id AND uar.user_id IN (xxx);
...