db design: Two "1 to many" relationship? - mysql

In my situation i have
TABLE vehicles (
id int(11) NOT NULL AUTO_INCREMENT,
transaction_type varchar(45),
PRIMARY KEY (`id`)
/* all other values of vehicle */
)
TABLE Origination(
id int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
TABLE Additions(
id int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
So i have this new vehicles table that originate either from Origination, or Additions. The reason it can come from two tables is business needs, it either orginates for the origin or it originates from a special addition after the fact. I'm unsure how to connect a "vehicle" relation to both tables as the id can come from either.
So i added a transaction_type field, where if type is "Additions" join tables based on Additions Id. Else if type is "origination" join on Origination Id.
So i have a Two Key "1 to many" relationship. is this a valid and useable design practice? I am not a database person, but am trying to learn how to handle this is the best way.

From what I understand you want a main table containing ALL your vehicles and two separate tables that add different data to those vehicles. This is how I'd make that work. If you be more specific about what each table does I can be of more help.
TABLE Vehicle (
id int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
/* all other values of vehicle */
)
TABLE Origination (
id int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
TABLE Addition (
id int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
TABLE Vehicle_Origination (
origination_id int(11),
vehicle_id int(11),
FOREIGN KEY (origination_id) REFERENCES Origination (id),
FOREIGN KEY (vehicle_id) REFERENCES Vehicle (id)
)
TABLE Vehicle_Addition (
addition_id int(11),
vehicle_id int(11),
FOREIGN KEY (addition_id) REFERENCES Addition (id),
FOREIGN KEY (vehicle_id) REFERENCES Vehicle (id)
)

Related

Adding continual data to a users ID? [duplicate]

I have the following tables:
CREATE TABLE `students` (
`student_id` int NOT NULL AUTO_INCREMENT,
`student_name` varchar(40) NOT NULL DEFAULT '',
PRIMARY KEY (`student_id`)
);
CREATE TABLE `courses` (
`course_id` int NOT NULL AUTO_INCREMENT,
`course_name` varchar(40) NOT NULL DEFAULT '',
PRIMARY KEY (`course_id`)
);
CREATE TABLE `students_courses` (
`id` int NOT NULL AUTO_INCREMENT,
`student_id` int NOT NULL DEFAULT '0',
`course_id` int NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
);
Here, am using the students_courses table to store the relationships between the Students and Courses. Because one Student can enroll to more than one Course.
The doubt am having is, what should be indexed and how for that table.
1) Shall I index student_id and course_id separately like this:
CREATE TABLE `students_courses` (
`id` int NOT NULL AUTO_INCREMENT,
`student_id` int NOT NULL DEFAULT '0',
`course_id` int NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY (`student_id`),
KEY (`course_id`)
);
2) Or, create a composite index for both the student_id and course_id
CREATE TABLE `students_courses` (
`id` int NOT NULL AUTO_INCREMENT,
`student_id` int NOT NULL DEFAULT '0',
`course_id` int NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY (`student_id`, `course_id`)
);
3) If going with composite key, should I remove that that id primary key and make the PRIMARY KEY composite?
I will be using this relationship table during JOIN mainly. So am a bit confused here.
Let's say we stick to using the Auto Increment id column as Primary Key. Now, we will also need to ensure that the data is consistent, i.e., there are no duplicate rows for a combination of (student_id, course_id) values. So, we will need to either handle this in application code (do a select every time before insert/update), or we can fix this thing structurally by defining a Composite UNIQUE constraint on (student_id, course_id).
Now, a Primary Key is basically a UNIQUE NOT NULL Key. If you look at your table definition, this newly defined UNIQUE constraint is basically a Primary Key only (because the fields are NOT NULL as well). So, in this particular case, you don't really need to use a Surrogate Primary key id.
The difference in overheads during random DML (Insert/Update/Delete) will be minimal, as you would also have similar overheads when using a UNIQUE index only. So, you can rather define a Natural Primary Composite Key (student_id, course_id):
-- Drop the id column
ALTER TABLE students_courses DROP COLUMN id;
-- Add the composite Primary Key
ALTER TABLE students_courses ADD PRIMARY(student_id, course_id);
Above will also enforce the UNIQUE constraint on the combination of (student_id, course_id). Moreover, you will save 4 bytes per row (size of int is 4 bytes). This will come handly when you would have large tables.
Now, while Joining from students to students_courses table, above Primary Key will be a sufficient index. However, if you need to Join from courses to students_courses table, you will need another key for this purpose. So, you can define one more key on course_id as follows:
ALTER TABLE students_courses ADD INDEX (course_id);
Moreover, you should define Foreign Key constraints to ensure data integrity:
ALTER TABLE students_courses ADD FOREIGN KEY (student_id)
REFERENCES students(student_id);
ALTER TABLE students_courses ADD FOREIGN KEY (course_id)
REFERENCES courses(course_id);

MySQL: Normalizing content types, categories, and subcategories

I have the following basic table structure
Content
id
type_id (FK to ContentType)
category_id (nullable FK to Category)
sub_category_id (nullable FK to SubCategory)
ContentType
id
Category
id
content_type_id (FK to ContentType)
SubCategory
id
category_id (FK to Category)
It is important that the content's category should match its type.
Similarly the content's subcategory should match its category.
I'm aware that currently this is wide open to mismatching data. For example, a content record may have a category not allowed by its type and a subcategory not allowed by its category.
How could I change the table structure to secure against this? Or should I stick to using application logic?
SCHEMA BY REQUEST
CREATE TABLE `content_type` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
CREATE TABLE `category` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`content_type_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_category_content_type` FOREIGN KEY (`content_type_id`) REFERENCES `content_type` (`id`),
)
CREATE TABLE `sub_category` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`category_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_sub_category_category` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`),
)
CREATE TABLE `content` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `type_id` int(10) unsigned NOT NULL,
 `category_id` int(10) unsigned DEFAULT NULL,
 `sub_category_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_content_type` FOREIGN KEY (`type_id`) REFERENCES `content_type` (`id`),
CONSTRAINT `fk_content_category` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`)
CONSTRAINT `fk_content_sub_category` FOREIGN KEY (`sub_category_id`) REFERENCES `sub_category` (`id`)
)
In the Category table, create a unique index on (id, content_type_id). In the SubCategory table, create a unique index on (id, category_id).
Then in Content table, create FKs like this:
constraint FK_ContentCategory foreign key( category_id, type_id )
references Category( id, content_type_id )
constraint FK_ContentSubCategory foreign key( sub_category_id, category_id )
references SubCategory( id, category_id )
The FKs in Category and SubCategory tables remain the same.
Since the subcategory depends on the category which depends on the content type, make the FK references reflect the same chain of dependency. Now once you enter a type_id in the Content table, the category and subcategory values must belong to the proper chain of dependency.
However, if you ever need to change the type of a Content entry, you must first change category_id and sub_category_id to NULL, modify type_id, then insert correct values into category_id and sub_category_id. Small price to pay for solid data integrity.
In MySQL you can only add basic constraints. There is no way you can add conditional constraints (would love to see that).
Alternatively, you could built the checks in a BEFORE INSERT/UPDATE TRIGGER. And use a SIGNAL to cancel the INSERT/UPDATE (see https://dev.mysql.com/doc/refman/5.5/en/signal.html)
TBH I think it could become a true issue if you have '3rd-party' systems talking directly to the database. If not, you could solve it in your app. logic, and create API for the 3rd-party access.
If you really want to solve this on RDBMS level, you could have a look at PostgreSQL (via CHECK).
NOTE: With regard to you category tables, i would consider adding a parent_id and keep it in one table (self relation). That would allow to nest your category's passed the second level.

Enforcing cross table uniqueness of records in mysql

I have three tables:
CREATE TABLE A (
id int(11) NOT NULL AUTO_INCREMENT,
adata varchar(255),
PRIMARY KEY (id)
);
CREATE TABLE B (
id int(11) NOT NULL AUTO_INCREMENT,
bdata varchar(255),
a_id int(11) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (a_id) REFERENCES A(id) ON DELETE CASCADE
);
CREATE TABLE C (
id int(11) NOT NULL AUTO_INCREMENT,
cdata varchar(255),
b_id int(11) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (b_id) REFERENCES B(id) ON DELETE CASCADE
);
Elements of C (associated with B's) must be unique only for a given A. I.e., the combination of (cdata and b_id.a_id) must be unique.
I realize that I could add another column (pointing to A) to the C table and create a unique key. Is there any way to enforce uniqueness without adding another column to C?

CASCADE DELETE on last matching reference (Foreign key on composite primary key)

I have multiple events with the same id (event.id) but different languages (event.lang), whereby id and lang form a composite primary key of table event. An event can be represented by multiple records in the event table (one record per language). The records in the n:m mapping table *event_company_map* define a unique relation between a company and an event regardless of the event language.
-- events
CREATE TABLE `event` (
`id` INT(10) AUTO_INCREMENT,
`lang` INT(3),
`title` VARCHAR(255),
PRIMARY KEY (`id`, `lang`),
INDEX `lang` (`lang`),
)
-- companies
CREATE TABLE `company` (
`id` INT(10) AUTO_INCREMENT,
`name` VARCHAR(255),
PRIMARY KEY (`id`),
)
-- n:m relation table mapping events and companies
CREATE TABLE `event_company_map` (
`event_id` INT(10),
`company_id` INT(10),
PRIMARY KEY (`event_id`, `company_id`),
FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) ON UPDATE CASCADE,
FOREIGN KEY (`event_id`) REFERENCES `event` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
)
Let's say, there are three events with an event.id = 42, each having a unique language id (event.lang) e.g. 1,2 and 3. Now, when I delete one event record (id = '42', lang = 1) the mapping record with event_id = 42 in 'event_company_map' will be deleted no matter if there are still other events with id = 42 left.
I'd like to have the delete cascaded ONLY when the last event reference with id 42 gets deleted. How could I achieve that in MySQL?
Thank you.

cakephp model has 0 or 1 relationship

In cakephp, I have Company and Profile model. I would like to establish Company has 0 or 1 Profile relationship. Is it appropriate to use Company hasOne Profile, and Profile belongsTo Company? Is there any implication I need to be aware of? The table's schema is below. Thanks.
create table companies (
id char(36) NOT NULL,
type int(10) NOT NULL,
primary key (id)
);
create table profiles (
id char(36) NOT NULL,
company_id char(36) NOT NULL,
provider_contact varchar(255) NULL,
primary key (id),
unique key (company_id),
foreign key (company_id) references companies(id)
);
Yes you can use the hasOne/belongsTo Relationship, if a Company has no Profile the sub-array will be empty.
In your profiles table you should use company_id to follow the Cake naming conventions.
To follow CakePHP's conventions, the field needs to be company_id, not companies_id.
The foreign key constraint is up to you. I typically don't add them. The unique constraint seems incorrect. This would imply that there can only be one profile per company. Which is not the same as a company can have 0 or 1 profiles.
Stick to conventions.
You need to adjust your schema to do that
create table companies (
id int(11) NOT NULL,
type int(11) NOT NULL,
primary key (id)
);
create table profiles (
id int(11) NOT NULL,
company_id int(11) NOT NULL,
provider_contact varchar(255) NULL,
primary key (id),
unique key (company_id),
foreign key (company_id) references companies(id)
);
It is company_id as others have mention. And also, ids have to be ints, and autoincremental. It's a pain to have them as chars.