SQL Query for one to many relationship - mysql

Here's my database:
Employee Table:
employee_id int(10) NOT NULL,
firstname varchar(50) NOT NULL,
lastname varchar(50) NOT NULL,
username varchar(15) NOT NULL,
password varchar(25) NOT NULL DEFAULT 'password',
contact_number varchar(13) NOT NULL,
email_address varchar(50) NOT NULL,
position varchar(50) NOT NULL,
teamleader_id int(11) DEFAULT NULL
Service Table:
service_id int(10) NOT NULL,
ticket_id int(10) NOT NULL,
employee_id int(10) NOT NULL,
status varchar(15) NOT NULL,
start_time datetime NOT NULL,
time_in datetime DEFAULT NULL,
time_out datetime DEFAULT NULL,
service_notes varchar(500) DEFAULT NULL
Query:
SELECT * FROM employee AS e
LEFT JOIN service AS s ON e.employee_id = s.employee_id
WHERE (s.status IS NULL OR s.status = 'Completed')
AND e.teamleader_id = ?
EDITED:
I want to select all employees except the ones with service.status = 'Ongoing'

You just have to add DISTINCT to your query :
SELECT DISTINCT e.* FROM employee e LEFT JOIN service s ON e.employee_id = s.employee_id
WHERE(s.status is null OR s.status = 'Completed') and teamleader_id = 3
it filters duplicated

Assuming you just want a list of employees that have completed a service (excluding those who have not and only showing one service per employee)
SELECT employee.*, COUNT(service.status)
FROM employee, service
WHERE service.employee_id = employee.employee_id
AND ( service.status IS NULL OR service.status = 'Completed' )
AND teamleader_id = 1
GROUP BY employee.employee_id;
Or If you do want to list the employees who have not completed any service
SELECT employee.*, COUNT(service.status)
FROM employee LEFT JOIN service ON service.employee_id = employee.employee_id
WHERE ( service.status IS NULL OR service.status = 'Completed' )
AND teamleader_id = 1
GROUP BY employee.employee_id;
or if you want all except where service.status = 'Ongoing'
SELECT employee.*, COUNT(service.status)
FROM employee LEFT JOIN service ON service.employee_id = employee.employee_id
WHERE employee.employee_id NOT IN ( SELECT DISTINCT service.employee_id FROM service WHERE service.status = 'Ongoing')
AND teamleader_id = 1
GROUP BY employee.employee_id;
Tested in SQL Fiddle
CREATE TABLE employee ( employee_id INT(9) PRIMARY KEY, teamleader_id INT NOT NULL, name VARCHAR(99) NOT NULL );
CREATE TABLE service ( id INT(9) PRIMARY KEY, employee_id INT(9) NOT NULL, status VARCHAR(99) NOT NULL );
INSERT INTO employee VALUES (1, 1, 'Bob');
INSERT INTO employee VALUES (2, 1, 'Alice');
INSERT INTO service VALUES (1, 2, 'Complete');
INSERT INTO service VALUES (2, 2, 'WIP');
INSERT INTO service VALUES (3, 2, 'Ongoing');

Related

Find line items are supplied by suppliers in AFRICA for orders made by customers in UNITED STATES

