Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Chevrolet doesn't make a Mustang. Ford didn't make a Mustang in 1960. Your structure will allow a lot of nonsense. </p> <p>The issue isn't that every column is a foreign key; there's nothing necessarily wrong with that. The issue is that the foreign keys are wrong. </p> <blockquote> <p>I split that into a tables of Makes (Ford, Chevrolet, BMW, etc) and Models (Impala, Camaro, F-150, etc) and Years (1920, ... 2012) and Engines (327, 350, etc).</p> </blockquote> <p>And that's why they're wrong. When you normalize a relation, you <em>start</em> with the relation, identify the candidate keys, and work out the functional dependencies. Just making single-column "lookup" tables for every column isn't normalization, and it doesn't constrain your data in the way that's needed. (And in this particular case, the constraints are the missing piece, not normalization to 5NF.)</p> <pre><code>Make Model Yr Engine -- Ford F-150 2012 3.7L V6 Ford F-150 2012 3.5L V6 EcoBoost Ford F-150 2012 5.0L V8 Ford F-150 2012 6.2L V8 Ford F-150 2011 3.7L V6 Ford F-150 2011 3.5L V6 EcoBoost Ford F-150 2011 5.0L V8 Ford F-150 2011 6.2L V8 Chevrolet Camaro 2012 3.6L V6 Chevrolet Camaro 2011 3.6L V6 Chevrolet Camaro 2011 6.2L V8 Chevrolet Camaro 1980 229ci V6 Chevrolet Camaro 1980 267ci V8 Chevrolet Camaro 1980 305ci V8 Cadillac CTS 2004 3.6L V6 Vauxhall Astra 1979 1.3L Vauxhall Astra 1979 1.6L Vauxhall Astra 1979 1.8L Opel Astra 1979 1.5L Opel Astra 1979 2.0L </code></pre> <p>It should be clear that the only candidate key is {Make, Model, Yr, Engine}. So this table is all key, and it has no non-prime attributes.</p> <p>To add "lookup" tables as constraints on data, it's not good enough to say that in the first column you have to choose from {Ford, Chevrolet, Cadillac, Vauxhall, Opel}, and in the second column you have to choose from {F-150, Camaro, CTS, Astra}. The right "lookup" table for make and model includes both make and model; you choose from {Ford F-150, Chevrolet Camaro, Cadillac CTS, Vauxhall Astra, Opel Astra}. (In this case, it goes even further. See the table model_years below.)</p> <pre><code>create table makes ( make varchar(25) primary key ); insert into makes values ('Ford'), ('Chevrolet'), ('Cadillac'), ('Vauxhall'), ('Opel'); create table models ( make varchar(25) not null references makes (make), model varchar(25) not null, primary key (make, model) ); insert into models values ('Ford', 'F-150'), ('Chevrolet', 'Camaro'), ('Cadillac', 'CTS'), ('Vauxhall', 'Astra'), ('Opel', 'Astra'); create table model_years ( make varchar(25) not null, model varchar(25) not null, year integer not null check (year between 1900 and 2050), primary key (make, model, year), foreign key (make, model) references models (make, model) ); insert into model_years values ('Ford', 'F-150', 2012), ('Ford', 'F-150', 2011), ('Chevrolet', 'Camaro', 2012), ('Chevrolet', 'Camaro', 2011), ('Chevrolet', 'Camaro', 1980), ('Cadillac', 'CTS', 2004), ('Vauxhall', 'Astra', 1979), ('Opel', 'Astra', 1979); create table model_year_engines ( make varchar(25) not null, model varchar(25) not null, year integer not null, engine varchar(25) not null, primary key (make, model, year, engine), foreign key (make, model, year) references model_years (make, model, year) ); insert into model_year_engines values ('Ford', 'F-150', 2012, '3.7L V6'), ('Ford', 'F-150', 2012, '3.5L V6 EcoBoost'), ('Ford', 'F-150', 2012, '5.0L V8'), ('Ford', 'F-150', 2012, '6.2L V8'), ('Ford', 'F-150', 2011, '3.7L V6'), ('Ford', 'F-150', 2011, '3.5L V6 EcoBoost'), ('Ford', 'F-150', 2011, '5.0L V8'), ('Ford', 'F-150', 2011, '6.2L V8'), ('Chevrolet', 'Camaro', 2012, '3.6L V6'), ('Chevrolet', 'Camaro', 2011, '3.6L V6'), ('Chevrolet', 'Camaro', 2011, '6.2L V8'), ('Chevrolet', 'Camaro', 1980, '229ci V6'), ('Chevrolet', 'Camaro', 1980, '267ci V8'), ('Chevrolet', 'Camaro', 1980, '305ci V8'), ('Cadillac', 'CTS', 2004, '3.6L V6'), ('Vauxhall', 'Astra', 1979, '1.3L'), ('Vauxhall', 'Astra', 1979, '1.6L'), ('Vauxhall', 'Astra', 1979, '1.8L'), ('Opel', 'Astra', 1979, '1.5L'), ('Opel', 'Astra', 1979, '2.0L'); </code></pre> <p>No engine can go in this table unless its row first exists in model_years. No year can go in model_years unless its row first exists in models. And no row can go in models unless its row first exists in makes. </p> <p>You can make a good case for using <code>ON UPDATE CASCADE</code> in a schema like this. You can also make a good case for not using it. Oracle doesn't support <code>ON UPDATE CASCADE</code>, which is one reason you see ID numbers peppering Oracle tables, and why you sometimes see people say "Primary key values must <em>never</em> change." </p> <p>These are the kinds of tables you need to implement your known requirements.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload