Я почесал голову над некоторыми запросами, которые не возвращаются со всеми строками, какими они должны быть, и я считаю, что причина в том, что переменные связывания упорядочены неправильно. Это беглая ошибка, или я делаю что-то очень неправильно?
Вот пример, обнаженный, чтобы показать, что происходит. Запрос q1 выбирает из таблицы с простым условием где. Запрос q2 присоединяется к таблице (вид) с условием в операторе ON. Основной запрос q присоединяется к таблице с произвольным условием.
$q1 = DB::table('c')->where('d', '=', 'second');
$q2 = DB::table('e')->join('f', function($join){$join->where('f.id', '=', 'third');});
$q = DB::table('x')->join('y', function($join){$join->where('y.id', '=', 'first');})
->unionAll($q1) // binds to 'second'
->unionAll($q2); // binds to 'third'
var_dump($q->toSql());
var_dump($q->getBindings());
Когда это выполняется, это массив запросов и привязок, который генерирует Fluent (в Laravel 4.2):
(select * from `x` inner join `y` on `y`.`id` = ?)
union all
(select * from `c` where `d` = ?)
union all
(select * from `e` inner join `f` on `f`.`id` = ?)
array(3) {
[0]=>
string(2) "first"[1]=>
string(2) "third"[2]=>
string(2) "second"}
Предполагая, что переменные связывания совпадают по порядку, от начала до конца, переменные связывания для второго и третьего запросов выполняются неправильно, например, (select * from
сwhere
d= 'f1')
который должен быть (select * from
сwhere
d= 'd1')
,
Кажется, что предложение where в q1 помещается в конец массива переменных связывания, независимо от того, сколько дополнительных запросов впоследствии добавляется в объединение. Или, может быть, именно так выглядит этот упрощенный пример. Возможно, переменные связывания не должны быть в том порядке, в котором, я думаю, они должны быть?
Laravel передает указанный выше запрос и связывает массив напрямую с PDO без дальнейшей обработки.
Ответ на мой вопрос — «да». Свободно смешивает переменные связывания между запросами UNION, поэтому окончательный массив переменных связывания находится в неправильном порядке.
Конструктор запросов Laravel сохраняет свои переменные связывания для каждого раздела запроса в массиве:
// Illuminate\Database\Query\Builder
protected $bindings = array(
'select' => [],
'join' => [],
'where' => [],
'having' => [],
'order' => [],
);
Таким образом, вы можете создать запрос в любом порядке — сначала ВЫБРАТЬ, ГДЕ сначала, или немного перемешать. Это отлично подходит для одного запроса.
Что делает Laravel при создании союзов, так это объединяет эти массивы из каждого запроса в объединении. Вот где переменные связывания перепутаны. Вместо этого он должен анализировать массив переменных связывания для каждого запроса в одиночные (линейные) массивы, а затем объединять эти массивы в порядке, в котором он объединяет запросы объединения.
Я поднял это как ошибку. Я не вижу простого исправления, поэтому использовал обходной путь: запускайте каждый запрос отдельно, затем объединяйте результаты вместе в PHP, а не ожидайте, что база данных объединит результаты в запросе объединения. Другие решения, такие как объединение запросов SQL и привязок вручную и передача их напрямую в PDO, будут работать, но я пытаюсь не переписывать логику в Laravel.
https://github.com/laravel/framework/issues/5833
Мой совет не использовать союзы в Laravel 4.2, по крайней мере. Если ни один из ваших запросов не имеет переменных связывания, вы можете использовать его. Однако имейте в виду, что построитель запросов будет использовать переменные связывания для любой строки, числа, даты, числа или массива, которые вы передаете в любую часть запроса.
Других решений пока нет …