SQL table definition for 1-N-M situation - sql

My situation is very complex, but I can simplify my problem to the following situation:
Assume a Kindergarden with 3 tables: "kids", "corner" and "day".
Each "day", any number of "kids" can play in one of the "corner"s.
The next day, the kids can chose another corner to play.
So for each day, the kids and the corners must be uniqe.
Here's an example DB to copy when fiddling:
CREATE TABLE kids (
id INT,
name TEXT
);
INSERT INTO kids VALUES (1, "Paul");
INSERT INTO kids VALUES (2, "Mary");
INSERT INTO kids VALUES (3, "Tom");
INSERT INTO kids VALUES (2, "Gina");
CREATE TABLE corner (
id INT,
name TEXT
);
INSERT INTO corner VALUES (1, "Kitchen");
INSERT INTO corner VALUES (2, "Cars");
CREATE TABLE days (
id INT,
day DATE
);

Basically, you would need another table to represent the relationship between the three entities:
create table plays (
kid_id int references kids(id),
corner_id int references corner(id),
day_id int references days(id),
primary key (kid_id, day_id)
);
Each column is a foreign key to the corresponding referential table. The primary key ensures that a given kid plays in only one corner per day.

Related

Create a view that joins data from other table multiple cells into one oracle sql

I need to create a view that combines two tables (game and developer) together and selects gameID gameName and gameDeveloper from the game table and repaces gameDeveloper with firstname and lastname (from developers table) into one column. gameDeveloper is the same as developerID, but it is not a foreign key yet on purpose. I want to select only firstname and lastname from the row that contains the matching developerID and combine them in one column in my new view. Heres what I have:
CREATE TABLE game (
gameID varchar(3) PRIMARY KEY NOT NULL,
gamenameName varchar(30) NOT NULL,
gameDeveloper varchar(3) NOT NULL
);
INSERT INTO game VALUES (1,'RDR2',2);
INSERT INTO game VALUES (2,'GTAV',7);
INSERT INTO game VALUES (3,'PUBG',9);
/
CREATE TABLE developers (
developerID varchar(3) PRIMARY KEY NOT
NULL,
fname varchar(20) NOT NULL,
lname varchar(20) NOT NULL,
gameID varchar(3) NOT NULL,
CONSTRAINT fk_game
FOREIGN KEY (gameID)
REFERENCES game (GameID)
);
INSERT INTO developers VALUES
(1,'Patrick','Kane',1);
INSERT INTO developers VALUES
(2,'Jonathan','Toews',1);
INSERT INTO developers VALUES
(3,'Alex','Debrincat',1);
INSERT INTO developers VALUES
(4,'Andrew','Shaw',2);
INSERT INTO developers VALUES
(5,'Alex','Nylander',2);
INSERT INTO developers VALUES
(6,'Oli','Maata',2);
INSERT INTO developers VALUES
(7,'Calvin','DeHaan',2);
INSERT INTO developers VALUES
(8,'Brandon','Saad',3);
INSERT INTO developers VALUES
(9,'Corey','Crawford',3);
INSERT INTO developers VALUES
(10,'Connor','Murphy',3);
/
CREATE OR REPLACE VIEW chairs AS
SELECT firstname, lastname
FROM developer
INNER JOIN
I'd like the final table to look something like this with the mapped and combined last cell but I am so lost on what to do.. I figured an inner join would be best?
You can do something like this - JOIN is the equivalent to INNER JOIN but you can be explicit.
CREATE VIEW chairs
AS
SELECT
g.gameID
,g.gamenameName
,d.fname + ' ' + lname AS gameDeveloper
FROM game g
JOIN developers d
ON g.gameDeveloper = d. developerID

How to deal with an id that needs to be matched to multiple ids in SQLite?

