【问题标题】:Ruby on Rails SQL OptimizationsRuby on Rails SQL 优化
【发布时间】:2009-09-01 20:33:00
【问题描述】:

假设:

网络has_many频道 网络has_many昵称

频道has_many 消息 频道belongs_to网络

昵称has_many 留言 昵称belongs_to网络

另外:

频道has_many 昵称,:through => :messages 昵称has_many频道,:through => :messages

我这里有这个记录消息的方法。非常简单,它将对其进行初始化并将其链接到所需的关联模型。

def log_msg(userinfo, target, message, type = nil, time = nil)
  # methods not shown yet obvious
  return unless chan = find_channel(target) 
  return unless nick = find_nickname(userinfo)

  msg = Message.new(:message => message)         

  msg.created_at = time unless time.nil?

  if !type.nil? && msg.respond_to?(type)
    msg.send("#{type}=", true)
  end

  msg.channel = chan
  msg.nickname = nick

  msg.save
end

以下是上述代码生成的所有查询。

  Channel Load (0.0ms) SELECT * FROM "channels" WHERE ("channels"."name" = '#main_chat') AND ("channels".network_id = 1) LIMIT 1←[0m

  Nickname Load (0.0ms) SELECT * FROM "nicknames" WHERE ("nicknames"."nickname" = 'bob_saget') LIMIT 1

  Channel Load (0.0ms) SELECT "channels".id FROM "channels" WHERE ("channels"."name" = '#main_chat' AND "channels".network_id = 1 AND "channels".id <> 1) LIMIT 1

  Network Load (0.0ms) SELECT * FROM "networks" WHERE ("networks"."id" = 1)

  Message Create (0.0ms) INSERT INTO "messages" ("message", "is_action", "channel_id", "is_part", "updated_at", "is_nick", "is_mode", "is_topic", "is_ban", "nickname_id", "is_init", "is_quit", "is_join", "is_kick", "created_at") VALUES(NULL, NULL, 1, NULL, '2009-09-01 20:21:47', NULL, NULL, NULL, NULL, 1, 't', NULL, NULL, NULL, '2009-09-01 20:21:47')

如您所见,它正在查询 Channel 表两次。我只是好奇为什么会这样。当我第一次找到频道时,它正在执行“SELECT *”。似乎没有必要再次查询它的 ID,因为它已经知道了。同样,它为什么还要查询网络表。这似乎也不需要,因为在创建新消息记录时不需要任何与网络相关的内容。

【问题讨论】:

    标签: sql ruby-on-rails


    【解决方案1】:

    ActiveRecord 选择了两次通道,因为两个“通道加载”查询不同。它们有不同的用途。

    第一个查询是您的 find_channel 方法,第二个查询几乎肯定来自以下验证:

    class Channel < ActiveRecord::Base
      validates_uniqueness_of :name, :scope => :network_id
      validates_associated :network
    end
    
    class Message < ActiveRecord::Base
      validates_associated :channel
    end
    

    保存消息会触发通道验证,并且此查询...

    Channel Load (0.0ms) SELECT "channels".id FROM "channels" WHERE ("channels"."name" = '#main_chat' AND "channels".network_id = 1 AND "channels".id <> 1) LIMIT 1
    

    ...然后触发验证网络,这个查询...

    Network Load (0.0ms) SELECT * FROM "networks" WHERE ("networks"."id" = 1)
    

    似乎按预期工作。但是,您当然可以通过不依赖 ActiveRecord 来验证关联模型的存在来消除这些无关查询。

    利用数据库中的外键来确保您的消息和频道(以及昵称)与实际存在的记录相关联。然后,您可以通过将您的 validates_associated 替换为 validates_presence_of :association_id 来摆脱查询。

    【讨论】:

      猜你喜欢
      • 2010-10-28
      • 1970-01-01
      • 2018-05-08
      • 1970-01-01
      • 1970-01-01
      • 2011-07-03
      • 2015-08-05
      • 2012-12-16
      • 1970-01-01
      相关资源
      最近更新 更多