一直在讨论一些不应该带回所有行的查询,我相信原因是绑定变量没有正确排序。这是一个流利的错误,还是我做错了什么?
这是一个裸露的示例,以显示正在发生的事情。查询q1从具有简单where条件的表中进行选择。查询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
c where
d = 'f1')
应为(select * from
c where
d = 'd1')
。
似乎q1的where子句放在绑定变量数组的末尾,无论随后将多少其他查询添加到union中。或许这就是这个简单化的例子。也许绑定变量不应该按照我认为它们应该的顺序?
Laravel传递上述查询并将数组直接绑定到PDO,无需进一步处理。
答案 0 :(得分:2)
我的问题的答案是"是"。 Fluent正在UNION查询之间混合绑定变量,因此最终绑定变量数组的顺序错误。
Laravel查询构建器为数组中查询的每个部分保留其绑定变量:
// Illuminate\Database\Query\Builder
protected $bindings = array(
'select' => [],
'join' => [],
'where' => [],
'having' => [],
'order' => [],
);
通过这种方式,您可以按照您喜欢的任何顺序构建查询 - 首先选择,首先选择WHERE,或者将其混合一点。这对于单个查询非常有用。
当构建联合时,Laravel所做的是将联合中每个查询的这些数组合并在一起。这就是绑定变量混淆的地方。相反,它应该将每个查询的绑定变量数组解析为单个(线性)数组,然后按照它们连接联合查询的顺序连接这些数组。
我把它作为一个bug提出来了。我无法看到一个简单的修复方法,因此使用了一种解决方法:单独运行每个查询,然后在PHP中将结果合并在一起,而不是期望数据库在联合查询中合并结果。其他解决方案,例如手动将查询SQL和绑定连接在一起,并将它们直接传递给PDO都可以,但我试图不在Laravel中重写逻辑。
https://github.com/laravel/framework/issues/5833
我的建议是至少不要在Laravel 4.2中使用工会。如果您的查询都没有任何绑定变量,那么您可以使用它。但是,请注意,查询构建器将对您传递到查询的任何部分的任何字符串,数字,日期,数字或数组使用绑定变量。