Pet*_*xey 9 activerecord ruby-on-rails arel squeel
我想使用Squeel重构下面的查询.我想这样做,以便我可以链接其中的运算符并重新使用查询的不同部分中的逻辑.
User.find_by_sql("SELECT
users.*,
users.computed_metric,
users.age_in_seconds,
( users.computed_metric / age_in_seconds) as compound_computed_metric
from
(
select
users.*,
(users.id *2 ) as computed_metric,
(extract(epoch from now()) - extract(epoch from users.created_at) ) as age_in_seconds
from users
) as users")
Run Code Online (Sandbox Code Playgroud)
查询必须全部在DB中运行,并且不应该是混合Ruby解决方案,因为它必须对数百万条记录进行排序和切片.
我已经设置了问题,因此它应该针对普通user表运行,以便您可以使用它的替代方案.
User具有所有常规属性的对象extra_metric_we_care_about,age_in_seconds和compound_computed_metric下面的解决方案不起作用,但它显示了我希望实现的优雅类型
class User < ActiveRecord::Base
# this doesn't work - it just illustrates what I want to achieve
def self.w_all_additional_metrics
select{ ['*',
computed_metric,
age_in_seconds,
(computed_metric / age_in_seconds).as(compound_computed_metric)]
}.from{ User.w.compound_computed_metric.w_age_in_seconds }
end
def self.w_computed_metric
where{ '(id *2 ) as computed_metric' }
end
def self.w_age_in_seconds
where{ '(extract(epoch from now()) - extract(epoch from created_at) ) as age_in_seconds' }
end
end
Run Code Online (Sandbox Code Playgroud)
请注意,我有点设计了这个问题,以便您可以使用现有的User课程并在控制台中使用它.
在你的情况下我有2个苏打.我的数据库是mysql,我简化了你的demo代码,我想你可以扩展它.
第一种是Squeel方式,我在Squeel中混合了"sift",在ActiveRecord Query中混合了"from".我刚刚安装了postgresql并测试了我的解决方案,似乎很难一起使用"squeel"和"epoch from",但我在postgresql中找到了另一种方法,它叫做"date_part".我还修改了sql并减少了重复计算:
class User < ActiveRecord::Base
sifter :w_computed_metric do
(id * 2).as(computed_metric)
end
sifter :w_age_in_seconds do
(date_part('epoch' , now.func) - date_part('epoch', created_at)).as(age_in_seconds)
end
sifter :w_compound_computed_metric do
(computed_metric / age_in_seconds).as(compound_computed_metric)
end
def self.subquery
select{['*', sift(w_computed_metric) , sift(w_age_in_seconds)]}
end
def self.w_all_additional_metrics
select{['*', sift(w_compound_computed_metric)]}.from("(#{subquery.to_sql}) users")
end
end
Run Code Online (Sandbox Code Playgroud)
它产生了sql:
SELECT *, "users"."computed_metric" / "users"."age_in_seconds" AS compound_computed_metric
FROM (SELECT *,
"users"."id" * 2 AS computed_metric,
date_part('epoch', now()) - date_part('epoch', "users"."created_at") AS age_in_seconds FROM "users"
) users
Run Code Online (Sandbox Code Playgroud)
您可以使用控制台进行测试:
> User.w_all_additional_metrics.first.computed_metric
=> "2"
> User.w_all_additional_metrics.first.age_in_seconds
=> "633.136693954468"
> User.w_all_additional_metrics.first.compound_computed_metric
=> "0.00315887551471441"
Run Code Online (Sandbox Code Playgroud)
第二种是ActiveRecord方式,因为你的sql不是很复杂,所以你可以在ActiveRecord Query中链接它,这对于一些范围就足够了:
class User < ActiveRecord::Base
scope :w_computed_metric, proc { select('id*2 as computed_metric') }
scope :w_age_in_seconds, proc { select('extract (epoch from (now()-created_at)) as age_in_seconds') }
scope :w_compound_computed_metric, proc { select('computed_metric/age_in_seconds as compound_computed_metric') }
def self.subquery
select('*').w_computed_metric.w_age_in_seconds
end
def self.w_all_additional_metrics
subquery.w_compound_computed_metric.from("(#{subquery.to_sql}) users")
end
end
Run Code Online (Sandbox Code Playgroud)
这会产生以下SQL:
SELECT
*, id*2 as computed_metric,
extract (epoch from (now()-created_at)) as age_in_seconds,
computed_metric / age_in_seconds as compound_computed_metric
FROM (
SELECT
*,
id*2 as computed_metric,
extract (epoch from (now()-created_at)) as age_in_seconds
FROM
"users"
) users
ORDER BY compound_computed_metric DESC
LIMIT 1
Run Code Online (Sandbox Code Playgroud)
希望它符合你的要求:)
| 归档时间: |
|
| 查看次数: |
4090 次 |
| 最近记录: |