Лишние запросы в ActiveRecord

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
kushchiro
Сообщения: 5
Зарегистрирован: 2016.03.29, 13:57

Лишние запросы в ActiveRecord

Сообщение kushchiro » 2016.03.29, 15:12

Постараюсь изложить суть проблемы коротко

Есть две таблицы связанные many to many: communications и units.
Нужно получить все записи из связной таблицы, с записями units по communication.id.
Важно это сделать одним запросом.
Опишу свои шаги

Шаг 1
связь:

Код: Выделить всё

public function getUnits()
{
    return $this->hasMany(Unit::className(), ['id' => 'unit_id'])
        ->viaTable('unit_communications', ['communication_id' => 'id']);
}
вызов:

Код: Выделить всё

Communication::find()->with(['units'])
    ->where([Communication::tableName().'.id' => 1])
    ->one();
результат: 3 запроса

Код: Выделить всё

SELECT * FROM "communications" WHERE "communications"."id"='1'
SELECT * FROM "unit_communications" WHERE "communication_id"=1
SELECT * FROM "units" WHERE "id" IN (5, 6, 7, 8)
Шаг 2
Заменяем with на joinWith:

Код: Выделить всё

Communication::find()->joinWith(['units'])
            ->where([Communication::tableName().'.id' => $id])
            ->one(); 
Добавляем модель для связной таблицы;
В модели Communication прописываем две связи:

Код: Выделить всё

public function getCommunicationUnits()
{
    return $this->hasMany(UnitCommunication::className(), ['communication_id' => 'id']);
}
    
public function getUnits()
{
    return $this->hasMany(Unit::className(), ['id' => 'unit_id'])
        ->via('communicationUnits');
} 
Результат:

Код: Выделить всё

SELECT "communications".* FROM "communications" LEFT JOIN "unit_communications" ON "communications"."id" = "unit_communications"."communication_id" LEFT JOIN "units" ON "unit_communications"."unit_id" = "units"."id" WHERE "communications"."id"='1'
SELECT * FROM "unit_communications" WHERE "communication_id"=1
SELECT * FROM "units" WHERE "id" IN (5, 6, 7, 8) 
все так же 3 запроса, безсмысленный join в первом

Шаг 3
заменяем ->one() на ->all(), добавляем поля в select и метод asArray()

Код: Выделить всё

Communication::find()->joinWith(['units'])
            ->where([Communication::tableName().'.id' => $id])
            ->select(['serial', 'send_time', 'filter_id']) // поля из связной таблицы
            ->asArray()
            ->all()
Результат:

Код: Выделить всё

SELECT "serial", "send_time", "filter_id" FROM "communications" LEFT JOIN "unit_communications" ON "communications"."id" = "unit_communications"."communication_id" LEFT JOIN "units" ON "unit_communications"."unit_id" = "units"."id" WHERE "communications"."id"='1'
SELECT * FROM "unit_communications" WHERE 0=1
SELECT * FROM "units" WHERE 0=1
В результате получаем один рабочий запрос, и два лишних
Эти запросы формируются в \yii\db\ActiveQuery.populate

Возможно я что-то делаю не так, если что, поправьте.
Заранее спасибо.

Аватара пользователя
TranceSmile
Сообщения: 562
Зарегистрирован: 2011.06.27, 19:04
Откуда: Украина
Контактная информация:

Re: Лишние запросы в ActiveRecord

Сообщение TranceSmile » 2016.03.29, 18:42

А теперь ещё надо выяснить что быстрее работает.
Изображение

kushchiro
Сообщения: 5
Зарегистрирован: 2016.03.29, 13:57

Re: Лишние запросы в ActiveRecord

Сообщение kushchiro » 2016.03.29, 18:52

TranceSmile писал(а):А теперь ещё надо выяснить что быстрее работает.
С удаленной базой каждый лишний запрос очень дорого обходится

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Лишние запросы в ActiveRecord

Сообщение zelenin » 2016.03.29, 20:40

kushchiro писал(а):
TranceSmile писал(а):А теперь ещё надо выяснить что быстрее работает.
С удаленной базой каждый лишний запрос очень дорого обходится
разбор результата такого запроса в модель и связаные модели плохо сказываются на архитектуре. так было в yii, в yii2 от этого ушли.

