Inner join 3 table - mysql

I have 6 table in my database . And now I would like to inner join car_space, transaction and sport_facilities. However, I got a problem.
When I use these two sql command respectively, these command also can be run and I can get the result I want.
-- car_space INNER JOIN transaction
SELECT * FROM car_space INNER JOIN transaction ON car_space.carSpaceId = transaction.carSpaceId ORDER BY transactionId;
-- sport_facilities INNER JOIN transaction
SELECT * FROM sport_facilities INNER JOIN transaction ON sport_facilities.sportFacilitiesId = transaction.sportFacilitiesId ORDER BY transactionId;
And then, I combine them into one command.
-- Combine But Not Work
SELECT * FROM transaction
INNER JOIN car_space ON transaction.carSpaceId = car_space.carSpaceId
INNER JOIN sport_facilities ON transaction.sportFacilitiesId = sport_facilities.sportFacilitiesId
ORDER BY transactionId;
Although this can be run, there are no result or records was shown.
I want to do is the database can be found the record in which table (car_space / sport_facilities) when I typed a transactionId.
For example:
I type WHERE transactionId = 1
Database can be searched this is from sport_facilities table rather that car_space.
Thank you. Here is some code for reference.
-- Create a database
CREATE DATABASE booking_system;
-- Use This database
USE booking_system;
-- Create smartcart table
CREATE TABLE card(
cardId CHAR(8) NOT NULL,
PRIMARY KEY (cardId)
);
-- Insert some recond to card table
INSERT INTO card VALUES
('4332A0D5'),
('637ED500'),
('B3895A02'),
('E32F3702')
;
-- Create user table
CREATE TABLE user(
userId INT(5) NOT NULL AUTO_INCREMENT,
cardNo CHAR(8) NOT NULL,
firstName VARCHAR(255) NOT NULL,
lastName VARCHAR(255) NOT NULL,
sex CHAR(1) NOT NULL,
dob DATE NOT NULL,
hkid CHAR(8) NOT NULL,
email VARCHAR(255) NOT NULL,
telNo INT(8) NOT NULL,
PRIMARY KEY (userId),
FOREIGN KEY (cardNo) REFERENCES card (cardId) ON DELETE CASCADE,
UNIQUE (hkid)
);
-- Alter user table
ALTER TABLE user AUTO_INCREMENT = 16001;
-- Insert some recond to user table
INSERT INTO user VALUES
('','4332A0D5','Andy','Ding','M','1962-04-20','K5216117','mkding#yahoo.com','98626229'),
('','637ED500','Emma','Dai','F','1972-06-15','D5060339','emmadai#yahoo.com.hk','62937453'),
('','B3895A02','Brinsley','Au','F','1984-02-24','P8172327','da224#live.hk','91961624'),
('','E32F3702','Eric','Fong','M','1990-04-15','Y1129323','ericfong0415#gmail.com','98428731')
;
-- Create car space price table
CREATE TABLE car_space_price(
spaceNo INT(2) NOT NULL AUTO_INCREMENT,
price INT(2) NOT NULL,
carSpaceDescription VARCHAR(16),
CHECK (carSpaceDescription IN ('motorcycles','small vehicles','medium vehicles','large vehicles')),
PRIMARY KEY (spaceNo)
);
-- Insert some recond to car space price table
INSERT INTO car_space_price VALUES
('','10','motorcycles'), -- 1
('','10','motorcycles'), -- 2
('','10','motorcycles'), -- 3
('','10','motorcycles'), -- 4
('','10','motorcycles'), -- 5
('','20','small vehicles'), -- 6
('','20','small vehicles'), -- 7
('','20','small vehicles'), -- 8
('','20','small vehicles'), -- 9
('','20','small vehicles'), -- 10
('','40','medium vehicles'), -- 11
('','40','medium vehicles'), -- 12
('','40','medium vehicles'), -- 13
('','80','large vehicles'), -- 14
('','80','large vehicles') -- 15
;
-- Create car space table
CREATE TABLE car_space(
carSpaceId INT(5) NOT NULL AUTO_INCREMENT,
spaceNo INT(2) NOT NULL,
cardNo VARCHAR(8) NOT NULL,
inTime DATETIME,
outTime DATETIME,
PRIMARY KEY (carSpaceId),
FOREIGN KEY (spaceNo) REFERENCES car_space_price (spaceNo) ON DELETE CASCADE,
FOREIGN KEY (cardNo) REFERENCES card (cardId) ON DELETE CASCADE
);
-- Insert some recond to car space table
INSERT INTO car_space VALUES
('','2','E32F3702','2015-02-23 14:24:18','2015-02-23 17:01:43'), -- 1 --16004
('','6','B3895A02','2016-02-24 11:56:43','2016-02-25 09:21:08'), -- 2 --16003
('','2','E32F3702','2016-02-24 16:42:34','2016-02-24 21:02:45'), -- 3 --16004
('','2','E32F3702','2016-02-25 14:25:32','2016-02-25 17:03:54'), -- 4 --16004
('','6','B3895A02','2016-02-25 17:12:11','2016-02-25 20:58:18'), -- 5 --16003
('','13','637ED500','2016-02-25 19:17:03','2016-02-27 18:05:28'), -- 6 --16002
('','6','B3895A02','2016-02-25 21:14:03','2016-02-25 23:53:28'), -- 7 --16003
('','6','B3895A02','2016-02-26 08:46:23','2016-02-26 17:21:08'), -- 8 --16003
('','2','E32F3702','2016-02-26 14:15:45','2016-02-26 21:01:15'), -- 9 --16004
('','6','B3895A02','2016-02-27 09:42:13','2016-02-27 15:48:45'), -- 10 --16003
('','2','E32F3702','2016-02-27 13:25:45','2016-02-27 15:15:45'), -- 11 --16004
('','6','B3895A02','2016-02-28 10:57:16','2016-02-28 14:41:25'), -- 12 --16003
('','2','E32F3702','2016-02-28 11:47:32','2016-02-28 13:43:15'), -- 13 --16004
('','13','637ED500','2016-02-28 13:04:43','2016-03-02 22:39:46'), -- 14 --16002
('','2','E32F3702','2016-02-28 14:42:34','2016-02-28 21:47:45'), -- 15 --16004
('','6','B3895A02','2016-02-29 08:50:42','2016-02-29 14:28:42'), -- 16 --16003
('','2','E32F3702','2016-02-29 12:12:35','2016-02-29 16:45:28'), -- 17 --16004
('','6','B3895A02','2016-03-01 11:26:43','2016-03-01 14:56:26'), -- 18 --16003
('','6','B3895A02','2016-03-03 13:45:26','2016-03-03 17:54:18') -- 19 --16003
;
-- Create sport facilities price table
CREATE TABLE sport_facilities_price(
sportNo INT(2) NOT NULL AUTO_INCREMENT,
sportType VARCHAR(10) NOT NULL,
price INT(2) NOT NULL,
sportDescription VARCHAR(20),
PRIMARY KEY (sportNo)
);
-- Insert some recond to sport facilities price table
INSERT INTO sport_facilities_price VALUES
('','snooker','15','Snooker Room 1'), -- 1
('','snooker','15','Snooker Room 2'), -- 2
('','snooker','15','Snooker Room 3'), -- 3
('','snooker','15','Snooker Room 4'), -- 4
('','table_tennis','15','Table Tennis Room 1'), -- 5
('','table_tennis','15','Table Tennis Room 2'), -- 6
('','table_tennis','15','Table Tennis Room 3'), -- 7
('','table_tennis','15','Table Tennis Room 4'), -- 8
('','tennis','30','Tennis Vanue 1'), -- 9
('','tennis','30','Tennis Vanue 2'), -- 10
('','badminton','30','Badminton Vanue 1'), -- 11
('','badminton','30','Badminton Vanue 2'), -- 12
('','basketball','60','Hall') -- 13
;
-- Create sport facilities table
CREATE TABLE sport_facilities(
sportFacilitiesId INT(5) NOT NULL AUTO_INCREMENT,
sportNo INT(2) NOT NULL,
cardNo VARCHAR(8) NOT NULL,
bookDate DATE NOT NULL,
startTime TIME NOT NULL,
endTime TIME NOT NULL,
PRIMARY KEY (sportFacilitiesId),
FOREIGN KEY (sportNo) REFERENCES sport_facilities_price (sportNo) ON DELETE CASCADE,
FOREIGN KEY (cardNo) REFERENCES card (cardId) ON DELETE CASCADE
);
-- Insert some recond to sport facilities table
INSERT INTO sport_facilities VALUES
('','1','E32F3702','2015-02-23','12:00:00','14:00:00'), -- 1 --16004
('','5','B3895A02','2016-02-23','14:00:00','15:00:00'), -- 2 --16003
('','8','637ED500','2016-02-23','17:00:00','21:00:00'), -- 3 --16002
('','2','E32F3702','2016-02-24','09:00:00','11:00:00'), -- 4 --16004
('','5','4332A0D5','2016-02-24','13:00:00','14:00:00'), -- 5 --16001
('','7','637ED500','2016-02-24','15:00:00','17:00:00'), -- 6 --16002
('','8','B3895A02','2016-02-24','16:00:00','18:00:00'), -- 7 --16003
('','10','4332A0D5','2016-02-25','09:00:00','10:00:00'), -- 8 --16001
('','12','B3895A02','2016-02-25','13:00:00','14:00:00'), -- 9 --16003
('','6','637ED500','2016-02-25','21:00:00','22:00:00'), -- 10 --16002
('','4','637ED500','2016-02-26','11:00:00','13:00:00'), -- 11 --16002
('','8','4332A0D5','2016-02-26','22:00:00','23:00:00'), -- 12 --16001
('','13','B3895A02','2016-02-27','09:00:00','14:00:00'), -- 13 --16003
('','4','637ED500','2016-02-28','12:00:00','14:00:00'), -- 14 --16002
('','3','B3895A02','2016-02-28','14:00:00','15:00:00'), -- 15 --16003
('','4','E32F3702','2016-02-28','17:00:00','19:00:00'), -- 16 --16004
('','5','B3895A02','2016-02-28','21:00:00','22:00:00'), -- 17 --16003
('','2','4332A0D5','2016-02-28','21:00:00','23:00:00'), -- 18 --16001
('','10','E32F3702','2016-02-28','19:00:00','20:00:00'), -- 19 --16004
('','11','B3895A02','2016-02-29','11:00:00','13::00:00'), -- 20 --16003
('','8','E32F3702','2016-02-29','12:00:00','14:00:00'), -- 21 --16004
('','4','4332A0D5','2016-02-29','15:00:00','18:00:00'), -- 22 --16001
('','6','E32F3702','2016-03-01','09:00:00','11:00:00'), -- 23 --16004
('','5','637ED500','2016-03-01','12:00:00','15:00:00'), -- 24 --16002
('','3','B3895A02','2016-03-02','09:00:00','11:00:00'), -- 25 --16003
('','7','4332A0D5','2016-03-02','12:00:00','13:00:00'), -- 26 --16001
('','4','637ED500','2016-03-02','15:00:00','17:00:00'), -- 27 --16002
('','1','E32F3702','2016-03-02','19:00:00','22:00:00'), -- 28 --16004
('','12','4332A0D5','2016-03-03','11:00:00','13:00:00'), -- 29 --16001
('','9','E32F3702','2016-03-03','15:00:00','16:00:00'), -- 30 --16004
('','10','B3895A02','2016-03-03','09:00:00','11:00:00'), -- 31 --16003
('','4','637ED500','2016-03-04','11:00:00','12:00:00'), -- 32 --16002
('','8','E32F3702','2016-03-04','14:00:00','16:00:00'), -- 33 --16004
('','6','B3895A02','2016-03-05','19:00:00','21:00:00'), -- 34 --16003
('','13','E32F3702','2016-03-05','11:00:00','12:00:00'), -- 35 --16004
('','8','637ED500','2016-03-05','14:00:00','15:00:00'), -- 36 --16002
('','4','4332A0D5','2016-03-05','16:00:00','18:00:00'), -- 37 --16001
('','5','E32F3702','2016-03-06','13:00:00','15:00:00'), -- 38 --16004
('','9','B3895A02','2016-03-06','17:00:00','18:00:00'), -- 39 --16003
('','11','4332A0D5','2016-03-07','20:00:00','21::00:00'), -- 40 --16001
('','5','B3895A02','2016-03-07','22:00:00','23:00:00') -- 41 --16003
;
-- Create transaction table
CREATE TABLE transaction(
transactionId INT(5) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT,
userId INT(5) NOT NULL,
carSpaceId INT(5),
sportFacilitiesId INT(5),
transactionDate DATE NOT NULL,
PRIMARY KEY (transactionId),
FOREIGN KEY (userId) REFERENCES user (userId) ON DELETE CASCADE,
FOREIGN KEy (carSpaceId) REFERENCES car_space (carSpaceId) ON DELETE CASCADE,
FOREIGN KEY (sportFacilitiesId) REFERENCES sport_facilities (sportFacilitiesId) ON DELETE CASCADE
);
-- Insert some recond to transaction table
INSERT INTO transaction VALUES
('','16004',NULL,'1','2015-02-23'), -- 1 -- Sport Facilities
('','16003',NULL,'5','2015-02-23'), -- 2 -- Sport Facilities
('','16004','2',NULL,'2015-02-23'), -- 3 -- Car Space
('','16002',NULL,'8','2015-02-23'), -- 4 -- Sport Facilities
('','16004',NULL,'2','2016-02-24'), -- 5 -- Sport Facilities
('','16003','6',NULL,'2016-02-24'), -- 6 -- Car Space
('','16001',NULL,'5','2016-02-24'), -- 7 -- Sport Facilities
('','16002',NULL,'7','2016-02-24'), -- 8 -- Sport Facilities
('','16003',NULL,'8','2016-02-24'), -- 9 -- Sport Facilities
('','16004','2',NULL,'2016-02-24'), -- 10 -- Car Space
('','16001',NULL,'10','2016-02-25'), -- 11 -- Sport Facilities
('','16003',NULL,'12','2016-02-25'), -- 12 -- Sport Facilities
('','16004','2',NULL,'2016-02-25'), -- 13 -- Car Space
('','16003','6',NULL,'2016-02-25'), -- 14 -- Car Space
('','16002','13',NULL,'2016-02-25'), -- 15 -- Car Space
('','16002',NULL,'6','2016-02-25'), -- 16 -- Sport Facilities
('','16003','6',NULL,'2016-02-25'), -- 17 -- Car Space
('','16003','6',NULL,'2016-02-26'), -- 18 -- Car Space
('','16002',NULL,'4','2016-02-26'), -- 19 -- Sport Facilities
('','16004','2',NULL,'2016-02-26'), -- 20 -- Car Space
('','16001',NULL,'8','2016-02-26'), -- 21 -- Sport Facilities
('','16003',NULL,'13','2016-02-27'), -- 22 -- Sport Facilities
('','16003','6',NULL,'2016-02-27'), -- 23 -- Car Space
('','16004','2',NULL,'2016-02-27'), -- 24 -- Car Space
('','16003','6',NULL,'2016-02-28'), -- 25 -- Car Space
('','16004','2',NULL,'2016-02-28'), -- 26 -- Car Space
('','16002',NULL,'4','2016-02-28'), -- 27 -- Sport Facilities
('','16002','13',NULL,'2016-02-28'), -- 28 -- Car Space
('','16003',NULL,'3','2016-02-28'), -- 29 -- Sport Facilities
('','16004','2',NULL,'2016-02-28'), -- 30 -- Car Space
('','16004',NULL,'4','2016-02-28'), -- 31 -- Sport Facilities
('','16003',NULL,'5','2016-02-28'), -- 32 -- Sport Facilities
('','16001',NULL,'2','2016-02-28'), -- 33 -- Sport Facilities
('','16004',NULL,'10','2016-02-28'), -- 34 -- Sport Facilities
('','16003','6',NULL,'2016-02-29'), -- 35 -- Car Space
('','16003',NULL,'11','2016-02-29'), -- 36 -- Sport Facilities
('','16004',NULL,'8','2016-02-29'), -- 37 -- Sport Facilities
('','16004','2',NULL,'2016-02-29'), -- 38 -- Car Space
('','16001',NULL,'4','2016-02-29'), -- 39 -- Sport Facilities
('','16004',NULL,'6','2016-03-01'), -- 40 -- Sport Facilities
('','16003','6',NULL,'2016-03-01'), -- 41 -- Car Space
('','16002',NULL,'5','2016-03-01'), -- 42 -- Sport Facilities
('','16003',NULL,'3','2016-03-02'), -- 43 -- Sport Facilities
('','16001',NULL,'7','2016-03-02'), -- 44 -- Sport Facilities
('','16002',NULL,'4','2016-03-02'), -- 45 -- Sport Facilities
('','16004',NULL,'1','2016-03-02'), -- 46 -- Sport Facilities
('','16001',NULL,'12','2016-03-03'), -- 47 -- Sport Facilities
('','16003','6',NULL,'2016-03-03'), -- 48 -- Car Space
('','16004',NULL,'9','2016-03-03'), -- 49 -- Sport Facilities
('','16003',NULL,'10','2016-03-03'), -- 50 -- Sport Facilities
('','16002',NULL,'4','2016-03-04'), -- 51 -- Sport Facilities
('','16004',NULL,'8','2016-03-04'), -- 52 -- Sport Facilities
('','16003',NULL,'6','2016-03-05'), -- 53 -- Sport Facilities
('','16004',NULL,'13','2016-03-05'), -- 54 -- Sport Facilities
('','16002',NULL,'8','2016-03-05'), -- 55 -- Sport Facilities
('','16001',NULL,'4','2016-03-05'), -- 56 -- Sport Facilities
('','16004',NULL,'5','2016-03-06'), -- 57 -- Sport Facilities
('','16003',NULL,'9','2016-03-06'), -- 58 -- Sport Facilities
('','16001',NULL,'11','2016-03-07'), -- 59 -- Sport Facilities
('','16003',NULL,'5','2016-03-07') -- 60 -- Sport Facilities
;

