Designing a recipe database - sql

I'm making a simple recipe app that allows user to create and save their recipes. I'm using SQLite if that makes any difference.
My database knowledge is a bit rusty so I've been looking around for inspiration in order to design an appropriate database.
One example that users here seem to point to is the following schema by Barry Williams: http://www.databaseanswers.org/data_models/recipes/index.htm
This db is overly detailed so I decided to used only the following tables:
Recipe (recipe_id(PK), name, description)
Ingredient (ingredient_id(PK), name)
Recipe_Steps (recipe_id(PF), step_number(PK), instructions)
Recipe_Step_Ingredients(recipe_id(PF), step_number(PF), ingredient_id(PF), amount)
From my understanding, each ingredient is associated to a step in the recipe whereas I feel like each ingredient should be associated directly to a recipe and that the steps could be an attribute of the recipe table or in a Step table directly related to Recipe. Here would be my design:
Recipe (recipe_id(PK), name, description)
Ingredient (ingredient_id(PK), name)
Recipe_Steps (recipe_id(PF), step_number(PK), instructions)
Recipe_Ingredients (recipe_id(PF), ingredient_id(PF), amount)
Mr. Williams knows much more about databases than I do so I don't know if I'm misunderstanding his schema and screwing it up with my version or if this all comes down to personal preference in design.
Is my version of the schema correct, if not, would you please be able to guide me towards the appropriate answer?
Thanks in advance.

Here is the database schema I used to implement the AnyMeal recipe software:
PRAGMA user_version = 1;
BEGIN;
CREATE TABLE recipes(id INTEGER PRIMARY KEY, title VARCHAR(60) NOT NULL, servings INTEGER NOT NULL, servingsunit VARCHAR(40) NOT NULL);
CREATE TABLE categories(id INTEGER PRIMARY KEY, name VARCHAR(40) UNIQUE NOT NULL);
CREATE TABLE category(recipeid INTEGER NOT NULL, categoryid INTEGER NOT NULL, PRIMARY KEY(recipeid, categoryid), FOREIGN KEY(recipeid) REFERENCES recipes(id), FOREIGN KEY(categoryid) REFERENCES categories(id));
CREATE TABLE ingredients(id INTEGER PRIMARY KEY, name VARCHAR(60) UNIQUE NOT NULL);
CREATE TABLE ingredient(recipeid INTEGER NOT NULL, line INTEGER NOT NULL, amountint INTEGER NOT NULL, amountnum INTEGER NOT NULL, amountdenom INTEGER NOT NULL, amountfloat REAL NOT NULL, unit CHARACTER(2) NOT NULL, ingredientid INTEGER NOT NULL, PRIMARY KEY(recipeid, line), FOREIGN KEY(recipeid) REFERENCES recipes(id), FOREIGN KEY(ingredientid) REFERENCES ingredients(id));
CREATE TABLE instruction(recipeid INTEGER NOT NULL, line INTEGER NOT NULL, txt TEXT NOT NULL, PRIMARY KEY(recipeid, line), FOREIGN KEY(recipeid) REFERENCES recipes(id));
CREATE TABLE ingredientsection(recipeid INTEGER NOT NULL, line INTEGER NOT NULL, title VARCHAR(60) NOT NULL, PRIMARY KEY (recipeid, line), FOREIGN KEY(recipeid) REFERENCES recipes(id));
CREATE TABLE instructionsection(recipeid INTEGER NOT NULL, line INTEGER NOT NULL, title VARCHAR(60) NOT NULL, PRIMARY KEY (recipeid, line), FOREIGN KEY(recipeid) REFERENCES recipes(id));
COMMIT;

Related

References with PostgreSQL

I have this table:
CREATE TABLE cars_info.cars
(
id SERIAL,
owner_id INTEGER,
brand VARCHAR(50) NOT NULL,
model VARCHAR(50) NOT NULL,
color VARCHAR(50) NOT NULL,
register_number VARCHAR(50) NOT NULL,
created DATE NOT NULL,
PRIMARY KEY(id, brand, model, color, register_number, created),
CONSTRAINT fk_owner_id
FOREIGN KEY(owner_id)
REFERENCES persons_info.persons(id)
);
But when I tried create another table like this:
CREATE TABLE cars_info.violations
(
id SERIAL PRIMARY KEY,
car_id INTEGER NOT NULL,
message VARCHAR(100) NOT NULL,
active BOOLEAN NOT NULL,
CONSTRAINT fk_car_id
FOREIGN KEY(car_id)
REFERENCES cars_info.cars(id)
);
I get an error about that
Target external table "cars" does not have a unique constraint corresponding to the given keys
How can I fix that? I'm a beginner in SQL and don't know how to go about googling that
Your primary key definition for cars
PRIMARY KEY(id, brand, model, color, register_number, created)
makes no sense: The id column, being serial, is itself unique and it alone should be the primary key.
Delete your primary key definition and change the id column definition to:
id serial not null primary key
Unrelated, but best practice is to name table in the singular; name your tables car and violation rather than cars and violations

