Table with 2 foreign keys coming up from one primary - mysql

I'm making this database with superheroes for one of my school projects and I have a superheroes table (obviously) and an enemies table. So the enemies table have two foreign keys:
bad_superhero_id
and
good_superhero_id
The purpose of this table is to link the good superheroes with the bad superheroes (their enemies) from the characters table (superheroes). The both foreign keys are taking values from the id of the superheroes table. The problem is that my teacher doesn't like this and I don't know why. I mean, I saw this example in a book called Beginning PHP5, Apache, and MySQL Web Development and I also asked my coworkers that have good experience in creating database structure. They said it's not a problem, but my teacher wanted me to give her example where this is used, because she doesn't think it's good relationship and wants me to create a stupid workaround that she thought of. I still think this is not a bad way to create this kind of relationship so I wanted to ask here to get third opinion on this problem. I will be grateful if you give your opinion so that I can understand is it bad, good or doesn't matter practice to use relationship like this.
EDIT:
CREATE TABLE superhero (
id INT NOT NULL AUTO_INCREMENT,
nick_name VARCHAR,
align ENUM ('good', 'bad'),
PRIMARY KEY(id)
) ENGINE=INNODB;
CREATE TABLE enemies_link (
id INT NOT NULL AUTO_INCREMENT,
good_sh_id INT NOT NULL,
bad_sh_id INT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (good_sh_id, bad_sh_id)
REFERENCES superheroes(id)
ON UPDATE CASCADE ON DELETE RESTRICT
) ENGINE=INNODB;
EDIT2: Yes, I forgot to add that I want that n to n connection. Let's say spider-man have venom and green goblin for his enemies and on the other hand venom has some other good superheroes as enemies and so on.

Your teacher may be right: you very likely should define both superhero and enemy ids as separate foreign keys:
FOREIGN KEY good_sh_id REFERENCES superheroes(id),
FOREIGN KEY bad_sh_id REFERENCES superheroes(id)
The syntax you specified, would instead specify superhero references as a composite foreign key. I have to admit I'm not sure what this even means. The only way composite foreign keys make sense to me is when you use them to reference a composite primary key.

Your design is not intrinsically a bad design but it needs work. You are using a cross/intersection table which defines an n-to-n relationship. These are used all the time in production databases, like the relationship between Student and Course where a student can be taking several courses and a course will have many students signed up. Yours is just referring both sides to the same table. That's fine too. A table of parts, for example, can contain both components and modules with a component used to make many modules and a module made up of many components.
In your particular instance, you have a flag which designates if the superhero is bad or good. That's good (although the concept of "bad hero" is somewhat jolting -- wouldn't "superbeing" be a better designation?), but the flag and the id must be defined together in a unique constraint/index. That may seem superfluous, since the id is a primary key and therefore unique all by itself. But a foreign key can only refer to a unique field or set of fields.
As for the cross table, you really don't need a separate id field. In fact, that opens up a possible chink in data integrity. When modeling, always try to make data integrity a prime factor. Make it as close to impossible as you can to get bogus data into the table. The table key will all be the foreign key fields as one big composite key. If a separate key is required by foolish design standards, then be sure to define the foreign key fields together in a unique index. Then you must enforce the values of the good/bad flags to insure the 'good' FK can only point to a 'good' superhero, and so forth.
CREATE TABLE superhero(
id INT NOT NULL AUTO_INCREMENT,
nick_name VARCHAR( 20 ),
align ENUM( 'good', 'bad' ) not null default 'good',
PRIMARY KEY( id ),
constraint unique id_align_uq( id, align )
) ENGINE=INNODB;
CREATE TABLE enemies_link(
good_sh_id INT NOT NULL,
good_align enum( 'good', 'bad' ) not null check( good_align = 'good' ),
bad_sh_id INT NOT NULL,
bad_align enum( 'good', 'bad' ) not null check( bad_align = 'bad' ),
PRIMARY KEY( good_sh_id, good_align, bad_sh_id, bad_align ),
FOREIGN KEY( good_sh_id, good_align )
REFERENCES superhero( id, align )
ON UPDATE CASCADE ON DELETE RESTRICT,
FOREIGN KEY( bad_sh_id, bad_align )
REFERENCES superhero( id, align )
ON UPDATE CASCADE ON DELETE RESTRICT
) ENGINE=INNODB;

