【问题标题】:Active Record NOT IN queries and NULL with MySQL使用 MySQL 的 Active Record NOT IN 查询和 NULL
【发布时间】:2013-09-22 22:36:41
【问题描述】:

我有一个类似的查询:

Tag.where('id not IN (?)', current_user.tags.pluck(:id)).uniq

什么时候

current_user.tags.pluck(:id)).uniq

返回 NULL,我没有从 Tag 查询中得到不是所需行为的结果。

我在这里做错了什么?

谢谢。

【问题讨论】:

    标签: mysql ruby-on-rails ruby-on-rails-3 activerecord rails-activerecord


    【解决方案1】:

    我认为current_user.tags.pluck(:id) 不会返回nil,它返回的是一个空数组。 ActiveRecord will treat an empty array as a NULL 在这种情况下。结果是一些像这样无意义的 SQL:

    select tags.* from tags where id in (null)
    

    由于 SQL 的 NULL 的特性(特别是 x = NULLx != NULL 对于所有 x 都是错误的),WHERE 子句中的 in (null)not in (null) 不会匹配任何内容。

    Rails 将 Ruby 的 [] 转换为 NULL 非常愚蠢(更多关于 over here 的讨论)但即使它足够聪明地引发异常,您仍然必须处理“空数组”用这样的东西手动案例:

    tag_ids = current_user.tags.pluck(:id)
    if(tag_ids.empty?)
      tags = Tag.all
    else
      tags = Tag.where('id not in (?)', tag_ids)
    end
    

    您不需要其中的uniq,SQL in 运算符会将其 RHS 视为一个集合,因此重复项将在幕后折叠。

    【讨论】:

      【解决方案2】:

      当传入的参数为空数组时,使用where.not 让Rails 处理。

      正如 mu 太短指出的那样,当传递给查询条件的值是一个空数组时,ActiveRecord 会将其转换为 NULL 这会弄乱您的查询并且总是返回一个空结果.

      处理这个问题的旧方法是有条件地检查一个空数组作为参数,而不是添加这个条件。然而,随着 Rails 4 引入了where.not,我们不再需要这个检查。

      我们可以这样做:

      Tag.where.not( id: current_user.tags.pluck(:id) ).uniq
      

      现在 ActiveRecord 将自动检查一个空数组,当它看到它时,条件变为1=1,这本质上是没有意义的,但更重要的是,它只是被忽略了,你的查询的其余部分将像那个条件一样运行从未添加到查询中。

      【讨论】:

      • 甚至不需要拔掉 id :D
      • @MikeHeft 真的吗? Rails 必须自动返回主键?我从来没有尝试过,可能是因为我们主要使用唯一 ID,但如果它有效,那就非常聪明了。
      • 是的,它很聪明,可以从对象中获取 id。仅适用于 id 列。对于任何其他专栏,您都需要勇气。 :D
      • @MikeHeft 美女!感谢您的确认。如果您愿意,请随时编辑答案。
      猜你喜欢
      • 1970-01-01
      • 2017-03-30
      • 1970-01-01
      • 1970-01-01
      • 2014-11-15
      • 2011-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多