Search code examples
sqlpostgresqlforeign-keysone-to-manycomposite-primary-key

SQL: Is creating multiple foreign keys with one statement equivalent to creating them with one statement?


For example let's have the following table definition:

CREATE TABLE table1 
(
    id INT UNIQUE,
    name VARCHAR(100) UNIQUE,
    description VARCHAR(100),

    PRIMARY KEY (id, name)
);

Now I would like to create another table which would have a foreign key to the above composite primary key. Would the following two statements be equivalent?

1)

CREATE TABLE table2 
(
     id INT PRIMARY KEY,
     table1_id INT,
     table1_name VARCHAR(100),

     FOREIGN KEY (table1_id) REFERENCES table1(id),
     FOREIGN KEY (table1_name) REFERENCES table1(name)
);

2)

CREATE TABLE table2 
(
     id INT PRIMARY KEY,
     table1_id INT,
     table1_name VARCHAR(100),

     FOREIGN KEY (table1_id, table1_name) REFERENCES table1(id, name),
);

I noticed that behind the scenes Postgre SQL creates two FK db objects in the case of 1) and one FK object in the case of 2). Would everything work the same anyway?


Solution

  • Not at all. A foreign key reference should be to the primary key. In this case you have a composite primary key (although that is probably not needed, see below). Although foreign key references to unique keys are allowed (and some databases even allow foreign key references to any indexed columns), that is not a best practice.

    When you use a composite primary key (your second example) you are guaranteeing that id/name are aligned in the first table. When you use separate references (your first example), you do not know that they are aligned, so the id could refer to one row in table1 and the name to another row. I doubt that is your intention.

    In any case, repeating redundant data among tables is a bad practice. The better data model is:

    CREATE TABLE table1 (
      id INT PRIMARY KEY,
      name VARCHAR(100) UNIQUE,
      description VARCHAR(100),
    );
    
    CREATE TABLE table2 (
      id INT PRIMARY KEY,
      table1_id INT,
      FOREIGN KEY (table1_id) REFERENCES table1(id)
    );
    

    Then, if you want the corresponding name, look up the name in the first table.

    As a note, in Postgres, I would expect the INT to really be SERIAL so the database assigns a unique, increasing value when you insert new rows.

    If you actually want two references to table1 then use two id references:

    CREATE TABLE table2 (
      id INT PRIMARY KEY,
      table1_id INT,
      table1_id_2 INT,
      FOREIGN KEY (table1_id) REFERENCES table1(id),
      FOREIGN KEY (table1_id_2) REFERENCES table1(id)
    );