Domanda

Perché non si dispone di una chiave esterna in una polimorfici associazione, come rappresentato di seguito come Rotaie modello?

class Comment < ActiveRecord::Base
  belongs_to :commentable, :polymorphic => true
end

class Article < ActiveRecord::Base
  has_many :comments, :as => :commentable
end

class Photo < ActiveRecord::Base
  has_many :comments, :as => :commentable
  #...
end

class Event < ActiveRecord::Base
  has_many :comments, :as => :commentable
end
È stato utile?

Soluzione

Una chiave esterna deve fare riferimento solo a una tabella padre.Questo è fondamentale sia per la sintassi SQL e teoria relazionale.

Polimorfi Associazione è quando una colonna può fare riferimento a uno dei due o più tabelle padre.Non c'è modo si può dichiarare che il vincolo in SQL.

Il Polimorfico Associazioni design rompe le regole di progettazione di un database relazionale.Io non consiglio di utilizzarlo.

Ci sono diverse alternative:

  • Esclusiva Archi: Creare più colonne di chiave esterna, ciascuno dei quali fa riferimento a un genitore.Applicare esattamente una di queste chiavi esterne possono essere non NULLO.

  • Invertire il Rapporto: Utilizzare tre molti-a-molti tavoli, ogni riferimenti Commenti e i rispettivi genitori.

  • Calcestruzzo Supertable: Invece l'implicita "commentable" superclasse, creare una vera e propria tabella che ciascun genitore tabelle di riferimento.Quindi collegare i Commenti che supertable.Pseudo-guide di codice dovrebbe essere qualcosa di simile (io non sono un Guide utente, in modo da trattare questo come una linea guida, non letterale), del codice):

    class Commentable < ActiveRecord::Base
      has_many :comments
    end
    
    class Comment < ActiveRecord::Base
      belongs_to :commentable
    end
    
    class Article < ActiveRecord::Base
      belongs_to :commentable
    end
    
    class Photo < ActiveRecord::Base
      belongs_to :commentable
    end
    
    class Event < ActiveRecord::Base
      belongs_to :commentable
    end
    

Anche io copertura polimorfici associazioni nella mia presentazione Pratico Object-Oriented Modelli in SQL, e il mio libro SQL Antipatterns:Evitando le Trappole della Programmazione di Database.


Re il tuo commento:Sì, so che c'è un'altra colonna note con il nome della tabella che la chiave esterna presumibilmente punti.Questo disegno non è supportato da chiavi esterne in SQL.

Cosa succede, per esempio, se inserisci un Commento nome e "Video" come il nome del padre tabella Comment?Nessuna tabella denominata "Video" esiste.Dovrebbe inserto essere interrotta con un errore?Cosa vincolo è violato?Come funziona il RDBMS sapere che questa colonna si suppone che il nome di una tabella esistente?Come fa a gestire e minuscole, i nomi di tabella?

Allo stesso modo, se si rilascia l' Events tabella, ma è necessario che le righe in Comments che indicano gli Eventi come il loro padre, quello che dovrebbe essere il risultato?Dovrebbe drop table essere interrotta?Dovrebbe righe Comments essere isolati?Dovrebbero cambiare per fare riferimento a un'altra tabella esistente come Articles?Do i valori di id che consente di scegliere Events alcun senso quando si punta a Articles?

Questi dilemmi sono tutti dovuti al fatto che Polimorfici Associazioni dipende dall'utilizzo dati (es.un valore di stringa) per fare riferimento ai metadati (nome di una tabella).Questo non è supportato da SQL.I dati e i metadati sono separati.


