从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