ActiveRecord在查询结尾添加了“AND(1 = 0)”

32

我一直在Rails控制台里尝试让事情正常运行,发现其中一个查询不应返回nil。查看生成的SQL查询后,我发现每次都附加了AND (1=0)。这有点让人恼火,而且我不知道为什么会这样。

注意:使用了actable gem。

重现步骤:

(连接到Rails控制台后)

2.1.2 :xxxx > @parent = Parent.take
Parent Load (38.1ms)  SELECT  `parents`.* FROM `parents`  LIMIT 1
 => #<Parent id: 37, ...>

2.1.2 :xxxx > @child = Child.where(id: @parent.actable_id)
SQL (0.7ms)  SELECT `childs`.`id` AS t0_r0, `childs`.`attribute` AS t0_r1, FROM `childs`
...
LEFT OUTER JOIN `parents` ON `parents`.`actable_id` = `childs`.`id` AND `parents`.`actable_type` = 'child type' WHERE `childs`.`id` = 20 AND (1=0)
 => #<ActiveRecord::Relation []>

为什么会发生这种情况?我该如何让它停止?


2
我注意到Rails在处理以下查询时采用了这种方式:User.where(:id => []),因为你无法在SQL中编写SELECT * FROM users WHERE id in ()。因此,我猜这是它用于已知返回空结果集的查询的方法,但却无法用SQL表示。 - MaxGabriel
@MaxGabriel,这对我来说就是答案。也许可以将这个作为回答而不仅仅是评论? - jgraft
@jgraft 好主意,我按照你的建议将其作为答案回复了。 - MaxGabriel
1个回答

68

当您查询值为空数组的列时,Rails将生成类似于AND (1=0)的SQL:

Child.where(id: [])

直觉上,你可能期望生成如下 SQL 语句:SELECT * FROM children WHERE id IN (),但 () 实际上并不是合法的 SQL。然而,由于此查询没有匹配的行,因此 Rails 通过生成一个等价的查询来避免这个问题,该查询也不会返回任何行:

SELECT * FROM children WHERE 1=0 AND id IN ()

注意,1=0 总是为假,所以这个查询将不会匹配到任何行。

SELECT * FROM children WHERE 1=0;

谢谢!这个答案帮助我解决了同样的困惑!可惜它从未被接受为正确答案。 - ChrisDekker
@alex0112 你会考虑接受这个答案吗? - MaxGabriel
如果您指定空哈希,这似乎也会发生。Child.joins(:parent).where(:parents => {}) - olivervbk
是的,通过测试查询确认了。谢谢@OliverKruster。 - MaxGabriel
1
感谢您的即时澄清,我已经在想它来自哪里了,但是一个快速的谷歌搜索可能为我节省了数小时。我不理解的是,如果Rails知道它将不返回任何行,为什么还要执行查询(对数据库运行它)? - SidOfc
1
我不确定,但可能的想法是:1)返回空结果的查询可能是一个子查询,它是一个实际返回结果或以某种方式修改数据库状态的更大查询的一部分;2)它增加了实现的复杂性;3)对于开发人员来说,数据库查询实际上没有命中数据库可能是意外的。 - MaxGabriel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接