【问题标题】:Rails ActiveRecord use SQL function as value for an attribute on createRails ActiveRecord 使用 SQL 函数作为创建属性的值
【发布时间】:2019-12-03 12:38:55
【问题描述】:

我在 6 号轨道上,

我想在创建记录时使用 sql 函数作为某些属性的值。

该 sql 函数将是动态的,如果它是静态的,则使用它作为默认值是有意义的。

我能做的是写一个before_create 回调,类似于:

def populate_value_from_sql
  self.attr_name = self.connection.select_value('SELECT dynamic_function()')
end 

但这会导致额外的 sql 查询,我想避免额外的查询,只需编译 inset 的 sql 以使用 sql 函数而不是硬编码值。 而且我想继续使用 Rails 方式,这样我的验证和回调等仍然可以正常工作,我不想采用完整的原始 sql 路由。

谢谢,

【问题讨论】:

  • 在 postgres 上,您可以使用触发器在数据库级别执行此操作。 postgresql.org/docs/9.1/sql-createtrigger.html
  • 我必须使用我想调用的函数的动态名称,为清楚起见,这些将是序列名称。所以我不能使用触发器,这些触发器名称将来自数据库之外
  • 你不能将函数的字符串值传递给触发器函数,这样你就可以在触发器函数中插入函数。然后在实际触发中,你引用触发函数?
  • 我不确定我是否正确理解了你@MikeHeft,但我认为你所说的是传递一个动态触发的值,而不是根据该值决定调用哪个函数?如果是这样,如果您可以证明您的解决方案作为答案,那就太好了,如果它非常适合我的用例,我对触发器没有任何反对意见。虽然我更喜欢没有这些的东西。

标签: ruby-on-rails activerecord rails-activerecord


【解决方案1】:

我还没有用触发器做过,但我发现这个answer 我也打算试试。但我不明白为什么它不起作用。我更改了答案以尝试使用%1$s 语法进行插值,因此您不必一遍又一遍地列出相同的变量。

CREATE OR REPLACE FUNCTION trigen(tbl text) RETURNS void AS $T1$
BEGIN
    EXECUTE format(
    'CREATE FUNCTION %1$s_function_name() RETURNS TRIGGER AS $T2$
    BEGIN
        UPDATE insertions SET n = n + 1 WHERE tablename = %1$s;
        RETURN NEW;
    END
    $T2$ LANGUAGE plpgsql', $1, quote_nullable($1));

    EXECUTE format('CREATE TRIGGER %1$s_inCnt BEFORE INSERT ON %s
    FOR EACH ROW EXECUTE PROCEDURE %1$s_insertCnt();', $1);
    END
$T1$ LANGUAGE plpgsql;

我不知道如何将动态名称传递给方法,但 Rails 中的示例方法可以是:

def generate_trigger(table)
  ActiveRecord::Base.connection.execute("SELECT trigen(#{table});")
end

要在你的数据库中创建触发函数,你可以创建一个迁移

def change
  reversible do |dir|
    dir.up
      execute <<-SQL
        #trigen function snippet
      SQL
    end

    dir.down do
     execute <<-SQL
       DROP FUNCTION IF EXISTS trigen() CASCADE
      SQL
    end
  end
end

为了删除触发器和触发函数本身,您可以在 Rails 代码中创建另一个方法,

def drop_trigger(trigger)
  ActiveRecord::Base.connection.base.execute("DROP TRIGGER IF EXISTS #{trigger} CASCADE;")
end

def drop_function(function)
  ActiveRecord::Base.connection.base.execute("DROP FUNCTION IF EXISTS #{function}() CASCADE;")
end

【讨论】:

  • 感谢您的触发器,WRT 到 rails 的示例以及它如何与 rails 一起工作,它的回调会有所帮助。
  • 感谢您的更新。似乎我们将在创建模型后触发这个 DB 函数,这意味着 2 个 DB 调用,在这种情况下,它与我已经拥有的没有什么不同,并且不能解决我的 2 个 DB 调用问题。除非我误解了您对解决方案的理解
猜你喜欢
  • 1970-01-01
  • 2010-12-05
  • 1970-01-01
  • 1970-01-01
  • 2011-02-20
  • 1970-01-01
  • 2013-11-09
  • 1970-01-01
  • 2014-06-26
相关资源
最近更新 更多