从ActiveRecord原始SQL获取类型化结果
在续集中,我可以这样做:
irb(main):003:0> DB["select false"].get => false
返回false布尔值。 我希望能够在ActiveRecord中做类似的事情:
irb(main):007:0> ActiveRecord::Base.connection.select_value "select false" => "f"
如您所见,它返回字符串"f"
。 有没有办法用ActiveRecord获取一个假布尔值? (同样,我可能正在调用一个返回timestamptz,数组等的函数 – 我希望返回的值具有正确的类型)
我的用例:我正在调用数据库函数,想要取回一个键入的结果而不是字符串。
这适用于轨道5
results = ActiveRecord::Base.connection.select_all(sql) results.rows.map{ |row| Hash[results.columns.zip(row)] }
给出了不错的结果
[{"person1"=>563, "person2"=>564, "count"=>1}, {"person1"=>563, "person2"=>566, "count"=>5}, {"person1"=>565, "person2"=>566, "count"=>1}]
虽然我毫不怀疑BjörnNilsson的答案在他发布时有效,但对于Postgres 9.4
和PG gem版本0.18.2
来说,它失败了。 在查看PG gem文档后,我发现以下内容有效:
pg = ActiveRecord::Base.connection @type_map ||= PG::BasicTypeMapForResults.new(pg.raw_connection) res = pg.execute("SELECT 'abc'::TEXT AS a, 123::INTEGER AS b, 1.23::FLOAT;") res.type_map = @type_map res[0] # => {"a"=>"abc", "b"=>123, "float8"=>1.23}
非常丑陋,但做你要求的:
res = ActiveRecord::Base.connection. select_all("select 1 as aa, false as aas, 123::varchar, Array[1,2] as xx") # Breaks unless returned values have unique column names res.map{|row|row.map{|col,val|res.column_types[col].type_cast val}} # Don't care about no column names res.map{|row| row.values.map.with_index{|val,idx| res.column_types.values[idx].type_cast val } }
得到:
[[1, false, "123", [1, 2]]]
这个怎么运作:
res.column_types
返回列名和Postgresql列类型的哈希
这是一个指向它如何工作的指针: https : //github.com/rails/docrails/blob/fb8ac4f7b8487e4bb5c241dc0ba74da30f21ce9f/activerecord/lib/active_record/connection_adapters/postgresql/oid/float.rb
没有足够的声誉点来回应,但是Bjorn的回答和相关的回复在Rails 5中被破坏。这有效:
res = ActiveRecord::Base.connection.select_all(sql) res.to_a.map{|o| o.each{|k, v| o[k] = res.column_types[k].cast v}}
我不知道是不是这样,但是你可以创建一个没有带有假列的表的activerecord模型:
class FunctionValue < ActiveRecord::Base def self.columns @columns ||= []; end def self.column(name, sql_type = nil, default = nil, null = true) columns << ActiveRecord::ConnectionAdapters::Column.new( name.to_s, default, sql_type.to_s, null ) end column :value, :boolean end
然后你可以运行这个:
function_value = FunctionValue.find_by_sql('select false as value').first function_value.value