How do you wish to combine the rows?
Looks like all your transactions that reference a car space have a NULL sports facility reference and vice versa.
Queries are done row-by-row, when you INNER JOIN transaction to just car spaces, you get all the transaction records with car space references with the car space record. All the other transactions are filtered out.
As none of these filtered transaction + car space rows have sports facility references (all NULL), when you then add the INNER JOIN to sports facilities there are no matching rows and again the non-matching rows are filtered out. This leaves you with an empty results set.
To get any results back from the double INNER JOIN query, the transaction row would have to reference (or link) a car space AND a sports facility.
If you want to keep all the transaction rows with either their car space or sports facility and a NULLed out record for whichever is not referenced, you could change the INNER JOINs to LEFT JOINs (just replace the words INNER with LEFT in your final query).

In this case, I believe you would want to use the UNION operator. You don't have transaction IDs that match in both tables, which is why you are returning 0 rows. LEFT/FULL joins may also work for you.
SELECT * FROM car_space INNER JOIN transaction ON car_space.carSpaceId = transaction.carSpaceId ORDER BY transactionId;
UNION
SELECT * FROM sport_facilities INNER JOIN transaction ON sport_facilities.sportFacilitiesId = transaction.sportFacilitiesId ORDER BY transactionId;

Related

How do I add values from two different tables using a MySQL trigger?

