我有一个用 Rails 编写的应用程序和一个 PostgreSQL 数据库。我的应用程序有一些复杂的查询,我用它们包装在 PostgreSQL 视图中,以便我可以将 Rails 模型“插入”它们 - 使应用程序级别的事情变得更简单。
到目前为止,上述方法是成功的,因为我可以通过附加到这些视图的模型来使用许多 ActiveRecord 功能。
但是,我面临着一种情况,我必须优化其中一些视图,但我发现的唯一方法是将 PostgreSQL 视图转换为 PostgreSQL 函数,以便我可以传递用作约束的参数(在
where
条款)。这种方法让我的浏览速度加快了 100 倍!
问题是,为了调用这些函数,我通常会执行类似
Model.find_by_sql('SELECT * FROM my_pg_function(?, ?)')
的操作 - 它返回模型实例的数组。这并不理想,因为我需要方法链接(如Model.find_by_sql('SELECT * FROM my_pg_function(?, ?)').find(...)
) - 否则我将不得不在我的应用程序中进行大量更改。
关于如何使用原始查询以可链接的方式加载数据有什么想法吗?
你应该能够使用一点 Arel 来实现这个目标。
function = Arel::Nodes::NamedFunction.new('my_pg_function',[1,2])
subquery = Arel::SelectManager.new.project(Arel.star).from(function).as(Model.table_name)
Model.from(subquery)
这将产生以下查询:
SELECT
models.*
FROM
( SELECT
*
FROM
my_pg_function(1, 2)
) models
在这种情况下,这将允许您动态替换函数
[1,2]
的参数。以及将附加条件链接到“表”,因为它将在此过程中返回一个 ActiveRecord::Relation
对象。
所以你可以像这样实现:
class MyModel < ApplicationRecord
self.table_name = :my_models
def self.filter(*args)
from(
Arel::SelectManager.new
.project(Arel.star)
.from(Arel::Nodes::NamedFunction.new('my_pg_function',*args))
.as(table_name)
)
end
end
并使用类似
MyModel.filter(1,2).where(x: 12..).order(:x)
请注意,传递给命名函数的参数必须可由 Arel 访问,因此如果您需要传递字符串,则需要将它们放入
Arel::Nodes
我首选的方法是使用 Arel::Nodes::build_quoted
,因为它能够很好地处理不同的数据类型,您可以找到引用的基础知识这里