我有这两次迁移:
class CreateBreakdowns < ActiveRecord::Migration[5.1]
def change
create_table :breakdowns do |t|
t.date :date
t.string :content
t.decimal :amount
t.timestamps
end
end
end
class CreateDailyTotals < ActiveRecord::Migration[5.1]
def change
create_table :daily_totals do |t|
t.date :date
t.decimal :total
t.timestamps
end
end
end
而且,我想在迁移中添加以下触发器:
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;
目的是当用户将数据插入故障表时,然后执行触发器来更新daily_totals。
我的问题是我必须添加哪个迁移文件才能添加此触发器?并且,如果我将此触发器添加到迁移文件中,它是否会起作用?
我用我的测试数据库尝试了触发器并且它可以工作,它只是不使用rails。我再次使用sqlite3,而不是mySQL或Oracle SQL。
谢谢!
答案 0 :(得分:1)
您可以在迁移中执行原始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中的程序有类似的用例,但不支持。
我创建了一个StoreProcedure
问题:
# 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
对于触发器你绝对可以做同样的事情!