I'm new to databases, so I'll start by showing what I would do if I was using a simple table in a csv file. Presently, I'm building a Shiny (R) app to keep track of people taking part in studies. The idea is to make sure no one is doing more than one study at the same time, and that enough time has passed between studies.
A single table would look like something like this:
study_title contact_person tasks first_name last_name
MX9345-3 John Doe OGTT Michael Smith
MX9345-3 John Doe PVT Michael Smith
MX9345-3 John Doe OGTT Julia Barnes
MX9345-3 John Doe PVT Julia Barnes
...
So each study has a single contact person, but multiple tasks. It is possible other studies will use the same tasks.
Each task should have a description
Each person can be connected to multiple studies (the final database has timestamps to make sure this does not happen at the same time), and consequently repeat the same tasks.
the SQLite code could look something like this
CREATE TABLE studies (study_title TEXT, contact_person TEXT);
CREATE TABLE tasks (task_name TEXT, description TEXT);
CREATE TABLE participants (first_name TEXT, last_name TEXT);
Now I'm stuck. If I add a primary key and foreign keys (say in studies an ID for each study, and foreign keys for each task and person), the primary keys will repeat, which is not possible. A Study is defined by the tasks it contains (akin to an album and music tracks).
How should I approach this situation in SQLite? And importantly, how are the INSERTs done in these situations? I've seen lots of SELECT examples, but few INSERTs that match all IDs in each table, for example when adding a new person to a running study.
What you do is use tables to map/reference/relate/associate.
The first step would be to utilise alias's of the rowid so instead of :-
CREATE TABLE studies (study_title TEXT, contact_person TEXT);
CREATE TABLE tasks (task_name TEXT, description TEXT);
CREATE TABLE participants (first_name TEXT, last_name TEXT);
you would use :-
CREATE TABLE studies (id INTEGER PRIMARY KEY,study_title TEXT, contact_person TEXT);
CREATE TABLE tasks (id INTEGER PRIMARY KEY, task_name TEXT, description TEXT);
CREATE TABLE participants (id INTEGER PRIMARY KEY, first_name TEXT, last_name TEXT);
With SQLite INTEGER PRIMARY KEY (or INTEGER PRIMARY KEY AUTOINCREMENT) makes the column (id in the above although they can have any valid column name) and alias of the rowid (max of 1 per table), which uniquely identifies the rows.
Why not to use AUTOINCREMENT plus more seeSQLite Autoincrement
Insert some data for demonstration :-
INSERT INTO studies (study_title, contact_person)
VALUES ('Maths','Mr Smith'),('English','Mrs Taylor'),('Geography','Mary White'),('Phsyics','Mr Smith');
INSERT INTO tasks (task_name,description)
VALUES ('Task1','Do task 1'),('Task2','Do task 2'),('Task3','Do task 3'),('Task4','Do task 4'),('Mark','Mark the sudies');
INSERT INTO participants (first_name,last_name)
VALUES ('Tom','Jones'),('Susan','Smythe'),('Sarah','Toms'),('Alan','Francis'),('Julian','MacDonald'),('Fred','Bloggs'),('Rory','Belcher');
First mapping/reference... Table :-
CREATE TABLE IF NOT EXISTS study_task_relationship (study_reference INTEGER, task_reference INTEGER, PRIMARY KEY (study_reference,task_reference));
Map/relate Study's with Tasks (many-many possible)
Do some mapping (INSERT some data) :-
INSERT INTO study_task_relationship
VALUES
(1,2), -- Maths Has Task2
(1,5), -- Maths has Mark Questions
(2,1), -- English has Task1
(2,4), -- English has Task4
(2,5), -- English has Mark questions
(3,3), -- Geography has Task3
(3,1), -- Geoegrapyh has Task1
(3,2), -- Geography has Task2
(3,5), -- Geography has Mark Questions
(4,4) -- Physics has Task4
;
- See comments on each line
List the Studies along with the tasks
SELECT study_title, task_name -- (just want the Study title and task name)
FROM study_task_relationship -- use the mapping table as the main table
JOIN studies ON study_reference = studies.id -- get the related studies
JOIN tasks ON task_reference = tasks.id -- get the related tasks
ORDER BY study_title -- Order by Study title
results in :-
List each study with all it's tasks
SELECT study_title, group_concat(task_name,'...') AS tasklist
FROM study_task_relationship
JOIN studies ON study_reference = studies.id
JOIN tasks ON task_reference = tasks.id
GROUP BY studies.id
ORDER by study_title;
results in :-
Add study-participants associative table and populate it :-
CREATE TABLE IF NOT EXISTS study_participants_relationship (study_reference INTEGER, participant_reference INTEGER, PRIMARY KEY (study_reference,participant_reference));
INSERT INTO study_participants_relationship
VALUES
(1,1), -- Maths has Tom Jones
(1,5), -- Maths has Julian MacDonald
(1,6), -- Maths has Fred Bloggs
(2,4), -- English has Alan Francis
(2,7), -- English has Rory Belcher
(3,3), -- Geography has Sarah Toms
(3,2) -- Susan Smythe
;
You can now, as an example, get a list of participants the tasks along with the study title :-
SELECT study_title, task_name, participants.first_name ||' '||participants.last_name AS fullname
FROM study_task_relationship
JOIN tasks ON study_task_relationship.task_reference = tasks.id
JOIN studies On study_task_relationship.study_reference = studies.id
JOIN study_participants_relationship ON study_task_relationship.study_reference = study_participants_relationship.study_reference
JOIN participants ON study_participants_relationship.participant_reference = participants.id
ORDER BY fullname, study_title
which would result in :-
FOREIGN KEYS
As you can see there is no actual need for defining FOREIGN KEYS. They are really just an aid to stop you inadvertently doing something like :-
INSERT INTO study_participants_relationship VALUES(30,25);
No such study nor no such participant
To utilise FOREIGN KEYS you have to ensure that they are enabled, the simplest is just to issue the command to turn them on (as if it were a normal SQL statment).
PRAGMA foreign_keys=1
A FOREIGN KEY is a constraint, it stops you INSERTING, UPDATING or DELETING a row that would violate the constraint/rule.
Basically the rule is that the column to which the FOREIGN key is defined (the child) must have a value that is in the referenced table/column the parent.
So assumning that FOREIGN KEYS are turned on then coding :-
CREATE TABLE IF NOT EXISTS study_participants_relationship
(
study_reference INTEGER REFERENCES studies(id), -- column foreign key constraint
participant_reference INTEGER,
FOREIGN KEY (participant_reference) REFERENCES participants(id) -- table foreign key constraint
PRIMARY KEY (study_reference,participant_reference
)
);
Would result in INSERT INTO study_participants_relationship VALUES(30,25); failing e.g.
FOREIGN KEY constraint failed: INSERT INTO study_participants_relationship VALUES(30,25);
It fails as there is no row in studies with an id who's value is 30 (the first column foreign key constraint). If the value 30 did exist then the second constraint would kick in as there is no row in participants with an id of 25.
There is no difference between a column Foreign key constraint and a table Foreign key constraint other than where and how they are coded.
However, the above wouldn't stop you deleting all rows from the study_participants_relationship table as it would stop you deleting a row from the studies or participants table if they were referenced by the study_participants_relationship table.
"deal with an id that needs to be matched to multiple ids in SQLite?"
For many-to-many couplings, make extra coupling tables, like the study_task and participent_task tables below. This is many-to-many since a task can be on many studies and a study can have many tasks.
"make sure no one is doing more than one study at the same time"
That could be handled by letting each participant only have a column for current study (no place for more than one then).
PRAGMA foreign_keys = ON;
CREATE TABLE study (id INTEGER PRIMARY KEY, study_title TEXT, contact_person TEXT);
CREATE TABLE task (id INTEGER PRIMARY KEY, task_name TEXT, description TEXT);
CREATE TABLE participant (
id INTEGER PRIMARY KEY,
first_name TEXT,
last_name TEXT,
id_current_study INTEGER references study(id),
started_current_study DATE
);
CREATE TABLE study_task (
id_study INTEGER NOT NULL references study(id),
id_task INTEGER NOT NULL references task(id),
primary key (id_study,id_task)
);
CREATE TABLE participant_task (
id_participant INTEGER NOT NULL references participant(id),
id_task INTEGER NOT NULL references task(id),
status TEXT check (status in ('STARTED', 'DELIVERED', 'PASSED', 'FAILED')),
primary key (id_participant,id_task)
);
insert into study values (1, 'MX9345-3', 'John Doe');
insert into study values (2, 'MX9300-2', 'Jane Doe');
insert into participant values (1001, 'Michael', 'Smith', 1,'2018-04-21');
insert into participant values (1002, 'Julia', 'Barnes', 1, '2018-04-10');
insert into task values (51, 'OGTT', 'Make a ...');
insert into task values (52, 'PVT', 'Inspect the ...');
insert into study_task values (1,51);
insert into study_task values (1,52);
insert into study_task values (2,51);
--insert into study_task values (2,66); --would fail since 66 doesnt exists (controlled and enforced by foreign key)
The PRAGMA on the first line is needed to make SQLite (above version 3.6 from 2009 I think) enforce foreign keys, without it it just accepts the foreign key syntax, but no controlling is done.