How to fix this code to show line items are supplied by suppliers in AFRICA for orders made by customers in UNITED STATES?
It only show like the lineitem are supplied by suppliers in AFRICA or orders of lineitem made by customers in UNITED STATES but i want the lineitem are supplied by suppliers in AFRICA and orders of lineitem made by customers in UNITED STATES.
select Sum(Result.counts) from (
SELECT count(l_orderkey) AS counts
FROM lineitem
INNER JOIN orders ON lineitem.l_orderkey = orders.o_orderkey
INNER JOIN customer ON orders.o_custkey = customer.c_custkey
INNER JOIN nation ON customer.c_nationkey = nation.n_nationkey
WHERE n_name = "UNITED STATES"
union
SELECT count(l_orderkey) AS counts
FROM lineitem
INNER JOIN orders ON lineitem.l_orderkey = orders.o_orderkey
INNER JOIN supplier ON lineitem.l_suppkey = supplier.s_suppkey
INNER JOIN nation ON supplier.s_nationkey = nation.n_nationkey
INNER JOIN region ON nation.n_regionkey = region.r_regionkey
WHERE r_name = "AFRICA"
)Result;
Those are table and keys:
CREATE TABLE region (
r_regionkey decimal(2,0) not null,
r_name char(25) not null,
r_comment varchar(152)
);
CREATE TABLE nation (
n_nationkey decimal(3,0) not null,
n_name char(25) not null,
n_regionkey decimal(2,0) not null,
n_comment varchar(152)
);
CREATE TABLE part (
p_partkey decimal(10,0) not null,
p_name varchar(55) not null,
p_mfgr char(25) not null,
p_brand char(10) not null,
p_type varchar(25) not null,
p_size decimal(2,0) not null,
p_container char(10) not null,
p_retailprice decimal(6,2) not null,
p_comment varchar(23) not null
);
CREATE TABLE supplier (
s_suppkey decimal(8,0) not null,
s_name char(25) not null,
s_address varchar(40) not null,
s_nationkey decimal(3,0) not null,
s_phone char(15) not null,
s_acctbal decimal(7,2) not null,
s_comment varchar(101) not null
);
CREATE TABLE partsupp (
ps_partkey decimal(10,0) not null,
ps_suppkey decimal(8,0) not null,
ps_availqty decimal(5,0) not null,
ps_supplycost decimal(6,2) not null,
ps_comment varchar(199) not null
);
CREATE TABLE customer (
c_custkey decimal(9,0) not null,
c_name varchar(25) not null,
c_address varchar(40) not null,
c_nationkey decimal(3,0) not null,
c_phone char(15) not null,
c_acctbal decimal(7,2) not null,
c_mktsegment char(10) not null,
c_comment varchar(117) not null
);
CREATE TABLE orders (
o_orderkey decimal(12,0) not null,
o_custkey decimal(9,0) not null,
o_orderstatus char(1) not null,
o_totalprice decimal(8,2) not null,
o_orderdate date not null,
o_orderpriority char(15) not null,
o_clerk char(15) not null,
o_shippriority decimal(1,0) not null,
o_comment varchar(79) not null
);
CREATE TABLE lineitem (
l_orderkey decimal(12,0) not null,
l_partkey decimal(10,0) not null,
l_suppkey decimal(8,0) not null,
l_linenumber decimal(1,0) not null,
l_quantity decimal(2,0) not null,
l_extendedprice decimal(8,2) not null,
l_discount decimal(3,2) not null,
l_tax decimal(3,2) not null,
l_returnflag char(1) not null,
l_linestatus char(1) not null,
l_shipdate date not null,
l_commitdate date not null,
l_receiptdate date not null,
l_shipinstruct char(25) not null,
l_shipmode char(10) not null,
l_comment varchar(44) not null
);
You can run an UNION ALL
select Sum(Result.counts) from (
SELECT count(l_orderkey) AS counts
FROM lineitem
INNER JOIN orders ON lineitem.l_orderkey = orders.o_orderkey
INNER JOIN customer ON orders.o_custkey = customer.c_custkey
INNER JOIN nation ON customer.c_nationkey = nation.n_nationkey
WHERE n_name = 'UNITED STATES'
union ALL
SELECT count(l_orderkey) AS counts
FROM lineitem
INNER JOIN orders ON lineitem.l_orderkey = orders.o_orderkey
INNER JOIN supplier ON lineitem.l_suppkey = supplier.s_suppkey
INNER JOIN nation ON supplier.s_nationkey = nation.n_nationkey
INNER JOIN region ON nation.n_regionkey = region.r_regionkey
WHERE r_name = 'AFRICA'
)Result;
or make something to differentiate both lines
select Sum(Result.counts) from (
SELECT count(l_orderkey) AS counts , 'UNITED STATES'
FROM lineitem
INNER JOIN orders ON lineitem.l_orderkey = orders.o_orderkey
INNER JOIN customer ON orders.o_custkey = customer.c_custkey
INNER JOIN nation ON customer.c_nationkey = nation.n_nationkey
WHERE n_name = 'UNITED STATES'
union
SELECT count(l_orderkey) AS counts , 'AFRICA'
FROM lineitem
INNER JOIN orders ON lineitem.l_orderkey = orders.o_orderkey
INNER JOIN supplier ON lineitem.l_suppkey = supplier.s_suppkey
INNER JOIN nation ON supplier.s_nationkey = nation.n_nationkey
INNER JOIN region ON nation.n_regionkey = region.r_regionkey
WHERE r_name = 'AFRICA'
)Result;
Here is a simplified version to show that both work https://dbfiddle.uk/LE5oHz95