I think you have the correct approach. I clearify and probably repeat what you have already designed with tis example.
table hero (in other words person)
person_id, name, good_bad
(by the way good_bad could vary, so consider if that is in the right place)
table opponent
person_id, opponent_person_id, battlefield
So you can have different opponents on different battlefields
The only problem you have is to assure double entries or a concept how to handle this: e.g.;
person_id = 7, opponent_person_id = 11, battlefield = Rome
person_id = 11, opponent_person_id =7, battlefield = Rome
in business this could be a realistic use:
table employment
chief_person_id,employe_person_id, department
chief_person_id = 7, employe_person_id = 10, department= 1
chief_person_id = 7, employe_person_id = 11, department= 1
chief_person_id = 9, employe_person_id = 12, department= 2
chief_person_id = 9, employe_person_id = 15, department= 2
chief_person_id = 12, employe_person_id = 16, department= 2 (even sub-hierarchies can be shown. see id=12)

Related

How to setup must exist in database

i need help. As you can see in my tables i have to setup primary and forign keys and i need help on this cascade updates.
For example i have a task like this:
If the value for Category_Name is changed in CATEGORY, the change should be reflected here as well.
But this doesent work :
If an OrderID in ORDERED is changed, then the change should be reflected here as well.
If an ORDERED is deleted, then any LINE_ITEM(s)associated with that order should also be deleted.
If the Item_Number of an ITEM is changed, then the change should be reflected here as well.
I dont know how to setup this correctly and i need help to better understand this.
CREATE DATABASE TEST1;
USE TEST1;
CREATE TABLE CATEGORY (
CategoryName VARCHAR(35) NOT NULL,
ShippingPerPound CHAR(4),
OffersAlowed ENUM('y', 'n'),
CONSTRAINT CATEGORY_PK PRIMARY KEY (CategoryName)
) ENGINE=INNODB;
CREATE TABLE ITEM(
ItemNumber INT UNSIGNED AUTO_INCREMENT,
Item_Name VARCHAR(35) NOT NULL,
Description VARCHAR(255),
Model VARCHAR(50) NOT NULL,
Price CHAR(8) NOT NULL,
parent_ItemNumber INT,
CategoryName VARCHAR(35),
CONSTRAINT ITEM_PK PRIMARY KEY (ItemNumber),
CONSTRAINT ITEM_CategoryName_fk FOREIGN KEY (CategoryName) REFERENCES
CATEGORY(CategoryName) ON UPDATE CASCADE)
ENGINE=INNODB;
CREATE table LINE_ITEM(
Quantitiy INT(255),
Shipping_amounth DECIMAL(4,2),
ItemNumber INT UNSIGNED,
OrderID INT UNSIGNED,
CONSTRAINT LINE_ITEM_PK PRIMARY KEY(ItemNumber),
CONSTRAINT LINE_ITEM_PK PRIMARY KEY(OrderID),
CONSTRAINT LINE_ITEM_OrderID FOREIGN KEY (OrderID) REFERENCES ORDERED(OrderID) ON UPDATE CASCADE),
CONSTRAINT LINE_ITEM_OrderID FOREIGN KEY (OrderID) REFERENCES ORDERED(OrderID) ON UPDATE DELETE),
CONSTRAINT LINE_ITEM_ItemNumber_fk FOREIGN KEY (ItemNumber) REFERENCES ITEM(ItemNumber) ON UPDATE CASCADE)
ENGINE=INNODB;
CREATE TABLE OFFER(
OfferCode varchar(15),
Discount_Amt varchar(35) NOT NULL,
MinAmount DECIMAL(2,2) NOT NULL,
ExpirationDate DATE NOT NULL,
CONSTRAINT OFFER_OfferCode PRIMARY KEY(OfferCode)
)
ENGINE=INNODB;
CREATE TABLE ORDERED(
OrderID INT UNSIGNED AUTO_INCREMENT,
total_cost DECIMAL(8,2),
CONSTRAINT ORDERED_PK PRIMARY KEY (OrderID),
CONSTRAINT OFFER_OfferCode FOREIGN KEY (OrderID) REFERENCES OFFER(OfferCode) ON UPDATE CASCADE),
CONSTRAINT CUSTOMER_CustomerID FOREIGN KEY (CustomerID) REFERENCES CUSTOMER(CustomerID) ON UPDATE CASCADE)
)
ENGINE=INNODB;
If the value for Category_Name is changed in CATEGORY, the change should be reflected [in the tables which reference Category] as well.
You have an XY Problem. You've already chosen a solution to your problem (using cascading updates) and asked about that. It's not a good solution, it just papers over the real. Instead, you should ask about the real problem.
The real problem is you have duplicate data, CategoryName is stored in two places. The question is how one deals with that? The answer is to redesign the schema to eliminate the duplication.
Looking at the Category table reveals the real real problem, CategoryName is the primary key, so it will be referenced in other tables. But if CategoryName can change it's a poor choice for a primary key. Instead, use a simple auto incrementing integer as the primary key and the problem goes away.
create table Category (
ID integer auto_increment primary key,
Name varchar(255) not null,
ShippingPerPound CHAR(4),
OffersAlowed ENUM('y', 'n'),
);
Now categories can be referenced using CategoryID integer references category(id). Category names can change as much as they like. Any query needing to know the category name will have to do a join Category on Category.id = CategoryID; simple joins like that are cheap.
If the Item_Number of an ITEM is changed, then the change should be reflected [in the tables which reference Item] as well.
Same problem: if the primary key can change, it's not a good primary key. Since Item_Number is auto incremented it should never change, so you might be worrying about a problem that doesn't exist.
If Item_Number can change, then you need two columns. One for the immutable primary key, just call it id, and one for the mutable Item_Number. They can be the same thing for most columns, that's fine, it just adds 4 bytes to each column.
Note that I increased the name constraint to 255. It's a bad practice to put business rules, like size limits on names, into the database schema. The database should not be limiting design choices, and there's no technical reason to limit it: 30 characters in a varchar(255) takes up as much space as 30 characters in varchar(30).
I'd also question why ShippingPerPound is a char(4). It seems it should be a number, probably a numeric(9,2) for money. numeric will store exact values and does not suffer from floating point error, it's a good choice for money.
Finally, I'd caution against using an unsigned integer as a primary key. Yes, it doubles your keyspace, but I can guarantee people referencing that key will forget and use a plain, signed integer. If you hit 2 billion rows it's likely you're growing so rapidly that you'll blow through the next 2 billion much faster than the first, so it isn't worth it. If you're really concerned about keyspace, use a bigint or UUID. But this isn't something you need to worry about now, tables can be altered later if it becomes an issue. But it's unlikely to be an issue for Items as it's unlikely you'll have 2 billion items. Keyspace exhaustion is something that happens to tables that are logging things, or tracking sales, things which grow exponentially as you get more users; not manually entered warehouse data.
Old database guides sometimes encourage bad practices like trying to use data as a primary key, or putting unnecessary limits on storage sizes. That might have made sense when disk and CPU was extremely limited, or when column sizes were fixed, but it makes little sense now. An auto incremented integer primary key is a good default choice.

