2009-09-01 9 views
1

laisse supposer:Ruby on Rails SQL Optimisations

réseau has_many Canaux réseau has_many Surnoms

Chaîne has_many Messages Canal belongs_to Réseau

Pseudo has_many Messages Pseudo belongs_to Réseau

supplémentaires ly:

Canal has_many Surnoms, :through => :messages Pseudo has_many Chaîne, :through => :messages

Je cette méthode ici que les messages journaux. Très simple, il va l'initialiser et le lier aux modèles associés nécessaires.

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 

Voici toutes les requêtes que génère le code ci-dessus.

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

Comme vous pouvez le voir, il interroge deux fois la table des canaux. Je suis juste curieux de savoir pourquoi cela se passe. Quand je trouve la chaîne pour la première fois, elle fait "SELECT *". Il semble inutile d'interroger à nouveau pour son ID puisqu'il est déjà connu. De même, pourquoi demande-t-il également la table réseau. Cela semble également inutile puisque tout ce qui est lié au réseau n'est pas nécessaire lors de la création de nouveaux enregistrements de messages.

Répondre

2

ActiveRecord sélectionne deux fois les canaux, car les deux requêtes "Channels Load" sont différentes. Ils servent des objectifs différents.

La première requête est votre méthode de find_channel, et la deuxième requête est presque certainement des validations suivantes:

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

class Message < ActiveRecord::Base 
    validates_associated :channel 
end 

Sauvegarder le message déclenche le canal, et cette validation requête ...

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 

... et déclenche alors la validation du réseau, et cette requête ...

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

Semble fonctionner comme prévu. Cependant, vous pouvez certainement supprimer ces requêtes superflues en ne s'appuyant pas sur ActiveRecord pour valider la présence des modèles associés. Tirez parti des clés étrangères de votre base de données pour vous assurer que vos messages et canaux (et pseudonymes) sont associés aux enregistrements qui existent réellement. Ensuite, vous pouvez vous débarrasser des requêtes en remplaçant votre validates_associated par validates_presence_of: association_id.