Pull to refresh
2
0
Send message

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

А зачем, если анонимный класс можно сделать таким ?

Согласен, совсем забыл, что в 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

и вычесть из общего числа записей число записей по условию прямо в коде.

Information

Rating
Does not participate
Registered
Activity

Specialization

Fullstack Developer, Web Developer
Senior
PHP
MySQL
OOP
Database
Git
Java
English
Bash
JavaScript