I have two tables, People and Shifts. I want to write a trigger that takes entries in Shifts and updates the values in People by adding a value from the Shifts.Shift column to the Coordinates column in People. However, my code seems to affect every entry under People the same way. I'll illustrate below.
CREATE TABLE People(
`id` INT,
`Name` VARCHAR(10),
`Coordinates` INT);
CREATE TABLE Shifts(
`id` INT,
`Name` VARCHAR(10),
`Shift` INT);
INSERT INTO People VALUES (1,"Ashley",28),(2,"Bob",101),(3,"Curtis",31),(4,"Daniel",69),(5,"Esther",3);
CREATE TRIGGER Shifting_Location
AFTER INSERT ON Shifts
FOR EACH ROW
UPDATE People
SET `id` = NEW.`id`,
`Name` = NEW.`Name`,
`Coordinates` = `Coordinates` + NEW.`Shift`;
Here are the tables:
People
id
Name
Coordinates
1
Ashley
28
2
Bob
101
3
Curtis
31
4
Daniel
69
5
Esther
3
Shifts (empty at first)
id
Name
Shift
Now if I insert a record into Shifts like so:
INSERT INTO Shifts VALUES (1, "Ashley", 12)
I want to get
id
Name
Coordinates
1
Ashley
40
2
Bob
101
3
Curtis
31
4
Daniel
69
5
Esther
3
id
Name
Shift
1
Ashley
12
Where the 28 next to Ashley had 12 added to it and is now 40.
However, what I get instead is:
id
Name
Coordinates
1
Ashley
40
1
Ashley
113
1
Ashley
43
1
Ashley
81
1
Ashley
15
The (1, "Ashley") has overwritten all the other record entries, and all the values in coordinates had 12 added to them instead of just the first row. Again, here is the trigger I wrote to achieve this:
CREATE TRIGGER Shifting_Location
AFTER INSERT ON Shifts
FOR EACH ROW
UPDATE People
SET `id` = NEW.`id`,
`Name` = NEW.`Name`,
`Coordinates` = `Coordinates` + NEW.`Shift`;
CREATE TRIGGER Shifting_Location
AFTER INSERT ON Shifts
FOR EACH ROW
UPDATE People
SET Coordinates = Coordinates + NEW.Shift
WHERE id = New.id;
https://dbfiddle.uk/WrUsqQnH

