Оптимизация левого запроса соединения, который использует "не нуль" - PullRequest
2 голосов
/ 23 декабря 2011

Я долго боролся с этим. Этот запрос выполняется довольно быстро для меньшего набора данных, но когда таблицы увеличиваются до 100 000 строк +, для их выполнения требуется от 30 с до нескольких минут:

SELECT  accounts.id 
        , accounts.name 
        , ..etc..
FROM    accounts   
        LEFT JOIN (
          SELECT  distinct secr.record_id as id 
          FROM    securitygroups secg
                  INNER JOIN securitygroups_users secu 
                    ON secg.id = secu.securitygroup_id 
                       AND secu.deleted = 0 
                       AND secu.user_id = 'seed_chris_id'
                  INNER JOIN securitygroups_records secr 
                    ON secg.id = secr.securitygroup_id 
                       AND secr.deleted = 0 
                       AND secr.module = 'Accounts'
          WHERE   secg.deleted = 0
        ) securitygroup_join ON securitygroup_join.id = accounts.id  
WHERE   (( accounts.assigned_user_id ='seed_chris_id' 
           OR securitygroup_join.id is not null)) 
        AND accounts.deleted=0 
ORDER BY 
        accounts.date_entered 
DESC    LIMIT 0,21

По сути, он должен возвращать все строки, в которых пользователь владеет записью (accounts.assigned_user_id) или является членом группы, связанной с записью (securitygroup_join.id не имеет значение null). Этот запрос определенным образом создается с помощью фреймворка, сталкивающегося с некоторыми ограничениями. Возможное решение, которое не может быть легко реализовано, состояло бы в том, чтобы изменить это на СОЮЗ. Хотел бы избежать этого маршрута. В прошлом делали предложение "где ... в", но это работало еще хуже. Я могу добавить в оператор join, where или манипулировать индексами по мере необходимости, но любые другие радикальные изменения в структуре запроса не могут быть легко выполнены.

1 Ответ

3 голосов
/ 23 декабря 2011

Вы можете попробовать WHERE EXISTS вместо LEFT JOIN. Например:

SELECT  accounts.id 
        , accounts.name 
        , ..etc..
FROM    accounts   
WHERE   (( accounts.assigned_user_id ='seed_chris_id' 
       OR EXISTS (SELECT  1
                  FROM    securitygroups secg
                          INNER JOIN securitygroups_users secu 
                            ON secg.id = secu.securitygroup_id 
                               AND secu.deleted = 0 
                               AND secu.user_id = 'seed_chris_id'
                          INNER JOIN securitygroups_records secr 
                            ON secg.id = secr.securitygroup_id 
                               AND secr.deleted = 0 
                               AND secr.module = 'Accounts'
                       WHERE   secr.record_id = accounts.id
                               AND secg.deleted = 0)
       )) 
    AND accounts.deleted=0 
ORDER BY 
    accounts.date_entered 
DESC    LIMIT 0,21

Я не проверял это, поэтому он может не работать лучше, но стоит попробовать.

...