foreign key of two possible tables

I have two tables: Student & User
the Student table has a primary key of INT(9)
the User table has a primary key of MEDIUMINT
Now please take a look at this picture
(source: imgh.us)
Now the problem is: in the messages table i've the messageFrom and messageTo cols, I don't know weather the sender or the receiver is a Student or a User.
I can't reference the two tables because of different primary key types, however, am trying to avoid major changes as possible.
the same issue is everywhere reportedPosts table, comment table. Everywhere.
HOW to get around this issue or a possible solutions to fix this ?
AND please feel free to feedback the database structure, i would like to know and learn from your advices.
thanks in advance.
Both Users and Students (the entities, not the tables) are both examples of People. There are attributes of Users that don't belong to Students and there are attributes of Students that don't belong to Users. Also, there are actions Users may take that Students cannot take and vice versa. However, there are attributes common to both (name, address, phone number, etc.) and actions both may take (send/receive messages, post comments, etc.). This strongly implies a separate table to contain the common attributes and allow the common actions.
create table People(
ID MediumInt auto_generating primary key,
PType char( 1 ) not null check( PType in( 'U', 'S' )) -- User or Student
Name varchar( 64 ) not null,
Address varchar( 128 ),
..., -- other common attributes
constraint UQ_PeopleIDType unique( ID, PType ) -- create anchor for FKs
);
create table Users(
UserID MediumInt not null primary key,
UType char( 1 ) check( UType = 'U' ),
..., -- attributes for Users
constraint FK_Users_People foreign key( UserID, UType )
references People( ID, PType )
);
create table Students(
StudentID MediumInt not null primary key,
SType char( 1 ) check( SType = 'S' ),
..., -- attributes for Students
constraint FK_Students_People foreign key( StudentID, SType )
references People( ID, PType )
);
Notice that if a Person is created with a type of 'S' (Student), the ID value for that Person can only be inserted into the Student table.
Now all tables that must refer to Users may FK to the Users table and those that must refer to Students may FK to the Students table. When tables can refer to either, they may FK to the People table.
Foreign Keys do not solve all problems. Add a suitable index instead of depending on the FK. Then:
Plan A: Do the equivalent of FK checks in the application code, or
Plan B: Forgo any FK checks.

