Пользователь
0,0
рейтинг
1 февраля 2014 в 02:45

Разработка → Использование EXPLAIN. Улучшение запросов перевод

Когда вы выполняете какой-нибудь запрос, оптимизатор запросов MySQL пытается придумать оптимальный план выполнения этого запроса. Вы можете посмотреть этот самый план используя запрос с ключевым словом EXPLAIN. EXPLAIN – это один из самых мощных инструментов, предоставленных в ваше распоряжение для понимания MySQL-запросов и их оптимизации, но печальным фактом является то, что многие разработчики редко его используют. В данной статье вы узнаете о том, какие данные предлагает EXPLAIN на выходе и ознакомитесь с примером того, как использовать его для оптимизации запросов.


Что предлагает EXPLAIN?


Использовать оператор EXPLAIN просто. Его необходимо добавлять в запросы перед оператором SELECT. Давайте проанализируем вывод, чтобы познакомиться с информацией, возвращаемой командой.

EXPLAIN SELECT * FROM categories


********************** 1. row **********************
id: 1
select_type: SIMPLE
table: categories
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 4
Extra: 
1 row in set (0.00 sec)


Вывод может не выглядеть точь-в-точь так, тем не менее, в нем будут содержаться те же 10 столбцов. Что же это за возвращаемые столбцы?

  • id – порядковый номер для каждого SELECT’а внутри запроса (когда имеется несколько подзапросов)
  • select_type – тип запроса SELECT.

    • SIMPLE — Простой запрос SELECT без подзапросов или UNION’ов
    • PRIMARY – данный SELECT – самый внешний запрос в JOIN’е
    • DERIVED – данный SELECT является частью подзапроса внутри FROM
    • SUBQUERY – первый SELECT в подзапросе
    • DEPENDENT SUBQUERY – подзапрос, который зависит от внешнего запроса
    • UNCACHABLE SUBQUERY – не кешируемый подзапрос (существуют определенные условия для того, чтобы запрос кешировался)
    • UNION – второй или последующий SELECT в UNION’е
    • DEPENDENT UNION – второй или последующий SELECT в UNION’е, зависимый от внешнего запроса
    • UNION RESULT – результат UNION’а

  • Table – таблица, к которой относится выводимая строка
  • Type — указывает на то, как MySQL связывает используемые таблицы. Это одно из наиболее полезных полей в выводе потому, что может сообщать об отсутствующих индексах или почему написанный запрос должен быть пересмотрен и переписан.
    Возможные значения:

    • System – таблица имеет только одну строку
    • Const – таблица имеет только одну соответствующую строку, которая проиндексирована. Это наиболее быстрый тип соединения потому, что таблица читается только один раз и значение строки может восприниматься при дальнейших соединениях как константа.
    • Eq_ref – все части индекса используются для связывания. Используемые индексы: PRIMARY KEY или UNIQUE NOT NULL. Это еще один наилучший возможный тип связывания.
    • Ref – все соответствующие строки индексного столбца считываются для каждой комбинации строк из предыдущей таблицы. Этот тип соединения для индексированных столбцов выглядит как использование операторов = или < = >
    • Fulltext – соединение использует полнотекстовый индекс таблицы
    • Ref_or_null – то же самое, что и ref, но также содержит строки со значением null для столбца
    • Index_merge – соединение использует список индексов для получения результирующего набора. Столбец key вывода команды EXPLAIN будет содержать список использованных индексов.
    • Unique_subquery – подзапрос IN возвращает только один результат из таблицы и использует первичный ключ.
    • Index_subquery – тоже, что и предыдущий, но возвращает более одного результата.
    • Range – индекс, использованный для нахождения соответствующей строки в определенном диапазоне, обычно, когда ключевой столбец сравнивается с константой, используя операторы вроде: BETWEEN, IN, >, >=, etc.
    • Index – сканируется все дерево индексов для нахождения соответствующих строк.
    • All – Для нахождения соответствующих строк используются сканирование всей таблицы. Это наихудший тип соединения и обычно указывает на отсутствие подходящих индексов в таблице.

  • Possible_keys – показывает индексы, которые могут быть использованы для нахождения строк в таблице. На практике они могут использоваться, а могут и не использоваться. Фактически, этот столбец может сослужить добрую службу в деле оптимизации запросов, т.к значение NULL указывает на то, что не найдено ни одного подходящего индекса .
  • Key– указывает на использованный индекс. Этот столбец может содержать индекс, не указанный в столбце possible_keys. В процессе соединения таблиц оптимизатор ищет наилучшие варианты и может найти ключи, которые не отображены в possible_keys, но являются более оптимальными для использования.
  • Key_len – длина индекса, которую оптимизатор MySQL выбрал для использования. Например, значение key_len, равное 4, означает, что памяти требуется для хранения 4 знаков. На эту тему вот cсылка
  • Ref – указываются столбцы или константы, которые сравниваются с индексом, указанным в поле key. MySQL выберет либо значение константы для сравнения, либо само поле, основываясь на плане выполнения запроса.
  • Rows – отображает число записей, обработанных для получения выходных данных. Это еще одно очень важное поле, которое дает повод оптимизировать запросы, особенно те, которые используют JOIN’ы и подзапросы.
  • Extra – содержит дополнительную информацию, относящуюся к плану выполнения запроса. Такие значения как “Using temporary”, “Using filesort” и т.д могут быть индикатором проблемного запроса. С полным списком возможных значений вы можете ознакомиться здесь


