在Rails中对PostgreSQL执行带有数组参数的原始SQL查询



我本希望这能奏效:

user_ids = [1, 154, 31908]
query = "SELECT id FROM users WHERE id = ANY (ARRAY[$1])"
ActiveRecord::Base.connection.exec_query(query, "SQL", [[nil, user_ids]])

然而,这导致了异常TypeError: can't cast Array

Rails在使用某个模型的where方法时似乎可以处理数组参数,但在进行原始SQL查询时,有没有任何方法可以使用数组参数,而不涉及ActiveRecord(除了获取连接(或任何模型?

首先创建一个可重用的复合类型定义,用于描述整数数组:

# Somewhere in the static context
IntegerArray = ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array.new(ActiveModel::Type::Integer.new).freeze

然后在查询中使用此类型:

user_ids = [1, 154, 31908]
query = "SELECT id FROM users WHERE id = ANY($1)"
params = IntegerArray.serialize(user_ids)
ActiveRecord::Base.connection.exec_query(query, "MyLogLabel", [[nil, params]])

注意,不需要直接使用PG::TextEncoder,也不需要使用PG::Connection#exec_params。仍然需要使用ActiveRecord::ConnectionAdapters::PostgreSQL,因为复合类型不是后端无关的。

不漂亮,但可以得到一个简单的PG::Connection连接,如下所示:

connection = ActiveRecord::Base.connection.instance_variable_get(:@connection)

不幸的是,pg-gem也不太优雅地支持数组,所以这只会让你走到一半,你必须对数组进行一些相当冗长的手动编码:

user_ids = [1, 154, 31908]
query = "SELECT id FROM users WHERE id = ANY ($1)"
params = [PG::TextEncoder::Array.new.encode(user_ids)]
connection.exec_params(query, params)

根据你想做的漂亮程度,你可以对ActiveRecord::ConnectionAdapters::PostgreSQLAdapter进行monkeypatch以公开@connection,或者将其子类化并配置Rails使用该子类。此外,pggem支持自定义类型转换行为,这将减少一些繁琐的工作。

如果要使用参数绑定,则必须将其转换为字符串,而不是直接使用数组:

user_ids = [1,154,31908]
user_ids = user_ids.join(',')
query = "SELECT id FROM users WHERE id = ANY (ARRAY[$1])"
ActiveRecord::Base.connection.exec_query(query, "SQL", [[nil, user_ids]])

这将产生查询SELECT id FROM users WHERE id = ANY (ARRAY[1,154,31908])

如果你只是在闲逛,尝试查询,你也可以放弃绑定,直接使用字符串插值:

query = "SELECT id FROM users WHERE id = ANY (ARRAY[#{user_ids.join(',')}])"

请记住,这会让您面临SQL注入和类似的麻烦,所以我不建议将这种方法用于任何可以从外部访问的内容。

以下内容对我有效。

user_ids = [1, 2, 3].to_s
user_ids = user_ids.gsub("[", "{")
user_ids = user_ids.gsub("]", "}")
query = "SELECT id FROM users WHERE id = ANY ($1)"
ActiveRecord::Base.connection.exec_query(query, "SQL", [[nil, user_ids]])

相关内容

最新更新