How would I create this MySQL Schema? - mysql

Suppose I have a blog post entity.
It has many attributes
It has comments attached to it.
It has many states (deleted/locked/invisible, etc).
It has many "tags". (keywords, school_id, user_id)
Obviously, comments should be its own table, with a many-to-one relationship to Blog table.
But what about "states" or "tags"? Would you put that in another table? Or would you stick that in many columns?
What about attributes...if they get too big? Because as my website grows, the blog post will have more and more attributes attached (title, author, blah, blah....). What happens if the attribute list goes as high as 100?

Here's a sample:
Again.. It's just a sample.. There are other approaches that you can use.
Here we go:
-- basic-basic blog
CREATE TABLE blog_entry (
blog_entry_id INT NOT NULL AUTO_INCREMENT,
blog_entry_title VARCHAR(255) NOT NULL,
blog_entry_text VARCHAR(4000) NOT NULL,
create_date DATETIME,
state_id INT
);
-- create a look-up table for your blog entry's state
CREATE TABLE be_state (
state_id INT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (state_id)
);
-- create a look-up table for your blog entry's tag/s
CREATE TABLE be_tag (
tag_id INT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (tag_id)
);
-- a table to store multiple tags to one entry
CREATE TABLE blog_entry_tags (
blog_entry_id INT NOT NULL,
tag_id INT NOT NULL,
PRIMARY KEY (blog_entry_id, tag_id)
);
-- a table to store definitions of attributes
CREATE TABLE be_attribute (
attribute_id INT NOT NULL AUTO_INCREMENT,
name CHAR(30)
);
-- now have a table to which you can assign multiple attributes to one blog
-- of course, this is if I understand you correctly
-- where you want to have additional attributes
-- aside from the basic properties of a blog entry
-- and will allow you, if you choose to do it
-- to not necessarily have all attributes for each entry
CREATE TABLE blog_entry_attributes (
blog_entry_id INT NOT NULL,
attribute_id INT NOT NULL,
PRIMARY KEY (blog_entry_id, attribute_id)
-- PK enforces one blog entry may have only one attribute of its type
-- meaning, no multiple attributes of 'location' attribute,
-- for example, for one blog. Unless of course you wrote half the entry
-- in one location and finished it in the next.. then you should
-- NOT enforce this primary key
);
blog_entry - your main table, where the goods go
be_state - define them here, and insert their state_id values in blog_entry.state_id
be_tag - have multiple tags like we do here
blog_entry_tags - since you can possibly have many tags for one blog entry, store them here and insert blog_entry.blog_entry_id and the corresponding be_tag.tag_id together. one tag of its type per blog entry. meaning you can't tag entry#1 (for example) the tag php twice or more.
be_attribute - store attribute definitions here like location, author, etc
blog_entry_attributes - similar to blog_entry_tags where you can assign one or more than one be_attribute to a blog entry.
Again, this is just one approach.

first of all, states should be a tightly structured thing, so you should create separate columns for them. Think about what you need at the beginning, but you can easily add one or two more columns later.
Tags like keywords shouldn't be stored in columns, because the amount is growing rapidly over time. That wouldn't make any sense. So for that, build a table with id and keyword in it and a link table with post_id and keyword_id. You could also omit the keyword_id and directly link post_id and keyword.
Make sure that both columns combined define the primary key, so you can not end up with a keyword stored several time to one particular post.
For attributes it can be the same. It is not a bad practice to create an attribute table with attribute_id, attribute_name and maybe more information and a link table attribute_id and post_id and content.
You can also easily enhance it to be multilingual by using attribute_ids.
Comments are the same, stored in a separate table with a link to a user and a post: comment_id, user_id, post_id, content and maybe parent_id, which can be a comment_id if you want comments to be commentable again.
That's it for a brief overview.

Related

"many to many to many" relation

