Multiple relations in MySQL DB - mysql

As an introductory project for my school subject, I was requested to create an app for showing the list of shops in my country with the possibility to sort it by location and category. What is more, it should not be just a static text, as a user may add his/her own shops to the list.
Although I am not experiencing any problems with the UI and functionality of my Java application, I am a complete newbie to databases and, particularly, to MySQL. Even though I have scrutinized some tutorials, I still keep toiling over one indeed primitive issue.
The problem is:
I created a table "Shop" with columns "id"(the primary key with auto increment), "name"(text type), "type"(text type) and "location"(text type).
Alike, two more tables were made:
1) "Types" which contains "id" again as the primary key and "type". For example:
id type
1 supermarket
2 grocery
3 bookshop
2) "Location" with "id" too and "city". Something like this:
id city
1 London
2 Nottingham
3 Southampton
What I attempted to do is to create "many to 1" relation between "Types"/"Location" and "Shop" tables (or "1 to 1" between "Types" and "Shop" as well as "Location" and "Shop"), because further I would want to sort shops by location and type and, consequently, print list of relevant shops in my app. However, I simply cannot find the logical sequence how to implement those connections.
Looking forward to any tips.
Thanks in advance!

Both of the relationships you describe here are one-to-many:
A shop has one and only one type. A type can be used in many different shops.
A shop has one and only one location. A location can be used in many different shops.
An extra consideration can be made about if you allow a shop to have an empty type or location. For simplicity, we will consider that is not happening here (but it can be implemented by using nullable foreign keys).
One-to-many relationships are implemented by adding a foreign key between the two involved tables. That is made by adding a column in the child table that contains the id (primary key) of the parent table, and creating a foreign key constraint for those columns.
That is, you have to add a typeId column to your Shop table, and define the foreign key constraint in your database. Same for the relationship with locations: add a locationId column to Shop and a foreign key constraint.
You shouldn't include in your Shop table the text columns for location and type names, only the columns for the IDs. When using that information to search you will do queries with joins between the different related tables. Something lke this:
SELECT s.name, t.type, l.city
FROM Shop s
INNER JOIN Types t ON s.typeId = t.id
INNER JOIN Location l ON s.locationId = l.id
WHERE t.type = 'supermarket'
More info about joins here.
The SQL needed for implementing the foreign keys is different if you are creating the tables with the foreign keys already, or if you are modifying your existing tables. This page contains examples of both approaches.
The simple case of creating the table with the foreign keys:
CREATE TABLE Shop (
id int NOT NULL,
name text NOT NULL,
typeId int NOT NULL,
locationId int NOT NULL,
PRIMARY KEY (id),
CONSTRAINT FK_TypeShop FOREIGN KEY (typeId)
REFERENCES Types(id),
CONSTRAINT FK_LocationShop FOREIGN KEY (locationId)
REFERENCES Location(id)
) ENGINE=INNODB;
An important remark: in MySql only the InnoDB storage engine supports foreign keys, so you must create your tables with that ENGINE=INNODB parameter at the end of the SQL command in order to use foreign key constraints. If you don't specify that then the default engine will be used instead of INNODB and your foreign keys will not work. Update: since version 5.5.5 InnoDB is the default engine, so unless the engine has been explicitly changed for your database you don't need the explicit engine parameter.
This page contains a good explanation of foreign keys in MySQL (perhaps too detailed for what you need now, but you can check it for specific information).
As a best practice, name all your tables either in plural or either in singular, but use the same criteria for all. Probably you should rename table Types to Type (or either rename the other two tables to give them plural names).

Related

Foreign Key Multi Table Possibility

