when should i create a separate table for fields? - mysql

Let's say i have this SQL table :
CREATE TABLE `article` (
`article_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`family` varchar(255) NOT NULL,
`sub_family` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`price` float NOT NULL,
PRIMARY KEY (`article_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Here every family of articles has one or multiple sub_family
should i create another table that has all possible combinations of family and sub_family and refer to it using a foreing key ?
Data example :

If you want to fix redundancy completely, please try the following table:
create table `family` (
`family_id` int(11) NOT NULL AUTO_INCREMENT,
`family` varchar(255) NOT NULL,
`parent_id` int(11) NOT NULL,
PRIMARY KEY (`family_id`),
index idx_family_sub (`family`, `parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `article` (
`article_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`family_id` int(11) not null,
`price` float NOT NULL,
PRIMARY KEY (`article_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
The data in there 2 table may look like:
mysql> select * from family order by family_id;
+-----------+-------------+-----------+
| family_id | family | parent_id |
+-----------+-------------+-----------+
| 1 | Main Course | 0 |
| 2 | Beverage | 0 |
| 3 | Pasta | 1 |
| 4 | Hot Drinks | 2 |
| 5 | Cold Drinks | 2 |
+-----------+-------------+-----------+
5 rows in set (0.00 sec)
mysql> select * from article;
+------------+-----------+-----------+-------+
| article_id | name | family_id | price |
+------------+-----------+-----------+-------+
| 1 | Spaghetti | 3 | 600 |
| 2 | Penne | 3 | 500 |
| 3 | Coffee | 4 | 100 |
| 4 | Penne | 5 | 150 |
+------------+-----------+-----------+-------+
4 rows in set (0.00 sec)
The query to get all data like data example is :
mysql> select
-> a.article_id, a.name, f2.family as family, f.family as sub_family, a.price
-> from `article2` a
-> join family2 f on a.family_id = f.family_id
-> join family2 as f2 on f.parent_id = f2.family_id;
+------------+-----------+-------------+-------------+-------+
| article_id | name | family | sub_family | price |
+------------+-----------+-------------+-------------+-------+
| 1 | Spaghetti | Main Course | Pasta | 600 |
| 2 | Penne | Main Course | Pasta | 500 |
| 3 | Coffee | Beverage | Hot Drinks | 100 |
| 4 | Penne | Beverage | Cold Drinks | 150 |
+------------+-----------+-------------+-------------+-------+
4 rows in set (0.00 sec)
From my perspective, this table is used to save food menu data, and it should be very small. I think it's OK to create a dedicated table to save family and sub_family.

I read your question and comments as well. I think you can create two different tables that'll for sure reduce the data redundancy and complexity.
You can create Family as a parent table:
In Family table you can declare a unique key(which you can use further for foreign key referencing) for each Family and sub-Family pair for example:
Family:
ID | Family_name | Subfamily
1 Beverage Hot Drinks
2 Beverage cold Drinks
3 main course Pasta
And you can create another table for Articles which will be connected with parent via foreign key reference ID.
Article:
ID | Family | Name | Price
1 2 Milk Shake 100.00
2 1 Coffee 200.00
You can try something like this. I hope you find this easy to understand.

Related

Mysql - Select, store in var, select in same query

I am trying to understand how to do this. Not much help found.
I have two tables.
First table:
CREATE TABLE `first` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
)
Second table:
CREATE TABLE `second` (
`id` int(11) NOT NULL,
`foo` varchar(3) NOT NULL ,
`bar` varchar(255) NOT NULL )
Id in both tables is same value, they are related that way.
I have to fetch some entries, for example with parent_id = '0' and to check if those entries have children. If they has I should get their ids too.
It could be done easily with little php and two queries but I would like to do it better. I tried something like:
SELECT `first`.*, `second`.*, children.*
FROM `first`
JOIN `second` ON `second`.id = `first`.id
INNER JOIN( SELECT
// Something
)AS children ON children.id = `second`.id
WHERE `first`.parent_id = '0'
GROUP BY `first`.id, `second`.la
But I did not make it.
How could I Select those result and their children in same query? Thanks!
EDIT
First:
-------------------------------------------------
| id | parent_id |
--------------------------------------------------
| 1 | 0 |
--------------------------------------------------
| 2 | 1 |
--------------------------------------------------
| 3 | 2 |
--------------------------------------------------
| 4 | 1 |
--------------------------------------------------
Second table:
-------------------------------------------------
| id | foo | name
--------------------------------------------------
| 1 | asd | apple
--------------------------------------------------
| 2 | asd | banana
--------------------------------------------------
| 3 | gsf | orange
--------------------------------------------------
| 4 | gre | potato
--------------------------------------------------
Desired Result would be to get row where id is 1 (joined both tables) and all ids of rows witch have parent_id 1.
Thanks!

Fetching App level and Global level settings from DB

I am building a dynamic application which will act based on settings.
The settings are stored in a MySQL table which consists of both App level data and global level data (app_id = 0).
My use case is, I want to select the settings of an App. If it does not exist, fetch the corresponding setting from the global level.
I have achieved this using sub queries and COALESCE function.
Question: Can the data be fetched in a single query? If not, Can the schema be modified to handle this App level and Global level in a much simpler way?
Schema
CREATE TABLE `settings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`partner_id` int(11) NOT NULL,
`app_id` int(11) DEFAULT NULL,
`type` varchar(64) DEFAULT NULL,
`name` varchar(64) DEFAULT NULL,
`value` varchar(300) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `key_partner_id` (`partner_id`),
KEY `key_app_id` (`app_id`),
KEY `key_type` (`type`),
KEY `key_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Data
| id | partner_id | app_id | type | name | value |
|----|------------|--------|-------|---------|--------|
| 1 | 500 | 0 | color | primary | blue |
| 2 | 500 | 100 | color | primary | green |
| 3 | 500 | 101 | color | primary | red |
query
SELECT * FROM settings WHERE app_id in (
COALESCE ((SELECT app_id FROM settings WHERE app_id = 100), 0)
);
| id | partner_id | app_id | type | name | value |
|----|------------|--------|-------|---------|-------|
| 2 | 500 | 100 | color | primary | green |
SELECT * FROM settings WHERE app_id in (
COALESCE ((SELECT app_id FROM settings WHERE app_id = 102), 0)
);
| id | partner_id | app_id | type | name | value |
|----|------------|--------|-------|---------|-------|
| 1 | 500 | 0 | color | primary | blue |
Single query to get the settings for the app or fall back on global settings
SELECT * FROM settings
WHERE app_id IN(0,102)
ORDER BY app_id DESC
LIMIT 1;
Obviously, this assumes a single row for the app settings.

Deleting equal rows in mySQL 5.7.9?

I have this table in mysql called ts1
+----------+-------------+---------------+
| position | email | date_of_birth |
+----------+-------------+---------------+
| 3 | NULL | 1987-09-03 |
| 1 | NULL | 1982-03-26 |
| 2 | Sam#gmail | 1976-10-03 |
| 2 | Sam#gmail | 1976-10-03 |
+----------+-------------+---------------+
I want to drop the equal rows using ALTER IGNORE.
I have tried
ALTER IGNORE TABLE ts1 ADD UNIQUE INDEX inx (position, email, date_of_birth);
and
ALTER IGNORE TABLE ts1 ADD UNIQUE(position, email, date_of_birth);
In both cases I get
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IGNORE TABLE ts1 ADD UNIQUE(position, email, date_of_birth)' at line 1
I'm using mySQL 5.7.9. Any suggestions?
To do it inline against the table, given just the columns you show consider the below. To do it in a new table as suggested by Strawberry, see my pastie link under comments.
create table thing
( position int not null,
email varchar(100) null,
dob date not null
);
insert thing(position,email,dob) values
(3,null,'1987-09-03'),(1,null,'1982-03-26'),
(2,'SamIAm#gmail.com','1976-10-03'),(2,'SamIAm#gmail.com','1976-10-03');
select * from thing;
+----------+------------------+------------+
| position | email | dob |
+----------+------------------+------------+
| 3 | NULL | 1987-09-03 |
| 1 | NULL | 1982-03-26 |
| 2 | SamIAm#gmail.com | 1976-10-03 |
| 2 | SamIAm#gmail.com | 1976-10-03 |
+----------+------------------+------------+
alter table thing add id int auto_increment primary key;
Delete with a join pattern, deleting subsequent dupes (that have a larger id number)
delete thing
from thing
join
( select position,email,dob,min(id) as theMin,count(*) as theCount
from thing
group by position,email,dob
having theCount>1
) xxx -- alias
on thing.position=xxx.position and thing.email=xxx.email and thing.dob=xxx.dob and thing.id>xxx.theMin
-- 1 row affected
select * from thing;
+----------+------------------+------------+----+
| position | email | dob | id |
+----------+------------------+------------+----+
| 3 | NULL | 1987-09-03 | 1 |
| 1 | NULL | 1982-03-26 | 2 |
| 2 | SamIAm#gmail.com | 1976-10-03 | 3 |
+----------+------------------+------------+----+
Add the unique index
CREATE UNIQUE INDEX `thing_my_composite` ON thing (position,email,dob); -- forbid dupes hereafter
View current table schema
show create table thing;
CREATE TABLE `thing` (
`position` int(11) NOT NULL,
`email` varchar(100) DEFAULT NULL,
`dob` date NOT NULL,
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
UNIQUE KEY `thing_my_composite` (`position`,`email`,`dob`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

Autoincrement id increase by 2

I use following query to create table news:
CREATE TABLE IF NOT EXISTS `news` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`news_title` varchar(500) NOT NULL,
`news_detail` varchar(5000) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
mysql> desc news;
+-------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| news_title | varchar(500) | NO | | | |
| news_detail | varchar(5000) | NO | | | |
+-------------+---------------+------+-----+---------+----------------+
mysql> insert into news (news_title, news_detail) values ('test','demod demo');
mysql> select * from news;
+----+--------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+
| id | news_title | news_detail |
+----+--------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+
| 3 | Advani wants to shift from Gujarat, BJP trying to convince him otherwise | testt |
| 5 | test | demod demo |
+----+--------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+
as you see in the select query the id is increment like 1,3,5,7.... means it increment by 2. So what is the problem here?
actually in my local, it is increment by 1 and working perfectly. but in my server it creates the problem.
Thanks in advance.
Why ?
The auto_increment value can be change with the variable auto_increment_increment.Normally, it’s always 1, but for some weird reason it was set to 2 in my case. I think MySQL Workbench may be involed.
You can change it be doing one of those :
SET ##auto_increment_increment=1
SET GLOBAL auto_increment_increment=1;
More information
You can find some information here and here.
Check system variable ##set_auto_increment_increment.
it should be
SET ##auto_increment_increment=1;

MySQL CONCAT multiple unique rows

So, here's basically the problem:
For starter, I am not asking anyone to do my homework, but to just give me a nudge in the right direction.
I have 2 tables containing names and contact data for practicing
Let's call these tables people and contact.
Create Table for people:
CREATE TABLE `people` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`fname` tinytext,
`mname` tinytext,
`lname` tinytext,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
Create Table for contact:
CREATE TABLE `contact` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`person_id` int(10) unsigned NOT NULL DEFAULT '0',
`tel_home` tinytext,
`tel_work` tinytext,
`tel_mob` tinytext,
`email` text,
PRIMARY KEY (`id`,`person_id`),
KEY `fk_contact` (`person_id`),
CONSTRAINT `fk_contact` FOREIGN KEY (`person_id`) REFERENCES `people` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
When getting the contact information for each person, the query I use is as follows:
SELECT p.id, CONCAT_WS(' ',p.fname,p.mname,p.lname) name, c.tel_home, c.tel_work, c.tel_mob, c.email;
This solely creates a response like:
+----+----------+---------------------+----------+---------+---------------------+
| id | name | tel_home | tel_work | tel_mob | email |
+----+----------+---------------------+----------+---------+---------------------+
| 1 | Jane Doe | 1500 (xxx-xxx 1500) | NULL | NULL | janedoe#example.com |
| 2 | John Doe | 1502 (xxx-xxx 1502) | NULL | NULL | NULL |
| 2 | John Doe | NULL | NULL | NULL | johndoe#example.com |
+----+----------+---------------------+----------+---------+---------------------+
The problem with this view is that row 1 and 2 (counting from 0) could've been grouped to a single row.
Even though this "non-pretty" result is due to corrupt data, it is likely that this will occur in a multi-node database environment.
The targeted result would be something like
+----+----------+---------------------+----------+---------+---------------------+
| id | name | tel_home | tel_work | tel_mob | email |
+----+----------+---------------------+----------+---------+---------------------+
| 1 | Jane Doe | 1500 (xxx-xxx 1500) | NULL | NULL | janedoe#example.com |
| 2 | John Doe | 1502 (xxx-xxx 1502) | NULL | NULL | johndoe#example.com |
+----+----------+---------------------+----------+---------+---------------------+
Where the rows with the same id and name are grouped when still showing the effective data.
Side notes:
innodb_version: 5.5.32
version: 5.5.32-0ubuntu-.12.04.1-log
version_compile_os: debian_linux-gnu
You could use GROUP_CONCAT(), which "returns a string result with the concatenated non-NULL values from a group":
SELECT p.id,
GROUP_CONCAT(CONCAT_WS(' ',p.fname,p.mname,p.lname)) name,
GROUP_CONCAT(c.tel_home) tel_home,
GROUP_CONCAT(c.tel_work) tel_work,
GROUP_CONCAT(c.tel_mob ) tel_mob,
GROUP_CONCAT(c.email ) email
FROM my_table
GROUP BY p.id