Ruby on rails 检索具有空数组的记录时出现问题

Ruby on rails 检索具有空数组的记录时出现问题,ruby-on-rails,ruby-on-rails-3,postgresql,activerecord,arel,Ruby On Rails,Ruby On Rails 3,Postgresql,Activerecord,Arel,我有一个大约100个用户的表,还有一个用户ID数组。我想做的是显示不属于此用户ID数组的所有用户。当我这样做的时候 User.where('id NOT IN (?)', [9, 2, 3, 4]) SELECT "users".* FROM "users" WHERE (id NOT IN (NULL)) 它成功返回用户id不属于该数组的记录。但是,如果该数组是空的,那么 User.where('id NOT IN (?)', []) 它不会返回任何用户,SQL查询如下所示 Us

我有一个大约100个用户的表,还有一个用户ID数组。我想做的是显示不属于此用户ID数组的所有用户。当我这样做的时候

 User.where('id NOT IN (?)', [9, 2, 3, 4])
 SELECT "users".* FROM "users" WHERE (id NOT IN (NULL))
它成功返回用户id不属于该数组的记录。但是,如果该数组是空的,那么

 User.where('id NOT IN (?)', [])
它不会返回任何用户,SQL查询如下所示

 User.where('id NOT IN (?)', [9, 2, 3, 4])
 SELECT "users".* FROM "users" WHERE (id NOT IN (NULL))
有人知道为什么会发生这种情况,或者这可能是一个bug吗?我在PostgreSQL中使用Rails 3.2.5。

ActiveRecord(至少3.2.1)将空数组视为空数组。
调用中的占位符,其中
调用由处理。如果您对代码进行一点跟踪,您会发现:

然后:

一个空数组将满足所有四个条件以使您到达
c.quote(nil)
,这就是NULL的来源。所有导致
c.quote(nil)
的特殊逻辑都表明这是故意行为

用空列表表示(或不表示):

where c in ()
应该会产生一个SQL错误,所以AR人员可能正试图通过悄悄地将错误的SQL转换为
c in(null)
来防止这种情况。请注意,以下两种情况均不存在:

select ... from t where c in (null);
select ... from t where c not in (null);
由于SQL的NULL行为,应该产生任何结果。这是一个典型的新手错误,AR人员真的应该更清楚

我自己也希望有一个例外:告诉我我将要部署一颗脚弹,比仅仅给我一把不同的枪要友好得多


执行摘要

  • 这种“空数组表示空”行为是故意的
  • 永远不要尝试
    where('c in(?),[])
    where('c not in(?),[])
    ,因为这两条语句都没有多大意义
  • 更新Ruby代码以检查空数组,并执行任何需要执行的操作以获得预期的结果

  • 在Rails 4中,您可以使用
    User.where.not(id:[])
    ,这将为您提供正确的结果。它产生:

    SELECT "users".* FROM "users" WHERE (1 = 1)
    
    不幸的是,
    用户。其中('id NOT IN(?),[])
    应该是等效的,但不是。它仍然会给您错误的结果:

    SELECT "users".* FROM "users" WHERE (id NOT IN (NULL))
    
    参考资料:


    使用ruby的活动记录包装器:

    User.where.not(id: [])
    

    这将为您处理空数组问题。

    我不知道这是否是要求解决的问题,但我来这里查找所有具有空(序列化)数组属性的记录。我为Rails 5.0解决了这个问题,如下所示:

    User.where(my_array_attribute: nil)
    
    反之亦然:

    User.where.not(my_array_attribute: nil)
    

    这看起来像是Rails问题,或者可能是
    Pg
    gem问题;它将空数组视为
    NULL
    。非常奇怪,而且行为恶劣。你能直接用
    Pg
    gem测试一个准备好的语句,看看它是这样对待数组参数的,还是Rails/ActiveRecord级别在这样做?@CraigRinger:这将是Rails/ActiveRecord问题,而不是
    Pg
    问题。AR自己处理占位符。@CraigRinger:AR的人有时在SQL方面出人意料地糟糕,他们是故意这么做的。在这里插入“儿子,我很失望”的图片。@muistooshort是的,这不是我会选择的工具。然后,Hibernate(至少同样流行)也有自己的一些令人兴奋的怪癖,比如默认忽略串行列的
    DEFAULT nextval(…)
    ,而是从所有表中共享的自己的
    Hibernate\u序列生成ID。求爱!然而,由于AR拥有自己的占位符,我不禁对SQL注入和安全性感到好奇;似乎不是。。。超级健壮。是的,理智的东西在这里肯定是个例外。@CraigRinger:我,像往常一样,责备MySQL:)我现在明白为什么我的测试失败了。:)我不同意要一个例外。如果我想让作用域无论是否传递空数组都能正常工作,那么构造一个AR作用域就更优雅了,在这里我不必执行空数组保护。@gabe欢迎你不同意,但“正常工作”通常是隐藏bug的捷径,我非常喜欢一个告诉我要讲道理的工具,而不是一个试图讲道理却默默地做错事的工具。在一个模糊相关的注释中,User.where(:id=>[[],[3]])给出了无效的sql:“选择
    用户
    *FROM
    用户
    其中
    用户
    id
    在(,3)”中请考虑清楚地解释为什么这可能是正确的答案。@ DavidL:这保证了数组不会是空的。这还假定没有值为0的id(在Rails中通常是这样)。
    User.where.not(my_array_attribute: nil)