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.