How should I structure my simple database?

I am creating a database (for data from an online game) in which I need to store Players and their Villages. Each Player has at least 1 village. All Villages are unique and every Village has exactly 1 owner(Player).
It is probably important to mention, that I intend to create a new table/tables every day since the online game in question releases an updated game world data file once every 24hours. Once a table is created, I will never have to make changes in it, I will only need to read data from it.
I came up with 2 solutions for now, but I dont know what the right approach is.
Solution1:
CREATE TYPE village_t AS (
id INTEGER,
x INTEGER,
y INTEGER,
village_id INTEGER,
village_name VARCHAR,
pop INTEGER,
region VARCHAR
);
CREATE TABLE players (
tribe INTEGER NOT NULL,
user_id INTEGER PRIMARY KEY,
user_name VARCHAR(30) UNIQUE NOT NULL,
aliance_id INTEGER NOT NULL,
aliance_name VARCHAR(30) NOT NULL,
villages village_t[]
);
Solution2:
CREATE TABLE players (
tribe INTEGER NOT NULL,
user_id INTEGER PRIMARY KEY,
user_name VARCHAR(30) UNIQUE NOT NULL,
aliance_id INTEGER NOT NULL,
aliance_name VARCHAR(30) NOT NULL
);
CREATE TABLE villages (
id INTEGER NOT NULL,
x INTEGER NOT NULL,
y INTEGER NOT NULL,
village_id INTEGER NOT NULL,
village_name VARCHAR NOT NULL,
user_id INTEGER,
CONSTRAINT fk_con FOREIGN KEY(user_id) REFERENCES players(user_id),
pop INTEGER NOT NULL,
region VARCHAR
);
CREATE INDEX idx ON villages(user_id);
You have a 1-n relationship. Each village ("n") has one owner ("1"). Your second solution is the canonical way of storing this information in a database.
Your first solution does not even enforce the notion that a village as one owner, so I'm not sure why you are considering it. The second imposes this condition as part of the data model.
Your second solution has set up the recommend foreign key and indexes for what you describe, by the way, so it looks fine.
If you need to read village data frequently, Solution 1 is not recommended. This is because the list of village ids design removes the scope of joining the person and village table
Solution 2 is the most preferred way of going about as it keeps all your options open (whether you would frequently need village data or not)
Another way of going about solution 2 is to create a cross-reference table for user_id and village_id
CREATE TABLE players (
tribe INTEGER NOT NULL,
user_id INTEGER PRIMARY KEY,
user_name VARCHAR(30) UNIQUE NOT NULL,
aliance_id INTEGER NOT NULL,
aliance_name VARCHAR(30) NOT NULL
);
CREATE TABLE villages (
id INTEGER NOT NULL,
x INTEGER NOT NULL,
y INTEGER NOT NULL,
village_id INTEGER NOT NULL,
village_name VARCHAR NOT NULL,
pop INTEGER NOT NULL,
region VARCHAR
);
CREATE TABLE xref_players_villages (
user_id INTEGER,
CONSTRAINT fk_con1 FOREIGN KEY(user_id) REFERENCES players(user_id),
village_id INTEGER,
CONSTRAINT fk_con2 FOREIGN KEY(village_id ) REFERENCES villages(village_id ),
);

there is no unique constraint matching given keys for referenced table in SQL

CREATE TABLE TEST( course_number INTEGER NOT NULL,
semester INTEGER NOT NULL,
time INTEGER NOT NULL,
room INTEGER NOT NULL,
day INTEGER NOT NULL,
credit_points INTEGER NOT NULL,
UNIQUE(course_number,semester),
CHECK(course_number>0),
CHECK(credit_points>0),
CHECK(room>0));
CREATE TABLE STUDENT (student_id INTEGER NOT NULL,
student_name text NOT NULL,
faculity text NOT NULL,
credit_points INTEGER NOT NULL,
UNIQUE(student_id),
CHECK(student_id>0),
CHECK(credit_points>=0));
CREATE TABLE STUDENT_REG
(student_id INTEGER NOT NULL,
course_number INTEGER NOT NULL,
semester INTEGER NOT NULL,
FOREIGN KEY (student_id) REFERENCES STUDENT(student_id),
FOREIGN KEY (course_number) REFERENCES TEST(course_number),
FOREIGN KEY (semester) REFERENCES TEST(semester));
I have three tables:
Test
Student
Student registration, it's purpose is to a student to a course.
I get this error when I compile the third table:
ERROR: there is no unique constraint matching given keys for referenced table "test"
I have no idea why, any help will be highly appreciated.
You want a compound foreign key rather than two distinct keys:
CREATE TABLE STUDENT_REG (
student_id INTEGER NOT NULL,
course_number INTEGER NOT NULL,
semester INTEGER NOT NULL,
FOREIGN KEY (student_id) REFERENCES STUDENT(student_id),
FOREIGN KEY (course_number semester) REFERENCES TEST(course_number, semester)
);
Why you need that is because table TEST as has compound unique key on these two columns:
UNIQUE(course_number,semester)
So for table STUDENT_REG to unambigously refer to a row in TEST, you need the combination of both columns, which means a 2-columns foreign key rather than two distinct foreign keys.