Код: Выделить всё

SELECT "serial", "send_time", "filter_id" FROM "communications" LEFT JOIN "unit_communications" ON "communications"."id" = "unit_communications"."communication_id" LEFT JOIN "units" ON "unit_communications"."unit_id" = "units"."id" WHERE "communications"."id"='1' 

kushchiro
Сообщения: 5
Зарегистрирован: 2016.03.29, 13:57

Re: Лишние запросы в ActiveRecord

Сообщение kushchiro » 2016.03.30, 11:08

zelenin писал(а):
kushchiro писал(а):
TranceSmile писал(а):А теперь ещё надо выяснить что быстрее работает.
С удаленной базой каждый лишний запрос очень дорого обходится
разбор результата такого запроса в модель и связаные модели плохо сказываются на архитектуре. так было в yii, в yii2 от этого ушли.

Код: Выделить всё

SELECT "serial", "send_time", "filter_id" FROM "communications" LEFT JOIN "unit_communications" ON "communications"."id" = "unit_communications"."communication_id" LEFT JOIN "units" ON "unit_communications"."unit_id" = "units"."id" WHERE "communications"."id"='1'
Странным для меня является то, что я результат получаю asArray, то-есть не прошу набор моделей, а запросы в методе populate все равно выполняются

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Лишние запросы в ActiveRecord

Сообщение zelenin » 2016.03.30, 11:51

kushchiro писал(а):
zelenin писал(а):
kushchiro писал(а):
С удаленной базой каждый лишний запрос очень дорого обходится
разбор результата такого запроса в модель и связаные модели плохо сказываются на архитектуре. так было в yii, в yii2 от этого ушли.

Код: Выделить всё

SELECT "serial", "send_time", "filter_id" FROM "communications" LEFT JOIN "unit_communications" ON "communications"."id" = "unit_communications"."communication_id" LEFT JOIN "units" ON "unit_communications"."unit_id" = "units"."id" WHERE "communications"."id"='1' 
Странным для меня является то, что я результат получаю asArray, то-есть не прошу набор моделей, а запросы в методе populate все равно выполняются
а, ну да. похоже на недоработку. не юзайте ar, если массив нужен.

Аватара пользователя
vitalik1183
Сообщения: 1673
Зарегистрирован: 2014.07.01, 08:42

Re: Лишние запросы в ActiveRecord

Сообщение vitalik1183 » 2016.03.30, 13:30

а, ну да. похоже на недоработку. не юзайте ar, если массив нужен.
а если сложная выборка(например 15 count с разными условиями), но нужен не массив?
Yii2!

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Лишние запросы в ActiveRecord

Сообщение zelenin » 2016.03.30, 13:35

vitalik1183 писал(а):
а, ну да. похоже на недоработку. не юзайте ar, если массив нужен.
а если сложная выборка(например 15 count с разными условиями), но нужен не массив?
это другой кейс

Аватара пользователя
vitalik1183
Сообщения: 1673
Зарегистрирован: 2014.07.01, 08:42

Re: Лишние запросы в ActiveRecord

Сообщение vitalik1183 » 2016.03.30, 13:49

zelenin писал(а):
vitalik1183 писал(а):
а, ну да. похоже на недоработку. не юзайте ar, если массив нужен.
а если сложная выборка(например 15 count с разными условиями), но нужен не массив?
это другой кейс
если можно то ответь, чтоб новый топег не создавать.
Yii2!

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Лишние запросы в ActiveRecord

Сообщение zelenin » 2016.03.30, 13:55

vitalik1183 писал(а):
zelenin писал(а):
vitalik1183 писал(а): а если сложная выборка(например 15 count с разными условиями), но нужен не массив?
это другой кейс
если можно то ответь, чтоб новый топег не создавать.
ну если нужны модели AR, то конечно надо через AR разруливать. Или в чем вопрос-то?

Просто если нужен массив, и есть связанная с этим бага/недоработка, то логично ar вообще не использовать. А если ar все-таки нужен, то вариантов нет.

Аватара пользователя
vitalik1183
Сообщения: 1673
Зарегистрирован: 2014.07.01, 08:42