После EXPLAIN в запросе вы можете использовать ключевое слово EXTENDED и MySQL покажет вам дополнительную информацию о том, как выполняется запрос. Чтобы увидеть эту информацию, вам нужно сразу после запроса с EXTENDED выполнить запрос SHOW WARNINGS. Наиболее полезно смотреть эту информацию о запросе, который выполнялся после каких-либо изменений сделанных оптимизатором запросов.

EXPLAIN EXTENDED SELECT City.Name FROM City
JOIN Country ON (City.CountryCode = Country.Code)
WHERE City.CountryCode = 'IND' AND Country.Continent = 'Asia'


********************** 1. row **********************
id: 1
select_type: SIMPLE
table: Country
type: const
possible_keys: PRIMARY
key: PRIMARY
key_len: 3
ref: const
rows: 1
filtered: 100.00
Extra: 
********************** 2. row **********************
           id: 1
  select_type: SIMPLE
        table: City
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 4079
     filtered: 100.00
        Extra: Using where
2 rows in set, 1 warning (0.00 sec)


SHOW WARNINGS


********************** 1. row **********************
  Level: Note
   Code: 1003
Message: select `World`.`City`.`Name` AS `Name` from `World`.`City` join `World`.`Country` where ((`World`.`City`.`CountryCode` = 'IND'))
1 row in set (0.00 sec)


Поиск и устранение проблем с производительностью с помощью EXPLAIN.


Теперь давайте посмотрим на то, как мы может оптимизировать не очень шустрый запрос, анализируя вывод команды EXPLAIN. Несомненно, что в действующих рабочих приложениях существует ряд таблиц со многими связями между ними, но иногда сложно предвидеть наиболее оптимальный способ написания запроса.

Я создал тестовую базу данных для приложения электронной торговли, которая не имеет никаких индексов или первичных ключей, и продемонстрирую влияние такого не очень хорошего способа создания таблиц при помощи “страшных” запросов. Дамп это таблицы вы можете скачать здесь — github.com/phpmasterdotcom/UsingExplainToWriteBetterMySQLQueries

EXPLAIN SELECT * FROM
orderdetails d
INNER JOIN orders o ON d.orderNumber = o.orderNumber
INNER JOIN products p ON p.productCode = d.productCode
INNER JOIN productlines l ON p.productLine = l.productLine
INNER JOIN customers c on c.customerNumber = o.customerNumber
WHERE o.orderNumber = 10101


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

UPDATE. Здесь лежит исправленный дамп без индексов. В оригинальном авторском дампе индексы почему-то изначально добавлены.

