2009-09-01 84 views
1

讓我們假設:Ruby on Rails的SQL優化

網絡has_many頻道 網絡has_many暱稱

通道has_many消息 通道belongs_to網絡

暱稱has_many消息 暱稱belongs_to網絡

附加LY:

頻道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') 

正如你可以看到它查詢頻道表兩次。我只是好奇爲什麼會發生這種情況。當我第一次找到頻道時,它正在做「SELECT *」。似乎沒有必要再次查詢它的ID,因爲它已經是已知的。同樣,它爲什麼還要查詢網絡表。這似乎也不需要,因爲創建新消息記錄時不需要任何網絡相關。

回答

2

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來擺脫查詢。