下面的查询会根据用户的下一个问题状态为我提供该问题。它获取该特定部分的所有问题,然后作用域对属于该用户的状态执行 LEFT JOIN
。
我的问题是,这似乎不是一种非常 Railsy 的方法 - 是否有更好的方法来过滤我的表而不是这种笨拙的 AND
和 .to_s
业务。我的问题是,显然,如果有任何用户回答了该问题,则左联接将填满该用户的答案,而我要求它为空。
本质上查询有效但很丑,我不知道这是否是最有效的方法!
scope :next_for_user, lambda { |user|
joins("LEFT JOIN user_question_statuses ON user_question_statuses.question_id = questions.id AND user_question_statuses.user_id = ", user.id.to_s).
reorder("user_question_statuses.answered ASC NULLS FIRST").
order("user_question_statuses.updated_at ASC NULLS FIRST").
limit(1)
}
编辑:
我意识到这种方法特别容易受到 SQL 注入(inject)的攻击,所以我将查询中的主线替换为:
joins(sanitize_sql_array(["LEFT JOIN user_question_statuses ON user_question_statuses.question_id = questions.id AND user_question_statuses.user_id = %d", user.id]))
这似乎有效并强制输入仅为整数。
编辑 2:
我的另一个选择是使用 find_each
然后使用用户 first_or_create
为当前用户的特定问题部分创建空问题状态。这可能发生在他们在寻找问题之前需要他们的时候。这将允许我从问题到这些状态做一个 RIGHT JOIN
,知道它们存在,但如果第一种方法是高效和安全的(并且尽可能 Railsy),那么没有理由改变它。
编辑 3:
我以这种方式构造此查询是因为 - 从 has_many
questions
的 section
模型 - 我想找到下一个 应该传递给
。用户
的问题
要找到它,我需要将所有 user_question_statuses
加入到所有 section
模型的问题中。唯一可以做到这一点的方法是在 question.id
上。但是,对于不同的用户,有许多具有该问题 ID 的 user_question_statuses
。因此,在加入时,我需要 AND
子句在加入发生之前将 user_question_statuses
过滤为仅来自该 user
的那些。很明显,用户每个问题
只有一个状态。
我使用 LEFT JOIN
这样如果状态尚不存在(它们仅在用户第一次尝试问题后创建),仍然有状态为 NULL
无处不在,以便他们创建一行,然后从该行移动到顶部(因此 NULLS FIRST
)并可能服务器到 user
。
这一切可能都非常不清楚!
最佳答案
您所做的是跳过 Active Record 提供的 ORM 层并自行构建查询。您的感觉是正确的,因为这种方法有很多限制并且不太适合 Rails 遵循的 MVC 模型。我建议通读 Active Record Query Interface获得以 rails/oop 方式进行操作的概念
关于ruby-on-rails - Rails 4 - 加入 AND 子句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28384137/