SQL set UNIQUE only for two columns

I would like for example that user can add name JHONE and age 25, so next time he can add JHONE, 26 or ALEX 25, BUT not JHONE, 25 again.
So I'm looking for two column unique NOT separately.
P.S. I'm sorry if same question was mentioned before.
EDIT:
This is my example:
Would like to make userIdG and doWithCar will be like this
102163096246025413003 View
102163096246025413003 Buy
102163096246025413003 Let
102163096246025413003 Sell
And for Id = 102163096246025413003 you can't add any more values, BECAUSE column doWithCar will have only 4 possible choice view, buy, rent and sell
You could specify more than one column in UNIQUE:
CREATE TABLE tab(ID INT IDENTITY(1,1) PRIMARY KEY, name VARCHAR(100), age INT
,UNIQUE(name, age));
INSERT INTO tab(name, age) VALUES ('John', 25);
INSERT INTO tab(name, age) VALUES ('John', 26);
-- INSERT INTO tab(name,age) VALUES ('John', 25);
-- Violation of UNIQUE KEY constraint 'UQ__tab__CF0426FD76D3370A'.
-- Cannot insert duplicate key in object 'dbo.tab'.
-- The duplicate key value is (John, 25).
-- The statement has been terminated.
SELECT * FROM tab;
LiveDemo
Note:
You should store date of birth and not age itself (or make age calculated column and set UNIQUE(name, dob)).
this is what I do not understand) how database will know that it should be two columns as unique and not each column is unique
These are different concepts. DB "knows" it from UNIQUE constraint definition:
UNIQUE(userIdG,doWithCar) -- pair of column is unique
!=
UNIQUE(userIdG),UNIQUE(doWithCar) -- each column is unique

