sql - 在 Rails/ActiveRecord 中命名 SQL 查询

标签 sql ruby-on-rails rails-activerecord apm

将 Rails 与 ActiveRecord(和 PostgreSQL)一起使用时,执行“简单”查询会为它们添加一个名称,例如打电话

Article.all
# => Article Load (2.6ms)  SELECT "articles".* FROM "articles"
命名查询 Article Load .但是,当执行稍微复杂的查询时,不会生成名称,例如
Article.group(:article_group_id).count
# => (1.2ms)  SELECT COUNT(*) AS count_all, "articles"."article_group_id" AS articles_article_group_id FROM "articles" GROUP BY "articles"."article_group_id"
如果使用 execute 执行自定义查询,我可以添加名称方法:
ActiveRecord::Base.connection.execute("SELECT * FROM articles", "My custom query name")
# => My custom query name (2.5ms)  SELECT * FROM articles
但是有没有办法将自定义名称添加到使用 ActiveRecord 方法构建的查询中?
如果您想知道为什么:该名称可用于各种监控,例如在 AppSignal 中查看慢查询时。

最佳答案

由于您只想自定义查询名称以进行监控,所以我认为您只需要更改 query nameActiveRecord::ConnectionAdapters#log方法,该方法是记录执行的sql查询,包括query name .
这是我的解决方案:

# lib/active_record/base.rb
# note that MUST be base.rb
# otherwise you need to add initializer to extend Rails core
#
module ActiveRecord
  module ConnectionAdapters
    class AbstractAdapter
      attr_accessor :log_tag

      private

      alias old_log log
      def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil)
        if name != 'SCHEMA'
          name = @log_tag
          @log_tag = nil # reset
        end

        old_log(sql, name, binds, type_casted_binds, statement_name) do
          yield
        end
      end
    end
  end

  module QueryMethods
    def log_tag(tag_name) # class method
      spawn.log_tag(tag_name)
      self
    end
  end

  module Querying
    delegate :log_tag, to: :all
  end

  class Relation
    def log_tag(tag_name) # instance method
      conn = klass.connection
      conn.log_tag = tag_name
      self
    end
  end
end
演示
Task.log_tag("DEMO").group(:status).count
# DEMO (0.7ms)  SELECT COUNT(*) AS count_all, "tasks"."status" AS tasks_status FROM "tasks" GROUP BY "tasks"."status"

Task.where(status: 6).log_tag("SIX").first(20)
# SIX (0.8ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."status" = ? ORDER BY "tasks"."id" ASC LIMIT ?

Task.where(status: 6).first(20)
# (0.8ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."status" = ? ORDER BY "tasks"."id" ASC LIMIT ?
备注
如果您想修复 query name对于特定查询,可以使用hash,key是整个特定sql字符串(或者整个sql的hash,比如Rails核心缓存查询的方式:query_signature = ActiveSupport::Digest.hexdigest(to_sql)),值为query name你要。
# set up before hand
ActiveRecord::ConnectionAdapters::LogTags[Product.where...to_sql] = "DEMO"
# AbstractAdapter
LogTags = Hash.new
def log(sql, name...)
  name = LogTags[sql]
  # ...
end 

关于sql - 在 Rails/ActiveRecord 中命名 SQL 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67328852/

相关文章:

sql - SQL 中的组合优化匹配

mysql - SQL 计算列中以逗号分隔的子字符串出现次数最多的次数

ruby-on-rails - to_formatted_s(:long_ordinal) returning 00:00 at the end

ruby-on-rails - Faraday::ConnectionFailed in Devise::SessionsController#create

ruby-on-rails - Carrierwave 在回调中返回 tmp 文件的路径而不是实际路径

ruby-on-rails - rails : ActiveRecord where vs merge

ruby-on-rails - Rails 3 合并作用域与连接

php - 对大型 MySQL 表的多个查询

mysql - 如何从每组中选择前两行

ruby-on-rails - 如何向 ActiveRecord 添加新属性