How to perform IS_THE_SUBSET_OF function in MySQL?

Added: the CREATE code for student and takes
CREATE TABLE `student` (
`sid` VARCHAR(6) NOT NULL,
`sname` VARCHAR(6) NULL DEFAULT NULL,
`sex` VARCHAR(2) NULL DEFAULT NULL,
`age` TINYINT(3) UNSIGNED NULL DEFAULT NULL,
`dept` VARCHAR(50) NULL DEFAULT NULL,
`class` VARCHAR(4) NULL DEFAULT NULL,
PRIMARY KEY (`sid`)
);
CREATE TABLE `takes` (
`sid` VARCHAR(6) NOT NULL,
`cid` VARCHAR(3) NOT NULL,
`score` TINYINT(3) UNSIGNED NULL DEFAULT NULL,
PRIMARY KEY (`sid`, `cid`)
)
I am going to display the IDs and names of all students who have taken the courses which are taken by the student whose ID is '31401'. I have the following code:
SELECT sid, sname
FROM student S
WHERE NOT EXISTS((SELECT cid
FROM takes
WHERE sid = '31401')
EXCEPT
(SELECT cid
FROM takes T
WHERE S.sid = T.sid));
However, there is no EXCEPT operation in MySQL. So I wonder if I could rewrite like this:
SELECT sid, sname
FROM student S
WHERE ((SELECT cid
FROM takes
WHERE sid = '31401')
IS_THE_SUBSET_OF
(SELECT cid
FROM takes T
WHERE S.sid = T.sid));
How can I implement the IS_THE_SUBSET_OF function?
There is no set-based operator that does what you want. You can use join, group by and some other logic:
select t.sid
from takes t join
takes ts
on t.cid = ts.cid and ts.sid = 31401 -- don't use single quotes for a numeric constant
group by t.sid
having count(*) = (select count(*) from takes ts2 where ts2.sid = 31401);
This formulation assumes that takes does not have duplicate sid/cid pairs.

How do I join 2 columns in MySql?

