您可以在迁移中执行原始 SQL:
class CreateUpdateTotalTrigger < ActiveRecord::Migration[5.1]
def change
execute <<-SQL
CREATE TRIGGER update_total AFTER INSERT ON breakdowns
BEGIN
UPDATE daily_totals
SET total = (SELECT SUM(amount) FROM breakdowns WHERE New.date = date)
WHERE New.date = date;
END;
SQL
end
end
我对 PostgreSQL 中不受支持的过程有类似的用例。
我创建了一个存储过程关注点:
# app/procedures/concerns/stored_procedure.rb
module StoredProcedure
extend ActiveSupport::Concern
included do
self.abstract_class = true
end
module ClassMethods
# without return value
def execute_sp(sql, *bindings)
perform_sp(:execute, sql, *bindings)
end
# select many return values
def fetch_sp(sql, *bindings)
perform_sp(:select_all, sql, *bindings)
end
# select single return value
def fetch_sp_val(sql, *bindings)
perform_sp(:select_value, sql, *bindings)
end
protected
def perform_sp(method, sql, *bindings)
sql = send(:sanitize_sql_array, bindings.unshift(sql)) if bindings.any?
connection.send(method, sql)
end
end
end
然后我为存储过程创建了一个 ActiveRecord 类:
# app/procedures/sp_active_record.rb
class SpActiveRecord < ActiveRecord::Base
self.abstract_class = true
class << self
def drop_function(function_name, *types)
connection.execute("DROP FUNCTION IF EXISTS #{function_name}(#{types ? types.join(', ') : nil});")
end
def create_function
connection.execute(minify("CREATE OR REPLACE FUNCTION #{yield};"))
end
private
def minify(string)
string.tr("\n", ' ').gsub(/\s+/, ' ')
end
end
end
最后我为每个过程创建一个类:
class SpYoutubeChannel < SpActiveRecord
include StoredProcedure
class << self
def show_interactions_by(interval, start_date = nil, end_date = nil)
fetch_sp(minify(interactions_statement), interval, start_date, end_date).map { |r| OpenStruct.new(r) }
end
def interactions_statement
<<-SQL
----
SQL
end
end
end
我可以在迁移中使用我的助手
class CreateYoutubeInteractionsF < ActiveRecord::Migration[5.0]
def up
SpActiveRecord.drop_function(:youtube_interactions_f, 'int', 'date', 'date')
SpActiveRecord.create_function do
<<-SQL
youtube_interactions_f(
interval_size int, start_period date, end_period date
)
---
SQL
end
end
def down
SpActiveRecord.drop_function(:youtube_interactions_f, 'int', 'date', 'date')
end
end
您绝对可以为触发器做同样的事情!