I am designing a database but I come to a struggle at this moment,
Currently I have 3 tables:
ex_artists
ex_tracks
ex_labels
Now I wish to have a Unique ID throughout these 3(or more) tables
So if 'Example Artist' had ID '1', then 'Example Track' can not also have ID '1'
but should get ID '2' since '1' already exists
I understand your concerns. Once you decide to design your database with technical IDs, there is always the danger of confusing IDs. While
insert into album_track (album, artist, track, no)
values ('B0016991-00', 'JBIEBER', 'BOYFRIEND0001', 2);
instead of
insert into album_track (album, artist, track, no)
values ('B0016991-00', 'BOYFRIEND0001', 'JBIEBER', 2);
would probably through an error,
insert into album_track (album_id, artist_id, track_id, no) values (40, 22, 12, 2);
instead of
insert into album_track (album_id, artist_id, track_id, no) values (40, 12, 22, 2);
would probably not, and the time you notice your program error it may be too late to tell the bad records from the good ones. Your data would be technically consistent, but a mess really.
To overcome this problem, you need one source to pull your IDs from. In Oracle for instance you would use a sequence. In MySQL you can create an ID table for this only purpose:
create table ids(id int auto_increment primary key);
create table album(id int primary key, album_no text, album_name text,
foreign key (id) references ids(id));
create table track(id int primary key, track_name text, record_date date, take int,
foreign key (id) references ids(id));
insert into ids values ();
insert into album (id, album_no, album_name) values
((select last_insert_id), 'B0016991-00', 'Justin Bieber – Believe - Deluxe Edition');
So whenever you insert a record in one of your tables, you must specify an ID (because it is not automatically got). You get the ID with an insert into your IDs table and then call MySQL's LAST_INSERT_ID().
An less safe, but simpler alternative would be to start the IDs at different offsets:
create table album(id int auto_increment primary key, album_no text, album_name text);
create table track(id int auto_increment primary key, track_name text, record_date date);
alter table track auto_increment=10000001;
create table artist(id int auto_increment primary key, artist_name varchar(100));
alter table artist auto_increment=20000001;
insert into artist (artist_name) values ('Justin Bieber');
This works as long as your IDs stay in the desired range.
it is not that i didn't read what you wrote :>
but you don't need a unique ID across them all, just unique ways of identifying rows.
consider the following:
-- drop table artist
create table artist
(
artist_id int unsigned auto_increment PRIMARY KEY,
artist_name varchar(255) not null
);
insert into artist (artist_name) values ('U2');
insert into artist (artist_name) values ('Cranberries');
-- drop table label
create table label
(
label_id int unsigned auto_increment PRIMARY KEY,
artist_id int not null, -- will leave FK RI to developer
label_name varchar(255) not null,
release_date datetime not null
);
insert into label(artist_id,label_name,release_date) values (1,'Boy','1980-10-20');
insert into label(artist_id,label_name,release_date) values (2,'No Need to Argue','1994-10-03');
create table track
(
id int unsigned auto_increment PRIMARY KEY, -- not completely necessary, will explain
track_id int not null, -- u number this 1 to n consistent with label layout
label_id int not null, -- will leave FK RI to developer
track_name varchar(255) not null
);
-- Cranberries:
insert track (track_id,label_id,track_name) values (1,2,'Zombie');
-- U2:
insert track (track_id,label_id,track_name) values (1,1,'I Will Follow');
insert track (track_id,label_id,track_name) values (2,1,'Twilight');
-- select * from track
artist and label rather obvious. track does not need the 'id' column but i threw it in anyway. That is, track can be identified as combo of artist/label id's
foreign key (FK) referential integrity is up to you but i could plop it in if you want
Related
I am practicing making Databases and tables, so I made a database for baseball info containing a table for Teams and Region they are from (not listed to accuracy). The teams show fine, but when I execute command SELECT * FROM region, it shows me all the columns of ID, name, region, but only has a null value. Code below:
CREATE DATABASE baseball;
USE baseball;
CREATE TABLE teams(
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE region (
id INT NOT NULL AUTO_INCREMENT,
region_name VARCHAR(255),
teams_id INT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (teams_id) REFERENCES teams(id)
);
INSERT INTO teams (name)
VALUES ('Yankees'), ('Red Sox'), ('Mets');
SELECT * FROM teams;
INSERT INTO region (region_name, teams_id)
VALUES ('NorthEast', 1),
('Northeast', 2),
('Central', 3),
('West', 4);
SELECT * FROM region;
Also, the names of teams repeat as well the more I mess with it, something is redundant about this. Thanks.
Let's suppose that I have a table created like:
create table my_rows(
id int primary key auto_increment;
/*...*/
)
and I want to earn some unused space and would like to convert it into a smallint, then, I think about modifying it like
alter table my_rows
modify column id unsigned int primary key auto_increment;
Let's assume that there were some ids, up to a certain level, let's say, 257. Is it true that after such a change the value of the id of the next row will be exactly 258, that is, the auto_increment counter will be intact? If not, how should I remedy this?
Works without issues you can play with it here: http://sqlfiddle.com/#!9/85a2d1/3/0
See how you can insert rows with setting the id by hand or letting the auto_increment do it's magic
create table my_rows(
id int NOT NULL primary key auto_increment,
name varchar(50)
);
insert into my_rows(name) values ('One'), ('Two');
insert into my_rows(id, name) values (3, 'Three');
ALTER TABLE my_rows MODIFY id smallint NOT NULL auto_increment;
insert into my_rows(name) values ('Four');
insert into my_rows(id, name) values (5, 'Five');
select * from my_rows;
SHOW COLUMNS FROM my_rows;
/* output looks like: */
id name
1 One
2 Two
3 Three
4 Four
5 Five
Note: Auto_increment does not fill "empty" spots. So if you set ids by hand and leave some empty spots in between and then continue with auto_increment the "empty spots" will not be filled. So here 6 and 7 will be missing
create table my_rows(
id int NOT NULL primary key auto_increment,
name varchar(50)
);
insert into my_rows(name) values ('One'), ('Two');
insert into my_rows(id, name) values (3, 'Three');
ALTER TABLE my_rows MODIFY id smallint NOT NULL auto_increment;
insert into my_rows(name) values ('Four');
insert into my_rows(id, name) values (5, 'Five');
insert into my_rows(id, name) values (8, 'Eight');
insert into my_rows(name) values ('Nine');
http://sqlfiddle.com/#!9/9b993d/1/0
I convert an id which is in a char column datatype. after that, I want to add it by 1 (plus 1).
Could you help me? why my query is not working?
query:
INSERT INTO `countries` (`id`, `country_name`) VALUES ((SELECT MAX(CAST(`id` as INTEGER)) AS `max_id` FROM `countries`) + 1, 'India');
The following would run:
INSERT INTO `countries` (`id`, `country_name`)
SELECT MAX(CAST(`id` as INTEGER)) + 1, 'India'
FROM `countries`;
But I think it would be easier if you just make the id column an AUTO_INCREMENT.
This is not how you should be doing identifiers.
If you want incrementing id values, you want to use the AUTO_INCREMENT feature when creating your table.
Your way is dangerous, there's always a possibility of two transactions running at the same time picking the same "next ID".
Just create a table with the flag on:
CREATE TABLE countries (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (id)
);
INSERT INTO countries (`name`) VALUES ('India');
Basically I want an intermediary Book, between what was a direct link from Line to User. Line elements already exist and can't lose the relation to corresponding already existing User element, so a default Book element needs to be created which handles already existing data.
Tables were created like this:
User (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(50) NOT NULL
);
Line (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(300),
id_user INT UNSIGNED NOT NULL,
FOREIGN KEY (id_user) REFERENCES User(id)
);
Now I need to create some SQL code that would produce tables like this:
User (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(50) NOT NULL
);
Book (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(300),
id_user INT UNSIGNED NOT NULL,
FOREIGN KEY (id_user) REFERENCES User(id)
);
Line (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(300),
id_book INT UNSIGNED NOT NULL,
FOREIGN KEY (id_book) REFERENCES Book(id)
);
... except that data already exists. so I have Line elements pointing to User elements and I can't create new tables from scratch. So I need the SQL code to modify the existing structure.
Basically I just want users to have books and books to have lines, instead of users have lines. And already existing lines to get a connection with the owner users. What would be the best approach?
///////////////////////////////////////////////////////
Some sample data representing what my database looks like currently:
Table user(id,username)
- 1, user1
- 2, user2
- 3, user3
Table line(id,title,id_user)
- 1, line1, 1
- 2, line2, 1
- 3, linex, 1
- 4, line1, 2
- 5, liney, 2
The output should be the following database:
Table user(id,username)
- 1, user1
- 2, user2
- 3, user3
Table book(id,title,id_user)
- 1, 'default', 1
- 2, 'default', 2
- 3, 'default', 3
Table line(id,title,id_book)
- 1, line1, 1
- 2, line2, 1
- 3, linex, 1
- 4, line1, 2
- 5, liney, 2
////////////////////////////////
What my pipeline currently looks like, my main problem is producing the final query:
create table book
$sql = "CREATE TABLE IF NOT EXISTS book(
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(300),
id_user INT UNSIGNED NOT NULL,
FOREIGN KEY (id_user) REFERENCES User(id)
); ";
$conn->exec($sql);
//insert default book corresponding to every user
$sql = "INSERT INTO Book(title, id_user) SELECT DISTINCT 'default', id FROM User;";
$conn->exec($sql);
//insert id_book foreign key into Line table pointing to Book table
$sql = "ALTER TABLE Line ADD id_book INT;"
$conn->exec($sql);
//make Line.id_book foreign key pointing to Line.id
$sql="ALTER TABLE Line ADD FOREIGN KEY (id_book) REFERENCES User(id);"
$conn->exec($sql);
//unfinished
$sql = "UPDATE
Line
SET
Line.id_book = Book.id
WHERE
Line.id_user = Book.id_user"
Create table book (like you already did):
CREATE TABLE IF NOT EXISTS book(
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(300),
id_user INT UNSIGNED NOT NULL,
FOREIGN KEY (id_user) REFERENCES User(id)
);
Populate the new table:
INSERT INTO Book(title, id_user) SELECT 'default', id FROM User;
I removed DISTINCT from your code because id is primary/unique and thus every selected row is guaranteed to be unique.
Add column id_book to the line table:
ALTER TABLE Line ADD id_book INT UNSIGNED;
It must be INT UNSIGNED to match the referenced data type.
Add foreign key:
ALTER TABLE Line ADD FOREIGN KEY (id_book) REFERENCES book(id);
It should reference a book, not a user like in your code.
Update data (Relate the line with the users (first) book):
UPDATE Line
SET Line.id_book = (
SELECT book.id
FROM book
WHERE book.id_user = Line.id_user
ORDER BY book.id
LIMIT 1
);
Drop line.id_user column:
-- use "SHOW CREATE TABLE line" to find out the foreign key name
ALTER TABLE Line DROP FOREIGN KEY line_ibfk_1;
ALTER TABLE Line DROP id_user;
http://sqlfiddle.com/#!9/3faa13/1
Note: If a user has two or more books, you don't know wich one to link with a line. In your question you didn't define how to handle this case. So i decided to take the first book by id (ORDER BY book.id LIMIT 1).
The UPDATE statement could also be:
UPDATE Line
SET Line.id_book = (
SELECT MIN(book.id)
FROM book
WHERE book.id_user = Line.id_user
);
I am working on MySQL database, and I need to select some data with procedure. So I have something like:
CREATE TABLE pet (id INT, name VARCHAR(20), own_id INT);
insert into pet values (1,"Rufus", 1);
insert into pet values (2,"Bali", 1);
insert into pet values (3,"Lolo", 2);
ref pet.own_id = own.id
CREATE TABLE own (id INT, own_name VARCHAR(20), own_color VARCHAR(20));
insert into own values (1,"Me", "Red");
insert into own values (2,"Other" ,"Green");
And now I wonder how to select / join data to get something like that (as results):
own_name own_color name
Me Red Rufus
Me Red Bali
Other Green Lolo
SELECT own_name, own_color, name
from pet
JOIN own on (pet.own_id = own.id)
;
You have only to join both tables:
Select own_name,own_color, name from own join pet on
pet.own_id = own.id
select o.own_name, o.own_color, p.name from own o, pet p
where p.own_id=o.id
It's a JOIN.
While a JOIN is a valid answer, some people find subqueries easier to read and write:
SELECT own_name, own_color, (SELECT name
FROM pet
WHERE pet.own_id = own.id) AS name
FROM own
This is basically the same as the JOIN method, but then as a subquery
I have a suggestion for your tables: own_id is a foreign key, so instead of
CREATE TABLE pet (id INT, name VARCHAR(20), own_id INT);
CREATE TABLE own (id INT, own_name VARCHAR(20), own_color VARCHAR(20));
I'd do:
CREATE TABLE own (
id INT NOT NULL AUTO_INCREMENT,
own_name VARCHAR(20) NOT NULL,
own_color VARCHAR(20) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE pet (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(20) NOT NULL,
own_id INT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (own_id) REFERENCES own (id)
);
I added some other things as well.
Improvements/changes:
I assumed none of the fields should be NULL. I added NOT NULL to all the fields making it impossible for a field to have a value of NULL. (If you want some fields to be allowed to have a value of NULL just remove the NOT NULL from the CREATE TABLE statements.)
I made both id fields increment automatically. This means the insert statements can now be just insert into own (own_name, own_color) values ("Me", "Red"); and the database will automatically keep track of the ids for you.
I added primary keys so the database knows that the rows can be identified by the id fields
I added a foreign key constraint, meaning that every own_id in pet must exist in own. If you try to insert or alter a pet row in a way that breaks this constraint mysql will throw an error at you.