I have the following table structure in my code and I am trying to pull username and name fields from users table, but the query currently pulls only from_user_id data. How do I modify this so that I get two separate columns that lists username and name for both to_user_id and from_user_id?
SELECT f.id, from_user_id, to_user_id, STATUS, u.username, u.name
FROM friend f
left JOIN users u ON f.from_user_id = u.id
WHERE f.id IN(
SELECT source_id
FROM notification
WHERE user_id = 5 AND notification_read = 1)
users table:
CREATE TABLE `users` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(60) NOT NULL,
`password` VARCHAR(64) NOT NULL,
`enabled` TINYINT(4) NOT NULL DEFAULT '1',
`email` VARCHAR(100) NOT NULL,
`name` VARCHAR(100) NOT NULL,
`created_on` DATETIME NOT NULL,
`role` VARCHAR(50) NULL DEFAULT 'ROLE_USER',
PRIMARY KEY (`id`),
UNIQUE INDEX `username` (`username`)
)
and friend table:
CREATE TABLE `friend` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`from_user_id` BIGINT(20) NOT NULL,
`to_user_id` BIGINT(20) NOT NULL,
`status` INT(2) NOT NULL,
`requested_date` DATETIME NULL DEFAULT NULL,
`accepted_date` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `from_user_id` (`from_user_id`),
INDEX `to_user_id` (`to_user_id`)
)
and a notification table:
CREATE TABLE `notification` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`user_id` BIGINT(20) NOT NULL,
`activity_type` TINYINT(4) NOT NULL,
`source_id` BIGINT(20) NOT NULL,
`parent_id` BIGINT(20) NULL DEFAULT NULL,
`parent_type` TINYINT(4) NULL DEFAULT NULL,
`notification_read` TINYINT(4) NOT NULL DEFAULT '0',
`created_on` DATETIME NOT NULL,
PRIMARY KEY (`id`),
INDEX `user_id` (`user_id`),
INDEX `created_on` (`created_on`)
)
You need to perform two joins against users - one for each side of the friend relationship, and include the appropriate columns in the SELECT list from both of those joins against users.
SELECT
f.id,
from_user_id,
to_user_id,
STATUS,
-- uf is an alias for the "from" user
-- You must alias the columns to distinguish them
uf.username AS from_username,
uf.name AS from_name,
-- ut is an alias for the "to" user
ut.username AS to_username,
ut.name AS to_name
FROM
friend f
-- Join first for the from user info
LEFT JOIN users uf ON f.from_user_id = uf.id
-- Join again for the to user info
LEFT JOIN users ut ON f.to_user_id = ut.id
WHERE f.id IN(
SELECT source_id
FROM notification
WHERE user_id = 5 AND notification_read = 1
)
A further note... You can substitute an INNER JOIN against notification instead of the IN () subquery, and you may achieve better performance.
SELECT
DISTINCT /* needed assuming multiple notification.source_id per f.id */
f.id,
from_user_id,
to_user_id,
STATUS,
uf.username AS from_username,
uf.name AS from_name,
ut.username AS to_username,
ut.name AS to_name
FROM
friend f
LEFT JOIN users uf ON f.from_user_id = uf.id
LEFT JOIN users ut ON f.to_user_id = ut.id
-- Join notification instead of the IN () subquery
INNER JOIN notification
ON f.id = notification.source_id
AND notification.user_id = 5
AND notification_read = 1

How can I build this query in mysql

Hope someone will help me to build this query. I need to get values from database which are belong to one particular user. These are the columns which I need to get.
01. city_name // From city table
02. district_name // From district table
03. category_name // From category table
04. subscription_period // From tutors table
05. date_registered // From tutors table
I have already got values tutor_id, city_id and category_id respectively 1, 53, 5
My address table has city_id column, City table has district_id column, category table has category_id column and tutor table has tutor_id and address_id.
I tried something like this, but this is not working.
SELECT city_name, district_name, category_name, subscription_period AS sp, date_registered AS date
FROM tutors AS t
INNER JOIN district AS d
INNER JOIN city ON city.district_id = d.district_id
INNER JOIN address ON address.city_id = 50
INNER JOIN category
WHERE t.tutor_id = 1 AND category.category_id= 5
My expecting result should have 1 row as output but this query give me more rows as output
My SQL Tables
CREATE TABLE tutors (
tutor_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
address_id SMALLINT NOT NULL,
tutor_name VARCHAR(80) NOT NULL,
subscription_period SMALLINT NOT NULL,
date_registered TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (tutor_id)
);
CREATE TABLE address (
address_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
address VARCHAR(40) NOT NULL,
city_id SMALLINT UNSIGNED NOT NULL,
PRIMARY KEY (address_id)
);
CREATE TABLE district(
district_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
district_name VARCHAR(30) NOT NULL,
PRIMARY KEY (district_id)
);
CREATE TABLE city(
city_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
city_name VARCHAR(30) NOT NULL,
district_id SMALLINT UNSIGNED NOT NULL,
PRIMARY KEY (city_id)
);
CREATE TABLE category (
category_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
category_name VARCHAR(60) NOT NULL,
PRIMARY KEY (category_id)
);
CREATE TABLE tutor_category(
tutor_id SMALLINT UNSIGNED NOT NULL,
category_id SMALLINT UNSIGNED NOT NULL,
PRIMARY KEY (tutor_id)
);
Try something like this:
SELECT c.city_name,
d.district_name,
c.category_name,
t.subscription_period AS sp,
t.date_registered AS date
FROM tutors t
INNER JOIN address a ON t.address_id = a.address_id
INNER JOIN city c ON c.city_id = a.city_id
INNER JOIN district d ON c.district_id = d.district_id
INNER JOIN tutor_category tc on t.tutor_id = tc.tutor_id
INNER JOIN category cat on tc.category_id = cat.category_id
WHERE t.tutor_id = 1 AND cat.category_id= 5 AND c.city_id = 50
You need to JOIN on your tables to ensure you only receive the desired results.
If you are receiving duplicate rows that are identical, you are probably doing something wrong or the definitions you provided are not correct (it appears that this is the case indeed). You could "solve" the issue by adding GROUP BY or DISTINCT to your query but that is not the best approach.

