Query Data from multiple sql tables - mysql

I have an ERD above. I want to get the price of the room based on channel and also the status (isInvisible) of the hotel that owns the room mentioned.
Also a RESTful-API endpoint for that, I tried many times on this assignment and can't get it right as I use Nodejs to write an API GET /api/${roomId}/price but the query doesn't work:
SELECT RoomPrice.price, Status.isInvisible
FROM RoomPrice
INNER JOIN Status
ON (RoomPrice.RoomID = Room.Id AND RoomPrice.ChannelID = ChannelId)
AND (Status.HotelID = Hotels.ID AND Status.ChannelID = ChannelID)
I use query below to create my database in WorkBench:
DROP TABLE IF EXISTS `address`;
CREATE TABLE `address` (
`id` int(9) unsigned NOT NULL AUTO_INCREMENT,
`hotel_id` int(9) unsigned NOT NULL,
`address` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY(hotel_id) REFERENCES hotels(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `address` (`hotel_id`, `address`)
VALUES
(1, '7008 Lynch Centers Apt. 596\nLysannemouth, RI 43355'),
(2, '04795 Stanley Mount Apt. 114\nDorrisborough, DC 38070-3542'),
(1, '24586 Eliseo Haven Suite 045\nKossville, WY 17890-7936'),
(2, '639 Toy Corners\nBashirianfort, CA 08964-7258');
DROP TABLE IF EXISTS `channels`;
CREATE TABLE `channels` (
`id` int(9) unsigned NOT NULL AUTO_INCREMENT,
`url` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO `channels` (`id`, `url`, `name`)
VALUES
(1, 'http://www.beahan.com/', 'quod'),
(2, 'http://www.douglas.com/', 'sit');
DROP TABLE IF EXISTS `hotels`;
CREATE TABLE `hotels` (
`id` int(9) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO `hotels` (`id`, `name`)
VALUES
(1, 'illum'),
(2, 'aliquid');
DROP TABLE IF EXISTS `rooms`;
CREATE TABLE `rooms` (
`id` int(9) unsigned NOT NULL AUTO_INCREMENT,
`hotel_id` int(9) unsigned NOT NULL,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY(hotel_id) REFERENCES hotels(id)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
INSERT INTO `rooms` (`id`, `hotel_id`, `name`)
VALUES
(1, 1, 'vel'),
(2, 2, 'fugit'),
(3, 1, 'doloribus'),
(4, 2, 'ut'),
(5, 1, 'et');
DROP TABLE IF EXISTS `room_prices`;
CREATE TABLE `room_prices` (
`room_id` int(9) unsigned NOT NULL,
`channel_id` int(9) unsigned NOT NULL,
`price` decimal(10,2) NOT NULL,
PRIMARY KEY (room_id, channel_id),
FOREIGN KEY(room_id) REFERENCES rooms(id),
FOREIGN KEY(channel_id) REFERENCES channels(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `room_prices` (`room_id`, `channel_id`, `price`)
VALUES
(1, 1, '50687.86'),
(1, 2, '6687.86'),
(2, 1, '10687.86'),
(2, 2, '274739.20'),
(3, 1, '3828.63'),
(3, 2, '12525.86'),
(4, 1, '2623587.86'),
(4, 2, '125151.00'),
(5, 1, '2358704.85'),
(5, 2, '7347473.86');
DROP TABLE IF EXISTS `status`;
CREATE TABLE `status` (
`hotel_id` int(9) unsigned NOT NULL,
`channel_id` int(9) unsigned NOT NULL,
`isInvisible` BOOLEAN NOT NULL,
PRIMARY KEY (hotel_id, channel_id),
FOREIGN KEY(hotel_id) REFERENCES hotels(id),
FOREIGN KEY(channel_id) REFERENCES channels(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `status` (`hotel_id`, `channel_id`, `isInvisible`)
VALUES
(1, 1, false),
(2, 1, true),
(1, 2, true),
(2, 2, true);
I want that once I query to search for the price of roomId (i.e 1) it returns 2 rows (as the mock data only have 2 rows in channels table) that show:
room_id channel_id price isInvisible
1 1 xxxx.xx 0
1 2 xxxx.xx 1
At this moment, I use the query as DRapp help
SELECT
rp.room_id,
rp.channel_id,
rp.price,
s.isInvisible
FROM
room_prices rp
JOIN status s
ON (rp.channel_id = s.channel_id)
JOIN rooms r
ON (rp.room_id = r.id)
JOIN hotels h
ON (r.hotel_id = h.id)
WHERE rp.room_id = 1
It returns 4 rows (instead of 2 rows as expected)
room_id channel_id price isInvisible
1 1 xxxx.xx 0
1 2 xxxx.xx 1
1 1 xxxx.xx 1
1 2 xxxx.xx 1

You need to identify each of the relations as your ERD shows. Each table is joined to its respective context. Dont jump/skip. The only time you can in this scenario is to skip through the channels since the RoomPrice and Status table EACH have a "ChannelId" to qualify the join
SELECT
rp.price,
s.isInvisible
FROM
Room_Prices rp
JOIN Rooms r
on rp.room_id = r.id
JOIN Status s
ON rp.Channel_ID = s.Channel_Id
AND r.hotel_id = s.hotel_id
WHERE
rp.room_id = 1
I had to revise the query. I looked deeper and noticed your CHANNEL table ALSO had the Hotel, so I had to go from the room prices to the room table. From the room table, I can get the hotel. Now that I have the channel from room prices, AND the hotel ID from the rooms table. So NOW I join to the status table on BOTH columns getting the expected single row per room you are expecting.

Related

Joining data from 3 separate MySQL tables

I have 3 MySQL tables: person, review & team.
I have been able to join 2 (person & review) together, however I'd like to include data from the 3rd in my result.
Can someone explain how this is done? :-)
CREATE TABLE `person` (
`id` int NOT NULL AUTO_INCREMENT,
`reference` varchar(100) NOT NULL,
`email` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
INSERT INTO `person` (`id`, `reference`, `email`) VALUES
(1, 'PK001', 'paulk#gmail.com');
CREATE TABLE `review` (
`id` int NOT NULL AUTO_INCREMENT,
`review_type` varchar(255) NOT NULL,
`review_body` varchar(255) NOT NULL,
`person_id` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
INSERT INTO `review` (`id`, `review_type`, `review_body`, `person_id`) VALUES
(1, 'Personality', 'He has a great personality!', 1),
(2, 'Skills', 'He has multiple skills!', 1);
CREATE TABLE `team` (
`id` int(11) NOT NULL,
`person_id` int(11) NOT NULL,
`team_name` varchar(255) NOT NULL,
`value` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
INSERT INTO `team` (`id`, `person_id`, `team_name`, `value`) VALUES
(1, 1, 'Man Utd', 500),
(2, 1, 'Real Madrid', 1500),
(3, 1, 'Ajax', 1000);
Using the following SQL:
SELECT p.id, group_concat(r.review_body)
FROM person p
inner join review r on r.person_id = p.id
group by p.id
gives me the output:
He has a great personality!,He has multiple skills!
However I'd ultimately like my output to be:
He has multiple skills!,He has a great personality,Man Utd-500|Real Madrid-1500|Ajax-1000
Is this possible to do with MySQL ? Any guidance would be greatly appreciated.
I realise I could optimise things a lot better - but I just want to see if I can connect all 3 tables together and go from there.
To get your required concatenated output you need to modify your join query.
For that your new query looks like this:
SELECT p.id,
GROUP_CONCAT(r.review_body) AS reviews,
(SELECT GROUP_CONCAT(CONCAT(team_name, '-', value) SEPARATOR '|')
FROM team
WHERE team.person_id = p.id) AS teams
FROM person p
INNER JOIN review r ON r.person_id = p.id
GROUP BY p.id;
Result :

Find item from EAV based on multiple rows

Basically I am trying to build a 'Search by Attribute' function, so a user can choose a field or combination of fields in an EAV database structure e.g.:
CREATE TABLE `form` (
`formID` int(11) NOT NULL AUTO_INCREMENT,
`formName` varchar(255) NOT NULL,
PRIMARY KEY (`formID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `form` (`formID`, `formName`) VALUES
(1, 'A Form');
CREATE TABLE `formFields` (
`fieldID` int(11) NOT NULL AUTO_INCREMENT,
`formID` int(11) NOT NULL,
`fieldName` varchar(255) NOT NULL,
PRIMARY KEY (`fieldID`),
KEY `formID` (`formID`),
CONSTRAINT `formFields_ibfk_1` FOREIGN KEY (`formID`) REFERENCES `form` (`formID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `formFields` (`fieldID`, `formID`, `fieldName`) VALUES
(1, 1, 'Fruit'),
(2, 1, 'Car Make'),
(3, 1, 'Colour');
CREATE TABLE `item` (
`itemID` int(11) NOT NULL AUTO_INCREMENT,
`formID` int(11) NOT NULL,
`notes` varchar(1000) DEFAULT NULL,
PRIMARY KEY (`itemID`),
KEY `formID` (`formID`),
CONSTRAINT `item_ibfk_1` FOREIGN KEY (`formID`) REFERENCES `form` (`formID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `item` (`itemID`, `formID`, `notes`) VALUES
(1, 1, 'Some notes....'),
(2, 1, 'don\'t find this one');
CREATE TABLE `itemDetails` (
`itemID` int(11) NOT NULL,
`fieldID` int(11) NOT NULL,
`itemValue` varchar(1000) NOT NULL,
UNIQUE KEY `itemID_fieldID` (`itemID`,`fieldID`),
KEY `fieldID` (`fieldID`),
CONSTRAINT `itemDetails_ibfk_1` FOREIGN KEY (`itemID`) REFERENCES `item` (`itemID`),
CONSTRAINT `itemDetails_ibfk_2` FOREIGN KEY (`fieldID`) REFERENCES `formFields` (`fieldID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `itemDetails` (`itemID`, `fieldID`, `itemValue`) VALUES
(1, 1, 'apple'),
(1, 2, 'ford'),
(1, 3, 'orange'),
(2, 1, 'banana'),
(2, 2, 'toyota'),
(2, 3, 'blue');
CREATE OR REPLACE VIEW item_viewDetails AS
SELECT ff.formID, ff.fieldID, id.itemID, ff.fieldName, id.itemValue
FROM formFields ff
LEFT JOIN itemDetails id ON ff.fieldID = id.fieldID;
I have a view set up to return the itemID, formID, fieldID, fieldName and value. each item will have a minimum number of rows based on how many fields are in the form (the value is null if no itemDetails record exists)
currently I have a select statement that can search for 1 field eg: `WHERE view.fieldID = X AND view.value = Y
But I would also like to be able to search for multiple fields at the same time in an AND fashion. but of course i cannot do WHERE (view.fieldID = X AND view.value = Y) AND (view.fieldID = A AND view.value = B) as the details are accross multiple rows with a shared itemID. Is there any way to do this in mysql? or do I have to just OR it and then do checking on the application side to discard invalid results?
I have come up with a solution that does work, but I don't know if it is the best as it involves a sub-query with a view.
SELECT * FROM (
SELECT
COUNT(i.itemID) AS matches,
ia.*
FROM item_viewDetails ia
INNER JOIN item i ON i.itemID = ia.itemID
WHERE (ia.fieldID = 2 AND ia.itemValue = 'toyota') OR (ia.fieldID = 3 AND ia.itemValue = 'blue')
GROUP BY ia.itemID) AS m
WHERE m.matches = 2;
then as I generate the query, I just have to count the number of fields I am trying to match.

SQL sorted by a diferent fields

I have the next tables and rows. And I need to write a SELECT that returns all categories sorted by:
(A) the number of items they have
and (B) the category name.
This query should fetch the following columns: the category name, the number of items (AS N_ITEMS) and the average price of the titles in that category (AS AVERAGE_PRICE)
CREATE TABLE `category` (
`CATEGORY_ID` int(11) NOT NULL,
`CATEGORY_NAME` varchar(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `category` (`CATEGORY_ID`, `CATEGORY_NAME`) VALUES
(1, 'Sports'),
(2, 'Actualités'),
(3, 'Animaux'),
(4, 'Economie'),
(5, 'Cuisine');
CREATE TABLE `item` (
`ITEM_ID` int(11) NOT NULL,
`CATEGORY_ID` int(11) NOT NULL,
`ITEM_NAME` varchar(50) NOT NULL,
`ITEM_PRICE` decimal(8,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `item` (`ITEM_ID`, `CATEGORY_ID`, `ITEM_NAME`, `ITEM_PRICE`) VALUES
(1, 1, 'Equip', '6.00'),
(2, 2, 'Le Monde', '3.00'),
(3, 2, 'Le Parisien', '2.50'),
(4, 2, 'France soir', '3.00'),
(5, 3, '30 Million damis', '6.20'),
(6, 3, 'Cheval pratique', '4.50'),
(7, 4, 'Capital', '2.50');
ALTER TABLE `category`
ADD PRIMARY KEY (`CATEGORY_ID`);
ALTER TABLE `item`
ADD PRIMARY KEY (`ITEM_ID`);
ALTER TABLE `category`
MODIFY `CATEGORY_ID` int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `item`
MODIFY `ITEM_ID` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;
You can try below -
select CATEGORY_NAME, count(ITEM_ID) as noofItem,avg(item_price) as avgprice
from category inner join item on category.category_id=item.category_id
group by CATEGORY_NAME
order by noofItem,CATEGORY_NAME

MySQL get all parents

We have orders and RMA's. RMA's can be created with order_products of an order. If in the RMA a new product is send, the product can also be added to a new RMA, and so on.
Now for example, I have this schematic structure of an order with RMA orders.
Order (1)
|-> RMA (2)
|-> RMA (3)
|-> RMA (4)
|-> RMA (5)
Now I want to traverse up from RMA (5), all the way up. So actually I want to get 5 records from the database containing all the RMA's and the original order (All from the order table). How can I do this, given the database structure shown below.
Here is a SQLFiddle.
CREATE TABLE `order` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`type` varchar(5) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
CREATE TABLE `order_product` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`order_id` int(11) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
CREATE TABLE `order_rma_product` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`order_id` int(11) DEFAULT NULL,
`order_product_id` int(11) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
INSERT INTO `order` (`id`, `type`, `description`)
VALUES
(1, 'order', 'Original order'),
(2, 'rma', 'RMA of order 1'),
(3, 'rma', 'RMA of order 2'),
(4, 'rma', 'RMA of order 3'),
(5, 'rma', 'RMA of order 4');
INSERT INTO `order_product` (`id`, `order_id`, `description`)
VALUES
(1, 1, 'Product'),
(2, 2, 'Product'),
(3, 3, 'Product'),
(4, 4, 'Product');
INSERT INTO `order_rma_product` (`id`, `order_id`, `order_product_id`, `description`)
VALUES
(1, 2, 1, 'Return product'),
(2, 3, 2, 'Return product'),
(3, 4, 3, 'Return product'),
(4, 5, 4, 'Return product');
I have tried this, but this give's me one row, so i tried contact_ws to get them all in on column. But this only works for the given set of JOINS, so not unlimited going up.
SELECT CONCAT_WS(',', op.order_id, op1.order_id, op2.order_id)
FROM `order` o
JOIN order_rma_product orp ON o.id = orp.order_id
JOIN order_product op ON orp.order_product_id = op.id
JOIN order_rma_product orp1 ON op.order_id = orp1.order_id
JOIN order_product op1 ON orp1.order_product_id = op1.id
JOIN order_rma_product orp2 ON op1.order_id = orp2.order_id
JOIN order_product op2 ON orp2.order_product_id = op2.id
WHERE o.id = '5';

MySQL return rows from last day only for a specific ID

I have 2 tables: 'clients' and 'orders', joined on the field 'client_id'.
CREATE TABLE IF NOT EXISTS `clients` (
`client_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(120) NOT NULL,
PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
INSERT INTO `clients` (`client_id`, `name`) VALUES
(1, 'Ted Bundy'),
(2, 'Terry Towl');
CREATE TABLE IF NOT EXISTS `orders` (
`order_id` int(11) NOT NULL AUTO_INCREMENT,
`client_id` int(11) NOT NULL,
`description` varchar(70) NOT NULL,
`order_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`order_id`),
KEY `client_id` (`client_id`),
KEY `created` (`order_date`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=11 ;
INSERT INTO `orders` (`order_id`, `client_id`, `description`, `order_date`) VALUES
(1, 1, 'Shirt', '2015-12-02 01:14:01'),
(2, 2, 'Trousers', '2015-12-02 03:31:53'),
(3, 2, 'Underware', '2015-12-04 11:07:46'),
(4, 2, 'Hat', '2015-12-06 11:27:16'),
(5, 2, 'Scarf', '2015-12-07 00:14:31'),
(6, 2, 'Shirt', '2015-12-07 07:17:03'),
(7, 1, 'Shoes', '2015-12-09 16:23:20'),
(8, 1, 'Socks', '2015-12-11 11:40:16'),
(9, 1, 'Sweater', '2015-12-13 05:20:11'),
(10, 1, 'Shorts', '2015-12-13 12:41:31');
ALTER TABLE `orders`
ADD CONSTRAINT `orders_ibfk_1`
FOREIGN KEY (`client_id`)
REFERENCES `clients` (`client_id`)
ON DELETE CASCADE
ON UPDATE CASCADE;
I need to find the orders for the most recent day for a specific client_id, and only 1 client at a time
Example output for client_id 2
client_id | name | description | order_date
-------------------------------------------------------------
2 | Terry Towl | Hat | 2015-12-07
2 | Terry Towl | Scarf | 2015-12-07
The issue is that we dont know the number of orders on that day, nor the date
The only way I can think to do this is to first query the date of the last order for a client, then to run another to find all records for that client on that date, however was hoping to be able to do this in one query.
Does anyone have an idea how to achieve this in one query?
Your idea is basically correct.
select *
from clients c join
orders o
on c.client_id = o.client_id
where c.client_id = $client_id and
o.order_date = (select max(o2.order_date)
from orders o2
where o2.client_id = c.client_id
);