Но даже если я напишу запрос получше, результат будет тем же самым, пока я не добавлю индексов. Указанный тип соединения ALL (худший), что означает, что MySQL не смог определить ни одного ключа, который бы мог использоваться при соединении. Отсюда следует и то, что possible_keys и key имеют значение NULL. Самым важным является то, что поле rows показывает, что MySQL сканирует все записи каждой таблицы для запроса. Это означает, что она просканирует 7 × 110 × 122 × 326 × 2996 = 91,750,822,240 записей, чтобы найти подходящие четыре (уберите из запроса EXPLAIN, проверьте сами). Это очень нехорошо и количество этих записей будет экспоненциально увеличиваться по мере роста базы данных.

Теперь давайте добавим очевидные индексы, такие, как первичный ключ для каждой таблицы, и выполним запрос еще раз. Взяв это за основное правило, в качестве кандидатов для добавления ключей вы можете использовать те столбцы которые используются в JOIN’ах, т.к. MySQL всегда сканирует их для нахождения соответствующих записей.

ALTER TABLE customers
    ADD PRIMARY KEY (customerNumber);
ALTER TABLE employees
    ADD PRIMARY KEY (employeeNumber);
ALTER TABLE offices
    ADD PRIMARY KEY (officeCode);
ALTER TABLE orderdetails
    ADD PRIMARY KEY (orderNumber, productCode);
ALTER TABLE orders
    ADD PRIMARY KEY (orderNumber),
    ADD KEY (customerNumber);
ALTER TABLE payments
    ADD PRIMARY KEY (customerNumber, checkNumber);
ALTER TABLE productlines
    ADD PRIMARY KEY (productLine);
ALTER TABLE products 
    ADD PRIMARY KEY (productCode),
    ADD KEY (buyPrice),
    ADD KEY (productLine);
ALTER TABLE productvariants 
    ADD PRIMARY KEY (variantId),
    ADD KEY (buyPrice),
    ADD KEY (productCode);


Давайте выполним наш прежний запрос после добавления индексов. Вы увидите это:

********************** 1. row **********************
           id: 1
  select_type: SIMPLE
        table: o
         type: const
possible_keys: PRIMARY,customerNumber
          key: PRIMARY
      key_len: 4
          ref: const
         rows: 1
        Extra: 
********************** 2. row **********************
           id: 1
  select_type: SIMPLE
        table: c
         type: const
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: const
         rows: 1
        Extra: 
********************** 3. row **********************
           id: 1
  select_type: SIMPLE
        table: d
         type: ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: const
         rows: 4
        Extra: 
********************** 4. row **********************
           id: 1
  select_type: SIMPLE
        table: p
         type: eq_ref
possible_keys: PRIMARY,productLine
          key: PRIMARY
      key_len: 17
          ref: classicmodels.d.productCode
         rows: 1
        Extra: 
********************** 5. row **********************
           id: 1
  select_type: SIMPLE
        table: l
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 52
          ref: classicmodels.p.productLine
         rows: 1
        Extra: 
5 rows in set (0.00 sec)


После добавления индексов, число считанных записей упало до 1 × 1 × 4 × 1 × 1 = 4 Для каждой записи order_number = 10101 в таблице orderdetails – это значит, что MySQL смогла найти соответствующие записи во всех других таблицах с использованием индексов и не стала прибегать к полному сканированию таблицы.

В первом выводе вы можете что использован тип соединения – “const”, который является самым быстрым типом соединения для таблиц с более, чем одной записью. MySQL смогла использовать PRIMARY KEY как индекс. В поле “ref” отображается “const”, что есть ни что иное, как значение 10101, указанное в запросе после ключевого слова WHERE.

Смотрим на еще один запрос. В нем мы выбираем объединение двух таблиц, products и productvariants, каждая объединена с productline. productvariants, которая состоит из разных вариантов продуктов с полем productCode – ссылкой на их цены.