Re: Лишние запросы в ActiveRecord

Сообщение vitalik1183 » 2016.03.30, 14:18

не, суть другая. с ар все понятно. но бывает чтоб запрос содержит всякую шнягу вида:

Код: Выделить всё

->select([
                'post_date' => 'DATE(' . WpPosts::tableName() . '.post_date)',
                'count' => 'COUNT(CASE WHEN ' . self::tableName() . '.status IN(' . $statuses . ') THEN ' . self::tableName() . '.id END)',
                'count_no_anchor' => 'COUNT(CASE WHEN ' . self::tableName() . '.status = "' . self::STATUS_NO_ANCHOR . '" THEN ' . self::tableName() . '.id END)',
                'count_yes_anchor' => 'COUNT(CASE WHEN ' . self::tableName() . '.status = "' . self::STATUS_YES_ANCHOR . '" THEN ' . self::tableName() . '.id END)',
            ])
 
приходится добавлять публичные свойства типа count_no_anchor так как в бд таких полей нет.
такие запросы я строю через (new \yii\db\Query()) верно ли это?
Yii2!

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Лишние запросы в ActiveRecord

Сообщение zelenin » 2016.03.30, 14:27

хз. я на yii не пишу уже больше 9 месяцев) мне эти проблемы ада уже не совсем понятны. Недавно написал себе легчайшую ОРМ и в ус не дую - полностью контролирую свои запросы без всякой магии.

Аватара пользователя
vitalik1183
Сообщения: 1673
Зарегистрирован: 2014.07.01, 08:42

Re: Лишние запросы в ActiveRecord

Сообщение vitalik1183 » 2016.03.30, 14:55

zelenin писал(а):хз. я на yii не пишу уже больше 9 месяцев) мне эти проблемы ада уже не совсем понятны. Недавно написал себе легчайшую ОРМ и в ус не дую - полностью контролирую свои запросы без всякой магии.
да тут вроде никакой магии нет :)
Yii2!

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Лишние запросы в ActiveRecord

Сообщение zelenin » 2016.03.30, 15:01

vitalik1183 писал(а):
zelenin писал(а):хз. я на yii не пишу уже больше 9 месяцев) мне эти проблемы ада уже не совсем понятны. Недавно написал себе легчайшую ОРМ и в ус не дую - полностью контролирую свои запросы без всякой магии.
да тут вроде никакой магии нет :)
я про все эти магические геттеры/сеттеры, публичные свойства и прочее блARдство.

Аватара пользователя
maleks
Сообщения: 1716
Зарегистрирован: 2012.12.26, 12:56

Re: Лишние запросы в ActiveRecord

Сообщение maleks » 2016.03.30, 15:03

vitalik1183, если ты запросы строишь через new \yii\db\Query() то откуда в нем self::tableName() ?

Аватара пользователя
vitalik1183
Сообщения: 1673
Зарегистрирован: 2014.07.01, 08:42

Re: Лишние запросы в ActiveRecord

Сообщение vitalik1183 » 2016.03.30, 15:04

просто в модели метод такой есь
Yii2!

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Лишние запросы в ActiveRecord

Сообщение zelenin » 2016.03.30, 15:07

maleks писал(а):vitalik1183, если ты запросы строишь через new \yii\db\Query() то откуда в нем self::tableName() ?
он в модель AR добавляет методы, которые что-то вытаскивает через Query)

Аватара пользователя
vitalik1183
Сообщения: 1673
Зарегистрирован: 2014.07.01, 08:42

Re: Лишние запросы в ActiveRecord

Сообщение vitalik1183 » 2016.03.30, 15:18

zelenin не в модели надо размещать такое? а где? :)
Yii2!

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Лишние запросы в ActiveRecord

Сообщение zelenin » 2016.03.30, 15:21

vitalik1183 писал(а):zelenin не в модели надо размещать такое? а где? :)
в сервисах

Аватара пользователя
vitalik1183
Сообщения: 1673
Зарегистрирован: 2014.07.01, 08:42

Re: Лишние запросы в ActiveRecord

Сообщение vitalik1183 » 2016.03.30, 15:21

пока еще не доехали до такова, по старенькому :D
Yii2!

Ответить