Sto avendo un momento difficile avvolgere la mia testa intorno al vostro "Concrete Supertable" la proposta di.

  • Definire Commentable come un vero tabella SQL, non solo aggettivo i Rails definizione del modello.No le altre colonne sono necessari.

    CREATE TABLE Commentable (
      id INT AUTO_INCREMENT PRIMARY KEY
    ) TYPE=InnoDB;
    
  • Definire le tabelle Articles, Photos, e Events come "sottoclassi" di Commentable, rendendo le proprie chiave primaria essere anche una chiave esterna che fa riferimento Commentable.

    CREATE TABLE Articles (
      id INT PRIMARY KEY, -- not auto-increment
      FOREIGN KEY (id) REFERENCES Commentable(id)
    ) TYPE=InnoDB;
    
    -- similar for Photos and Events.
    
  • Definire il Comments tabella con una chiave esterna Commentable.

    CREATE TABLE Comments (
      id INT PRIMARY KEY AUTO_INCREMENT,
      commentable_id INT NOT NULL,
      FOREIGN KEY (commentable_id) REFERENCES Commentable(id)
    ) TYPE=InnoDB;
    
  • Quando si desidera creare un Article (per esempio), si deve creare una nuova riga nella Commentable troppo.Così anche per Photos e Events.

    INSERT INTO Commentable (id) VALUES (DEFAULT); -- generate a new id 1
    INSERT INTO Articles (id, ...) VALUES ( LAST_INSERT_ID(), ... );
    
    INSERT INTO Commentable (id) VALUES (DEFAULT); -- generate a new id 2
    INSERT INTO Photos (id, ...) VALUES ( LAST_INSERT_ID(), ... );
    
    INSERT INTO Commentable (id) VALUES (DEFAULT); -- generate a new id 3
    INSERT INTO Events (id, ...) VALUES ( LAST_INSERT_ID(), ... );
    
  • Quando si desidera creare un Comment, utilizzare un valore che esiste in Commentable.

    INSERT INTO Comments (id, commentable_id, ...)
    VALUES (DEFAULT, 2, ...);
    
  • Quando si desidera eseguire la query commenti di un dato Photo, fare qualche join:

    SELECT * FROM Photos p JOIN Commentable t ON (p.id = t.id)
    LEFT OUTER JOIN Comments c ON (t.id = c.commentable_id)
    WHERE p.id = 2;
    
  • Quando si hanno solo l'id di un commento e si desidera trovare quello che commentable risorsa è un commento.Per questo, si potrebbe scoprire che è utile per il Commentable tabella di designare quale risorsa di riferimento.

    SELECT commentable_id, commentable_type FROM Commentable t
    JOIN Comments c ON (t.id = c.commentable_id)
    WHERE c.id = 42;
    

    Quindi è necessario eseguire una seconda query per ottenere i dati dalla rispettiva tabella risorse (Foto, Articoli, etc.), dopo la scoperta da commentable_type la tabella a cui unirti.Non è possibile farlo nella stessa query, perché SQL richiede che le tabelle di essere nominato esplicitamente;non è possibile aderire a una tabella determinato dai risultati dei dati nella stessa query.

Certo, alcuni di questi passaggi rompere le convenzioni utilizzate dai Binari.Ma le Guide le convenzioni sono sbagliato con il rispetto per la corretta progettazione di database relazionali.

Altri suggerimenti

Bill Karwin è corretto che le chiavi esterne non possono essere utilizzati con i rapporti polimorfi a causa di SQL in realtà non avendo un concetto nativo relazioni polimorfiche. Ma se il vostro obiettivo di avere una chiave esterna è quella di applicare l'integrità referenziale è possibile simulare via trigger. Questo diventa DB specifico, ma sotto è alcuni trigger recenti che ho creato per simulare il comportamento a cascata cancellare di una chiave esterna in una relazione polimorfica:

CREATE FUNCTION delete_related_brokerage_subscribers() RETURNS trigger AS $$
  BEGIN
    DELETE FROM subscribers
    WHERE referrer_type = 'Brokerage' AND referrer_id = OLD.id;
    RETURN NULL;
  END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER cascade_brokerage_subscriber_delete
AFTER DELETE ON brokerages
FOR EACH ROW EXECUTE PROCEDURE delete_related_brokerage_subscribers();


CREATE FUNCTION delete_related_agent_subscribers() RETURNS trigger AS $$
  BEGIN
    DELETE FROM subscribers
    WHERE referrer_type = 'Agent' AND referrer_id = OLD.id;
    RETURN NULL;
  END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER cascade_agent_subscriber_delete
AFTER DELETE ON agents
FOR EACH ROW EXECUTE PROCEDURE delete_related_agent_subscribers();

Nel mio codice di un record nella tabella brokerages o un record nella tabella agents può riferirsi a un record nella tabella subscribers.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top