Sql 优化困难的查询(可能使用squel)
有这样的代码(使用PublicActivity gem和squel) 但它会创建8SQL请求:Sql 优化困难的查询(可能使用squel),sql,ruby-on-rails,activerecord,rails-activerecord,squeel,Sql,Ruby On Rails,Activerecord,Rails Activerecord,Squeel,有这样的代码(使用PublicActivity gem和squel) 但它会创建8SQL请求: SELECT "activities".* FROM "activities" WHERE "activities"."trackable_type" = 'Post' ORDER BY "activities"."created_at" DESC LIMIT 20 SELECT "posts".* FROM "posts" WHERE "posts"."id" IN (800, 79
SELECT "activities".* FROM "activities" WHERE "activities"."trackable_type" = 'Post' ORDER BY "activities"."created_at" DESC LIMIT 20
SELECT "posts".* FROM "posts" WHERE "posts"."id" IN (800, 799, 798, 797, 796, 795, 794, 793, 792, 791, 790, 789, 788, 787, 786, 785, 784, 783, 782, 781)
SELECT "users".* FROM "users" WHERE "users"."id" IN (880, 879, 878, 877, 876, 875, 874, 873, 872, 871, 869, 868, 867, 866, 865, 864, 863, 862, 861, 860)
SELECT "projects".* FROM "projects" WHERE "projects"."id" IN (80, 79)
SELECT "activities".* FROM "activities" WHERE "activities"."trackable_type" = 'Project' ORDER BY "activities"."created_at" DESC LIMIT 20
SELECT "projects".* FROM "projects" WHERE "projects"."id" IN (80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61)
SELECT "users".* FROM "users" WHERE "users"."id" IN (870, 859, 848, 837, 826, 815, 804, 793, 782, 771, 760, 749, 738, 727, 716, 705, 694, 683, 672, 661)
+
)不适用于上述代码有没有优化它的想法?这是一个相当大的问题。。。从外观上看,您可以在一个选择中完成,但为了可读性,我将使用两个,一个用于项目,一个用于帖子 这假设活动与后期/项目之间的关系为1:1。如果这不正确,可以通过子查询解决问题
select * from activities a
where a.trackable_type = 'Post'
left join posts p
on p.id = a.trackable_id -- or whatever fields join these two tables
left join users u
on a.user_id = u.id --this is joining to the main table, may want to join trackable, not sure
left join projects p
on a.project_id = p.id
order by a.created_at DESC LIMIT 20
或者,如果有1:多的关系,类似这样:
select * from
( select * from activities a
where a.trackable_type = 'Post'
order by a.created_at DESC LIMIT 20 ) activities
left join posts p
...
编辑:当我读到这篇文章时,我意识到我有点过时了。。。。我认为,如果您打算使用如此大的原始sql查询,您应该创建一个数据库函数,而不是使用sql中的一个简单开关将其编码到应用程序中:
def index
table_name = Activity.table_name
@activities = Activity.where(trackable_type: ['Post', 'Project'])
.order("CASE #{table_name}.owner_type WHEN 'Post' THEN 'a' ELSE 'z' END, #{table_name}.created_at DESC")
end
然后,您可以轻松添加所需的包含项;) 我相信由于
limit(20)
子句,您将需要至少两次AR查询调用(正如您目前所做的那样)。您的查询目前最多可提供20篇文章和20个项目,因此在一个查询中对这两种活动类型进行聚合限制不会得到预期的结果
我认为您需要做的就是在查询中使用eager\u load
,而不是includes
来强制执行单个查询。连接
,之间的差异包括
,预加载
,急切加载
和引用
方法
因此,使用AR和squeel:
def index
@activities = Activity.limit(20).order { created_at.desc }
@one = @activities.where{trackable_type == 'Post'}.eager_loads(trackable: [:author, :project])
@two = @activities.where{trackable_type == 'Project'}.eager_loads trackable: [:owner]
@activities = @one + @two
end
没有挤压,只使用常规ActiveRecord 4:
def index
@activities = Activity.limit(20).order(created_at: :desc)
@one = @activities.where(trackable_type: 'Post').eager_loads(trackable: [:author, :project])
@two = @activities.where(trackable_type: 'Project').eager_loads(trackable: :owner)
@activities = @one + @two
end
你不需要squel,我最近从我的项目中删除了它,因为在我的经验中,它不能正确地处理许多复杂的查询,其中AR 4和Arel都是正常的。非rails-4、非squel解决方案是:
def index
@activities = Activity.limit(20).order("created_at desc")
@one = @activities.where(trackable_type: 'Post') .joins(trackable: [:author, :project]).includes(trackable: [:author, :project])
@two = @activities.where(trackable_type: 'Project').joins(trackable: [:owner]) .includes(trackable: [:owner])
@activities = @one + @two
end
连接
和包含
的组合看起来很奇怪,但在我的测试中,它运行得出奇地好
这将把它减少到两个查询,而不是一个。和@activities仍将由一个数组执行。但也许将这种方法与squel结合使用也能解决这个问题。不幸的是,我不使用squel,也不能测试它
编辑:我完全没有抓住多态关联的要点。上面的工作是强制的
如果您想使用AR提供的内容,这有点粗糙,但您可以定义只读关联项目和帖子:
belongs_to :project, read_only: true, foreign_key: :trackable_id
belongs_to :post, read_only: true, foreign_key: :trackable_id
在这些情况下,上述强制快速加载的方法应该会起作用。where
条件仍然需要,因此这些关联仅在正确的活动中调用
def index
@activities = Activity.limit(20).order("created_at desc")
@one = @activities.where(trackable_type: 'Post') .joins(post: [:author, :project]).includes(post: [:author, :project])
@two = @activities.where(trackable_type: 'Project').joins(project: [:owner]) .includes(project: [:owner])
@activities = @one + @two
end
这不是一个干净的解决方案,应该对关联进行属性保护,以确保它们不会被意外设置(我希望这会破坏多态性),但从我的测试来看,它似乎起到了作用。简而言之,如果不使用SQL,就无法进一步优化。这就是Rails开展业务的方式。它不允许访问构成查询的AR模型之外的联接字段。因此,为了在其他表中获取值,它会对每个表进行查询 它也不允许提供解决问题的其他方法的
联合
或花式WHERE
条件
好消息是,这些查询都是有效的(考虑到可跟踪的_类型已被索引)。如果结果的大小很大(比如几十行),那么i/o时间将支配7个简单查询而不是1个复杂查询的轻微额外开销
即使使用SQL,也很难在一个查询中获得所需的所有联接结果。(这是可以做到的,但结果将是一个散列而不是AR实例。因此依赖代码将很难看。)每个表的一个查询被深深地连接到活动记录中
@Yoshi先生的解决方案使用最小SQL是一个很好的折衷方案,除了它不允许您基于可跟踪类型
字段选择性地加载作者
或项目
+所有者
编辑
上述内容对于Rails 3都是正确的。对于rails4,正如@CMW所说的,
eager\u load
方法将使用外部连接而不是单独的查询来执行与includes
相同的操作。这就是为什么我如此爱你!我总是能学到一些东西。您总是可以在rails中执行原始查询。只需构造sql字符串,然后就可以在请求端解析返回的数据集。。。这比做8个问题要快得多1)这不是rails的方式。2) 我对SQL不是很在行,也不知道最后应该如何查询视图:)嗯,我不同意这不是rails的方式。除了cookie cutter查询之外的任何查询,您无论如何都必须使用自定义where子句。helper函数只是添加了一些抽象和portability@Chris你能提示一下完成的SQL查询应该是什么样子吗?我的回答有帮助吗?它不起作用:不能急切地加载多态关联:trackable
。我使用的是rails 4Oh,我完全没有意识到它是关于多态关系的。我的错。我不确定rails的多态性方法是否有急切的负载。让我提出一个不同的方法。上面的代码不起作用。现在我不知道这个SQL是如何工作的,所以我会去阅读SQLcase
:)是的,这不是我的方式:)无论如何谢谢你的帮助!在对我的答案的编辑中,我指出了一种方法,可以在两个查询中(或者在一个查询中,使用sqeel)完成这项工作,而无需编写自己的SQL。基本上,这里阻碍您的是:trackable
是一种多态关系。但因为在这种情况下,只需要关联的两个特定变体,所以可以将它们放入代码中,从而消除所有的问题
def index
@activities = Activity.limit(20).order("created_at desc")
@one = @activities.where(trackable_type: 'Post') .joins(post: [:author, :project]).includes(post: [:author, :project])
@two = @activities.where(trackable_type: 'Project').joins(project: [:owner]) .includes(project: [:owner])
@activities = @one + @two
end