Как мне создать ПЕРЕСЕЧЕНИЕ с помощью Eloquent Builder без наличия коллекции

#php #laravel #postgresql #eloquent

#php #laravel #postgresql #eloquent

Вопрос:

Вот что я получил. Мы используем IlluminateDatabaseEloquentBuilder для построения материализованных представлений в Postgres

Три таблицы по сути, мы создаем список электронных писем, объединяя таблицу subscriptions и таблицу email, после чего мы получаем результат списка электронных писем подписчиков. Электронные письма.

На данный момент у нас есть третья таблица, в которой есть три элемента.

 Variable_ID | Email | Value
  

Ожидаемое поведение заключается в том, что пользователь выбирает переменную (например, адрес), а затем вводит значение, и мы сопоставляем значения на основе этого. Итак, затем мы берем эту объединенную таблицу подписок / электронных писем и сравниваем таблицу переменных / электронных писем / значений и находим совпадения.

Проблема заключалась в том, что я хочу это сделать:

$подписки->leftJoin(‘faker_email_var_table’, ‘faker_email_var_table’.’.email’, ‘=’, ’email.email’) ->выбрать(‘faker_email_var_table’.’.email’) ->где(‘variable_id’, ‘1002015’) ->где(‘значение’, ‘=’, ‘1234 Anywhere street’) -> где(‘variable_id’, ‘1002707’)-> где(‘значение’, ‘=’, ‘Смит’)

Это не работает. Потому что мне нужно сопоставить variable_id: 1002015 И значение: 1234 Anywhere street, а затем мне нужно сопоставить variable_id: 1002707 И значение: Smith

То, что работает в редакторе SQL, — это ПЕРЕСЕЧЕНИЕ.

 SELECT var_Email_Val_Table.email
   FROM backend.subscriptions
     JOIN backend.emails ON subscriptions.email_id = emails.id
     LEFT JOIN backend.var_Email_Val_Table ON var_Email_Val_Table.email::text = emails.email
  WHERE var_Email_Val_Table.variable_id = 1002015 AND var_Email_Val_Table.value::text = '1234 Anywhere street'::text

INTERSECT

SELECT var_Email_Val_Table.email from backend.var_Email_Val_Table 
  WHERE var_Email_Val_Table.variable_id = 1002707 AND 
var_Email_Val_Table.value::text = 'Rhodes'::text

INTERSECT

 SELECT var_Email_Val_Table.email from backend.var_Email_Val_Table 
  WHERE var_Email_Val_Table.variable_id = 1002706 AND var_Email_Val_Table.value::text = 'Engineer'::text
  

Но, когда я пытаюсь сделать что-то подобное:

 $subscriptions->leftJoin(var_Email_Val_Table, var_Email_Val_Table.'.email', '=', 'emails.email')
    ->select(var_Email_Val_Table.'.email')
    ->where('variable_id', '1002015')->where('value', '=', '1234 Anywhere street')
    ->intersect(DB::table(var_Email_Val_Table)->select(var_Email_Val_Table.'.email')
    ->where('variable_id', '1002707')->where('value', '=', 'Smith'));
  

Я получаю эту ошибку:
Call to undefined method Illuminate\Database\Eloquent\Builder::intersect()

Если бы это был php, я мог бы выполнить серию условных выражений IF, чтобы получить свои результаты, но в SQL этого нет.

Так что, если кто-нибудь может подсказать мне, как я могу создать INTERSECT без использования коллекции, это было бы здорово!

Спасибо, Стек Фам!

Ответ №1:

У меня это есть в одном из моих контроллеров, он использует EXCEPT , но вы можете изменить его на intersect.

Я предварительно создаю 2 отдельных запроса. Чем я:

 $query = Thread::query() 
    ->fromRaw( 
        '(SELECT * FROM ((' . $unioned->toSql() . ') EXCEPT ' . $excludeExplicit->toSql() . ') AS threads) AS threads', 
        array_merge($unioned->getBindings(), $excludeExplicit->getBindings()) 
    );
  

Мне было бы любопытно узнать, является ли это лучшим способом. По крайней мере, это помогает мне.

Комментарии:

1. похоже, что eloquent просто не создан для каких-либо сложных запросов, но я подброшу его и посмотрю, прилипнет ли он! Спасибо!

2. Ну, прелесть этого в том, что два отдельных запроса создаются с помощью query builder, и этот объединенный запрос в конце возвращает мне eloquent models. Таким образом, это похоже на использование eloquent, только с большим количеством необработанного SQL.

Ответ №2:

То, что я в итоге сделал, было довольно специфичным, и я хотел бы, чтобы был лучший способ. Но это работает.

Я упоминал, как это создает материализованное представление, большая часть которого создается с использованием Eloquent. В какой-то момент эта модель Eloquent builder преобразуется в raw SQL с использованием ->toSql() . Поэтому я просто объединяю INTERSECT ‘s после этого в toSql для каждой дополнительной переменной, добавленной в представление.

 $rawQuery = $subscriptions->leftJoin('variable_relational_tbl', 'variable_relational_tbl'.'email', '=', 'emails.email')
            ->select('variable_relational_tbl'.'.email')
            ->join('emails', 'subscriptions.email_id', 'emails.id')
            ->select('emails.email', 'emails.md5 as email_md5')->toSql();

$intersect = $this->buildIntersects($query['variables']);

$query = $this->formatQuery($subscriptions);
if (! empty($intersect)) {
    $query = $query.' '.$intersect;
}

// INTERSECT QUERY BUILDER
protected function buildIntersects($variables)
{
    $interstr = ' INTERSECT ';
    $subquery = '';
    $cntr = 0;
    foreach ($variables as $var) {
        if ($cntr > 0) {
            $subquery = $interstr." SELECT `variable_relational_tbl`.email FROM`variable_relational_tbl` 
                WHERE `variable_relational_tbl`.variable_id = '$var['variable_id']' AND `variable_relational_tbl`.value = '$var['variable_value']' ";
        }
    }
    return $subquery;
}

  

Я создаю строку intersects с помощью цикла
Нашими переменными являются variable_id and variable_value .
Я упростил это, чтобы показать, что я сделал, конечно, для ясности.

Но я просто добавляю ПЕРЕСЕЧЕНИЯ после toSql() метода. Это некрасиво, и это не сработает, если вы не планируете создавать из него представление.

Но это то, что я сделал. И наоборот, я не смог найти другого способа получить совпадающие строки, которые я хотел, кроме или intersect. Что является своего рода облом.

Мне нужно было удалить строки из верхнего запроса и сопоставить и с несколькими ссылками из variable_relational_tbl

Например, мне нужны все строки с var_id = 123, где значение равно ‘1234 Example Street’, И все строки с var_id = 321, где значение равно ‘1234 Example Road’, Но поскольку строка не может иметь два разных пересечения var_id, это единственный способ, которым я мог это сделать.

Я не знаю, поможет ли это кому-нибудь, но у Eloquent нет INTERSECT . И это в основном ответ на этот вопрос.