А зачем, если анонимный класс можно сделать таким ?
Согласен, совсем забыл, что в PHP можно менять сигнатуру конструктора и приватных методов в классах-наследниках без последствий.
С тем же успехом в blade шаблоне может измениться список используемых методов. Если есть уверенность что "точно нет", то лучше переопределить эти методы в анонимном классе, а не тащить рефлексию туда, где она не нужна.
Изменение списка используемых методов не повлияет на работу тестового двойника, он же их наследует. Может измениться набор полей, но его легко отредактировать в сервисе-клиенте и всё, больше нигде его менять не надо, ничего не надо редактировать в коде «того микросервиса». А рефлексия в данном случае помогает решить возможную проблему с приватными полями в классе-родителе.
А так, снова согласен, сам люблю минимализм, и с удовольствием обошёлся бы анонимным классом.
Также в самом первом EXPLAIN-е два WHERE - это опечатка?
Да, опечатка. Исправил, спасибо.
Структура БД слегка подозрительная. table1 ссылается на table2, table2 на table3, table3 на table4 по первичному ключу. Кажется, что в такой ситуации в каждой последующей таблице должно быть не больше записей, чем в предыдущей, но из explain-ов видно, что в table1 проверяется 475k записей, а в table4 их 22M. Откуда лишние записи? Не стоит ли разделить какую-то из таблиц на несколько или просто почистить?
На table4 ссылается много разных таблиц, поэтому в ней больше записей, чем в table1.
Необязательно писать в селекте именно ту таблицу, из которой нужно выбрать данные. Например, если, скажем, в какой-нибудь из таблиц в цепочке мало записей, можно попробовать джойнить остальные таблицы к ней.
Не очень понял, что предлагается, учитывая описанные в статье ограничения: оптимизированный запрос формируется программно и может являться частью другого запроса.
Там обычный B-Tree индекс. Думаю дело просто в подавляющей разнице количества записей с 0 и 1. В обоих случаях используется отбор по индексу, просто число записей с 1 очень мало, поэтому и получилось такое ускорение. И даже действительно медленный вариант обора NOT IN не испортил общий результат оптимизации.
У нас MySQL 5.7, но смена c eq_ref на ref произошла, потому что при обращении объединения в запросах для отбора стал использоваться не уникальный индекс.
С точки зрения сервера, будь у него разум, такой запрос - изощрённое, тонкое издевательство. Вот зачем вы используете LEFT JOIN, если финальный WHERE превращает их все в INNER? Чтобы сервер попыхтел лишнего?
Да, вы правы. внешнее объединение тут лишнее, сейчас попробовал на сервере и отыгралось еще 0,1 секунды. Спасибо.
Вообще-то WHERE NOT IN - самая медленная из возможных реализаций операции вычитания наборов записей. WHERE NOT EXISTS или LEFT JOIN WHERE IS NULL будут гарантированно не медленнее, а скорее всего быстрее. А если версия позволяет, то доступен ещё и EXCEPT.
Не понял какой рабочий вариант предлагаете.
Поле с именем id в подавляющем большинстве случаев первичный индекс, как правило автоинкрементный. И, судя по использованию PRIMARY в планах, так оно и есть. А если так, то связывание вообще не нужно. Достаточно посчитать отдельно записи в таблице 1, отдельно в подзапросе, а потом тупо вычесть. Результат будет тем же, а геморрою серверу ну куда как меньше.
Вроде написал об этом:
Поэтому было бы идеально решить проблему двумя простыми запросами:
/* Находим общее число записей в table1 */ SELECT COUNT(t1.id) FROM table1 t1
/* Находим число записей в table1, для которых выполняется условие в связанной таблице t4.some_field = 1 */ SELECT COUNT(t1.id) FROM table1 t1 LEFT JOIN table2 t2 ON t2.id=t1.table2_id LEFT JOIN table3 t3 ON t3.id=t2.table3_id LEFT JOIN table4 t4 ON t4.id=t3.table4_id WHERE t4.some_field = 1
и вычесть из общего числа записей число записей по условию прямо в коде.
Сделано.
Я понял вашу идею. Если получится, то на выходных дополню статью сводной таблицей по результатами нашего небольшого обсуждения.
Согласен, совсем забыл, что в PHP можно менять сигнатуру конструктора и приватных методов в классах-наследниках без последствий.
Изменение списка используемых методов не повлияет на работу тестового двойника, он же их наследует. Может измениться набор полей, но его легко отредактировать в сервисе-клиенте и всё, больше нигде его менять не надо, ничего не надо редактировать в коде «того микросервиса». А рефлексия в данном случае помогает решить возможную проблему с приватными полями в классе-родителе.
А так, снова согласен, сам люблю минимализм, и с удовольствием обошёлся бы анонимным классом.
Спасибо за статью, код можно улучшать до бесконечности, но как заготовка для редактора для каких-либо специфичных целей вполне сгодиться.
Да, опечатка. Исправил, спасибо.
На table4 ссылается много разных таблиц, поэтому в ней больше записей, чем в table1.
Не очень понял, что предлагается, учитывая описанные в статье ограничения: оптимизированный запрос формируется программно и может являться частью другого запроса.
Там обычный B-Tree индекс. Думаю дело просто в подавляющей разнице количества записей с 0 и 1. В обоих случаях используется отбор по индексу, просто число записей с 1 очень мало, поэтому и получилось такое ускорение. И даже действительно медленный вариант обора NOT IN не испортил общий результат оптимизации.
У нас MySQL 5.7, но смена c eq_ref на ref произошла, потому что при обращении объединения в запросах для отбора стал использоваться не уникальный индекс.
Да, вы правы. внешнее объединение тут лишнее, сейчас попробовал на сервере и отыгралось еще 0,1 секунды. Спасибо.
Не понял какой рабочий вариант предлагаете.
Вроде написал об этом: