Struggling to SELECT all elements from a table - mysql

MySQL is a new language to me and I struggle with selecting more data from my loans table when I do this query:
My objective is to print all elements of the Loans table that match the Bank IDs, all I get is outputs 1-10 where I have over 13 elements in my loans table.
EDIT 1: Bank Table serves as a link between all tables, I know the problem resides in my DML query however cluelessly not sure what to do.
When running my query, only matching primary key to foreign key appears. That is if Bank ID is 1 and Loans ID is 1 it shows, but when Bank ID is 1 and Loans ID is 13 it does not appear in the query.
Please save your criticism, as mentioned above, my experience is green.
My DML:
SELECT bank.bankID, bankcustomer.FirstName, bankcustomer.LastName, loans.FirstPaymentDate
FROM bank
JOIN bankcustomer ON bank.bankID = bankcustomer.customerID
JOIN loans ON loans.LoansID = bank.bankID;
Tables DDL:
CREATE TABLE bankCustomer(
CustomerID int(11) NOT NULL AUTO_INCREMENT,
FirstName varchar(20) DEFAULT NULL,
MiddleName varchar(20) DEFAULT NULL,
LastName varchar(20) DEFAULT NULL,
Address_Line1 varchar(50) DEFAULT NULL,
Address_Line2 varchar(50) DEFAULT NULL,
City varchar(20) DEFAULT NULL,
Region varchar(20) DEFAULT NULL,
PostCode varchar(20) DEFAULT NULL,
Country varchar(30) DEFAULT NULL,
DateOfBirth DATE DEFAULT NULL,
telephoneNumber int(13) DEFAULT 0,
openingAccount int CHECK (openingAccount >= 50),
PRIMARY KEY (CustomerID),
KEY CustomerID (CustomerID)
) ENGINE=INNODB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE bank(
BankID int(11) NOT NULL AUTO_INCREMENT,
customerID int,
PRIMARY KEY (BankID),
FOREIGN KEY (CustomerID) REFERENCES bankCustomer(CustomerID) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=INNODB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE loans(
LoansID int(11) NOT NULL AUTO_INCREMENT,
BankID int,
PaymentRate int(100) DEFAULT 300,
NumOfMonthlyPayments int(12) DEFAULT NULL,
FirstPaymentDate DATE DEFAULT NULL,
MonthlyDueDate DATE DEFAULT NULL,
PRIMARY KEY (LoansID),
FOREIGN KEY (BankID) REFERENCES bank(BankID) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=INNODB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
INSERT DML's:
INSERT INTO bank (BankID, CustomerID) VALUES (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10);
INSERT INTO loans (LoansID, BankID, PaymentRate, NumOfMonthlyPayments, FirstPaymentDate, MonthlyDueDate) VALUES (1, 1, 400, 12, '2008-02-03', '2008-03-25'),
(11, 1, 150, 10, '2008-02-04', '2008-04-25'),
(12, 1, 150, 10, '2008-02-07', '2008-04-25'),
(2, 2, 100, 20, '2011-04-01', '2011-04-25'),
(3, 3, 85, 5, '2015-07-03', '2015-08-25')...

Thank you all for your dear help, I managed to resolve my issue. The problem was the order of JOINing clauses.
SELECT loans.LoansID, bankcustomer.FirstName, customerbankcard.AccountNumber, loans.FirstPaymentDate
FROM bank
JOIN loans ON loans.BankID = bank.bankID
JOIN bankcustomer ON bankcustomer.customerID = bank.customerID
JOIN customerbankcard ON customerbankcard.bankID = bank.bankID
GROUP BY loans.LoansID ASC;
The outcome was to avoid loop, repeating wrongly assigned account numbers with customers whose IDs did not match.

If you want to select all tables use *. Also, you are joining tables incorrectly.
SELECT bank.*, bankcustomer.*, loans.*
FROM bank
JOIN bankcustomer ON bank.customerID = bankcustomer.customerID --Since you want to join data on customer ID you select custemerID in both tables
JOIN loans ON loans.BankID = bank.bankID; --The same problem here when joining tables
Goodluck and happy coding!

Firstly, joins could only be used between the tables when the columns are common between them (though they might have different names in different tables). That is why the concept of foreign key is of paramount importance as it references the column to the referenced table as you have duly done in your DDL commands.
Secondly, try using semicolon (;) as it denotes the end of any command and might get you out of the looping.

Related

adding foreign key from other table based on column values of first table - MySql

I am new to mysql
I have two Tables in my database.
id_details_table
CREATE TABLE `id_details_table` (
`ID` int(11) NOT NULL,
`CNIC` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
sample record
INSERT INTO `id_details_table` (`ID`, `CNIC`) VALUES
(1, '3230328119795'),
(2, '4200004873681'),
(3, '4230188867895'),
(4, '3740566124323'),
(5, '4220191179125');
mobiles_sim_details
> CREATE TABLE `mobiles_sim_details` (
`Mobile` double DEFAULT NULL,
> `CNIC` varchar(255) DEFAULT NULL, `id_cnic` int(11) DEFAULT NULL )
> ENGINE=InnoDB DEFAULT CHARSET=latin1;
sample Record for table
INSERT INTO `mobiles_sim_details` (`Mobile`, `CNIC`, `id_cnic`) VALUES
(3000651082, '3230328119795', 0),
(3040877459, '4200004873681', 0),
(3013329415, '4230188867895', 0),
(3028590340, '3740566124323', 0),
(3000720166, '4220191179125', 0);
2 columns CNIC are present in both tables
Now What I want is, where there is 0 in the 2nd table I want it to be auto filled with the relevant ID from table 1 based on the data of CNIC from 2nd table.
I have tried this query but it didn't worked
UPDATE `mobiles_sim_details` SET `id_cnic`='[value-3]' WHERE (SELECT id FROM id_details_table WHERE CNIC = mobiles_sim_details.CNIC);
How can i Acheive this ?
UPDATE mobiles_sim_details t1
JOIN id_details_table t2 USING (CNIC)
SET t1.id_cnic = t2.id

How to make a certain set of values available in just one row

I would like to set up a database for a example project I have. I have chosen a driving school and work with tables including teachers, pupils, cars, time_lessons and bookings. The code can be seen in the code section. The four first tables i.e. teachers, pupils, cars and time_lessons are all foreign keys in bookings.
My issue is that I need certian combinations of these foreign keys to be available once. Let me clarify:
- A teacher cannot teach a same time_lessons twice
- A pupil cannot drive in a driving time_lessons twice
- A car cannot be used during the same time_lessons twice
- However a time in time_lessons can be used multiple times providing you have a pupil, a teacher and a car that haven't already been booked in that time.
How do I set this up?
See code below to see what I've done so far...
DROP DATABASE IF EXISTS c4trafik;
CREATE DATABASE c4trafik;
USE c4trafik;
CREATE TABLE teachers(
id INT PRIMARY KEY AUTO_INCREMENT,
t_fname VARCHAR(20) NOT NULL,
t_lname VARCHAR(20) NOT NULL,
auth_lvl INT DEFAULT 3
);
CREATE TABLE pupils(
id INT PRIMARY KEY AUTO_INCREMENT,
p_fname VARCHAR(20) NOT NULL,
p_lname VARCHAR(20) NOT NULL,
persnr CHAR(10) NOT NULL,
email VARCHAR(50) DEFAULT NULL,
telnr VARCHAR(10) DEFAULT NULL,
to_pay INT DEFAULT 0,
has_pay INT DEFAULT 0
);
CREATE TABLE cars(
id INT PRIMARY KEY AUTO_INCREMENT,
regnr VARCHAR(6) NOT NULL,
auto_gear BOOLEAN DEFAULT false
);
CREATE TABLE time_lessons(
id INT PRIMARY KEY AUTO_INCREMENT,
t_start TIMESTAMP NOT NULL,
t_end TIMESTAMP NOT NULL,
les_type VARCHAR(20) DEFAULT 'Driving Lesson'
);
CREATE TABLE bookings(
teachers_id INTEGER NOT NULL,
pupils_id INTEGER NOT NULL,
cars_id INTEGER NOT NULL,
time_lessons_id INTEGER NOT NULL,
FOREIGN KEY(teachers_id) REFERENCES teachers(id),
FOREIGN KEY(pupils_id) REFERENCES pupils(id) ON DELETE CASCADE,
FOREIGN KEY(cars_id) REFERENCES cars(id),
FOREIGN KEY(time_lessons_id) REFERENCES time_lessons(id) ON DELETE CASCADE,
PRIMARY KEY(teachers_id, pupils_id, cars_id, time_lessons_id)
);
INSERT INTO teachers (t_fname, t_lname, auth_lvl) VALUES
('Ali', 'Alisson', 1),
('Adam', 'Adamsson', 2),
('Noah', 'Noahsson', 3);
INSERT INTO pupils(p_fname, p_lname, persnr, email, telnr, to_pay, has_pay) VALUES
('Jakob', 'Jaboksson', '8702111254', 'pupil1#gmail.com','0704585962', 2800, 2800),
('Hassan','Hassansson', '9504234858' ,NULL,'0704125463',5000,1000),
('Mona','Monasson', '9410118547',NULL, NULL, 10200, NULL);
INSERT INTO cars (regnr, auto_gear) VALUES
('MNS111', false),
('OJS111', true),
('MNF111', false);
INSERT INTO time_lessons (t_start, t_end ) VALUES
('2019-06-10 08:00:00', '2019-06-10 08:40:00'),
('2019-06-10 08:50:00', '2019-06-10 09:30:00'),
('2019-06-10 09:40:00', '2019-06-10 10:20:00'),
('2019-06-10 10:30:00', '2019-06-10 11:10:00'),
('2019-06-10 11:20:00', '2019-06-10 12:00:00'),
('2019-06-10 13:10:00', '2019-06-10 13:50:00'),
('2019-06-10 14:00:00', '2019-06-10 14:40:00'),
('2019-06-10 14:50:00', '2019-06-10 15:30:00'),
('2019-06-10 15:40:00', '2019-06-10 16:20:00');
INSERT INTO bookings(teachers_id ,pupils_id, cars_id, time_lessons_id) VALUES
(1,2,1,1),(2,1,1,1),
(1,2,1,2),(2,1,2,2),
(1,1,1,3),(2,2,1,3);
-- Ska ge error om man duplicerar!!
-- Find main admin
SELECT t_fname, t_lname FROM teachers WHERE auth_lvl = 1;
-- How many manual geared cars?
SELECT COUNT(*) AS manually_geared FROM cars
WHERE auto_gear = false;
-- all booked student
SELECT p_fname, p_lname FROM bookings
INNER JOIN pupils ON pupils.id = bookings.pupils_id
GROUP BY p_lname, p_fname;
-- What time will each student drive?
SELECT p_fname, p_lname, t_start, t_end FROM bookings
INNER JOIN pupils ON pupils.id = bookings.pupils_id
INNER JOIN time_lessons ON time_lessons.id = bookings.time_lessons_id
ORDER BY p_lname, p_fname, t_start;
-- All time_lessons for teacher number 1
SELECT t_fname, p_fname, p_lname, t_start, t_end FROM bookings
INNER JOIN teachers ON teachers.id = bookings.teachers_id
INNER JOIN pupils ON pupils.id = bookings.pupils_id
INNER JOIN time_lessons ON time_lessons.id = bookings.time_lessons_id
WHERE teachers.id = 1;
So what I'm wondering is do I change my structure? Am I missing some code to make what I mentioned above work.
A teacher cannot teach a same time_lessons twice
Add a UNIQUE constraint on the (teacher_id,time_lesson_id) tuple in the booking, e.g.
CREATE UNIQUE INDEX booking_UX1 ON booking (teacher_id,time_lesson_id)
This will disallow rows with duplicates of a combination of values. If an attempt to INSERT a new row (or UPDATE and existing row) would cause two rows to have the same values, the statement will fail with error 1062 "Duplicate entry '' for key ".
A pupil cannot drive in a driving time_lessons twice
A UNIQUE index will prevent duplicates:
CREATE UNIQUE INDEX booking_UX2 ON booking (pupil_id,time_lesson_id)
(My preference is for entity tables be named in the singular, to name what one row represents.)

SQL group by using distinct column

I am currently collecting lap times in a sql database and are having some difficulties with extracting the drivers with fastest laptimes!
The structure looks like the following!
CREATE TABLE IF NOT EXISTS `leaderboard` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`driver` varchar(50) NOT NULL,
`car` varchar(50) NOT NULL,
`best` double NOT NULL,
`guid` bigint(255) NOT NULL,
`server_name` varchar(255) NOT NULL,
`track` varchar(55) NOT NULL,
PRIMARY KEY (`id`),
KEY `driver` (`driver`),
KEY `server_name` (`server_name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1213 ;
Data example
INSERT INTO `leaderboard` (`id`, `driver`, `car`, `best`, `guid`, `server_name`, `track`) VALUES
(1, 'dave.38', 'bmw_m3_e30', 88.379, 76561198084629688, 'A++%21+A++%21+------+Saturdaynightracing.tk+-+%5BRACE-SERVER%5D+-+%5BMagione%5D+%23SNR', 'magione'),
(2, 'Gabriel Porfírio', 'bmw_m3_e30', 87.318, 76561197987062834, 'A++%21+A++%21+------+Saturdaynightracing.tk+-+%5BRACE-SERVER%5D+-+%5BMagione%5D+%23SNR', 'magione'),
(3, 'xX_VEGA_Xx', 'bmw_m3_e30', 88.23, 76561198182074333, 'A++%21+A++%21+------+Saturdaynightracing.tk+-+%5BRACE-SERVER%5D+-+%5BMagione%5D+%23SNR', 'magione'),
(4, 'dave.38', 'bmw_m3_e30', 88.379, 76561198084629688, 'A++%21+A++%21+------+Saturdaynightracing.tk+-+%5BRACE-SERVER%5D+-+%5BMagione%5D+%23SNR', 'magione'),
(5, 'Gabriel Porfírio', 'bmw_m3_e30', 87.318, 76561197987062834, 'A++%21+A++%21+------+Saturdaynightracing.tk+-+%5BRACE-SERVER%5D+-+%5BMagione%5D+%23SNR', 'magione');
Now i am trying to sort out the drivers with best time using column best using the following SQL but it appears as if some times are discarded, the combination of sort and order does not work.
SELECT DISTINCT guid, car, best, driver FROM `leaderboard` WHERE `server_name` like '%%' AND `track` = 'magione' GROUP BY(driver) ORDER BY `best` * 1 LIMIT 10
Please help this is driving me mad!
Some fields in your data are not very clear, so I made such assumptions:
guid means driver's guid (because it is the same for the same driver in your data).
car is the same for the same driver.
With these assumptions you can use simple GROUP BY to get the results that you need:
SELECT driver, car, MIN(best) as best_time, guid
FROM leaderboard
WHERE `server_name` like '%%' AND `track` = 'magione'
GROUP BY driver, car, guid
ORDER BY MIN(best)

EXPLAIN SELECT.., why TYPE = ALL?

Having these 3 tables:
users
CREATE TABLE `users` (
`user_id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
`first_name` VARCHAR(64) NOT NULL,
`last_name` VARCHAR(64) NOT NULL,
PRIMARY KEY (`user_id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1;
posts
CREATE TABLE `posts` (
`post_id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
`category_id` MEDIUMINT(8) UNSIGNED NOT NULL,
`author_id` MEDIUMINT(8) UNSIGNED NOT NULL,
`title` VARCHAR(128) NOT NULL,
`text` TEXT NOT NULL,
PRIMARY KEY (`post_id`),
INDEX `FK_posts__category_id` (`category_id`),
INDEX `FK_posts__author_id` (`author_id`),
CONSTRAINT `FK_posts__author_id` FOREIGN KEY (`author_id`) REFERENCES `users` (`user_id`) ON UPDATE CASCADE,
CONSTRAINT `FK_posts__category_id` FOREIGN KEY (`category_id`) REFERENCES `categories` (`category_id`) ON UPDATE CASCADE ON DELETE CASCADE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1;
categories
CREATE TABLE `categories` (
`category_id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(64) NOT NULL,
PRIMARY KEY (`category_id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1;
And data in tables:
INSERT INTO `users` (`user_id`, `first_name`, `last_name`) VALUES
(1, 'John', 'Doe'),
(2, 'Pen', 'Poe'),
(3, 'Robert', 'Roe');
INSERT INTO `categories` (`category_id`, `name`) VALUES
(1, 'Category 1'),
(2, 'Category 2'),
(3, 'Category 3'),
(4, 'Category 4');
INSERT INTO `posts` (`post_id`, `category_id`, `author_id`, `title`, `text`) VALUES
(1, 1, 1, 'title 1', 'text 1'),
(2, 1, 2, 'title 2', 'text 2');
I want to make a simple select (and let MySQL EXPLAIN it):
EXPLAIN SELECT p.post_id, p.title, p.text, c.category_id, c.name, u.user_id, u.first_name, u.last_name
FROM posts AS p
JOIN categories AS c
ON c.category_id = p.category_id
JOIN users AS u
ON u.user_id = p.author_id
WHERE p.category_id = 1
I got this:
What I don't understand is, why has MySQL to do a full table scan at u (users). I mean there will be only two users it has to retrieve data about (with id 1 and 2), and these two can be found by primary key user_id. Can somebody with more experience help me to understand this? Is there a better way of creating indexes so MySQL don't has to make a full scan on the users table to retrieve data about the post authors?
Thanks you!
So with such a small amount a index search is going to be slower than a sequential search. Thus MySQL is choosing to use a simple table read.
It has to do with operational efficiency here. Lets simply the operations that MySQL has to do to read the entire table vs using a index.
Full read:
Open table
Read each line one at a time and match criteria
Return result set
That is 5 operations.
Index Read
Open table
For the criteria read the index for each row
Using the index pointer locate the row on disk for each row
Return resultset
In this case 8 operations.
This is very simplified but unless you have enough data your indexes can slow you down. As the table grows MySQL might choose a different query path. That is why you dont force the use of indexes.
You only have ~3 rows in your users table, according to your test data and your EXPLAIN report.
The optimizer can produce skewed results if you have too few rows in the tables. It may do a table-scan for a tiny table, even if it would use an index for the same query against the same tables with a few hundred or a few thousand rows.
So when doing development, it's important to have a non-trivial amount of test data in your tables if you want to get accurate optimizer reports.

Aggregate Query depending on several tables

I want to create a database with information of employees, their jobs, salaries and projects
I want to keep information of the cost of a project (real value of project and the days a employee invested)
For employee and project each Employee has one role on the Project through the PK constraint, and allows for the addition of a new role type ("Tertiary" perhaps) in the future.
CREATE TABLE Employee(
EmployeeID INTEGER NOT NULL PRIMARY KEY,
Name VARCHAR(30) NOT NULL,
Sex CHAR(1) NOT NULL,
Address VARCHAR(80) NOT NULL,
Security VARCHAR(15) NOT NULL,
DeptID INTEGER NOT NULL,
JobID INTEGER NOT NULL
);
CREATE TABLE Departments (
DeptID INTEGER NOT NULL PRIMARY KEY,
DeptName VARCHAR(30) NOT NULL
);
CREATE TABLE Jobs (
JobID INTEGER NOT NULL PRIMARY KEY,
JobName VARCHAR(30) NOT NULL,
JobSalary DOUBLE(15,3) NOT NULL default '0.000',
JobSalaryperDay DOUBLE(15,3) NOT NULL default '0.000',
DeptID INTEGER NOT NULL
);
CREATE TABLE Project(
ProjectID INTEGER NOT NULL PRIMARY KEY,
ProjectDesc VARCHAR(200) NOT NULL,
StartDate DATE NOT NULL,
EndDate DATE NOT NULL,
DaysOfWork INTEGER NOT NULL,
NoEmployees INTEGER NOT NULL,
EstimatedCost DOUBLE(15,3) NOT NULL default '0.000',
RealCost DOUBLE(15,3) NOT NULL default '0.000'
);
CREATE TABLE `Project-Employee`(
ProjectID INTEGER NOT NULL,
EmployeeID INTEGER NOT NULL,
Note VARCHAR(200),
DaysWork INTEGER NOT NULL,
CONSTRAINT fk_ProjectID FOREIGN KEY (ProjectID) REFERENCES Project(ProjectID),
CONSTRAINT fk_EmployeeID FOREIGN KEY (EmployeeID) REFERENCES Employee(EmployeeID)
);
INSERT INTO `Departments` VALUES (1, 'Outsourcing');
INSERT INTO `Departments` VALUES (2, 'Technician');
INSERT INTO `Departments` VALUES (3, 'Administrative');
INSERT INTO `Jobs` VALUES (1, 'welder' ,500.550,16.7 ,2);
INSERT INTO `Jobs` VALUES (2, 'turner' ,500.100,16.67,2);
INSERT INTO `Jobs` VALUES (3, 'assistant' ,650.100,21.67,2);
INSERT INTO `Jobs` VALUES (4, 'supervisor',800.909,26.70,3);
INSERT INTO `Jobs` VALUES (5, 'manager' ,920.345,30.68,3);
INSERT INTO `Jobs` VALUES (6, 'counter' ,520.324,17.35,1);
INSERT INTO `Employee` VALUES (10, 'Joe', 'M', 'Anywhere', '927318344', 1, 3);
INSERT INTO `Employee` VALUES (20, 'Moe', 'M', 'Anywhere', '827318322', 2, 3);
INSERT INTO `Employee` VALUES (30, 'Jack', 'M', 'Anywhere', '927418343', 3, 4);
INSERT INTO `Employee` VALUES (40, 'Marge','F', 'Evererre', '127347645', 1, 6);
INSERT INTO `Employee` VALUES (50, 'Greg' ,'M', 'Portland', '134547633', 3, 5);
INSERT INTO `Project` VALUES (1, 'The very first', '2008-7-04' , '2008-7-24' , 20, 5, 3000.50, 2500.00);
INSERT INTO `Project` VALUES (2, 'Second one pro', '2008-8-01' , '2008-8-30' , 30, 5, 6000.40, 6100.40);
INSERT INTO `Project-Employee` VALUES (1, 10, 'Worked all days' , 20);
INSERT INTO `Project-Employee` VALUES (1, 20, 'Worked just in defs', 11);
INSERT INTO `Project-Employee` VALUES (1, 30, 'Worked just in defs', 17);
INSERT INTO `Project-Employee` VALUES (1, 40, 'Contability ' , 8);
INSERT INTO `Project-Employee` VALUES (1, 50, 'Managed the project', 8);
So to get the total amount of the cost of a project and have it for future Work quote I would just sum the working days of each job for each employee in an aggregate query.
What would be the query to sum all working days knowing the employees involved in a particular project to know the cost generated for their work, Is it possible to know this with this design?
So lets suppose I know that in project 1, 5 employees were involved, and I know by other table "jobs" the salary I would pay each one of them per day
I am doing some queries here with sqlfiddle
UPDATE
CREATE TABLE `Sexes` (
Sex char(1) primary key
);
INSERT INTO Sexes values ('M');
INSERT INTO Sexes values ('F');
CREATE TABLE `Employee`(
EmployeeID INTEGER NOT NULL PRIMARY KEY,
Name VARCHAR(130) NOT NULL,
Sex CHAR(1) NOT NULL,
Address VARCHAR(380) NOT NULL,
Security VARCHAR(15) NOT NULL,
FOREIGN KEY (Sex) references Sexes (Sex),
CONSTRAINT `uc_EmployeeInfo` UNIQUE (`EmployeeID`,`Name`,`Security`)
);
CREATE TABLE `Department` (
DeptID INTEGER NOT NULL PRIMARY KEY,
DeptName VARCHAR(30) NOT NULL,
CONSTRAINT `uc_DeptName` UNIQUE (`DeptID`,`DeptName`)
);
CREATE TABLE `Dept-Employee`(
EmployeeID INTEGER NOT NULL,
DeptID INTEGER NOT NULL,
CONSTRAINT fk_DeptID FOREIGN KEY (DeptID) REFERENCES `Department`(DeptID),
CONSTRAINT fk_EmployeeID FOREIGN KEY (EmployeeID) REFERENCES `Employee`(EmployeeID)
);
CREATE TABLE `Dept-Manager`(
EmployeeID INTEGER NOT NULL,
DeptID INTEGER NOT NULL,
CONSTRAINT fk_DeptIDs FOREIGN KEY (DeptID) REFERENCES `Department`(DeptID),
CONSTRAINT fk_EmployeeIDs FOREIGN KEY (EmployeeID) REFERENCES `Employee`(EmployeeID)
);
CREATE TABLE `Jobs` (
JobID INTEGER NOT NULL PRIMARY KEY,
JobName VARCHAR(30) NOT NULL,
JobSalary DECIMAL(7,3) NOT NULL default '0000.000',
JobSalaryperDay DECIMAL(7,3) NOT NULL default '0000.000',
CONSTRAINT `uc_jobs` UNIQUE (`JobID`,`JobName`)
);
CREATE TABLE `Jobs-Employee`(
EmployeeID INTEGER NOT NULL,
JobID INTEGER NOT NULL,
CONSTRAINT fk_JobIDs FOREIGN KEY (JobID) REFERENCES `Jobs`(JobID),
CONSTRAINT fk_EmployeeIDss FOREIGN KEY (EmployeeID) REFERENCES `Employee`(EmployeeID)
);
CREATE TABLE `Project`(
ProjectID INTEGER NOT NULL PRIMARY KEY,
ProjectName VARCHAR(200) NOT NULL,
StartDate DATE NOT NULL,
DaysOfWork INTEGER NOT NULL,
NoEmployees INTEGER NOT NULL,
EstimatedCost DECIMAL(9,3) NOT NULL default '000000.000',
RealCost DECIMAL(9,3) NOT NULL default '000000.000',
CONSTRAINT `uc_project` UNIQUE (`ProjectID`,`ProjectName`)
);
CREATE TABLE `Project-Employee`(
ProjectID INTEGER NOT NULL,
EmployeeID INTEGER NOT NULL,
Note VARCHAR(200),
DaysWork INTEGER NOT NULL,
CONSTRAINT fk_ProjectIDsss FOREIGN KEY (ProjectID) REFERENCES `Project`(ProjectID),
CONSTRAINT fk_EmployeeIDsss FOREIGN KEY (EmployeeID) REFERENCES `Employee`(EmployeeID)
);
INSERT INTO `Department` VALUES (1, 'Outsourcing');
INSERT INTO `Department` VALUES (2, 'Technician');
INSERT INTO `Department` VALUES (3, 'Administrative');
INSERT INTO `Jobs` VALUES (1, 'welder' ,500.550, 16.7 );
INSERT INTO `Jobs` VALUES (2, 'turner' ,500.100, 16.67);
INSERT INTO `Jobs` VALUES (3, 'assistant' ,650.100, 21.67);
INSERT INTO `Jobs` VALUES (4, 'supervisor',800.909, 26.70);
INSERT INTO `Jobs` VALUES (5, 'manager' ,920.345, 30.68);
INSERT INTO `Jobs` VALUES (6, 'counter' ,520.324, 17.35);
INSERT INTO `Employee` VALUES (10, 'Joe', 'M', 'Joewhere', '927318344');
INSERT INTO `Employee` VALUES (20, 'Moe', 'M', 'Moewhere', '827318322');
INSERT INTO `Employee` VALUES (30, 'Jack', 'M', 'Jaswhere', '927418343');
INSERT INTO `Employee` VALUES (40, 'Marge','F', 'Evererre', '127347645');
INSERT INTO `Employee` VALUES (50, 'Greg' ,'M', 'Portland', '134547633');
INSERT INTO `Dept-Employee` VALUES (10,1);
INSERT INTO `Dept-Employee` VALUES (20,2);
INSERT INTO `Dept-Employee` VALUES (30,3);
INSERT INTO `Dept-Employee` VALUES (40,1);
INSERT INTO `Dept-Employee` VALUES (50,3);
INSERT INTO `Jobs-Employee` VALUES (10,3);
INSERT INTO `Jobs-Employee` VALUES (20,3);
INSERT INTO `Jobs-Employee` VALUES (30,4);
INSERT INTO `Jobs-Employee` VALUES (40,6);
INSERT INTO `Jobs-Employee` VALUES (50,5);
INSERT INTO `Project` VALUES (1, 'The very first', '2008-7-04' , 20, 5, 3000.50, 2500.00);
INSERT INTO `Project` VALUES (2, 'Second one pro', '2008-8-01' , 30, 5, 6000.40, 6100.40);
INSERT INTO `Project-Employee` VALUES (1, 10, 'Worked all days' , 20);
INSERT INTO `Project-Employee` VALUES (1, 20, 'Worked just in defs', 11);
INSERT INTO `Project-Employee` VALUES (1, 30, 'Worked just in defs', 17);
INSERT INTO `Project-Employee` VALUES (1, 40, 'Contability ' , 8);
INSERT INTO `Project-Employee` VALUES (1, 50, 'Managed the project', 8);
To the new structure I did this
CREATE VIEW `Emp-Job` as
SELECT e.*,j.jobID
FROM Employee e,`Jobs-Employee` j
WHERE e.EmployeeID = j.EmployeeID;
CREATE VIEW `employee_pay` as
select e.*, j.jobname, j.jobsalary, j.jobsalaryperday
from `Emp-Job` e
inner join `Jobs` j
on e.JobID = j.JobID;
create view project_pay as
select pe.projectid, pe.employeeid, pe.dayswork,
e.jobsalaryperday, (e.jobsalaryperday * dayswork) as total_salary
from `Project-Employee` pe
inner join `employee_pay` e
on e.employeeid = pe.employeeid
The data at the end of your question doesn't seem to match the data in your INSERT statements.
Have you ever heard of "divide and conquer"? This is a good time to use it. Here's what I'd do.
create view employee_pay as
select e.*, j.jobname, j.jobsalary, j.jobsalaryperday
from employee e
inner join jobs j on e.jobid = j.jobid
create view project_pay as
select pe.projectid, pe.employeeid, pe.dayswork,
e.jobsalaryperday, (e.jobsalaryperday * dayswork) as total_salary
from project_employee pe
inner join employee_pay e
on e.employeeid = pe.employeeid
I'd do that, because I expect those views to be generally useful. (Especially for debugging.) Having created those views, the total for a project is dead simple.
select projectid, sum(total_salary) as total_salaries
from project_pay
group by projectid
projectid total_salaries
--
1 1509.91
You really don't want to use DOUBLE for money. Use DECIMAL instead.
Use this query to sort out why my sum doesn't match yours.
select p.*, e.name
from project_pay p
inner join employee e on e.employeeid = p.employeeid;
projectid employeeid dayswork jobsalaryperday total_salary name
1 10 20 21.67 433.4 Joe
1 20 11 21.67 238.37 Moe
1 30 17 26.7 453.9 Jack
1 40 8 17.35 138.8 Marge
1 50 8 30.68 245.44 Greg
Anti-patterns
Broken identity
Whenever you see a table like this one
CREATE TABLE Departments (
DeptID INTEGER NOT NULL PRIMARY KEY,
DeptName VARCHAR(30) NOT NULL
);
you should assume its structure is wrong, and dig deeper. (It's presumed guilty until proven innocent.) The anti-pattern you look for
integer as an artificial primary key, along with
no other unique constraints.
A table like this allows the real data to be duplicated, eliminating the usefulness of an artificial key.
DeptID DeptName
--
1 Wibble
2 Wibble
...
175 Wibble
A table like this will allow multiple foreign key references, too. That means some of the foreign keys might reference Wibble (DeptID = 1), some might reference Wibble (DeptID = 175), and so on.
To fix that, add a UNIQUE constraint on DeptName.
Missing foreign key references
Whenever you see a table like this one
CREATE TABLE Employee(
EmployeeID INTEGER NOT NULL PRIMARY KEY,
Name VARCHAR(30) NOT NULL,
...
DeptID INTEGER NOT NULL,
JobID INTEGER NOT NULL
);
you should assume its structure is wrong, and dig deeper. (Again, it's presumed guilty until proven innocent.) The anti-pattern you look for
ID numbers from other tables, along with
no foreign key constraints referencing those tables.
To fix that, add foreign key constraints for DeptID and JobID. On MySQL, make sure you're using the INNODB engine, too. (As of MySQL 5.6, MyISAM still won't enforce foreign key constraints, but it won't give you an error or warning if you write them. They're parsed and ignored.)
If you come to MySQL from another dbms, you'll be surprised to find that MySQL doesn't support inline foreign key reference syntax. That means you can't write this.
DeptID integer not null references Departments (DeptID)
Instead, you have to write a separate foreign key clause in the CREATE TABLE statement. (Or use a separate ALTER TABLE statement to declare the FK reference.)
DeptID integer not null,
foreign key (DeptID) references Departments (DeptID)
Search this page for "inline ref", but read the whole thing.
Missing CHECK() constraints
MySQL doesn't enforce CHECK() constraints, so for columns that beg for a CHECK() constraint, you need a table and a foreign key reference. When you see a structure like this
CREATE TABLE Employee(
EmployeeID INTEGER NOT NULL PRIMARY KEY,
Name VARCHAR(30) NOT NULL,
Sex CHAR(1) NOT NULL,
the column "Sex" begs for a CHECK() constraint.
CREATE TABLE Employee(
EmployeeID INTEGER NOT NULL PRIMARY KEY,
Name VARCHAR(30) NOT NULL,
Sex CHAR(1) NOT NULL CHECK( Sex IN ('M', 'F')),
But MySQL doesn't enforce CHECK() constraints, so you need another table and a foreign key reference.
create table sexes (
sex char(1) primary key
);
insert into sexes values ('M');
insert into sexes values ('F');
CREATE TABLE Employee(
EmployeeID INTEGER NOT NULL PRIMARY KEY,
Name VARCHAR(30) NOT NULL,
Sex CHAR(1) NOT NULL,
...
foreign key (Sex) references Sexes (Sex)
I'd consider CHECK() constraints for most of these columns. Some can be implemented as tables with foreign key references.
Employee.Security
Jobs.JobSalary
Jobs.JobSalaryperDay
Project.DaysOfWork
Project.NoEmployees
Project.EstimatedCost
Project.RealCost
Project_Employee.DaysWork
Using floating-point data types for money
Don't do that. Floating-point numbers are useful approximations, but they're still approximations. Use DECIMAL instead.