Designing relationships around an inheritance structure

I have a conceptual question regarding how best to organise my database.
Currently I have four core tables users, teachers, students and notifications. However both the teachers and students tables inherit from the users table so contain the foreign key user_id.
The notifications table as you might have guessed refers to notifications. These need to appear for all users that belong to an employee group i.e. under the employment of another.
Both students and teachers can employ other users.
So the crux is I need an eloquent way of modelling this. The basic workflow of the code would be the below:
getCurrentUser->getAllEmployer(s)->getNotifications
This is the Laravel Eloquent I'm used to $user->employers()->notifications;
Unfortunately it's not as simple as that as in this case an employer can refer to two tables.
So my choices are as follows.
Create an Eloquent Relationship for both the student and teacher
relationship as employers. The shortfall being I need to write if
tests to check if the current user belongs to either and this code
would be repeated frequently.
Add a teacher_id and student_id to
the users table. However one would obviously be redundant in each
record. The chance of needing to add other columns is very likely as
well due to the emergence of new employer entities.
Create an employer_employee table that contains two columns both referencing a user_id. A SQL query would LEFT JOIN both student and
teacher tables with the employer_employee table and then a JOIN
with notifications would return all those relevant. However would
so many joins reduce the speed of the query when compared with the
other options.
Something I haven't considered.
I'm really looking for the most efficient, scalable solution.
Any help is appreciated. If you could clarify why your answer is the most efficient scalable solution as well that would be superb.
There is a similar question here using a Media supertype and adding subtypes of CD, VCR, DVD, etc.
This is scalable in that in creating, say, a BluRay subtype, you create the table to contain the BluRay-specific data and add an entry to the MediaTypes table. No changes needed for existing data or code -- except, of course, to add the code that will work with BluRay data.
In your case, Users would be the supertype table with Teachers and Students the subtype tables.
create table Users(
ID int not null auto_generating,
Type char( 1 ) check( Type in( 'T', 'S' )),
-- other data common to all users,
constraint PK_Users primary key( ID ),
constraint UQ_UserType unique( ID, Type ),
constraint FK_UserTypes foreign key( Type )
references UserTypes( ID )
);
create table Teachers(
TeacherID int not null,
TeacherType char( 1 ) check( TeacherType = 'T' )),
-- other data common to all teachers...,
constraint PK_Teachers primary key( TeacherID ),
constraint FK_TeacherUser foreign key( TeacherID, TeacherType )
references Users( ID, Types )
);
The makeup of the Students table would be similar to the Teachers table.
Since both teachers and students may employ other teachers and students, the table that contains this relationship would refer to the Users table.
create table Employment(
EmployerID int not null,
EmployeeID int not null,
-- other data concerning the employment...,
constraint CK_EmploymentDupes check( EmployerID <> EmployeeID ),
constraint PK_Employment primary key( EmployerID, EmployeeID ),
constraint FK_EmploymentEmployer foreign key( EmployerID )
references Users( ID ),
constraint FK_EmploymentEmployee foreign key( EmployeeID )
references Users( ID )
);
As I understand it, Notifications are grouped by employer:
create table Notifications(
EmployerID int not null
NotificationDate date,
NotificationData varchar( 500 ),
-- other notification data...,
constraint FK_NotificationsEmployer foreign key( EmployerID )
references Users( ID )
);
The queries should be simple enough. For example, if a user wanted to see all the notifications from his employer(s):
select e.EmployerID, n.NotificationDate, n.NotificationData
from Employment e
join Notifications n
on n.EmployerID = e.EmployerID
where e.EmployeeID = :UserID;
This is an initial sketch, of course. Refinements are possible. But to your numbered points:
The Employment table relates employers to employees. The only check if to make user employers cannot employee themselves, but otherwise any user can be both an employee and employer.
The Users table forces each user to be either a teacher ('T') or student ('S'). Only users defined as 'T' can be placed in the Teachers table and only users defined as 'S' can be placed in the Students table.
The Employment table joins only to the Users table, not to both the Teachers and Students tables. But this is because both teachers and students can be both employers and employees, not for any performance reason. In general, don't worry about performance during the initial design. Your primary concern at this point is data integrity. Relational databases are very good with joins. If a performance issue should crop up, then fix it. Don't restructure your data to solve problems that do not yet exist and may never exist.
Well, give this a try and see how it works.

