Как выполнить подзапрос в предложении FROM с Yii 1.1? - PullRequest
0 голосов
/ 08 января 2020

У меня довольно большой и сложный запрос, генерируемый Yii. Чтобы сгенерировать этот запрос, мы используем CDbCritera::with для активной загрузки нескольких связанных моделей, и мы используем несколько областей для ограничения возвращаемых записей. Сгенерированный запрос имеет длину около 700 строк, но выглядит примерно так:

SELECT `t`.`column1` as `t0_c0`,
       `t`.`column2` as `t0_c1`,
       `related1`.`column1` as `t1_c0`,
       ...
       `related9`.`column5` as `t9_c4`
FROM `model` `t`
    LEFT OUTER JOIN `other_model` `related1`
                 ON ( `t`.`other_model_id` = `related1`.`id` )
    ...
    LEFT OUTER JOIN `more_models` `related9`
                 ON ( `t`.`more_models_id` = `related9`.`id` )
WHERE
    ...big long WHERE clause using all of related1 - related9 to filter model...
LIMIT 10

В нашей базе данных есть немалый объем данных, но он также непристойен. В этом случае таблица model имеет около 126000 строк, каждая «связанная» модель имеет отношение BELONGS_TO, а индекс t.XXX_id имеет индекс, поэтому объединение довольно тривиально. Проблема заключается в сложности предложения WHERE, содержащего несколько предложений COALESCE и IF и CASE. Выполнение фильтра для наших 126000 строк занимает 2,6 секунды - намного больше, чем хотелось бы для конечной точки API.

Предложение WHERE разделено на несколько различных разделов, например:

WHERE
    ( ... part 1 ... )
    AND
    ( ... part 2 ... )
    AND
    ( ... part 3 ... )

С каждой частью, соответствующей одной из областей действия, и с каждой частью, использующей одну или несколько связанных моделей

одна из областей применения фильтрует только на одну связанную модель, и при этом фильтрует нашу таблицу с 126000 строк до примерно 2000 строк. Я экспериментально (в MySQL Workbench) обнаружил, что могу получить наш запрос от 2,6 до 0,2 секунды, просто выполнив следующее:

SELECT `t`.`column1` as `t0_c0`,
       `t`.`column2` as `t0_c1`,
       `related1`.`column1` as `t1_c0`,
       ...
       `related9`.`column5` as `t9_c4`
FROM
    (
        SELECT `model`.*
        FROM `model`
        LEFT OUTER JOIN `other_model`
                     ON ( `t`.`other_model_id` = `other_model`.`id` )
        WHERE
            ( ... part 1 ... )
    ) `t`
    LEFT OUTER JOIN `other_model` `related1`
                 ON ( `t`.`other_model_id` = `related1`.`id` )
    ...
    LEFT OUTER JOIN `more_models` `related9`
                 ON ( `t`.`more_models_id` = `related9`.`id` )
WHERE
    ( ... part 2 ... )
    AND
    ( ... part 3 ... )
LIMIT 10

Таким образом, вместо выполнения очень сложного предложения WHERE для всех 126000 строк исходной таблицы model, мы выполняем гораздо более простое (и с расширенным индексом) предложение WHERE для этих 126000 строк, а затем выполняем сложное предложение WHERE только для 2000 соответствующих строк. Результаты этих двух запросов идентичны, но использование подзапроса в предложении FROM заставляет его работать в 13 раз быстрее.

Проблема в том, что я понятия не имею, как это сделать в Yii. Я знаю, что могу использовать CDbCommand для построения запроса и даже передать raw SQL, но я вернусь к массиву «строк» ​​- они не будут поняты Yii и должным образом преобразованы в правильные модели.

Есть ли у системы Yii ActiveRecord способ сказать что-то вроде следующего?

$criteria = new CDbCriteria;
$criteria->scopes = array("part1");
$subQuery = Model::model()->buildQuery($criteria);

$criteria = new CDbCriteria;
$criteria->scopes = array("part2", "part3");
$fullQuery = $subQuery->findAll($criteria);

1 Ответ

0 голосов
/ 27 января 2020

Хотя это и не идеальное решение, я нашел что-то, что почти как хорошее. Разбейте исходный запрос на два:

  1. Получите идентификаторы или модели, которые вы выберете sh для выбора в подзапросе FROM
  2. Добавьте WHERE к внешнему запросу с помощью id in (...)

Если кому-то будет интересно, я найду код, который я написал для этого, чтобы опубликовать ответ в качестве примера, но пока этому вопросу уделяется очень мало внимания, и как только я нашел псевдо-приличный Решение, которое я перешел.

...