I have a simple database of cosmetics. Each product may consist of multiple ingredients. Therefore it is a simple many to many relation and currently I have a database like this:
CREATE TABLE `products` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` TEXT NOT NULL,
PRIMARY KEY (`id`),
);
CREATE TABLE `ingredients` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` TEXT NOT NULL,
PRIMARY KEY (`id`),
);
CREATE TABLE `ingredient_to_product` (
`ingredient_id` INT UNSIGNED NOT NULL,
`product_id` INT UNSIGNED NOT NULL,
PRIMARY KEY (`ingredient_id`, `product_id`),
INDEX (`product_id`)
);
However, some ingredients can have many different names. For example: "LINALOOL ESSENTIAL OIL", "LINALYL ALCOHOL" and "3,7-DIMETHYL-1,6-OCTADIEN-3-OL" refers to the same ingredient.
When I am presenting one product, I would like to keep ingredient name as on product label. So in case of one product it will be "LINALYL ALCOHOL" and in case of another product it may be something other, but referring to identical ingredient.
Now I would like to query database for all names of the product by specifying one of its names or by its ID, however how one ID could refer to many names? Do I have to keep two tables: one for specific ingredient in general and another table just for names (relation many names to one ingredient)? Is there any better way?
Ingredients
ID (int) (pkey) (ai)
Name (varchar)
Synonyms
ID (int) (pkey) (ai)
IngredientID (int) (foreign key maps to: Ingredients.ID)
Name (varchar)
Flag (optional; sample values: IUPAC Name, Common Name, Spanish Name, ...)
So yes, for the rules you described, you will at minimum have a primary name, and then a synonym table using the Ingredients.ID
I also introduced a Flag field so that you can programmatically choose when to use a synonym rather than just hard-coding it all the time. For example, if you're listing a product flagged as an industrial chemical, it would be programmed to pull the IUPAC* name
*IUPAC refers to conventions for naming chemicals, and is used by professional chemists almost always, but almost never used in consumer supermarkets: http://www.chem.uiuc.edu/GenChemReferences/nomenclature_rules.html
I suppose the good news is that one name cannot be used for multiple different ingredients, right?
So yes, you probably need an "ingredient name" table, something like
`name` TEXT NOT NULL,
`ingredient_id` INT UNSIGNED NOT NULL
(I'd probably just use name as the PK here, but if your design calls for always using surrogates then also add ingredient_name_id I guess...)
And then the details of the ingredient (except name) would still go in ingredient table. (You could keep a default name in the ingredient table as well, but I wouldn't. If you need that concept, I'd consider adding a "default flag" to the ingredient name table or something like that.)
Since you want to know the name used for each ingredient by a given product, you would replace your current "ingredient to product xref" with an "ingredient-name to product xref".
Why don't you create a separate table for other ingredient name/chemical names, etc. and have it connected with the product id so it can be used separately when needed, and to avoid having any NULL values in your main table.

How can I create categories with different options of it in mysql tables

I want to design a database in MySQL about categories with options for categories.
Example : Script classified ..
When you want to add ads into the "cars category" you must add some options for this car,but when you add ads in "Apartments for sale category" you must add some options for this Apartment, for example, this image from Dubizzle site illustrates for classified.
When you want to add a car in "cars category", you will show these options
and when you want to add ads in "Apartments for sale category"
you will show these options:
How can I design the database as such?
categories and sub-categories with different options for any categories
create table category
(
id int auto_increment primary key,
catName varchar(100) not null
);
create table catOption
( -- no attempt to share option across multiple categories
id int auto_increment primary key,
catId int not null,
descr varchar(100) not null,
listingOrder int not null -- order listed on screen
-- include FK into category
);
create table listing
(
id int auto_increment primary key,
catId int not null,
title varchar(100) not null,
verbeage text,
price decimal(12,0) not null
-- include FK into category
);
create table listOptions
( -- this table houses a particular listing options (answers if you will)
-- programmer chooses whether or not to save 'blanks'
id int auto_increment primary key,
listingId int not null,
optionId int not null,
answer varchar(100) not null -- you choose datatype
-- include FK into listing
-- include FK into catOption
);
The FOREIGN KEY constraint is used to prevent actions that would
destroy links between tables.
The FOREIGN KEY constraint also prevents invalid data from being
inserted into the foreign key column, because it has to be one of the
values contained in the table it points to.
Categories
First, you need to define your categories table, like categories(id, name, description)
Control Types
As we have seen from the pictures that you have shared, sometimes you have a set of checkboxes, sometimes you have dropdown options, etc. To support this, you need a table like control_types(id, name, description). In your application you will know by the control type value what control needs to be used and how the selections should be understood.
Controls
Now, you can create your controls, like this: controls(id, control_type_id, title). Note that we do not have a category_id in this table, because we want to be able to support the possibility of reusing some controls in multiple categories as long as they are compatible with those. For instance, the price control that you have is needed for both categories. So it is not enough to assume that each control is linked to a single category, quite the contrary, a category can have multiple controls and a control might belong to multiple categories.
Controls of Categories
To achieve the many-to-many relation described in the previous chapter, we create a table where we store the pairs: controls_of_categories(id, category_id, control_id).
Control Items
It's nice that we have the controls, but sometimes they are not trivial. Sometimes we need to select a single value from some options (dropdown) or, we might need to be able to select several options at the same time (checkboxes). To achieve this, we will need another table, like this: control_items(id, control_id, label, value).
Category_sub_category_tab:
CId PK
,CName Text
,SubCatId FK(Category_sub_category_tab(CId))
Options:
ID PK
,OName Text
,CID FK(Category_sub_category_tab(CId))
It a dynamic scheme , first you have to define your categories , controls (text, drop-down ,etc) then you have to define categoryFields this will contian categoryId, field info.. , and you have to define field values table for type drop-down and stuff like that

Data Model, alternative to EAV

I have a Product Database, and I want to attach text, images, videos to the products. I also want that each entity (text, images or videos) have a tag, for further organisation on application.
I thought of using this model:
Content:
content_id|content_product_id|content_type|content_tag_id|content_url|content_title|content_text
Tag
tag_id|tag_name
This mean to use Entity(content_product_id) - Attribute(content_tag_id) - Value (content_url or content_title|content_text) Model.
After reading a lot, I understood that is a bad idea to use this modeling pattern (described as a database antipattern, unscalable and causing performance issues), have you an idea for an alternative method for this ?
I want to use Doctrine ORM, and I would like to find an method that will be easily compatible with that data mapper
I'd create a general table for any type of content:
CREATE TABLE ProductContents(
content_id INT AUTO_INCREMENT PRIMARY KEY,
content_type INT NOT NULL
-- other general attributes like when it was created, by whom, etc.
);
For each text, image, or video, insert one row into this table. If you use an auto-increment primary key, this table is responsible for generating the id number.
For tags, now you simply have a many-to-many relationship between ProductContent and Tags. This is represented by an intersection table.
CREATE TABLE Tags (
tag_id INT AUTO_INCREMENT PRIMARY KEY,
tag TEXT NOT NULL
);
CREATE TABLE ProductContentTagged (
content_id INT,
tag_id INT,
PRIMARY KEY (content_id, tag_id),
FOREIGN KEY (content_id) REFERENCES ProductContents(content_id),
FOREIGN KEY (tag_id) REFERENCES Tags(tag_id),
);
Then if you have any attributes specific to each type of content, create auxiliary tables for each type, with a one-to-one relationship to the content table.
CREATE TABLE ProductContentTexts (
content_id INT PRIMARY KEY,
content TEXT NOT NULL,
FOREIGN KEY (content_id) REFERENCES ProductContents(content_id)
);
CREATE TABLE ProductContentImages (
content_id INT PRIMARY KEY,
image_path TEXT NOT NULL,
FOREIGN KEY (content_id) REFERENCES ProductContents(content_id)
);
CREATE TABLE ProductContentVideos (
content_id INT PRIMARY KEY,
video_path TEXT NOT NULL,
FOREIGN KEY (content_id) REFERENCES ProductContents(content_id)
);
Note these auxiliary tables don't have an auto-increment column. They don't need to -- they will always use the value that was generated by the ProductContents table, and you're responsible for inserting that value.
Bill Karwin's answer is very good.
However, since you say:
I want to use Doctrine ORM, and I would like to find an method that will be easily compatible with that data mapper
I'll relate his answer to that particular ORM.
What Bill describes is inheritance. You have a superclass of "content", represented by a table that holds all the shared data. Then you have subclasses (text, image, video) that extend that superclass by adding content-type-specific columns.
Doctrine2 will do essentially what Bill has suggested when you use class-table inheritance. Once you configure your entities properly, it will create a set of tables very similar to what Bill describes.
So, with Doctrine you cave the Content entity, which is extended by Image, Text, and Video.
As far as the tagging goes, you would just create a basic Tag entity, and Content would have a ManyToMany relationship to Tag. Doctrine will handle creating the intermediate table for you.

MySQL table design - refer to multiple foreign rows

Minimilized background (ie in bare pseudo code details)
I am making a record keeping (among other things) php/mysql app for my farm. There are lots of types of animals etc that could have pictures (or other records - videos etc.) but just for simplicity I'll only refer to one of each (Goats and Pictures). so say the
tables are approximately like so:
CREATE TABLE BMD_farmrecords_goats (
goat_id INT NOT NULL AUTO_INCREMENT,
goat_name TEXT,
...more columns but whatever, unimportant...
primary_key(goat_id))
CREATE TABLE BMD_farmrecords_pictures (
media_id INT NOT NULL AUTO_INCREMENT,
media_name TEXT,
media_description TEXT,
media_description_short TEXT,
media_date_upload DATE,
media_date_taken DATE,
media_uploader INT, //foreign key constrained to user table but unimportant for question
media_type ENUM('jpg','gif','png'),
media_hidden BOOL,
media_category INT, //foreign key constrained to category table but unimportant for question
PRIMARY KEY (media_id)
So the problem(s):
Obviously a picture could have multiple goats in it so I can't just
have one foreign key in picture to refer to goat.
there are more than one livestock tables that would also make that a poor choice but not worried about that right now
Basically no optimization has been applied as of yet (ie no lengths set, using TEXT rather than varchar(length)) etc; I'm not worried about that until I populate it a bunch and see exactly how long I want everything.
so the question:
what is the best_ way to link a picture to multiple goats (in terms of A) best performance B) best code conformance to standards. I'm thinking I'll have to do an extra table:
create TABLE BMD_farmrecords_goatpictures (
id INT NOT NULL AUTO_INCREMENT
picture_id INT //foreign key to BMD_farmrecords_pictures->media_id
goat_id INT//foreign key to BMD_farmrecords_goats->goat_id
So is there any better way to do that?
Of course with that method I'll probably have to change *_goats table to be a parent *_animals table with then a type field and reference animal_id instead but I'm not worried about that, just about whether or not the extra table referencing both tables is the best method.
thanks;
From the discussion just changing my original idea to use a composite primary key:
create TABLE BMD_farmrecords_goatpictures (
picture_id INT //foreign key to BMD_farmrecords_pictures->media_id
goat_id INT//foreign key to BMD_farmrecords_goats->goat_id
PRIMARY KEY (picture_id, goat_id))

How to relate the data in two MySQL tables?

I am designing a web app which basically has to store two kinds of elements: posts and containers (which are arrays of posts)
I have done the database so that i store in each row of the container the stringified version of the array of the posts which it holds.
The issue is that when a post is deleted each time i call the container i have the id of the post and i have to check if the post is alive or not.
Is there a better way to do this ? for example structuring the container table to hold "pointers" to the actual posts ?
Thanks a lot!
If I understand correctly, you have a one-to-many relation: each container can have many posts, but each post is related to a single container. So your database design should reflect this.
Example:
create table tbl_containers (
containerId int unsigned not null auto_increment primary key,
containerDescription varchar(100)
);
create table tbl_posts (
postId int unsigned not null auto_increment primary key,
containerId int unsigned not null,
postText varchar(100),
index idxContInd(containerId)
);
When you fill your data, every post must be associated with a container; in other words, before a post can be entered there must be a container to hold it, and if there's no "suitable" container, it should be created before the creation of the post.
After your data is in the tables, it is easy to look for the posts, and delete them, independently of the container each post belongs to.
Hope this helps you
Using a foreign key relationship between the two tables would be ideal.
First, if I understand the relationship between your data properly, your Container table would be the table that holds information relating to a unique id. The `Posts table then has its own unique id, a containerId, and the post content itself.
Drawn out, it'd look a little like this.
Container
---
id
Posts
---
id | containerId | post
The relationship between Container and Posts is presumed to be one-to-many; that is to say, one container may have many posts, but one post can't belong to many containers. If you wanted this relationship, you'd likely have to add another column for that to Posts.
Here goes the answer to back my comment that recieved some positive feedback :) although you already have answers up there.
Your Container is going to hold the posts. So it's like one basket holds many items in it.
So for each post, there's a container id. For container table - container id is primary key. posts table - post id is primary key. To have the Foreign key relationship, you set your container id as foreign key in your posts table. If this is confusing - just see the table schema below. If you want to delete any posts related to a container when the container is deleted, you can use ON CASCADE DELETE on the parent key (foreign key) to remove any orphans (childrens without parents).
If you wish to have auto incremented ID, you can use the AUTO_INCREMENT otherwise just remove it.
And please take a look sql syntax for further understanding as well.
CREATE TABLE tblParent (
`CID` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
`field2` varchar(100),
`field3` varchar(100)
);
CREATE TABLE tblchild (
`PID` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
`PCID` int NOT NULL,
`field2` varchar(100),
`field3` varchar(100),
FOREIGN KEY REFERENCE tblParent ('CID')
ON DELETE CASCADE
);
I'm not sure if I understood you correctly but I would do this the other way around: You have your POST table, which has a field called container_ID. If the post gets deleted then a row in the POST table would get removed (including the pointer to its parent entity in the container_ID). In this case you can create a foreign key relations between the tables in order to keep the data consistent and relations intact within the database. Please clarify if I misunderstood something..
You can create a third table with two fields, 'container_id' and 'post_id' for example.
Then when you delete a post, you delete all entries containing the post_id, same thing for containers. Make sure that the table primary_key is composed by the two_fields to avoid entries duplication.