MySQL Select Datetime Overlapping Entries

I have a table with the following setup:
CREATE TABLE IF NOT EXISTS `appointment` (
`appId` tinyint(3) UNSIGNED AUTO_INCREMENT NOT NULL,
`startDateTime` datetime,
`duration` time DEFAULT NULL
);
Sample Data:
appId startDateTime duration
1 2015-05-04 16:15:00 00:14:00
2 2015-05-12 08:15:00 05:54:00
3 2015-05-12 08:35:00 02:14:00
4 2016-05-04 08:11:00 04:11:00
5 2015-05-13 19:30:00 02:50:00
Expected Output:
appId startDateTime duration
2 2015-05-12 08:15:00 05:54:00
3 2015-05-12 08:35:00 02:14:00
I need a query that is able to check every entry in the table and return and entries that collide. In the sample data above, 2 and 3 will overlap. I can convert both of the fields to unix time and calculate the end time however I am not sure how to compare each entry
Any idea?
Using Faisal's fiddle...
-- ----------------------------
-- Table structure for appointment
-- ----------------------------
DROP TABLE IF EXISTS `appointment`;
CREATE TABLE `appointment` (
`appId` tinyint(10) NOT NULL AUTO_INCREMENT,
`startDateTime` datetime DEFAULT NULL,
`duration` time DEFAULT NULL,
PRIMARY KEY (`appId`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
-- ----------------------------
-- Records of appointment
-- ----------------------------
INSERT INTO `appointment` VALUES
('1', '2015-05-04 16:15:00', '00:14:00'),
('2', '2015-05-12 08:15:00', '05:54:00'),
('3', '2015-05-12 08:35:00', '02:14:00'),
('4', '2016-05-04 08:11:00', '04:11:00'),
('5', '2015-05-13 19:30:00', '02:50:00');
SELECT DISTINCT x.*
FROM appointment x
JOIN appointment y
ON y.startdatetime < x.startdatetime + INTERVAL TIME_TO_SEC(x.duration) SECOND
AND y.startdatetime + INTERVAL TIME_TO_SEC(y.duration) SECOND > x.startdatetime
AND y.appid <> x.appid;
appId startDateTime duration
3 12.05.2015 08:35:00 02:14:00
2 12.05.2015 08:15:00 05:54:00
http://rextester.com/YJA59081
Try with this below query. Hope it should be solve your problem.
SELECT
tbl1.*
FROM
appointment tbl1,
appointment tbl2
WHERE
tbl2.appId <> tbl1.appId
AND tbl2.startDateTime < tbl1.startDateTime + INTERVAL TIME_TO_SEC(tbl1.duration) SECOND
AND tbl2.startDateTime + INTERVAL TIME_TO_SEC(tbl2.duration) SECOND > tbl1.startDateTime;
By clicking on the below link you can see your expected result in live which you want.
SQL Fiddle Demo

SQL result table, match in second table SET type

The following two tables are not liked by any type of constraint.
First i have a table called subscription_plans that looks like this:
name | price | ID
-------------------
plan_A | 9.99 | 1
Plan_B | 19.99 | 2
plan_C | 29.99 | 3
I have a second table called pricing_offers. The subscription_plan_ID is a of type SET and can only contain values that match the ID's of the subscription_plans.ID (column from the above table). This table looks like this:
p_o_name | subscription_plan_ID | ID
-----------------------------------------
free donuts | 1 | 1
extra sauce | 1,2,3 | 2
pony ride | 3 | 3
bus fare -50% | 1,2,3 | 4
I'm trying to do a query to select everything (all fields *) from the first table and all names from the second table and the resulting rows should look like this:
name | price | p_o_name | ID
-------------------------------------------------------------
plan_A | 9.99 | free donuts, extra sauce, bus fare -50% | 1
Plan_B | 19.99 | extra_sauce, bus fare -50% | 2
plan_C | 29.99 | extra_sauce, pony ride, bus fare -50% | 3
The idea being that it should, for each row in the subscription_plans table, look ID field. Then go trough the second table and see what rows contain in the subscription_plan_ID, the ID of the row above. Gather those into a field caller p_o_name and insert its values to the matching response rows.
I tried doing this:
SELECT subscription_plans.*, pricing_offers.name
FROM subscription_plans INNER JOIN pricing_offers ON
FIND_IN_SET(subscription_plans.ID,subscription_plan_ID)
but i get instead of:
plan_A | 9.99 | free donuts, extra sauce, bus fare -50% | 1
this:
plan_A | 9.99 | free donuts | 1
plan_A | 9.99 | extra sauce | 1
plan_A | 9.99 | bus fare -50% | 1
Note: i get a response with all rows, but i just put the first one here to exemplify the difference.
Now, while i could do the processing in the response on my PHP page, i'm interested in knowing if i get the DB engine to output my desired result.
Do i need to create a type of constraint between the tables? If so how would i do it? I would be grateful for any help that would help me get to my proffered output result (even a better title for the question!).
If there are any unclear points, please let me know and i will clarify them.
Example of junction/intersect table usage.
create table subscription_plans
(
id int not null auto_increment primary key, -- common practice
name varchar(40) not null,
description varchar(255) not null,
price decimal(12,2) not null
-- additional indexes:
);
create table pricing_offers
(
id int not null auto_increment primary key, -- common practice
name varchar(40) not null,
description varchar(255) not null
-- additional indexes:
);
create table so_junction
( -- intersects mapping subscription_plans and pricing_offers
id int not null auto_increment primary key, -- common practice
subId int not null,
offerId int not null,
-- row cannot be inserted/updated if subId does not exist in parent table
-- the fk name is completely made up
-- parent row cannot be deleted and thus orphaning children
CONSTRAINT fk_soj_subplans
FOREIGN KEY (subId)
REFERENCES subscription_plans(id),
-- row cannot be inserted/updated if offerId does not exist in parent table
-- the fk name is completely made up
-- parent row cannot be deleted and thus orphaning children
CONSTRAINT fk_soj_priceoffer
FOREIGN KEY (offerId)
REFERENCES pricing_offers(id),
-- the below allows for only ONE combo of subId,offerId
CONSTRAINT soj_unique_ids unique (subId,offerId)
-- additional indexes:
);
insert into subscription_plans (name,description,price) values ('plan_A','description',9.99);
insert into subscription_plans (name,description,price) values ('plan_B','description',19.99);
insert into subscription_plans (name,description,price) values ('plan_C','description',29.99);
select * from subscription_plans;
insert into pricing_offers (name,description) values ('free donuts','you get free donuts, limit 3');
insert into pricing_offers (name,description) values ('extra sauce','extra sauce');
insert into pricing_offers (name,description) values ('poney ride','Free ride on Wilbur');
insert into pricing_offers (name,description) values ('bus fare -50%','domestic less 50');
select * from pricing_offers;
insert so_junction(subId,offerId) values (1,1); -- free donuts to plans
insert so_junction(subId,offerId) values (1,2),(2,2),(3,2); -- extra sauce to plans
insert so_junction(subId,offerId) values (3,3); -- wilbur
insert so_junction(subId,offerId) values (1,4),(2,4),(3,4); -- bus to plans
select * from so_junction;
-- try to add another of like above to so_junction
-- Error Code 1062: Duplicate entry
-- show joins of all
select s.*,p.*
from subscription_plans s
join so_junction so
on so.subId=s.id
join pricing_offers p
on p.id=so.offerId
order by s.name,p.name
-- show extra sauce intersects
select s.*,p.*
from subscription_plans s
join so_junction so
on so.subId=s.id
join pricing_offers p
on p.id=so.offerId
where p.name='extra sauce'
order by s.name,p.name
Basically you insert and delete from the junction table (no good really updating ever in this example).
Clean and fast joins without having to mess with slow, unwieldy sets without indexes
No one can ride the Wilbur the Poney anymore? Then
delete from so_junction
where offerId in (select id from pricing_offers where name='poney ride')
Ask if you have any questions.
And good luck!

Generating Depth based tree from Hierarchical Data in MySQL (no CTEs)

Hi For many days I have been working on this problem in MySQL, however I can not figure it out. Do any of you have suggestions?
Basically, I have a category table with domains like: id, name (name of category), and parent (id of parent of the category).
Example Data:
1 Fruit 0
2 Apple 1
3 pear 1
4 FujiApple 2
5 AusApple 2
6 SydneyAPPLE 5
....
There are many levels, possibly more than 3 levels. I want to create an sql query that groups the datas according to he hierarchy: parent > child > grandchild > etc.
It should output the tree structure, as follows:
1 Fruit 0
^ 2 Apple 1
^ 4 FujiApple 2
- 5 AusApple 2
^ 6 SydneyApple 5
- 3 pear 1
Can I do this using a single SQL query? The alternative, which I tried and does work, is the following:
SELECT * FROM category WHERE parent=0
After this, I loop through the data again, and select the rows where parent=id. This seems like a bad solution. Because it is mySQL, CTEs cannot be used.
You can do it in a single call from php to mysql if you use a stored procedure:
Example calls
mysql> call category_hier(1);
+--------+---------------+---------------+----------------------+-------+
| cat_id | category_name | parent_cat_id | parent_category_name | depth |
+--------+---------------+---------------+----------------------+-------+
| 1 | Location | NULL | NULL | 0 |
| 3 | USA | 1 | Location | 1 |
| 4 | Illinois | 3 | USA | 2 |
| 5 | Chicago | 3 | USA | 2 |
+--------+---------------+---------------+----------------------+-------+
4 rows in set (0.00 sec)
$sql = sprintf("call category_hier(%d)", $id);
Hope this helps :)
Full script
Test table structure:
drop table if exists categories;
create table categories
(
cat_id smallint unsigned not null auto_increment primary key,
name varchar(255) not null,
parent_cat_id smallint unsigned null,
key (parent_cat_id)
)
engine = innodb;
Test data:
insert into categories (name, parent_cat_id) values
('Location',null),
('USA',1),
('Illinois',2),
('Chicago',2),
('Color',null),
('Black',3),
('Red',3);
Procedure:
drop procedure if exists category_hier;
delimiter #
create procedure category_hier
(
in p_cat_id smallint unsigned
)
begin
declare v_done tinyint unsigned default 0;
declare v_depth smallint unsigned default 0;
create temporary table hier(
parent_cat_id smallint unsigned,
cat_id smallint unsigned,
depth smallint unsigned default 0
)engine = memory;
insert into hier select parent_cat_id, cat_id, v_depth from categories where cat_id = p_cat_id;
/* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */
create temporary table tmp engine=memory select * from hier;
while not v_done do
if exists( select 1 from categories p inner join hier on p.parent_cat_id = hier.cat_id and hier.depth = v_depth) then
insert into hier
select p.parent_cat_id, p.cat_id, v_depth + 1 from categories p
inner join tmp on p.parent_cat_id = tmp.cat_id and tmp.depth = v_depth;
set v_depth = v_depth + 1;
truncate table tmp;
insert into tmp select * from hier where depth = v_depth;
else
set v_done = 1;
end if;
end while;
select
p.cat_id,
p.name as category_name,
b.cat_id as parent_cat_id,
b.name as parent_category_name,
hier.depth
from
hier
inner join categories p on hier.cat_id = p.cat_id
left outer join categories b on hier.parent_cat_id = b.cat_id
order by
hier.depth, hier.cat_id;
drop temporary table if exists hier;
drop temporary table if exists tmp;
end #
Test runs:
delimiter ;
call category_hier(1);
call category_hier(2);
Some performance testing using Yahoo geoplanet places data
drop table if exists geoplanet_places;
create table geoplanet_places
(
woe_id int unsigned not null,
iso_code varchar(3) not null,
name varchar(255) not null,
lang varchar(8) not null,
place_type varchar(32) not null,
parent_woe_id int unsigned not null,
primary key (woe_id),
key (parent_woe_id)
)
engine=innodb;
mysql> select count(*) from geoplanet_places;
+----------+
| count(*) |
+----------+
| 5653967 |
+----------+
so that's 5.6 million rows (places) in the table let's see how the adjacency list implementation/stored procedure called from php handles that.
1 records fetched with max depth 0 in 0.001921 secs
250 records fetched with max depth 1 in 0.004883 secs
515 records fetched with max depth 1 in 0.006552 secs
822 records fetched with max depth 1 in 0.009568 secs
918 records fetched with max depth 1 in 0.009689 secs
1346 records fetched with max depth 1 in 0.040453 secs
5901 records fetched with max depth 2 in 0.219246 secs
6817 records fetched with max depth 1 in 0.152841 secs
8621 records fetched with max depth 3 in 0.096665 secs
18098 records fetched with max depth 3 in 0.580223 secs
238007 records fetched with max depth 4 in 2.003213 secs
Overall i'm pretty pleased with those cold runtimes as I wouldn't even begin to consider returning tens of thousands of rows of data to my front end but would rather build the tree dynamically fetching only several levels per call. Oh and just incase you were thinking innodb is slower than myisam - the myisam implementation I tested was twice as slow in all counts.
More stuff here : http://pastie.org/1672733
Hope this helps :)
There are two common ways of storing hierarchical data in an RDBMS: adjacency lists (which you are using) and nested sets. There is a very good write-up about these alternatives in Managing Hierarchical Data in MySQL. You can only do what you want in a single query with the nested set model. However, the nested set model makes it more work to update the hierarchical structure, so you need to consider the trade-offs depending on your operational requirements.
You can't achieve this using a single query. Your hierarchical data model is ineffective in this case. I suggest you try two other ways of storing hierarchical data in a database: the MPTT model or the "lineage" model. Using either of those models allows you to do the select you want in a single go.
Here is an article with further details: http://articles.sitepoint.com/article/hierarchical-data-database
The linear way:
I am using a ugly function to create a tree in a simple string field.
/ topic title
/001 message 1
/002 message 2
/002/001 reply to message 2
/002/001/001/ reply to reply
/003 message 3
etc...
the table can be used to select all the rows in the tree order with a simple SQL Query:
select * from morum_messages where m_topic=1234 order by m_linear asc
INSERT is just select the parent linear (and children) and calculate the string as needed.
select M_LINEAR FROM forum_messages WHERE m_topic = 1234 and M_LINEAR LIKE '{0}/___' ORDER BY M_LINEAR DESC limit 0,1
/* {0} - m_linear of the parent message*/
DELETE is simple as delete the message, or delete by linear all replies of the parent one.