I have seen similar posts and have not found a definitive answer.
I have a series of tables that store data about certain events. Each of these tables have the same structure. Each of these tables has a foreign key constraint for an id showing what item the data is related to.
Each of these tables id structure is also the same CHAR(24). The tables these ids come from must remain separate because they are all completely different.
I am interested in combining all of my data tables into one with a foreign key being constrained to one of 3 tables. So, a row in my data table will have to have an id that is present in one of the three tables. Additionally this foreign key will need the possibility of ON DELETE settings. Is this possible? And to that, is this poor design?
Items A
- id
- ...
Items B
- id
- ...
Items C
- id
- ...
Data
- id FK
No. What you're describing is sometimes called polymorphic-associations but it should be a clue that it's not good design because you can't make a foreign key constraint for it. That is, the FOREIGN KEY syntax only allows you to reference one table, not three.
The only way you could make a real foreign key constraint that performs ON DELETE actions is to make three separate foreign keys:
Data
- idA FK
- idB FK
- idC FK
For a given row in Data, presumably only one of these three foreign keys would be non-NULL. The other two would be NULL. Ensuring this could be done in a trigger or CHECK constraint, or else you would just have to implement it in application logic (i.e. don't insert a row with more than one of these columns non-NULL).
Polymorphic associations, that is storing a single column that may reference one of three different tables, is not a valid relational design.
You can see past answers I've written about polymorphic associations: https://stackoverflow.com/search?q=%5Bpolymorphic-associations%5D+user%3A20860

Am I supposed to use foreign keys for these 3 tables which are connected?

So I have 3 tables:
Table: Albums
Columns: Id, Name, Description, Author, Folderpath, Thumbnail, Upvotes, Downvotes
Table: AlbumsConnection
Columns: Id, AlbumId, AlbumImagesId
Table: AlbumImages
Columns: Id, InAlbum, Imagepath
So far I've been using these tables without actually using foreign keys. Am I supposed to use foreign keys here? I understand that I'd have to add 2 foreign keys to AlbumsConnection, 1 for each table and each foreign key will reference to the primary keys ( which are the ids ) of the other 2 tables. Is that correct?
Foreign keys help ensure relational integrity of the database. There is no requirement for declaring them explicitly, but it is a good idea, particularly if you are learning to use databases.
The foreign key let's the database know that a column in one table is related to a column in another table. I don't think MySQL's optimizer uses this information explicitly, although it does create an index on the foreign key column (unlike most other databases).
In addition, a declared foreign key relationship can help you deal with changes to the database. It will prevent invalid albums from being inserted into the junction table. If you delete an album, it gives you control over how the deletion and updating is handled (via cascading constraints).

Same primary key as Foreign Key for two different tables

I want to make three tables i.e Provinces, Languages, and Cities.
My Provinces and Cities use Languages. So I made join tables i.e Provinces_Languages and Cities_Languages. All tables contains id and name as columns.
Provinces
id
name
Languages
id
name
Provinces_Languages
province_id
languages_id
Foreign Keys
Similarly for cities:
Cities
id
name
Using same Languages Table
Cities_Languages
cities_id
languages_id
Foreign Keys
However, it can't use the same primary key from language the table. I am getting this error:
How i can resolve this?
Thanks in advance
You already have a constraint called FK_Language. Use a different name
FK_Language exists already when you try to create it again.
You should create separate alter table statements, making sure that the FK get's dropped before you create it again.
Basically: You try to drop the FK and create it again at the same time, that won't work.
By the way, you should be consistent on whether to use Cities_Languages or langauges_cities.

Featured value in one to many relation, which table should hold that?

say that i have a one to many relations where there are two tables, a Person table and a Belonging table. Now, each Person has ONLY ONE favorite belonging and a specific belonging cannot belong to another person as well.
My question is, where would that information be better kept ? In the Person table as a favorite_belonging_id or in the Belonging table as an is_favorite entry ? To my eyes, the first choice seems to be the better version, but I would like to hear what sql knowledgeable people have to say about it.
EDIT : A Person has many belongings but only ONE favorite belonging and each belonging can only belong to one person. It's a one to many association.
I'd be tempted to go with your first suggestion (a favourite_belonging_id column in the Person table), as one can then create a foreign key reference from (person_id, favourite_belonging_id) to (owner_id, belonging_id) in the Belonging table.
If one were to go the other route of creating a is_favourite flag in the Belonging table, there is no obvious way of ensuring the 1:1 nature of person-favourite belonging relationships (a composite UNIQUE index over (owner_id, is_favourite) would fail when a person has multiple belongings that are not their favourite).
That said, it doesn't feel like this information really belongs in the Person table, as it isn't really a property of the person but rather it's a property of the Belonging. If you feel strongly about it, you could create a Favourites table that has a UNIQUE (or PRIMARY) index over person_id.
to me it does NOT belong in the person table since it has nothing to do with the base person.
if you have only the belonging table - which i also assume has a person_id in it, then this is where you are expressing the relationship between the belonging and the person, and it is where the qualifier should also go.
another option is to have a third table in the middle linking the two - in this case, the favorite flag goes there.
edit:
my preference in design would be the third table option - here you can put a begin date and end date as well as the favorite flag - this would allow you to theoretically trade a belonging to another person at some point in time and still know what happened.
I see that pretty much all the different options have already been laid out in different answers, but instead of commenting on all to give you my impression on what I think you should do, I'll just create an answer myself.
Just to be clear on how I understand how the system works: All users can have multiple belongings, but any belonging can only be help by one person.
In this case, it makes the most sense to have a user_id in the belongings table that can tie a belonging to a person. Once a user_id is set, nobody else can claim it anymore.
Now, as to the 'favorite' part, there are several things you can do. What truly is the best way to do it strongly depends on the queries you plan on running on it. Some consider adding a JOIN table, but honestly this is a lot of additional data that is rather pointless; there is likely going to be the exact amount of rows in it as the user table and by putting it in a separate table, there is a lot you can't do (for example, see how many people DON'T have a favorite). Likewise, a JOIN table would make no sense for the user_belonging relationship, as there is a 1:1 relationship between the belonging and the amount of people who can have it.
So I believe there are two viable options: either add a field (/switch) in the belongings table to indicate of a user's belonging is his/ her favorite, or add a field to the user table to indicate which belonging is the user's favorite. I would personally think that the latter holds the most merit, but depending on the queries you run, it might make more sense to to the former. Overall, the biggest difference is whether you want to process things pre-insert or post-select; e.g. in the latter situation, you will have to run an independent query to figure out if the user already has a favorite (in the former case this won't be necessary as you would put a unique index on the field in the user table), whereas in a post-select situation you will have to do cross reference which of the selected belongings from the belonging table is the user's favorite.
Please let me know if I explained myself clearly, or if you have any further questions.
The following may not be the best options because it offers a somewhat unconventional method of flagging the favourite belonging. The advantage, though, is that this way you'll have just two tables with no circular references and every person will be guaranteed to have no more than one favourite belonging.
So, it's two tables, people (or persons) and belongings. The people table has this structure:
person_id INT AUTO_INCREMENT,
other columns as necessary,
PRIMARY KEY (person_id)
The belongings table is created like this:
belonging_id INT AUTO_INCREMENT,
person_id INT NOT NULL,
is_favourite enum ('1'),
other columns as necessary,
PRIMARY KEY (belonging_id),
FOREIGN KEY (person_id) REFERENCING people (person_id),
UNIQUE (person_id, is_favourite)
The key element is declaring is_favourite as a nullable enum with a single possible value. This way, when you declare a unique constraint on the pair of (person_id, is_favourite), you are allowed to have as many rows with the same person_id and empty (null) is_favourite as possible, because unique constraints ignore rows where at least one member is null. And you won't be able to create more than one person_id with is_favourite = '1', because that would violate the unique constraint.
Neither. My suggestion is to add another table person_favourite_belonging, like this:
CREATE TABLE person
( person_id INTEGER NOT NULL
--- various other columns about Persons
, PRIMARY KEY (person_id)
) ;
CREATE TABLE belonging
( belonging_id INTEGER NOT NULL
, person_id INTEGER NOT NULL
--- various other columns about Belongings
, PRIMARY KEY (belonging_id)
, UNIQUE KEY (person_id, belonging_id) --- this Unique constraint is needed
, FOREIGN KEY (person_id)
REFERENCES person (person_id)
) ;
CREATE TABLE person_favourite_belonging
( person_id INTEGER NOT NULL
, belonging_id INTEGER NOT NULL
, PRIMARY KEY (person_id)
, FOREIGN KEY (person_id, belonging_id) --- for this Foreign Key constraint
REFERENCES belonging (person_id, belonging_id)
) ;
This is just my preferred way of doing this. There are alternatives and all have their pros and cons. The pros with this approach are:
No circular path in the Foreign Key constraints (and therefore):
No chicken and egg problems when inserting, deleting or updating Persons, Belongings or Favourite Belongings.
All foreign key columns can be defined as NOT NULL.
The integrity can be enforced at the database level.
If your requirements change and you want to have 2 (or more) favourites per person, you only change appropriately the constraints at the Favourite table.
Check also my answer in this question (with an almost identical problem): In SQL, is it OK for two tables to refer to each other?
favourite_thing is a FK to the belonging table (if that table exists, otherwise it could be a domain) , but in an additional constraint, you can force belonging_id in the persons table to be unique.
UPDATE:
DROP table belonging;
CREATE table belonging
( id INTEGER PRIMARY KEY
, description varchar
);
DROP table person;
CREATE table person
( id INTEGER PRIMARY KEY
, description varchar
, favourite_thing INTEGER REFERENCES belonging (id)
);
-- Now add the unique constraint
-- NOTE: favourite_thing can still be NULL
ALTER TABLE person
ADD CONSTRAINT must_be_unique UNIQUE (favourite_thing)
;
UPDATE 2: if every belonging belongs to exactly one person, you could add an owner field to belongings:
CREATE table belonging
( id INTEGER PRIMARY KEY
, owner_id INTEGER NOT NULL REFERENCES person(id)
, description varchar
);
DROP table person CASCADE;
CREATE table person
( id INTEGER PRIMARY KEY
, description varchar
, favourite_thing INTEGER REFERENCES belonging (id)
);
ALTER TABLE person
ADD CONSTRAINT must_be_unique UNIQUE (favourite_thing)
;
Actually you present a one-to-one relation.
So you can:
1. Hold it in Person table.
2. Hold it in Belonging table.
3. Hold it in both.
4. Hold it in separate table.

Linq DBML Mapping a Table With A Primary Key That Spans 2 Columns

I have three tables: Login, LoginRoles and Roles. Login joins to LoginRoles and LoginRoles joins to Roles. LoginRoles is a many-to-many table, and I want to constrain the columns as being unique TOGETHER.
LoginRoles: MemberId (int), RoleId(int)
This is similar to the aspnet_UsersInRoles table in the default membership database:
Table 3.2
I set the primary key for this table as both of the columns.
When dragging the table into the DBML diagram I can not see this primary key represented in the diagram and I get the warning (and other similar ones):
Warning 1 DBML1062: The Type attribute 'LoginRole' of the Association element 'Login_LoginRole' of the Type element 'Login' does not have a primary key. No code will be generated for the association. 0 0
Any ideas on how to make it recognise the Primary Key?
You are trying to use 2 value keys as the primary key for the table. This should really be done in the SQL Table Definition. If you right click inside the table definition you should be able to access the Indexes/Keys option. In there you are able to specify the 2 columns you want to use as the primary key.
Alternatively you could run a small SQL Script, something like:
ALTER TABLE LoginRoles
ADD CONSTRAINT pk_MemberRole PRIMARY KEY (MemberID,RoleID)
Once you have updated your table you need to delete/re-drop your table back onto the DBML designer and the code will update.