EXPLAIN SELECT * FROM (
SELECT p.productName, p.productCode, p.buyPrice, l.productLine, p.status, l.status AS lineStatus FROM
products p
INNER JOIN productlines l ON p.productLine = l.productLine
UNION
SELECT v.variantName AS productName, v.productCode, p.buyPrice, l.productLine, p.status, l.status AS lineStatus FROM productvariants v
INNER JOIN products p ON p.productCode = v.productCode
INNER JOIN productlines l ON p.productLine = l.productLine
) products
WHERE status = 'Active' AND lineStatus = 'Active' AND buyPrice BETWEEN 30 AND 50


********************** 1. row **********************
           id: 1
  select_type: PRIMARY
        table: <derived2>
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 219
        Extra: Using where
********************** 2. row **********************
           id: 2
  select_type: DERIVED
        table: p
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 110
        Extra: 
********************** 3. row **********************
           id: 2
  select_type: DERIVED
        table: l
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 52
          ref: classicmodels.p.productLine
         rows: 1
        Extra: 
********************** 4. row **********************
           id: 3
  select_type: UNION
        table: v
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 109
        Extra: 
********************** 5. row **********************
           id: 3
  select_type: UNION
        table: p
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 17
          ref: classicmodels.v.productCode
         rows: 1
        Extra: 
********************** 6. row **********************
           id: 3
  select_type: UNION
        table: l
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 52
          ref: classicmodels.p.productLine
         rows: 1
        Extra: 
********************** 7. row **********************
           id: NULL
  select_type: UNION RESULT
        table: <union2,3>
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
        Extra: 
7 rows in set (0.01 sec)


Вы можете заметить ряд проблем в этом запросе. Он сканирует все записи в products и productvarians. Т.к. в этих таблицах нет индексов для столбцов productLine и buyPrice, в полях possible_keys и key отображаются значения NULL. Статус таблиц products и productlines проверяется после UNION’а, поэтому перемещение их внутри UNION’а уменьшит число записей. Добавим индексы.

CREATE INDEX idx_buyPrice ON products(buyPrice);
CREATE INDEX idx_buyPrice ON productvariants(buyPrice);
CREATE INDEX idx_productCode ON productvariants(productCode);
CREATE INDEX idx_productLine ON products(productLine);


EXPLAIN SELECT * FROM (
SELECT p.productName, p.productCode, p.buyPrice, l.productLine, p.status, l.status as lineStatus FROM products p
INNER JOIN productlines AS l ON (p.productLine = l.productLine AND p.status = 'Active' AND l.status = 'Active') 
WHERE buyPrice BETWEEN 30 AND 50
UNION
SELECT v.variantName AS productName, v.productCode, p.buyPrice, l.productLine, p.status, l.status FROM productvariants v
INNER JOIN products p ON (p.productCode = v.productCode AND p.status = 'Active') 
INNER JOIN productlines l ON (p.productLine = l.productLine AND l.status = 'Active')
WHERE
v.buyPrice BETWEEN 30 AND 50
) product


********************** 1. row **********************
          id: 1
  select_type: PRIMARY
        table: <derived2>
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 12
        Extra: 
********************** 2. row **********************
           id: 2
  select_type: DERIVED
        table: p
         type: range
possible_keys: idx_buyPrice,idx_productLine
          key: idx_buyPrice
      key_len: 8
          ref: NULL
         rows: 23
        Extra: Using where

********************** 3. row **********************
           id: 2
  select_type: DERIVED
        table: l
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 52
          ref: classicmodels.p.productLine
         rows: 1
        Extra: Using where

********************** 4. row **********************
           id: 3
  select_type: UNION
        table: v
         type: range
possible_keys: idx_buyPrice,idx_productCode
          key: idx_buyPrice
      key_len: 9
          ref: NULL
         rows: 1
        Extra: Using where

********************** 5. row **********************
           id: 3
  select_type: UNION
        table: p
         type: eq_ref
possible_keys: PRIMARY,idx_productLine
          key: PRIMARY
      key_len: 17
          ref: classicmodels.v.productCode
         rows: 1
        Extra: Using where

********************** 6. row **********************
           id: 3
  select_type: UNION
        table: l
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 52
          ref: classicmodels.p.productLine
         rows: 1
        Extra: Using where

********************** 7. row **********************
           id: NULL
  select_type: UNION RESULT
        table: <union2,3>
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
        Extra: 