SQL: Creating a Relational table with 2 different auto_increment

I have 2 tables, each with their own auto incremented IDs, which are of course primary keys.
When I want to create a 3rd table to establish the relation between these 2 tables, I always have an error.
First one is that you can have only 1 automatically-incremented column, the second one occurs when I delete the auto_increment statement from those 2, therefore AQL doesn't allow me to make them foreign keys, because of the type matching failure.
Is there a way that I can create a relational table without losing auto increment features?
Another possible (but not preferred) solution may be there is another primary key in the first table, which is the username of the user, not with an auto_increment statement, of course. Is it inevitable?
Thanks in advance.
1 Concept
You have misunderstood some basic concepts, and the difficulties result from that. We have to address the concepts first, not the problem as you perceive it, and consequently, your problem will disappear.
auto incremented IDs, which are of course primary keys.
No, they are not. That is a common misconception. And problems are guaranteed to ensue.
An ID field cannot be a Primary Key in the English or technical or Relational senses.
Sure, in SQL, you can declare any field to be a PRIMARY KEY, but that doesn't magically transform it into a Primary Key in the English, technical, or Relational senses. You can name a chihuahua "Rottweiller", but that doesn't transform it into a Rottweiller, it remains a chihuahua. Like any language, SQL simply executes the commands that you give it, it does not understand PRIMARY KEY to mean something Relational, it just whacks an unique index on the column (or field).
The problem is, since you have declared the ID to be a PRIMARY KEY, you think of it as a Primary Key, and you may expect that it has some of qualities of a Primary Key. Except for the uniqueness of the ID value, it provides no benefit. It has none of the qualities of a Primary Key, or any sort of Relational Key for that matter. It is not a Key in the English, technical, or Relational senses. By declaring a non-key to be a key, you will only confuse yourself, and you will find out that there is something terribly wrong only when the user complains about duplicates in the table.
2 Relational Model
2.1  Relational tables must have row uniqueness
A PRIMARY KEY on an ID field does not provide row uniqueness. Therefore it is not a Relational table containing rows, and if it isn't that, then it is a file containing records. It doesn't have any of the integrity, or power (at this stage you will be aware of join power only), or speed, that a table in a Relational database has.
Execute this code (MS SQL) and prove it to yourself. Please do not simply read this and understand it, and then proceed to read the rest of this Answer, this code must be executed before reading further. It has curative value.
-- [1] Dumb, broken file
-- Ensures unique RECORDS, allows duplicate ROWS
CREATE TABLE dumb_file (
id INT IDENTITY PRIMARY KEY,
name_first CHAR(30),
name_last CHAR(30)
)
INSERT dumb_file VALUES
( 'Mickey', 'Mouse' ),
( 'Mickey', 'Mouse' ),
( 'Mickey', 'Mouse' )
SELECT *
FROM dumb_file
Notice that you have duplicate rows. Relational tables are required to have unique rows. Further proof that you do not have a relational table, or any of the qualities of one.
Notice that in your report, the only thing that is unique is the ID field, which no user cares about, no user sees, because it is not data, it is some additional nonsense that some very stupid "teacher" told you to put in every file. You have record uniqueness but not row uniqueness.
In terms of the data (the real data minus the extraneous additions), the data name_last and name_first can exist without the ID field. A person has a first name and last name without an ID being stamped on their forehead.
The second thing that you are using that confuses you is the AUTOINCREMENT. If you are implementing a record filing system with no Relational capability, sure, it is helpful, you don't have to code the increment when inserting records. But if you are implementing a Relational Database, it serves no purpose at all, because you will never use it. There are many features in SQL that most people never use.
2.2  Corrective Action
So how do you upgrade, elevate, that dumb_file that is full of duplicate rows to a Relational table, in order to get some of the qualities and benefits of a Relational table ? There are three steps to this.
You need to understand Keys
And since we have progressed from ISAM files of the 1970's, to the Relational Model, you need to understand Relational Keys. That is, if you wish to obtain the benefits (integrity, power, speed) of a Relational Database.
In Codd's Relational Model:
a key is made up from the data
and
the rows in a table must be unique
Your "key" is not made up from the data. It is some additional, non-data parasite, caused by your being infected with the disease of your "teacher". Recognise it as such, and allow yourself the full mental capacity that God gave you (notice that I do not ask you to think in isolated or fragmented or abstract terms, all the elements in a database must be integrated with each other).
Make up a real key from the data, and only from the data. In this case, there is only one possible Key: (name_last, name_first).
Try this code, declare an unique constraint on the data:
-- [2] dumb_file fixed, elevated to table, prevents duplicate rows
-- still dumb
CREATE TABLE dumb_table (
id INT IDENTITY PRIMARY KEY,
name_first CHAR(30),
name_last CHAR(30),
CONSTRAINT UK
UNIQUE ( name_last, name_first )
)
INSERT dumb_table VALUES
( 'Mickey', 'Mouse' ),
( 'Minnie', 'Mouse' )
SELECT *
FROM dumb_table
INSERT dumb_table VALUES
( 'Mickey', 'Mouse' )
Now we have row uniqueness. That is the sequence that happens to most people: they create a file which allows dupes; they have no idea why dupes are appearing in the drop-downs; the user screams; they tweak the file and add an index to prevent dupes; they go to the next bug fix. (They may do so correctly or not, that is a different story.)
The second level. For thinking people who think beyond the fix-its. Since we have now row uniqueness, what in Heaven's name is the purpose of the ID field, why do we even have it ??? Oh, because the chihuahua is named Rotty and we are afraid to touch it.
The declaration that it is a PRIMARY KEY is false, but it remains, causing confusion and false expectations. The only genuine Key there is, is the (name_last, name_fist), and it is a Alternate Key at this point.
Therefore the ID field is totally superfluous; and so is the index that supports it; and so is the stupid AUTOINCREMENT; and so is the false declaration that it is a PRIMARY KEY; and any expectations you may have of it are false.
Therefore remove the superfluous ID field. Try this code:
-- [3] Relational Table
-- Now that we have prevented duplicate data, the id field
-- AND its additional index serves no purpose, it is superfluous,
-- like an udder on a bull. If we remove the field AND the
-- supporting index, we obtain a Relational table.
CREATE TABLE relational_table (
name_first CHAR(30),
name_last CHAR(30),
CONSTRAINT PK
PRIMARY KEY ( name_last, name_first )
)
INSERT relational_table VALUES
( 'Mickey', 'Mouse' ),
( 'Minnie', 'Mouse' )
SELECT *
FROM relational_table
INSERT relational_table VALUES
( 'Mickey', 'Mouse' )
Works just fine, works as intended, without the extraneous fields and indices.
Please remember this, and do it right, every single time.
2.3  False Teachers
In these end times, as advised, we will have many of them. Note well, the "teachers" who propagate ID columns, by virtue of the detailed evidence in this post, simply do not understand the Relational Model or Relational Databases. Especially those who write books about it.
As evidenced, they are stuck in pre-1970 ISAM technology. That is all they understand, and that is all that they can teach. They use an SQL database container, for the ease of Access, recovery, backup, etc, but the content is pure Record Filing System with no Relational Integrity, Power, or speed. AFAIC, it is a serious fraud.
In addition to ID fields, of course, there are several items that are key Relational-or-not concepts, that taken together, cause me to form such a grave conclusion. Those other items are beyond the scope of this post.
One particular pair of idiots is currently mounting an assault on First Normal Form. They belong in the asylum.
3  Solution
Now for the rest of your question.
3.1  Answers
Is there a way that I can create a relational table without losing auto increment features?
That is a self-contradicting sentence. I trust you will understand from my explanation, Relational tables have no need for AUTOINCREMENT "features"; if the file has AUTOINCREMENT, it is not a Relational table.
AUTOINCREMENT or IDENTITY is good for one thing only: if, and only if, you want to create an Excel spreadsheet in the SQL database container, replete with fields named A, B, and C, across the top, and record numbers down the left side. In database terms, that is the result of a SELECT, a flattened view of the data, that is not the source of data, which is organised (Normalised).
Another possible (but not preferred) solution may be there is another primary key in the first table, which is the username of the user, not with an auto increment statement, of course. Is it inevitable?
In technical work, we don't care about preferences, because that is subjective, and it changes all the time. We care about technical correctness, because that is objective, and it does not change.
Yes, it is inevitable. Because it is just a matter of time; number of bugs; number of "can't dos"; number of user screams, until you face the facts, overcome your false declarations, and realise that:
the only way to ensure that user rows are unique, that user_names are unique, is to declare an UNIQUE constraint on it
and get rid of user_id or id in the user file
which promotes user_name to PRIMARY KEY
Yes, because your entire problem with the third table, not coincidentally, is then eliminated.
That third table is an Associative Table. The only Key required (Primary Key) is a composite of the two parent Primary Keys. That ensures uniqueness of the rows, which are identified by their Keys, not by their IDs.
I am warning you about that because the same "teachers" who taught you the error of implementing ID fields, teach the error of implementing ID fields in the Associative Table, where, just as with an ordinary table, it is superfluous, serves no purpose, introduces duplicates, and causes confusion. And it is doubly superfluous because the two keys that provide are already there, staring us in the face.
Since they do not understand the RM, or Relational terms, they call Associative Tables "link" or "map" tables. If they have an ID field, they are in fact, files.
3.2  Lookup Tables
ID fields are particularly Stupid Thing to Do for Lookup or Reference tables. Most of them have recognisable codes, there is no need to enumerate the list of codes in them, because the codes are (should be) unique.
ENUM is just as stupid, but for a different reason: it locks you into an anti-SQL method, a "feature" in that non-compliant "SQL".
Further, having the codes in the child tables as FKs, is a Good Thing: the code is much more meaningful, and it often saves an unnecessary join:
SELECT ...
FROM child_table -- not the lookup table
WHERE gender_code = "M" -- FK in the child, PK in the lookup
instead of:
SELECT ...
FROM child_table
WHERE gender_id = 6 -- meaningless to the maintainer
or worse:
SELECT ...
FROM child_table C -- that you are trying to determine
JOIN lookup_table L
ON C.gender_id = L.gender_id
WHERE L.gender_code = "M" -- meaningful, known
Note that this is something one cannot avoid: you need uniqueness on the lookup code and uniqueness on the description. That is the only method to prevent duplicates in each of the two columns:
CREATE TABLE gender (
gender_code CHAR(2) NOT NULL,
name CHAR(30) NOT NULL
CONSTRAINT PK
PRIMARY KEY ( gender_code )
CONSTRAINT AK
UNIQUE ( name )
)
3.3  Full Example
From the details in your question, I suspect that you have SQL syntax and FK definition issues, so I will give the entire solution you need as an example (since you have not given file definitions):
CREATE TABLE user ( -- Typical Identifying Table
user_name CHAR(16) NOT NULL, -- Short PK
name_first CHAR(30) NOT NULL, -- Alt Key.1
name_last CHAR(30) NOT NULL, -- Alt Key.2
birth_date DATE NOT NULL -- Alt Key.3
CONSTRAINT PK -- unique user_name
PRIMARY KEY ( user_name )
CONSTRAINT AK -- unique person identification
PRIMARY KEY ( name_last, name_first, birth_date )
)
CREATE TABLE sport ( -- Typical Lookup Table
sport_code CHAR(4) NOT NULL, -- PK Short code
name CHAR(30) NOT NULL -- AK
CONSTRAINT PK
PRIMARY KEY ( sport_code )
CONSTRAINT AK
PRIMARY KEY ( name )
)
CREATE TABLE user_sport ( -- Typical Associative Table
user_name CHAR(16) NOT NULL, -- PK.1, FK
sport_code CHAR(4) NOT NULL, -- PK.2, FK
start_date DATE NOT NULL
CONSTRAINT PK
PRIMARY KEY ( user_name, sport_code )
CONSTRAINT user_plays_sport_fk
FOREIGN KEY ( user_name )
REFERENCES user ( user_name )
CONSTRAINT sport_occupies_user_fk
FOREIGN KEY ( sport_code )
REFERENCES sport ( sport_code )
)
There, the PRIMARY KEY declaration is honest, it is a Primary Key; no ID; no AUTOINCREMENT; no extra indices; no duplicate rows; no erroneous expectations; no consequential problems.
3.4  Relational Data Model
Here is the Data Model to go with the definitions.
As a PDF
If you are not used to the Notation, please be advised that every little tick, notch, and mark, the solid vs dashed lines, the square vs round corners, means something very specific. Refer to the IDEF1X Notation.
A picture is worth a thousand words; in this case a standard-complaint picture is worth more than that; a bad one is not worth the paper it is drawn on.
Please check the Verb Phrases carefully, they comprise a set of Predicates. The remainder of the Predicates can be determined directly from the model. If this is not clear, please ask.

