Rails 要小心使用 Where ("xxx NOT IN (?)", array) 。。。

zhangjinzhu · 2013年10月31日 · 最后由 zhangjinzhu 回复于 2013年11月01日 · 4812 次阅读

最近在项目上实际使用我用 go 写的 ORM 的时候发现一个问题

db.Where("id NOT IN (?)", array) , 如果是数组为空,返回的结果竟然为 0, 我看了下生成的 SQL 是类似于这样:

SELECT * FROM USERS WHERE ID NOT IN (NULL); // 这样子的SQLmysql 会返回空值

我突然好奇,Rails 的 ActiveRecord 是怎么实现的。。。难道会 scan 一下 where 条件来判断,因为我印象中没有发现过这个问题。。。

可是,试了一下竟然发现,如果条件为空数组是话,Rails 竟然也会返回一个空值。。。而不是我想象中全部返回

User.where("id NOT IN (?)", [])

搜索了一下,竟然发现 Ruby China 没有人提过这个事情。。。。。不知道是大家都知道呢,还是都没有注意过。。。

然后看了下,推荐的做法就是:

Rails 4:

where.not(id: ids) 

之前的 Rails 版本就只能自己写 scope 处理了,例如

scope :excluding_ids, lambda { |ids|
  where(['id NOT IN (?)', ids]) if ids.any?
}

不过这样子只针对 ID。。。其它的字段,一个一个写 scope 吧。。。或者写个方法

当然我也更新了下我的 go 的 ORM。。。加上了 Not 方法,类似这样子。。。

db.Not(ids).First(&user)
db.Not("name", names).First(&user)

作为 paranoid 的程序员,做 in 查询之前都会先判断下列表空不空...

以前我也遇见过这个问题,就是在 where 之前先做判断了。而且无论是只有一个数字也得放在[ ]里。

@luikore @JeskTop 需要自己检查的什么的最讨厌了。。。。。。。。。

还真不太注意到。。

hibernate 的 Restrictions.in 也是这个样子的

Order.where.not(id: []).to_sql
=> "SELECT `orders`.* FROM `orders`  WHERE (1=1)"
7 楼 已删除

@Victor ActiveRecord 上面的生成的 SQL 还是不够聪明

"SELECT `orders`.* FROM `orders`  WHERE (1=1)"

"SELECT `orders`.* FROM `orders`"

慢 0.01 秒

虽然我奇怪怎么可能慢 0.01 秒呢。。。。只是一个 1=1,不过根据我人工测试了好几次,好像都是类似这个结果。。。。

这个问题还真没有注意过,还好不怎么用,不然真可能会中招 我用 pg 测也一样,但我用 count(id) 的方法会快一点,这个我看 CSDN 上讨论过,但结果不知道是怎样了:)

#8 楼 @zhangjinzhu 你先执行无 1=1 的,后执行 1=1,会发现相反的结果,这个涉及到表结构 cache。

@quakewang 所以我说我执行了好多次,第一次是 0.1 几秒

#11 楼 @zhangjinzhu 你如果执行好多次的话,后面的查询应该都是 0 秒,有 query cache 的,你应该用重启后的 mysql 来测试。

我的测试:

MariaDB [youji_dev]> flush table cache;
Query OK, 0 rows affected (0.00 sec)

MariaDB [youji_dev]> select count(*) from users where 1 = 1;
+----------+
| count(*) |
+----------+
|    71041 |
+----------+
1 row in set (0.03 sec)

MariaDB [youji_dev]> flush table cache;
Query OK, 0 rows affected (0.00 sec)

MariaDB [youji_dev]> select count(*) from users;
+----------+
| count(*) |
+----------+
|    71041 |
+----------+
1 row in set (0.04 sec)
需要 登录 后方可回复, 如果你还没有账号请 注册新账号