7 rows in set (0.01 sec)


Как вы видите, в результате количество сканированных строк уменьшилось с 2,625,810 (219 × 110 × 109) до 276 (12 × 23), что является отличным приобретением в производительности. Если вы выполните этот же запрос без предыдущих перестановок в запросе сразу после добавления индексов, вы не увидите такого уменьшения просканированных строк. MySQL не способна использовать индексы, когда в производном результате используется WHERE. После помещения этих условий внутри UNION становится возможных использование индексов. Это значит, что добавления индексов не всегда достаточно. MySQL не сможет их использовать до тех пор, пока вы не будете писать подходящие запросы. (http://www.php.su/mysql/manual/?page=MySQL_indexes – доп. информация).

Итог


В статье рассмотрено ключевое слово EXPLAIN, информация на выводе и примеры того, как вы можете использовать вывод команды для улучшения запросов. В реальном мире данная команда может быть более полезна, чем в рассмотренных сценариях. Почти всегда вы будете соединять ряд таблиц вместе, используя сложные конструкции с WHERE. При этом, просто добавленные индексы к таблицам не всегда приведут к нужному результату. В таком случае нужно пересмотреть ваши запросы.
Перевод: Shameer C
@hbuser
карма
16,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (12)

  • 0
    Интересно, что в оригинале потерялся слеш перед G (похоже, на сайтпонте не умеют форматировать текст для SQL или HTML), а в переводе пропала и сама буква. А вместе с ней и самый первый запрос —
    EXPLAIN SELECT * FROM categories\G
    

    В итоге для многих может быть непонятно, откуда взялся вертикальный формат вывода результатов запроса.
    И код в статье, вообще-то, желательно форматировать.

    А сама статья неудачная. Объяснений очень мало, примеры я попробовал — не исполняются.
    Лучше читать книгу или презентации svetasmirnova
  • 0
    Да, строчку забыл, поправил. По поводу G. Там почему-то почти в каждом примере кода в конце непонятная G. Из-за нее код не исполнялся. А примеры исполняются, сам все проверял. Возможно что-то пропущено, но тогда это мое упущение, надо исправить.
    • 0
      Еще раз.
      Перед G должен стоять слеш, как в моем примере. Тогда вывод будет такой же, как в статье.
      Примеры не исполняются. Таблицы city нет вообще. Можно догадаться что она просто в качестве иллюстрации, но все равно неаккуратно. Для приведенного же дампа запросы исполняются, но заявленный вывод не выдают — все останавливается на Impossible WHERE noticed after reading const tables.
      Код в статье надо форматировать
      • 0
        Про форматировать — дельное замечание.
        Если вы про помещение его в блок code, то в этом случае получается месиво букв, переносы строк не смог поставить, привести к пристойному виду — тоже. Пришлось в голом виде в тело статьи добавлять. Так хоть переносы строк можно контролировать.

        Теперь про это:

        EXPLAIN SELECT * FROM categories\G

        Такой таблицы в дампе нет. Это, судя по всему, просто пример произвольного запроса с EXPLAIN и вывод, показанный ниже — это примерный вывод (примерный потому, что нельзя сказать, он точный, т.к. не известна структура исходной таблицы). Там, вроде, так и сказано, что вывод не обязательно может быть таким. Это демонстрация использования ключевого слова EXPLAIN. И второе. Может уровень моих знаний не достаточен. Расскажите, буду знать. Что это за «магическая» в моем понимании G со слешем в конце и каким образом вы получили такой же как в статье вывод на несуществующей таблице? И отдельно по поводу G. Я вот добавляю /G к любому запросу и у меня появляется ошибка.

        Таблицы city нет потому, что это тоже просто пример, оторванный от реального мира, чтобы продемонстрировать как использовать ключевое слово EXTENDED и то, как выглядит информация в результате.

        Тем более эти запросы приведены до непосредственной ссылки на дамп. А значит логично предположить, что к дампу и другому разделу статьи они никакого отношения не имеют.
        • +1
          Надо добавлять \G, а не /G
          Запрос надо помещать не в блок code, а в блок source. С соответствующим языком:
          EXPLAIN SELECT * FROM
          orderdetails d
          INNER JOIN orders o ON d.orderNumber = o.orderNumber
          INNER JOIN products p ON p.productCode = d.productCode
          INNER JOIN productlines l ON p.productLine = l.productLine
          INNER JOIN customers c on c.customerNumber = o.customerNumber
          WHERE o.orderNumber = 10101
          

          Код, языка для которого не находится — помещать в любой подходящий. Например — HTML
          EXPLAIN SELECT * FROM categories
          
          ********************** 1. row **********************
          id: 1
          select_type: SIMPLE
          table: categories
          type: ALL
          possible_keys: NULL
          key: NULL
          key_len: NULL
          ref: NULL
          rows: 4
          Extra:
          1 row in set (0.00 sec)
          

          Получается гораздо читабельнее.

          Запросы до дампа — это ерунда. Хотя тоже не красит автора.
          но проблема в том, что и после дампа вывод эксплейнов не соответствует запросам. И дело совсем не в наличии индексов. Хотя это, опять же — необъяснимая тупость автора. А точнее — лень.
          Но дело, повторюсь, не в индексах, а в самом дампе. Запросы не находят данных.
          Я, когда готовлю статью, вылизываю исходные данные до последней запятой, перепроверяю по 10 раз. А этот индус тяп-ляп набросал — разбирайтесь сами.
          Даже понимая, как должен работать его код, я не буду тратить время на поиск и исправление опечаток. А человек, который, как предполагается, будет изучать explain, вряд ли пройдёт этот квест вообще.

          Тщательнее надо подходить к выбору статей для перевода.
          А перед написанием статьи на хабр желательно сначала изучить средства форматирования.
          • 0
            Спасибо за подсказку c source. Так гораздо лучше. А с запросами — раз вы так настойчивы, проверю еще раз. Я вроде написал, что все проверял сам и все работало, но вы этого не заметили, т.к. иначе бы конкретизировали претензию.
            • 0
              Да, это был у меня косяк при импорте. Этот вопрос снимается.
              Но наличие индексов в учебном дампе, при том что в примерах требуется сначала база без индексов, а самой статье приведен код для их создания — это всё равно неприемлемо для обучающего материала.
              • 0
                Это да. В статье об этом упомянуто. Когда обнаружил это, то просто решил упомянуть. Ссылка на github. А там я ничего поменять не смогу. Пришлось подстраиваться.
                • +1
                  Ну, вообще-то, github — это синоним слова «поменять». Весь смысл гитхаба — это возможность одной кнопкой сделать форк и менять в нём всё, что только заблагорассудится. Потом можно дать ссылку на свой репозиторий с исправленной версией.
                  • +1
                    В таком случае еще раз спасибо за то, что обогатили мои знания. Подправил дамп. В статью добавил ссылку на дамп без ключей.
          • 0
            Ну, а про статью. Раз я ее опубликовал, значит думаю, что она достойна опубликования. Да, о том же самом, о чем писали сотню раз. Да, для новичков. Не буду приводить примеры того, что есть здесь и чего я не нашел в других источниках, чтобы не скатываться до обсуждения неправильности/правильности решения о выборе статьи. Для меня оно было правильным, для вас оно может быть другим. По поводу того, что подобное было уже описано. В разных предметных областях пишут кучи книг об одном и том же. Главное — угол зрения. Если данный угол зрения на описанную тему поможет хотя бы одному человеку, то все было не зря.
  • 0
    Могу отметить, что говоря о WHERE, нельзя забывать об ORDER BY.

    Запрос такого вида:
    SELECT table1.field1, table1.field2, table1.field3 FROM table1 INNER JOIN table2 ON (table1.field4 = table2.field1)
    WHERE table1.field5 IN (?, ?, ?) AND table1.field6 NOT LIKE 'Europe/%' ANВ table1.field7 = ?
    ORDER BY table1.field1
     LIMIT ?, ?
    


    Может оказаться быстрее при наличии индекса по table1(field5, field1), а не при наличии индекса по table1(field5, field7, field6).

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.