问题 为什么Rails(3+)中仍然不支持存储过程?


我熟悉Ruby on Rails,DB(MS)驱动程序和存储过程之间长期以来爱恨交织的关系,我从版本2.3.2开始就开发了Rails应用程序。

然而,每隔一段时间出现一种情况,其中SP只是比(更慢)应用程序级别上的数据组合更好的选择。具体而言,运行组合来自多个表的数据的报告通常更适合SP。

为什么存储过程仍然很难集成到Rails或MySQL gem中。我目前正在开发一个使用Rails 3.0.10和MySQL2 gem 0.2.13的项目,但据我所知,即使最新的Edge Rails和MySQL gem 0.3+仍然在使用SP时发脾气。

过去和现在仍然存在的问题是在调用SP后数据库连接丢失。

>> ActiveRecord::Base.connection.execute("CALL stored_proc")
=> #<Mysql::Result:0x103429c90>
>> ActiveRecord::Base.connection.execute("CALL stored_proc")
ActiveRecord::StatementInvalid: Mysql::Error: Commands out of sync; 
[...]
>> ActiveRecord::Base.connection.active?
=> false
>> ActiveRecord::Base.connection.reconnect!
=> nil
>> ActiveRecord::Base.connection.execute("CALL proc01")
=> #<Mysql::Result:0x1034102e0>
>> ActiveRecord::Base.connection.active?
=> false

从技术上讲,这是一个非常难以解决的问题,还是Rails的设计选择?


2465
2017-07-25 19:30


起源

FWIW:自2006年2月起,DHH采访:“我对存储过程,触发器等奇特功能完全不感兴趣” - dev.mysql.com/tech-resources/interviews/... - Jesse Wolgamott
也 “我认为存储过程和约束是卑鄙的,而且是鲁莽的连贯性驱逐者”... - dbr
2006年......古老的原则(是的,6年在像Rails这样的世界中很古老)很少对技术的发展有意义(640k足够,有人吗?)有理论和实践领域。从理论上讲,我绝对支持Rails范例,但有时候最好的规范化数据库会给现实世界带来最差的性能。当SP可以在不到100ms的时间内收集报告的数据并且执行它时“Rails方式”需要2-3秒并且在共享数据库中发出多个查询,我知道谁是明显的赢家。 - ChrisDekker
它们与SQLServer适配器一起工作得很好 ActiveRecord::Base.execute_procedure(:some_proc_name, input1, input2),也许有人只需要在适配器中正确实现它? - Unixmonkey


答案:


rails中支持存储过程。你得到的不同步错误是因为 MULTI_STATEMENTS 在Rails中默认情况下不启用MySQL标志。此标志允许过程返回多于1个结果集。

请参阅此处以获取有关如何启用它的代码示例: https://gist.github.com/wok/1367987

存储过程与MS SQL Server一起开箱即用。

我几乎在所有基于mySQL和SQL Server的rails项目中都使用了存储过程而没有任何发布。


10
2018-02-25 09:17





这是为了让postgres执行一个返回MyClass实例的存储过程。

sql=<<-SQL
select * from my_cool_sp_with_3_parameters(?, ?, ?) as 
foo(
  column_1 <type1>,
  column_2 <type2>
)
SQL

MyClass.find_by_sql([sql, param1, param2, param3]);

将foo()内的列列表替换为模型中的列和存储过程结果。我确信通过检查类的列可以使它成为通用的。


3
2017-07-27 17:13





获得同步错误的人可能会有生成多个结果的过程。你需要做这样的事情来处理它们:

raise 'You updated Rails. Check this duck punch is still valid' unless Rails.version == "3.2.15"
module ActiveRecord
  module ConnectionAdapters
    class Mysql2Adapter
      def call_stored_procedure(sql)
        results = []
        results << select_all(sql)
        while @connection.more_results?
          results << @connection.next_result
        end
        results
      end
    end
  end
end

像这样打电话:

ActiveRecord::Base.connection.call_stored_procedure("CALL your_procedure('foo')")

0
2018-03-17 22:42