Can anyone give many to many MySQL step-by-step guide? - mysql

after searching up and down, reading all possible articles and tutorials, I grasped the basics of the concept, but still cannot do it, and so as many others as I can see.
Can someone please post 100% practical and dummy proof guide to creation and most basic usage of MySQL many to many relationship, I'm so sure many will benefit from it.
What I have is a table that has a number of items, say I1, I2, I3...
I have another table, with a number of properties, say P1, P2, P3...
For each of the items, each of the properties may hold false or true, for example
I1 has properties P2 and P3
I2 has properties P1, P2 and P3
I3 has properties P1
...
So how to go about creating the relationship? (please give code if possible)
And once created, how to
insert properties for some item I
read which properties apply to some existing item I
Thanks in advance

Step 1 - Setup tables:
You should have a table structure like below (*'s are primary keys):
Item
---------
ItemId*
ItemName
ItemProperties
--------------
ItemId
PropertyId
Properties
----------
PropertyId*
PropertyName
Step 2 - Set foreign key relationships:
Both columns in the ItemProperties table are foreign keys to their corresponding table (ItemId to Item table, PropertyId to Properties table)
Step 3 - Code:
To associate the properties with PropertyIds 35 and 44 to the Item with ID 111 you would run the following:
INSERT INTO ItemProperties (ItemId,PropertyId) VALUES (111,35)
INSERT INTO ItemProperties (ItemId,PropertyId) VALUES (111,44)
To select all properties associated with an item you would run the following:
SELECT ip.PropertyId, ip.PropertyName
FROM Item as i
INNER JOIN ItemProperties as ip ON i.ItemId = ip.ItemId
WHERE i.ItemId = 111

Create an intermediary table between the two, that has ids from both the other tables. That way there can be multiple references (rows) in this table connecting different items to multiple different properties. Does this make sense?

what I would do is create a table called ItemProperties with columns ItemId, PropertyId and PropertyValue.
the key (ItemId, PropertyId) would unique for this table.

This links products to properties, allowing multiple items with the same name.
CREATE TABLE products (
productid int(11) NOT NULL AUTO_INCREMENT,
productname varchar(20) NOT NULL,
description text NOT NULL,
PRIMARY KEY (productid),
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE products_property (
relationid int(11) NOT NULL AUTO_INCREMENT,
productid int(11) NOT NULL,
propertyname int(11) NOT NULL,
PRIMARY KEY (relationid)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE properties (
property varchar(30) NOT NULL,
value enum('true','false') NOT NULL,
PRIMARY KEY (property)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

Related

Sequelize loading in initial data

I need to load data into a DB using Sequelize on first application load. The initial excel data was given in the following format:
Car group fields: title | group_code
Car group data:
('Mercedes','M'),
('Volkswagen','VW');
Car Fields: car_code | owner | group_code
Car data:
('11-1135','Fred','M'),
('11-1146','Bob','VW');
--
Ideally what I want to end up with in the DB is the following:
Car group fields: group_id | title | group_code
Car group data:
(1, 'Mercedes','M'),
(2, 'Volkswagen','VW');
Car Fields: car_id | car_code | owner | group_id (refers to the group id created above)
Car data:
(1, '11-1135','Fred', 1),
(2, '11-1146','Bob', 2);
--
What is the best approach to doing this in Sequelize? In SQL I did the following to get around this problem:
1- Converted my Excel file into a bunch of SQL statements
2- Created the following script using those statements (and then i added my own code to fill in the group_id):
CREATE TABLE CarGroup(
group_id INT NOT NULL AUTO_INCREMENT,
title VARCHAR(100) NOT NULL,
group_code VARCHAR(5) NOT NULL,
PRIMARY KEY (`group_id`),
CONSTRAINT UN_car_group_code UNIQUE (group_code)
) ENGINE=InnoDB;
INSERT INTO CarGroup(title,group_code) VALUES ('Mercedes','M');
INSERT INTO CarGroup(title,group_code) VALUES ('Volkswagen','VW');
CREATE TABLE IF NOT EXISTS Car(
car_id INT NOT NULL AUTO_INCREMENT,
car_code VARCHAR(10),
owner VARCHAR(100) NOT NULL,
group_id SMALLINT, -- populated after insert
group_code VARCHAR(10) NOT NULL, -- deleted after insert
PRIMARY KEY (id),
CONSTRAINT `UN_car_code` UNIQUE (`car_code`),
CONSTRAINT `FK_car_group_id` FOREIGN KEY (`group_id`) REFERENCES `CarGroup` (`group_id`)
) ENGINE=InnoDB;
INSERT INTO Car(car_code,owner,group_code) VALUES ('11-1135','Fred','M');
INSERT INTO Car(car_code,owner,group_code) VALUES ('11-1146','Bob','VW');
-- GENERATE GROUP ID'S BASED ON GROUP CODE AND DROP GROUP CODE COLUMN --
update Car INNER JOIN CarGroup ON Car.group_code = CarGroup.group_code
SET Car.group_id = CarGroup.group_id;
alter table Car drop column group_code
I can't see how the above can be achieved by using migrations and seeding as I need to create the model then do seeding and then run the alteration. Is it easier to just run plain SQL statements in Sequelize in this case? Or should I just use the data as it is and link the two tables as a foreign key via the group_code (which is a string - not best performance in comparison to plain INT id).
Any direction on this is muchly appreciated!
Not sure if this is the best approach but since no one answered, but i have decided to do the following:
Create two tables, OriginalCars and Cars. OriginalCars has the original fields that the excel file has (i.e. car_code). The Cars table has the car_id and other fields.
Create the models
Sync the models
Check manually if there is any data in the tables, if not then populate the originalCars table with data. I then do an innerjoin of the OriginalCars with the group table, the resulting data is parsed and added to the Car table with car_id.
Delete the original table as its no longer needed
Feels a tad hacky but it only has to do this on initial load of the App to populate the initial data.

How to add columnName and its value dynamically into MYSQL Table

I have a master table, For e.g Fruits_Details(Master Table) which contains column name as:
f_name | f_price | location
I want to present a form to the user which consist of the above fields but also contains an additional "+" sign from which he can add new details as key, value pair.
For e.g :
fruit_color - red
fruit_season - spring
And many more details like this. I want that these two details should be stored in a different table(Child Table-I will implement foreign key concept).
But I am confused that how will design a query which will dynamically add column name and its related value in my child table.
The child table should have a attibute name and value there has to be nothing dynamic.
child table
-----------
fruit_id
attribute_name
attribute_value
insert into child (fruit_id, attribute_name, attribute_value)
values (1, 'color', 'red')
Since the potential is endless, don't do it by columns, but by rows...
You'll be able to extract the info one way, and display it another.
A table called fruits_attributes, that will contain the columns:
attribute_id, f_id (foreign key), attribute_name, attribute_value
CREATE TABLE `fruits_attributes` (
`attribure_id` int(8) unsigned NOT NULL AUTO_INCREMENT,
`f_id` mediumint(8) unsigned NOT NULL,
`attribure_name` varchar(50) NOT NULL,
`attribure_value` varchar(50) NOT NULL,
PRIMARY KEY (`id`),
KEY `f_id` (`offer_id`),
KEY `attribure_name` (`attribure_name`),
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
;
Then a SELECT query should be something like
SELECT f.*, fa.* FROM
Fruits_Details f
LEFT JOIN fruits_attributes fa ON fa.id = f_id

Inserting data with foreign keys and avoiding duplication

I'm very beginner at MySQL and have just started to play around with foreign keys and INNER JOIN operator.
So I've made a few tables like:
CREATE TABLE `models`
(
`id` TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR (255) NOT NULL,
`price` MEDIUMINT UNSIGNED NOT NULL,
PRIMARY KEY( `id` )
);
CREATE TABLE `vendors`
(
`id` TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR (255) NOT NULL,
`id_model` TINYINT UNSIGNED NOT NULL,
PRIMARY KEY( `id` ),
FOREIGN KEY (`id_model`) REFERENCES models(`id`)
);
CREATE TABLE `cars`
(
`serial_number` MEDIUMINT UNSIGNED NOT NULL,
`id_vendor` TINYINT UNSIGNED NOT NULL,
FOREIGN KEY (`id_vendor`) REFERENCES vendors(`id`),
PRIMARY KEY( `serial_number` )
);
I know how to get output with that. However, the problem is that I don't know how to insert data properly. All I can do is insert data table by table. But how to do it in one query, and if I am inserting Honda Civic and already have Honda Accord, for example, wouldn't it duplicate Honda vendor in the database?
It seems that the structure of the database is not really coherent. Maybe I don't understand what you are trying to do, but ... anyway, here goes.
Assuming that what you want to do is store a list a of cars in a properly normalized relational database, first thing you want to do is think what is happenning in "real life":
Manufacturers (vendors) make/sell cars
Manufacturers (vendors) make different models of cars
Cars have a model (and a serial number in your case)
Models belong to a vendor (manufacturers)
Considering this, your table structure is:
Vendors
- id
- name
Models
- id
- name
- vendor ( foreign key => vendor.id )
Cars
- id
- serial_number
- model ( foreign key => model.id )
You don't need to have a reference to the vendor in the cars table becoause you have a reference to the model, which in turn has a reference to the vendor.
Whe inserting, you do it one by one, making sure that the foreign key entries already exist.
When you insert a car object, you just need to provide the model id.
When you insert a model object you need to provide a vendor id.
So the Honda Civic/Accord situation does not duplicate Honda. The Tables should be something like this:
Vendor
id, name
1, "Honda"
Model
id, name, vendor
1, "Civic", 1
2, "Accord", 1
Cars
id, serial_no, model
1, "A serial", 2 -> a honda accord
2, "Another serial", 1 -> a honda civic
Hope this helps somewhat.
You do need to check if duplicated record exists yourself.
IF EXISTS (SELECT * FROM vendors WHERE Name = 'Honda')
BEGIN
-- Insert into cars with existing vendor id
END
ELSE
BEGIN
IF EXISTIS (SELECT * FROM models WHERE Name = 'your model name')
BEGIN
-- insert into vendors with existing model id
END
ELSE
BEGIN
-- insert into models
-- insert into vendors
-- insert into cars
END
END
You can create stored procedure for it and pass car, vendor and model as parameters.
Or you can list models first, insert them; then all vendors and all cars. Just a silly answer. Welcome more sophisticated solutions.

MySQL multiple columns as unordered sets

I'm somewhat new to MySQL and SQL in general, so hopefully this isn't a simple question.
I have a table that represents items in a customer's basket at checkout. This table represents a situation in which a customer is limited to 3 items, so I currently have a column for each item in the basket. It looks like this:
+------------------------------------------------------+
+ id | item1 | item2 | item3 | val |
+------------------------------------------------------+
where val is just some value associated with the basket. The ordering of the items means nothing in terms of my processing, so in theory I would like to have them represented as an unordered set. This means that a row of (i1,i2,i3,val) is functionally equivalent to (i2,i1,i3,val).
My question is, how do I implement this in my table and/or in SQL such that selecting (i3,i2,i1,val) will return the row for (i1,i2,i3,val)?
I also need to have something that catches uniqueness when I'm inserting. For example, if I insert (i2,i3,i1,newval), I would want the table to update (i1,i2,i3,val) to be (i1,i2,i3,newval).
You could standardise your model by using a 0 to many relation between customer and item:
-- assuming the existing table to be named `yourtable`
-- assuming your customer's table to be named `customer`
-- assuming your customer's id in the customer's table to be named `id`
-- assuming innodb (remove fk constraint if not)
CREATE TABLE `customer_item` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`id_customer` INT(10) NOT NULL,
`item` VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`)
)
ENGINE=innodb
SELECT NULL AS `id`, t.id AS `id_customer`, t.`item`
FROM (
SELECT id, item1 AS `item`
FROM
yourtable
UNION
SELECT id, item2 AS `item`
FROM
yourtable
UNION
SELECT id, item3 AS `item`
FROM
yourtable
) t
ORDER BY t.id ASC
;
CREATE INDEX UNIQUE `idx_customer_item_cust` ON `customer_item` (`id_customer`, `item`);
ALTER TABLE `customer_item` ADD CONSTRAINT `fk_customer_item_cust` FOREIGN KEY (`id_customer`) REFERENCES `customer` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- once you check the data is consistent:
DROP TABLE `yourtable`;
Once that done, no item could be possibly inserted twice for the same customer.
Please note:
the union select skips duplicates already at table creation, in case some items were repeated for some customers
your data is normalised, from the customer to item point of view
your data is still not normalised, from the item point of view. You should have an item table, and the customer_item table should reference the id of items in the item table instead of using item names or description for varchars.

mysql query help, getting values from table using relational id's from another table

THE SQL THAT BUILDS THE TABLES,
--
-- Table structure for table `careers`
--
CREATE TABLE IF NOT EXISTS `careers` (
`career_id` int(11) NOT NULL auto_increment,
`career_name` varchar(75) NOT NULL,
`career_desc` text NOT NULL,
`degree_needed` enum('Yes','No') NOT NULL,
`useful_info` text,
`useful_links` text,
PRIMARY KEY (`career_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=34 ;
-- --------------------------------------------------------
--
-- Table structure for table `course`
--
CREATE TABLE IF NOT EXISTS `course` (
`course_id` int(11) NOT NULL auto_increment,
`course_type` varchar(75) NOT NULL,
`course_names` text NOT NULL,
`extra_needed` enum('Yes','No') default NULL,
`course_link` varchar(150) NOT NULL,
`grades_grade_id` int(11) NOT NULL,
PRIMARY KEY (`course_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=87 ;
-- --------------------------------------------------------
--
-- Table structure for table `grades`
--
CREATE TABLE IF NOT EXISTS `grades` (
`grade_id` int(11) NOT NULL auto_increment,
`grade_desc` text NOT NULL,
`careers_career_id` int(11) NOT NULL,
PRIMARY KEY (`grade_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=87 ;
-- --------------------------------------------------------
An overview of my theory behind the tables, is that each grade is associated with a career and one career can have many grades, from that one course is only associated to one course, but a user may need to do an extra course if the one they pick is not accredited highly enough.
So my question is how do I select the course details for the higher level courses if the user selects a low level course,
for example the user wants to be an electrician, and they have 2 D grades in school this means they can only do a level 2 course, this means that to complete the course they have to do a higher level course. I need to be able to show what the other courses are based on the fact they have selected electrician and a level 2 course, it is worth noting that courses that require extra work have a field 'extra_needed` that is marked as yes.
I cannot for the live or me work out how to get the right data out, I have tried the following,
SELECT *
FROM `course` , `grades` , `careers`
WHERE `course`.`extra_needed` IS NULL
AND `grades`.`grade_id` = `careers`.`career_id`
AND `careers`.`career_id` =6
however this brings back 59 rows of data where as it should bring back 2 rows of data, the other to rows of data that the user could select if they chose the other grade choices.
Looks to me like you are joining on the wrong fields, the relationships look like they would be as follows:
careers.career_id = grades.careers_career_id
grades.grade_id = course.grades_grade_id
so for all courses related to career.career_id = 6 the query would look as follows:
select course.*
from course,
careers,
grades
where course.grades_grade_id = grades.grade_id
and grades.careers_career_id = careers.career_id
and careers.career_id = 6
You would need a more complex query to do what you originally asked though which would involve specifying not only a career_id but also a course_id and then a conditional statement to say whether any further courses are required but I'm not sure if you have all the fields necessary to do this as you would need to know the relationship between the course they have selected and all other courses pertaining to the relevant career. If you simply wish to see all the other courses relating to that career then you would add a line like:
and course.course_id <> (The course they have selected)
If there are only ever two levels of courses then you could add a line like below as if they have selected the higher level it can't satisfy both the last statement and this one whereas if they have selected the lower level both will be true:
and course.extra_needed IS NULL
Replace your query by this one:
SELECT *
FROM careers AS c
LEFT JOIN grades AS g ON g.careers_career_id = c.career_id
LEFT JOIN course AS crs ON crs.grades_grade_id = g.grade_id
WHERE c.career_id =6
AND crs.extra_needed IS NULL
It should work,
Good luck