Database Structure/Design

I couldn't think of a title for this and so didn't even know where to start researching for myself.
I have to make a database where I have a table for CD/DVDs but the type of entertainment on them requires different attributes in terms of metadata/information for example music CDs have artist, publisher, producer, CDNo. etc. Whereas a piece of software may have similarities but has some that music wont have and likely the same with movies and games. And so I'm not sure how this would work in terms of an E-R diagram, so far I decided on:
CD/DVDs being in the items table or stock table not sure on the name yet.
tbl_items -> item_id,
item_format(DVD or CD, maybe axpand to blu-ray or hd-dvd),
item_entertainment_type(Music, Movie etc.) <--- Maybe in another not sure.
foreign key to a metadata table, this is so that when deliveries for new CD/DVDs are made if the metadata already exists I just enter a new item and so its a one to many between metadata and items (items >-- meta).
The question I think is, is it bad practice to have null able foreign key fields and Just choose which to add a relation to, so musicMeta_id INT NULL, FOREIGN KEY musicMetaID REFERENCES tbl_musicMeta(musicMeta_id)
like that for each type? or somehow merge them, or is there a trick databaes have.
I'm using MySQL with php.
Thanks!
There is no general rule or Best Practice the foreign keys should not be nullable. Many times it makes perfect sense for an entity not to have a relationship with another entity. For example, you may have a table of artists you track but, at the moment, you have no CDs recorded by those artists.
As for having Media (CD, DVD, BluRay) that can be either music/audio or software, you can have a table with the information in common and then two foreign keys, one to each extension table (AudioData and SoftwareData), but one must be NULL. This presents a situation called, among other things, an exclusive arc. This is generally considered to be...problematic.
Think of a superclass and two derived classes in an OO language like Java or C++. One way to represent that in a relational schema is:
create table Media(
ID int not null, -- identity, auto_generated, generated always as identity...
Type char( 1 ) not null,
Format char( 1 ) not null,
... <other common data>,
constraint PK_Media primary key( ID ),
constraint FK_Media_Type foreign key( Type )
references MediaTypes( ID ), -- A-A/V, S-Software, G-Game
constraint FK_Media_Format foreign key( Format )
references MediaFormats( ID ) -- C-CD, D-DVD, B-BluRay, etc.
);
create unique index UQ_Media_ID_Type( ID, Type ) on Media;
create table AVData( -- For music and video
ID int not null,
Type char( 1 ) not null,
... <audio-only data>,
constraint PK_AVData primary key( ID ),
constraint CK_AVData_Type check( Type = 'A',
constraint FK_AVData_Media foreign key( ID, Type )
references Media( ID, Type )
);
create table SWData( -- For software, data
ID int not null,
Type char( 1 ) not null,
... <software-only data>,
constraint PK_SWData primary key( ID ),
constraint CK_SWData_Type check( Type = 'S',
constraint FK_SWData_Media foreign key( ID, Type )
references Media( ID, Type )
);
create table GameData( -- For games
ID int not null,
Type char( 1 ) not null,
... <game-only data>,
constraint PK_GameData primary key( ID ),
constraint CK_GameData_Type check( Type = 'G',
constraint FK_GameData_Media foreign key( ID, Type )
references Media( ID, Type )
);
Now if you are looking for a movie, you search the AVData table, then join with the Media table for the rest of the information and so on with software or games. If you have an ID value but don't know what kind it is, search the Media table and the Type value will tell you which of the three (or more) data tables to join with. The point is that the FK is referring to the generic table, not from it.
Of course, a movie or game or software can be released on more than one media type, so you can have intersection tables between the Media table and the respective data tables. Otoh, those are generally labeled with different SKUs so you may want to also treat them as different items.
The code, as you might expect, can get fairly complicated, though not too bad. Otoh, our design goal is not code simplicity but data integrity. This makes it impossible to mix, for instance, game data with a movie item. And you get rid of having a set of fields where only one must have a value and the others must be null.
My opinion: Get rid of the FOREIGN KEYs; just be sure you have suitable INDEXes.