Make sure no duplicates in "parent" of parent - mysql

I'm using MariaDB and PHPMyAdmin, but my code is all neutral so happy to switch to Postgres or whatever, but this should be straight forward enough. I haven't designed anything yet, just after best approaches.
I have 3 database tables that describes a premises. Let's say a hotel.
This theoretical hotel has multiple venues - 2 restaurants and a bar. Each of those has a few different rooms/areas. In these rooms are tables that customers can sit at.
In SQL, I imagine the tables would look like this
Venues
Venue ID
Venue Name
1
Restaurant 1
2
Restaurant 2
3
Bar
Rooms
Room ID
Room Name
Parent Venue (foreign key)
1
Patio
1
2
Function Room
1
3
Alcove
3
4
Private Dining
2
Tables
Table ID
Table Name
Parent Room (foreign key)
1
Table 1
1
2
Table 2
1
3
Table 3
1
4
Table 4
2
5
Table 1
3
6
Table 2
3
7
Table 3
3
8
Table 4
3
9
Table 1
4
10
Table 2
4
11
Table 3
4
I hope that data is correct :p
What I want to do is define a relationship whereas it's impossible to add a Table Name if it already exists in that venue. It doesn't matter what room the table is in.
E.g if I was to add another "Table 4", it would succeed in being entered if it was entered into Room 4, as Room 4 belongs to Restaurant 2, which does not already have a "Table 4". However if it was entered into any other room, it would fail as Restaurant 1 and Bar already have a "Table 4" in one of their rooms.
Now in the server side code this is fairly easy to check as I can do multiple queries or joins or a myriad of other ways, however I was wondering how to do this in SQL/PhpMyAdmin directly. I'm having a bit of trouble finding my way around MyAdmin.
Cheers

My recommendation is to redundantly include the parent venue in the tables table. So tables would have the additional column:
venueID
rooms would have a unique constraint (which is redundant):
alter table rooms add constraint unq_rooms_venueId
unique (venueId, roomId);
Then tables would have a unique constraint:
alter table tables add constraint unq_table_venueId
unique (tableName, venueId);
This solves the problem without having to resort to triggers.