returning a query with 3 table joins- one being an outer join

i am building a car booking system, the query i need to build is one that displays all the cars in the system- but each car can be booked by 2 people at the same time.
I only want to display cars that have not been fully booked or only have been booked by a single person. I want to be able to see the persons first name and last name if it has been booked by a single person so others can tell who they will be driving with.
This is my query so far but its returning the first record multiple times
SELECT cars.*, people.first_name, people.last_name
FROM
cars
LEFT JOIN booking on cars.id = booking.car LEFT OUTER JOIN people ON booking.person_1 = people.id
WHERE cars.id NOT IN
(SELECT car
FROM booking WHERE slot = 'morning_drive' AND dated = '2011-11-05' AND person_1 != '' AND person_2 != '') AND cars.type = '911'
Heres the structure of the three tables
CREATE TABLE `cars` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(100) NOT NULL,
`description` text NOT NULL,
`large_img_url` varchar(250) NOT NULL,
`small_img_url` varchar(250) NOT NULL,
`nr` varchar(100) NOT NULL,
`license_plate` varchar(100) NOT NULL,
`exterior_color` varchar(100) NOT NULL,
`interior_color` varchar(100) NOT NULL,
`PDK` tinyint(1) NOT NULL,
`Manual` tinyint(1) NOT NULL,
`type` enum('911','vintage') NOT NULL,
`power` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=27 ;
CREATE TABLE `booking` (
`id` int(11) NOT NULL auto_increment,
`slot` enum('morning_drive','afternoon_loop','return_drive') NOT NULL,
`type` enum('911','vintage_911') NOT NULL,
`car` int(11) NOT NULL,
`person_1` int(11) default NULL,
`person_2` int(11) default NULL,
`dated` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=25 ;
CREATE TABLE `people` (
`id` int(11) NOT NULL auto_increment,
`first_name` varchar(50) NOT NULL,
`last_name` varchar(50) NOT NULL,
`organisation` varchar(100) NOT NULL,
`event_date` date NOT NULL,
`wave` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=454 ;
so how can i have my query only return one row per car basically, displaying only cars that have no (person_2) and no (person_1 or person_2), if the car does have person_1- it will retrieve that persons first_name and last_name
update
i have figured out that the main select query is returning all the rows- i need it restricted to slot = 'morning_drive' AND dated = '2011-11-05' so it shows if a person has booked the car for that time slot but also it needs to return the cars where no user has booked for that timeslot
update 2
based on realising new requirements of the query i have added a left outer join subquery instead of using a full table-
SELECT cars.*, people.first_name, people.last_name
FROM
cars
LEFT OUTER JOIN (SELECT car, person_1 FROM booking WHERE slot = 'morning_drive' AND dated = '2011-11-05' AND booking.person_1 != '' AND booking.person_2 = '') bookings on cars.id = bookings.car LEFT JOIN people ON bookings.person_1 = people.id
WHERE cars.id NOT IN
(SELECT car
FROM booking WHERE slot = 'morning_drive' AND dated = '2011-11-05' AND person_1 != '' AND person_2 != '')
AND cars.type = '911'
this is closer to what i want returned but its not join the people table first_names and last_names- its returning nothing
Try:
SELECT cars.*, people.first_name, people.last_name
FROM cars
JOIN booking on cars.id = booking.car
JOIN people on booking.person_1 = people.id
WHERE booking.slot = 'morning_drive' AND
booking.dated = '2011-11-05' AND
coalesce(booking.person_2,'') = '' AND
cars.type = '911'
UNION
SELECT cars.*, '' first_name, '' last_name
FROM cars
WHERE cars.type = '911' AND
NOT EXISTS
(SELECT NULL FROM booking
WHERE slot = 'morning_drive' AND
dated = '2011-11-05' AND
cars.id = bookings.car)