create table syntax error in Microsoft Access?

Here is my code, i'm trying to make multiple tables:
Create Table Order_t
(
Id AutoIncrement Not Null,
OrderDate DateTime Not Null,
CustId Int Not Null,
Primary Key(Id),
Foreign Key(CustId) References Customer_t(Id)
(;
Create Table PersonRole_t
(
PersonRoleID Autoincrement Not Null,
Person_ID int Not Null,
Primary Key(PersonRoleID, Person_ID),
Foreign Key(Person_ID) References Person_T(Person_ID)
(;
Create Table Product_t
(
Id Text(10) Not Null,
Name Text(30) Not Null,
Description Text(30),
Finish Text(30),
UnitPrice Currency Not Null,
Primary Key(Id)
) ;
Whenever I run it in Microsoft Access, I get an error in the CREATE TABLE statement (it highlights the PersonRole_T table definition). Not sure what to do, rather new to SQL.
Use CLOSE parenthesis at the end of CREATE Table statement instead of OPEN parenthesis
Create Table Order_t
(
Id AutoIncrement Not Null,
OrderDate DateTime Not Null,
CustId Int Not Null,
Primary Key(Id),
Foreign Key(CustId) References Customer_t(Id)
); -- Not (;
Create Table PersonRole_t
(
PersonRoleID Autoincrement Not Null,
Person_ID int Not Null,
Primary Key(PersonRoleID, Person_ID),
Foreign Key(Person_ID) References Person_T(Person_ID)
); -- Not (;
The table Person_T with a key of Person_ID needs to exist before the References Person_T(Person_ID) statement can execute. Based on your naming convention, I would guess the statement should rather be References Person_T(Id).
Consider changing your naming convention so that a data element does not change its name depending its location realtive to tables/views. Also consider whether the _t suffix is worth the bother.
#Prdp's point about the close parens is valid too.

Check constraint in database db2

I'm designing a database that has, amongst others, the following entities:
create table Director(
ID integer not null,
YearsExperience integer not null,
StudioAffiliation varchar(30),
NetWorth REAL,
primary key (ID)
);
create table Movie(
ID varchar(20) not null,
Title varchar(40) not null,
Genre char(1) not null,
ReleaseDate date not null,
Earning real not null,
primary key (ID)
);
Now Director and Movie have 2 relationship: Direct and WonAward. The later is to record whether a director has won an award or more with the movie he/she directed:
create table Directs(
DirectorID integer not null,
MovieID varchar(20) not null,
primary key (DirectorID, MovieID),
foreign key (DirectorID) references Director (ID) on delete cascade,
foreign key (MovieID) references Movie (ID) on delete cascade
);
create table WonAward(
DirectorID integer not null,
MovieID varchar(20) not null,
AwardName varchar(30) not null,
Year integer not null,
Budget REAL not null,
primary key (DirectorID, MovieID),
foreign key (DirectorID) references Director (ID) on delete cascade,
foreign key (MovieID) references Movie (ID) on delete cascade
);
I need to make sure that a Director gets awards for only movies he/she has directed. Now I have tried using Check to achieve that but it keeps giving me error for the following 2 condition at the end of the WonAward relationship:
check (DirectorID = (SELECT DirectorID FROM Directs WHERE Directs.MovieID = MovieID)),
check (MovieID = (SELECT MovieID FROM Directs WHERE Directs.DirectorID = DirectorID))
This is the error I keep getting:
DB21034E The command was processed as an SQL statement because it was not a
valid Command Line Processor command. During SQL processing it returned:
SQL0548N A check constraint or generated column that is defined with "SELECT"
is invalid. SQLSTATE=42621
Without these constraints I found out that a user can enter into a database an award won for a director for a movie that wasn't directed by them. How can I make this happen?
You cannot create check constraints that query data outside the row in question. What you could do is create a BEFORE TRIGGER to verify the conditions are met. There are some examples in the CREATE TRIGGER documentation that could be customized to your needs.
Define a foreign key from WonAward to Directs, instead:
create table WonAward(
DirectorID integer not null,
MovieID varchar(20) not null,
AwardName varchar(30) not null,
Year integer not null,
Budget REAL not null,
primary key (DirectorID, MovieID),
foreign key (DirectorID, MovieID) references Directs
);