What I'd do is switch from technical IDs to composite natural keys. You can use numbers for this (i.e. give a venue a number, a room a number, maybe a table a number) or use the names, if these are guaranteed not to change. E.g.:
venues
(
venue_name,
primary key (venue_name)
);
rooms
(
venue_name,
room_name,
primary key (venue_name, room_name),
foreign key (venue_name) references venues (venue_name)
)
tables
(
venue_name,
room_name,
table_name,
primary key (venue_name, room_name, table_name),
foreign key (venue_name, room_name) references rooms (venue_name, room_name),
unique constraint (venue_name, table_name)
)
(If using the names for keys and your tables don't contain any other information aside from the names, you can easily remove the tables venues and rooms of course and only keep the tables table, if you want.)
Same thing with numbers:
venues
(
venue_no,
name,
primary key (venue_no)
);
rooms
(
venue_no,
room_no,
name,
primary key (venue_no, room_no),
foreign key (venue_no) references venues (venue_no)
)
tables
(
venue_no,
room_no,
table_no,
name,
primary key (venue_no, room_no, table_no),
foreign key (venue_no, room_no) references rooms (venue_no, room_no),
unique constraint (venue_no, name)
)

Related

mysql Script for updating data in a second foreign key column using an existing foreign key relationship

I have 3 tables. Leagues, teams, players. Each team belongs to only one league and each player belongs to only one team. Every player is on a team and every team is in a league.
Table 2 [teams] has a foreign key to table 1 [leagues]. ( t2fk = t1id)
Table 3 [players] has a foreign key to table 2 [teams]. (t3fk = t2id)
I want to use the above relationship to write a script that adds [t1id] to table 3 as a second foreign
key. In other words, I want to add the league id [t1id] to the players table using
the shared relationship with teams.
The end goal is for table 3 to have two foreign keys [t3fk = t2id] and [t3fk = t1id].

Is there a way to check whether a new row in a table agrees with entries in other tables? [SQL]

I am using MySQL Workbench CE for a college assignment. I have 4 tables: Applicants, Universities, Job, Interview. Interview has foreign keys in the other 3 tables: appID (applicant ID), uniID (university ID) and jobID (job ID). Additionally, the job table has a uni column, which is a foreign key to universities.id. Essentially, each job has a job number and a university associated with it. Each interview has an applicant, a job and a university, in addition to some other information.
I am trying to figure out how to check whether the job offering (job number + its associated university) entered into the interview table actually exists in the job table.
For example, if the only row in job is:
ID | JobID | UniID | ..
1 1 1 .
Then the interview table shouldn't accept something like:
ID | AppID | JobID | UniID | ...
x x 1 2 .
as there is no job with JobID = 1 and UniID = 2.
SELECT * FROM job as J LEFT JOIN interview as I ON J.uni = I.uniid WHERE J.id = I.jobid is a check I came up with for this issue, however, I'm not sure whether I should attempt to use it in a trigger or as part of an assertion.
You are looking for a foreign key relationship.
If the jobs table does not already have a primary/unique key on the two columns, then define one:
alter table jobs add constraint unq_jobs_jobid_uniid on job(jobID, uniID);
Then, you can add a foreign key to interviews:
alter table interviews add constraint fk_interviews_jobid_uniID
foreign key (jobID, uniID) references job(jobID, uniID);
Note that this works even if jobID is already the primary key in jobs.

Inputting multiple values into a relational database field

I am creating a database application for a restaurant stock management system. I currently have two database tables, one for the ingredients, and one for each dish.
Database tables and relationships
Each ingredient has a unique ID which is the PK of the table. And the 'ingredients' field in the DISH table is a foreign key linking to ingredient_ID. Each dish will obviously have many ingredients, but the ingredients can also belong to multiple dishes. I have read elsewhere about creating another table called ingredient_dish for example, but I'm not sure I fully understand how this works. Could somebody either point me in the right direction or attempt to explain please, and what sort of columns would I have in this joining table for the above tables?
Thanks a lot.
Since you have a Many-to-Many relationship between Ingredients and Dishes, you may want Ingredient_Dish to be a Cross-Reference table.
Its fields would include ingredient_id, a foreign key to the Ingredients table, and dish_id, a foreign key to the Dish table. Set the primary key of this new table to the combination of both fields. A creation command might look like the following, tailor it to suit:
CREATE TABLE Ingredient_Dish (
ingredient_id INT NOT NULL,
dish_id INT NOT NULL,
CONSTRAINT PK_Ingredients_by_dish PRIMARY KEY (ingredient_id, dish_id))
ALTER TABLE Ingredient_Dish
ADD CONSTRAINT FK_Ingredients FOREIGN KEY (ingredient_id) REFERENCES Ingredient (ingredient_id )
ALTER TABLE Ingredient_Dish
ADD CONSTRAINT FK_Dishess FOREIGN KEY (dish_id) REFERENCES Dish (dish_id)
Many-to-many relationships are best done with such an intermediate (or "bridge") table. The bridge table basically consists of the primary keys of the other tables to form a "bigger" foreign key, which allows you to implement a many-to-many relationship by providing primary key combinations.
Your use case (simplified) would necessitate tables like these:
dishes:
dish_id | other_columns
-------------------------
1 | ...
2 | ...
3 | ...
ingredients:
ingredient_id | other_columns
-------------------------------
1 | ...
2 | ...
3 | ...
dishes_ingredients_bridge:
dish_id | ingredient_id
-------------------------
1 | 1
1 | 2
2 | 2
2 | 3
3 | 3
This means you have 3 dishes and 3 ingredients, where
dish 1 consists of ingredients 1 and 2
dish 2 consists of ingredients 2 and 3
dish 3 consists only of ingredient 3
ingredient 1 is used only in dish 1
ingredient 2 is used in dishes 1 and 2
ingredient 3 is used in dishes 2 and 3
Also, make sure, the combinations in your bridge are unique (create a multi-column unique key). Omitting this usually doesn't result in big problems, but in some edge cases your application could behave in an undefined or unexpected way.
Now you can query (for example) all ingredients of dish 1 like this:
SELECT i.*
FROM ingredients as i
LEFT JOIN dishes_ingredients_bridge dib
ON dib.ingredient_id = dib.ingredient_id
LEFT JOIN dishes as d
ON d.dish_id = dib.dish_id
"Connecting" a dish to an ingredient means adding a row to the bridge. "Disconnecting" a dish from an ingredient means deleting such rows, respectively.
Hope this helps.
You are basically describing a bridge table. It's the only way to describe a many-to-many relationship without concatenating values (which is a very nasty can of worms). I suggest you read this further:
http://www.kimballgroup.com/2012/02/design-tip-142-building-bridges/
Hope it helps.

normalization - 1NF clarification

i have a question about the 1 normal form and will explain it by an example.
lets imagine that we have a set of students that are working on a set of projects, but not necessarily only one, but more than one (many to many relation). we have a table where the information's of the students are recorded, and one for the projects. but we need to link them together. but since the 1NF says redundancy and only value per tuple, how would you do it?
both fields are primary keys here
illustation 1:
student_ID project_ID
1 7
2 7,1
3 4,1,9
4 1,3
5 1
illustration 2:
student_ID project_ID
1 7
2 1
2 7
3 4
3 1
3 9
4 1
4 3
5 1
Illustration 1: I know that if this would be a result of a table, this would violate the 1NF becuase one than one value per tuple.
Ilustration 2: since they are primary keys they are not allowed to be duplicated, even if i remove the primary key from the student_ID i still would be redundant.
How can i fix this issue?
thanks in advance :)
The primary key of this table will be a composite of the two fields. They must both together be unique. Both fields are foreign keys to their respective tables and they will be unique in their respective tables.
What you have here is basically a junction table, and your second illustration shows the correct way to normalize it.
Note that, as is typical for junction tables, the primary key for your table will consist of both of the columns together. Together, each unique combination of values in these columns specifies a distinct student–project pairing.
Edit: In MySQL, you would define this table e.g. as:
CREATE TABLE student_projects (
student_id INTEGER NOT NULL,
project_id INTEGER NOT NULL,
PRIMARY KEY (student_id, project_id)
)
To enforce relational consistency, you may also want to add explicit foreign key constraints to each of the columns.