PL/SQL rownum updates

I am working on a database with a couple of tables. They are a
districts table
PK district_id
student_data table
PK study_id
FK district_id
ga_data table
PK study_id
district_id
The ga_data table is data that I am adding in. Both the student_data table and ga_data have 1.3 million records. The study_id's are 1 to 1 between the two tables, but the ga_data.district_id's are NULL and need to be updated. I am having trouble with the following PL/SQL:
update ga_data
set district_id = (select district_id from student_data
where student_data.study_id = ga_data.study_id)
where ga_data.district_id is null and rownum < 100;
I need to do it incremently so that's why I need rownum. But am I using it correctly? After running the query a bunch of times, it only updated about 8,000 records of the 1.3 million (should be about 1.1 million updates since some of the district_ids are null in student_data). Thanks!
ROWNUM just chops off query after the first n rows. You have some rows in STUDENT_DATA which have a NULL for DISTRICT_ID. So after a number of runs your query is liable to get stuck in a rut, returning the same 100 QA_DATA records, all of which match one of those pesky STUDENT_DATA rows.
So you need some mechanism for ensuring that you are working your way progressively through the QA_DATA table. A flag column would be one solution. Partitioning the query so it hits a different set of STUDENT_IDs is another.
It's not clear why you have to do this in batches of 100, but perhaps the easiest way of doing this would be to use BULK PROCESSING (at least in Oracle: this PL/SQL syntax won't work in MySQL).
Here is some test data:
SQL> select district_id, count(*)
2 from student_data
3 group by district_id
4 /
DISTRICT_ID COUNT(*)
----------- ----------
7369 192
7499 190
7521 192
7566 190
7654 192
7698 191
7782 191
7788 191
7839 191
7844 192
7876 191
7900 192
7902 191
7934 192
8060 190
8061 193
8083 190
8084 193
8085 190
8100 193
8101 190
183
22 rows selected.
SQL> select district_id, count(*)
2 from qa_data
3 group by district_id
4 /
DISTRICT_ID COUNT(*)
----------- ----------
4200
SQL>
This anonymous block uses the Bulk processing LIMIT clause to batch the result set into chunks of 100 rows.
SQL> declare
2 type qa_nt is table of qa_data%rowtype;
3 qa_recs qa_nt;
4
5 cursor c_qa is
6 select qa.student_id
7 , s.district_id
8 from qa_data qa
9 join student_data s
10 on (s.student_id = qa.student_id);
11 begin
12 open c_qa;
13
14 loop
15 fetch c_qa bulk collect into qa_recs limit 100;
16 exit when qa_recs.count() = 0;
17
18 for i in qa_recs.first()..qa_recs.last()
19 loop
20 update qa_data qt
21 set qt.district_id = qa_recs(i).district_id
22 where qt.student_id = qa_recs(i).student_id;
23 end loop;
24
25 end loop;
26 end;
27 /
PL/SQL procedure successfully completed.
SQL>
Note that this construct allows us to do additional processing on the selected rows before issuing the update. This is handy if we need to apply complicated fixes programmatically.
As you can see, the data in QA_DATA now matches that in STUDENT_DATA
SQL> select district_id, count(*)
2 from qa_data
3 group by district_id
4 /
DISTRICT_ID COUNT(*)
----------- ----------
7369 192
7499 190
7521 192
7566 190
7654 192
7698 191
7782 191
7788 191
7839 191
7844 192
7876 191
7900 192
7902 191
7934 192
8060 190
8061 193
8083 190
8084 193
8085 190
8100 193
8101 190
183
22 rows selected.
SQL>
It is kind of an odd requirement to only update 100 rows at a time. Why is that?
Anyway, since district_id in student_data can be null, you might be updating the same 100 rows over and over again.
If you extend your query to make sure a non-null district_id exists, you might end up where you want to be:
update ga_data
set district_id = (
select district_id
from student_data
where student_data.study_id = ga_data.study_id
)
where ga_data.district_id is null
and exists (
select 1
from student_data
where student_data.study_id = ga_data.study_id
and district_id is not null
)
and rownum < 100;
If this is a one-time conversion you should consider a completely different approach. Recreate the table as the join of your two tables. I promise you will laugh out loud when you realise how fast it is compared to all kinds of funny 100-rows-at-a-time updates.
create table new_table as
select study_id
,s.district_id
,g.the_remaining_columns_in_ga_data
from student_data s
join ga_data g using(study_id);
create indexes, constraints etc
drop table ga_data;
alter table new_table rename to ga_data;
Or if it isn't a one time conversion or you can't re-create/drop tables or you just feel like spending a few extra hours on data loading:
merge
into ga_data g
using student_data s
on (g.study_id = s.study_id)
when matched then
update
set g.district_id = s.district_id;
The last statement can also be rewritten as an updatable-view, but I personally never use them.
Drop/disable indexes/constraints on ga_data.district_id before running the merge and recreate them afterward will improve on the performance.