SQL datable query to insert multiple column values

How to add multiple values in a single column of table in SQL? My table looks like this:
Create table emp
(
id number(5),
name varchar(25),
phone varchar(25)
);
Now I want to add values and multiple phones in the phone column. How to do that? I tried using
insert into emp values (id, name, phone)
values (1, lee, (23455, 67543));
but this is not working
Use two insert statements instead
insert into emp values (id, name,phone) values (1,'lee','23455');
insert into emp values (id, name,phone) values (1,'lee','67543');
or If you want to store both the values in single row
insert into emp values (id, name,phone) values (1,'lee','23455,67543');
Here table is not normalised. You either need to store Phone Number info in separate table or use two different column in same table.
Try changing you table design like this.
EMP table
CREATE TABLE emp
(
emp_id INT IDENTITY(1, 1) PRIMARY KEY,
name VARCHAR(25)
);
PhoneNumber Table
CREATE TABLE PhoneNumber
(
phoneno_id INT IDENTITY(1, 1),
emp_id INT,
Phone_Number int,
Cell_Number Int,
FOREIGN KEY (emp_id) REFERENCES emp(emp_id)
)
Note : Auto increment syntax may differ based on the database you are using.
The proper and only real well-designed way to do this in a relational setting is to use a separate table for your phones (this is in SQL Server specific syntax - it might be slightly different, depending on which concrete database system you're using):
Create table emp
(
id INT PRIMARY KEY,
name varchar(25)
)
create table phone
(
phoneId INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
empid INT NOT NULL,
phone varchar(25) NOT NULL,
CONSTRAINT FK_Phone_Emp
FOREIGN KEY(empid) REFERENCES dbo.emp(id)
);
and then you insert the employee data into emp :
insert into emp(id, name)
values (1, lee);
and the phones into phone:
insert into phone(empid, phone) values(1, 23455);
insert into phone(empid, phone) values(1, 67543);
With this setup, you have proper normalization for the database, and you can store as many phones as you like, for each employee.

Insert into table from temporary table takes a long time

Morning folks,
I have a temporary table with 135,000 rows and 24 columns, of those rows I need to insert about 8,000 of them into an 8 column table. If run my insert the first time round (i.e. when my 8 column table is empty) it runs in about 6 seconds. When I run the same query again (this time round it shouldn't insert anything as the rows have already been inserted) it takes 30 minutes!!
I've been unable to re-create this with a small simplified sample, but here's some sql for you to run anyway. It's running the last insert when the programme table has entries which causes the problems. Can anyone shed some light as to why this might be?
CREATE TEMPORARY TABLE TVTEMPTABLE (
PROGTITLE TEXT, YR YEAR, DIRECTOR TEXT, GENRE TEXT
);
CREATE TABLE GENRE (
GENREID INT NOT NULL AUTO_INCREMENT, GENRE TEXT, PRIMARY KEY(GENREID)
) ENGINE=INNODB;
CREATE TABLE PROGRAMME (
PROGRAMMEID INT NOT NULL AUTO_INCREMENT, GENREID INT NOT NULL, PROGTITLE TEXT, YR YEAR,
DIRECTOR TEXT, PRIMARY KEY(PROGRAMMEID), INDEX (GENREID), FOREIGN KEY (GENREID) REFERENCES GENRE(GENREID)
) ENGINE=INNODB;
INSERT INTO GENRE(GENRE) VALUES
('Consumer'),('Entertainment'),('Comedy'),('Film'),('Drama'),('Sport'),
('Sitcom'),('Travel'),('Documentary'),('Factual');
INSERT INTO TVTEMPTABLE(PROGTITLE, YR, DIRECTOR, GENRE) VALUES
('Breakfast','2011','n/a','Consumer'),('Breakfast','2011','n/a','Consumer'),
('Wanted Down Under','2011','n/a','Entertainment'),('Wanted Down Under','2011','n/a','Entertainment'),
('Lorraine','2011','n/a','Comedy'),('Lorraine','2011','n/a','Comedy'),
('Supernanny USA','2011','n/a','Film'),('Supernanny USA','2011','n/a','Film'),
('Three Coins in the Fountain','2011','n/a','Drama'),('Three Coins in the Fountain','2011','n/a','Drama'),
('The Wright Stuff','2011','n/a','Sport'),('The Wright Stuff','2011','n/a','Sport'),
('This Morning','2011','n/a','Sitcom'),('This Morning','2011','n/a','Sitcom'),
('Homes Under the Hammer','2011','n/a','Travel'),('Homes Under the Hammer','2011','n/a','Travel'),
('LazyTown','2011','n/a','Documentary'),('LazyTown','2011','n/a','Documentary'),
('Jeremy Kyle','2011','n/a','Factual'),('Jeremy Kyle','2011','n/a','Factual');
INSERT INTO PROGRAMME (
PROGTITLE, GENREID, YR,
DIRECTOR)
SELECT
T.PROGTITLE, MAX(G.GENREID),
MAX(T.YR), MAX(T.DIRECTOR)
FROM
TVTEMPTABLE T
INNER JOIN GENRE G ON G.GENRE=T.GENRE
LEFT JOIN PROGRAMME P ON P.PROGTITLE=T.PROGTITLE
WHERE P.PROGTITLE IS NULL
GROUP BY T.PROGTITLE;
Edit: Is this what you mean by index?
CREATE TEMPORARY TABLE TVTEMPTABLE (
PROGTITLE VARCHAR(50), YR YEAR, DIRECTOR TEXT, GENRE VARCHAR(50), INDEX(PROGTITLE,GENRE)
);
CREATE TABLE PROGRAMME (
PROGRAMMEID INT NOT NULL AUTO_INCREMENT, GENREID INT NOT NULL, PROGTITLE VARCHAR(50), YR YEAR,
DIRECTOR TEXT, PRIMARY KEY(PROGRAMMEID), INDEX (GENREID,PROGTITLE), FOREIGN KEY (GENREID) REFERENCES GENRE(GENREID)
) ENGINE=INNODB;
Edit 2: This is the result from the desc extended. After indexing (I may have done this wrong?). The insert still takes a long time
Ok yes the answer was to properly index my tables, what I didn't realise however was
INDEX(A,B,C);
Is different from
INDEX(A),INDEX(B),INDEX(C);