Proper query or proper normalization: MySQL

What I am trying to do is to access the proper row in a table, so that it returns the right location name, address, etc. The address is returned correctly, but there are three results rather than one. These are our various international locations.
Either my tables aren't normalized properly or I'm writing my query incorrectly. I can't figure out which. Maybe a little of both. Here are my tables:
DEALERS TABLE:
channel_partner_id company
------------------ --------
626 Company Inc.
626 Company GmBH
626 Company Ltd.
DEALERS_LOCATIONS TABLE:
channel_partner_id location_id
------------------ -----------
626 18
626 19
626 20
LOCATIONS TABLE:
location_id address name_url
---------- -------------------- -------
18 1234 Anywhere St. anywhere-st
19 3245 Nowhere St. nowhere-st
20 90 Everywhere St. everywhere-st
I want to join them on the name_url.
So here's my query in CodeIgniter/Active Record (which is translated into standard MySQL easy enough):
$this->db->where('l.name_url', $name_url);
$this->db->join('all_dealers_locations dl', 'dl.channel_partner_id = d.channel_partner_id', 'inner');
$this->db->join('all_locations l', 'l.location_id = dl.location_id', 'inner');
$row = $this->db->get('all_dealers d')->row();
But I get back three results from this. Why, if I am using the where clause of the name_url (which is being passed properly into the function)? Is it the type of join I have? I tried left and outer and that didn't help.
What am I doing wrong?
Your tables have some issues.
The big red flag is that dealers has multiple rows with the same id (which means that channel_partner_id can not be the primary key).
This is a problem because it looks like dealers_locations is supposed to be an intersection table. The standard way to implement such a table is to take the primary keys of the two tables, which in this case would be locations and dealers. Since dealer_locations doesn't have the primary key of dealers, it's not going to work.
Here's how I would implement this instead:
create table dealers (
channel_partner_id int primary key,
name varchar(30)
);
create table locations (
location_id int primary key,
address varchar(30),
name_url varchar(30)
);
create table dealer_locations (
location_id int,
foreign key (location_id) references locations(location_id),
channel_partner_id int,
foreign key (channel_partner_id) references dealers(channel_partner_id),
primary key (location_id, channel_partner_id)
);
Note the two-part primary key for dealer_locations.