说`Post`是一个模型,一个class继承自rails中的`ApplicationRecord`。那么,Post.arel_table.create_table_alias 有什么作用呢?

say `Post` is a model, a class that inherits from `ApplicationRecord`, in rails. Then, what does Post.arel_table.create_table_alias does?

假设我有这个代码:

new_and_updated = Post.where(:published_at => nil).union(Post.where(:draft => true))
post = Post.arel_table
Post.from(post.create_table_alias(new_and_updated, :posts))

我从关于 arel 的 post 中获得了这段代码,但并没有真正解释 create_table_alias 的作用。只有最后的结果是一个活动的 activeRecord::Relation 对象,这是先前定义的联合的结果。为什么需要传递 :posts 作为 create_table_alias 的第二个参数,这是数据库中 table 的名称吗?

来自Rails官方文档,from查询方法是这样做的:

Specifies table from which the records will be fetched.

因此,为了从 new_and_updated 关系中获取帖子,我们需要一个别名 table,这就是 post.create_table_alias(new_and_updated, :posts) 所做的。

Rubydoc for Arel's create_table_alias方法告诉我们实例方法包含在Table模块中。

此处 :posts 参数指定要创建的别名 table 的名称,而 new_and_updated 提供 ActiveRecord::Relation 对象。

希望对您有所帮助。

Arel的本质如下

alias = Arel::Table.new(table_name)
table = Arel::Nodes::As.new(table_definition,alias)

这为新的 table 定义创建了一个 SQL 别名,以便我们可以在查询中引用它。

TL;DR

让我们根据您发布的代码解释这是如何工作的。

new_and_updated= Post.where(:published_at => nil).union(Post.where(:draft => true))

这个语句可以转换成下面的SQL

SELECT 
   posts.*
FROM 
   posts
WHERE 
   posts.published_at IS NULL
UNION 
SELECT 
   posts.*
FROM 
   posts
WHERE 
   posts.draft = 1 

这是一个很好的查询,但是您不能 select 从它作为子查询而没有语法错误。这是别名出现的地方,所以这一行(如上文所述 Arel

post.create_table_alias(new_and_updated, :posts)

变成

(SELECT 
   posts.*
FROM 
   posts
WHERE 
   posts.published_at IS NULL
UNION
SELECT 
   posts.*
FROM 
   posts
WHERE 
   posts.draft = 1) AS posts -- This is the alias

现在包装 Post.from 可以从这个子查询 select 这样最终的查询是

SELECT 
  posts.* 
FROM 
  (SELECT 
     posts.*
   FROM 
     posts
   WHERE 
     posts.published_at IS NULL
   UNION 
   SELECT 
     posts.*
   FROM 
     posts
   WHERE 
     posts.draft = 1) AS posts 

顺便说一句,如果您使用 rails 5,您的查询可以稍微简化,这也消除了对其余代码的需要,例如

 Post.where(:published_at => nil).or(Post.where(:draft => true))

会变成

SELECT 
   posts.*
FROM 
   posts
WHERE 
   posts.published_at IS NULL OR posts.draft = 1