Mysql joining tables question - mysql

I have a tables rooms, apartments and hotel. Now its easy for me to get all free rooms, name of hotel address or same thing for apartments by using separate queries ex :
Get all free rooms :
SELECT hotel.name, count(*) AS num_of_free_rooms
FROM hotel, rooms
WHERE rooms.occupied !=0
AND hotel.hotel_id = rooms.room_hotel
GROUP BY hotel.hotel_id
Get all free apartments :
SELECT hotel.name, count(*) AS num_of_free_ap
FROM hotel, apartments
WHERE apartments.occupied_ap !=0
AND hotel.hotel_id = apartments.apartment_hotel
GROUP BY hotel.hotel_id
How could I get results like this :
Name of a hotel | Number of free rooms other than 0 | Number of apartments 0 or any other
Should I organize my data differently, like adding in the table rooms field type 1 for rooms, 2 for apartments or do it with separate tables. I'm open to any suggestions in order to get results that I need.

If you have rooms and apartments in separate tables, and you try to count them in a single query, you'll produce what's called a Cartesian product between rooms and apartments. The effect is that your counts of each will be much higher than reality (they'll be multiplied together).
Keep in mind you don't have to solve every problem in SQL with a single query! Running separate queries is simpler and often runs faster than a single overly-complex query. So don't overlook the option of simply running two queries.
That said, if I were designing this database, I would make rooms and apartments part of the same table, and use an attribute to distinguish between these two types, just as you describe. Unless you need a lot of columns specific to one type or the other.
For more options, see my answer in "Product table, many kinds of product, each product has many parameters."

You can use this until someone comes up with a (more) efficient solution :)
SELECT tmp.name, tmp.num_of_free_rooms, num_of_free_ap
FROM
(SELECT hotel.name, count(*) AS num_of_free_rooms
FROM hotel, rooms
WHERE rooms.occupied !=0
AND hotel.hotel_id = rooms.room_hotel
GROUP BY hotel.hotel_id) AS tmp
LEFT JOIN
(SELECT hotel.name, count(*) AS num_of_free_ap
FROM hotel, apartments
WHERE apartments.occupied_ap !=0
AND hotel.hotel_id = apartments.apartment_hotel
GROUP BY hotel.hotel_id) AS tmp2
ON tmp1.hotel.name = tmp2.hotel_name
WHERE tmp.num_of_free_rooms > 0

If rooms and apartments share many attributes, it's worth putting them both in the same table (with a flag) just to simplify queries like the one you mentioned. You can put the special attributes in room_details and apt_details tables and only refer to them when needed.

Related

Query to obtain the lowest price per room per hotel in a relational database

I have a relational database model based on a hotel booking site set up for a uni project, however I am stumped by one query.
This is as far as I have gotten:
SELECT DISTINCT
property.property_id,
property_name,
property_description,
star_rating,
room_base_price
FROM
property
INNER JOIN rooms ON property.property_id = rooms.property_id;
The problem is that I have several rooms per property listed in my database. In this query, I want to select the room with the minimum price per hotel. The way I am currently doing it, it is showing every single property with every single room and its individual price.
Can anyone advise me on how I can write a query where I will be able to return the lowest price of a room in a property(i.e. a hotel), where it will only be one row per hotel instead of returning multiple rows for each property?
Try following query:
SELECT DISTINCT
property.property_id,
property_name,
property_description,
star_rating,
room_base_price
FROM
property
INNER JOIN rooms ON property.property_id = rooms.property_id;
where (rooms.property_id,rooms.room_base_price) in
(select property_id,min(room_base_price)
from rooms
group by property_id
)
;
Hope it helps!

Include a complex logic in a single MySQL Query

I wanted to reduce the programming logic in a code that search hotels by using a single query.
Let say this is the database table. There are several contracts from a single hotel. I wanted to get all the raws that satisfy input number of rooms for max_adults 1(capacity 1) rooms, number of rooms for rooms for max_adults 2(capacity 2) rooms and number of rooms for rooms for max_adults 3(capacity 3) rooms.
eg: Input : 2 rooms of max_adults=1(capacity 1) 1 rooms of max_adults=2(capacity 2) 3 rooms of max_adults=3(capacity 3)
The rows that satisfy all the 3 conditions with the same hotel id must be output.
As in here the output must contain all the raws of hotel 1 and hotel 2
Since they are two separate results, an extra column is needed to show a generated result_id (kind of serial number as in here which must has duplicates acording to this problem).
I'm thinking lots of ways to do this, but nothing works well. Will this be possible to do in a single query?
Use a self join:
SELECT r1.HOTEL_ID, r1.MAX_ADULTS, r1.NO_OF_ROOMS,
r2.MAX_ADULTS, r2.NO_OF_ROOMS,
r3.MAX_ADULTS, r3.NO_OF_ROOMS,
FROM rooms AS r1
INNER JOIN rooms AS r2 ON r1.HOTEL_ID=r2.HOTEL_ID
INNER JOIN rooms AS r3 ON r1.HOTEL_ID=r3.HOTEL_ID
WHERE r1.MAX_ADULTS=1
AND r2.MAX_ADULTS=2
AND r3.MAX_ADULTS=3
You will have to add clauses to check the input number of rooms conditions.
The generated result_id would be r1.HOTEL_ID.
You would get a single row per hotel.

count number of repeating entries

I am fairly new to Databases and I am just beginning to understand the DML/queries, I have two tables, one named customer this contain customer data and one named requested_games, this contains games requested by the customers, I would like to write a query that will return the customers that have requested more than two games, so far when I run the query, I don't get the desired result, not sure if I'm doing it right.
Can anyone assist with this thanks,
Below is a snippet of the query
select customers.customer_name, wants_list.requested_game, wants_list.wantslists_id,count(wants_list.customers_ID)
from customers, wants_list
where customers.customers_ID = wants_list.customers_id
and wants_list.wantslists_id = wants_list.wantslists_id
and wants_list.requested_game > '2';
just include a HAVING clause
GROUP BY customers_ID
HAVING COUNT(*) > 2
depending on how you have your data setup you may need to do
HAVING COUNT(wants_list.requested_game) > 2
This is how I like to describe how a query works maybe itll help you visualize how the query executes :)
SELECT is making an order at a restaurant....
FROM is the menu you want to order from....
JOIN is what sections of the menu you want to include
WHERE is any customization you want to make to your order (aka no mushrooms)....
GROUP BY (and anything after) is after the order has been completed and is at your table...
GROUP BY tells your server to bring your types of food together in groups
ORDER BY is saying what dishes you want first (aka i want my entree then dessert then appetizer ).
HAVING can be used to pick out any mushrooms that were accidentally left on the plate....
etc..
I would like to write a query that will return the customers that
have requested more than two games
For this to happen you need to do the following
First you need to use GROUP BY to group the games based on customers (customers_id)
Then you need to use HAVING clause to get customers who requested more than two games
Then make this a SUBQUERY if you need more information on the customer like name
Finally you use a JOIN between customers and the sub query (temp) to display more information on the customer
Like the following query
SELECT customers.customer_id, customers.customer_name, game_count
FROM (SELECT customer_id, count(wantslists_id) AS game_count
FROM wants_list
GROUP BY customer_id
HAVING count(requested_game) > '2') temp
JOIN customers ON customers.customer_id = temp.customer_id

one to many mysql query

Hey guys sorry to ask but i need help with this query please I've been messing around with different solutions but so far have not been able to solve it myself.
I have 4 tables called customer, figures, notes and lender. They all have a field called reference, which is what I'm using to link them together. Customer is the primary table and there is only one record in the figures table for each customer so i can do:
select * From customer, figures
where customer.reference = figures.reference
However, there may be multiple notes and lender records for each customer. How can I link them to show only one record?
Ideally, there would be a way to display it as:
reference, name, figures, lender 1, lender 2, note 1, note 2, note 3
You can use group_concat():
SELECT customer.reference, customer.name, figures.name,
GROUP_CONCAT(DISTINCT lender.name),
GROUP_CONCAT(DISTINCT notes.name)
FROM customer
JOIN figures ON figures.reference = customer.reference
LEFT JOIN lender ON lender.reference = customer.reference
LEFT JOIN notes ON notes.reference = customer.reference
GROUP BY customer.reference;
Assuming that each of the tables has a field name, you should change it to whatever your columns are.

How do I model product ratings in the database?

What is the best approach to storing product ratings in a database? I have in mind the following two (simplified, and assuming a MySQL db) scenarios:
Create two columns in the products table to store the number and the sum of all votes respectively. Use the columns to get an average at run time or using a query.
This approach means I only need to access one table, simplifying things.
Normalize the data by creating an additional table to store the ratings.
This isolates the ratings data into a separate table, leaving the products table to furnish data on available products. Although it would require a join or a separate query for ratings.
Which approach is best, normalised or denormalised?
A different table for ratings is highly recommended to keep things dynamic. Don't worry about hundreds (or thousands or tens of thousands) of entries, that's all peanuts for databases.
Suggestion:
table products
id
name
etc
table products_ratings
id
productId
rating
date (if needed)
ip (if needed, e.g. to prevent double rating)
etc
Retrieve all ratings for product 1234:
SELECT pr.rating
FROM products_ratings pr
INNER JOIN products p
ON pr.productId = p.id
AND p.id = 1234
Average rating for product 1234:
SELECT AVG(pr.rating) AS rating_average -- or ROUND(AVG(pr.rating))
FROM products_ratings pr
INNER JOIN products p
ON pr.productId = p.id
AND p.id = 1234;
And it's just as easy to get a list of products along with their average rating:
SELECT
p.id, p.name, p.etc,
AVG(pr.rating) AS rating_average
FROM products p
INNER JOIN products_ratings pr
ON pr.productId = p.id
WHERE p.id > 10 AND p.id < 20 -- or whatever
GROUP BY p.id, p.name, p.etc;
I know that my answer is not what you actually ask for, but you might want to have a chance of facilitating that new products with your system can almost never beat the old products. Say that you would get a product with 99% rating. It would be very difficult for new products to get high if you sort by products with the highest rating.
Do not store a record of each rating unless you absolutely need them specifically. An example of such a case could be a psychological experiment that tends to analyze specific properties of the raters themselves. So, yeah! You'd have to be just as crazy to store each rate in a separate record.
Now, coming to the solution, add two more columns to your product table: AverageRating and RateCount.
What would you store in them? Well, suppose you have an already-calculated average of the two numbers: 2 and 3, which is 2.5; having a new rate of 10, you'll multiply the average (2.5) by the rate count (2 in this case). Now, you have 5. Add this result to the new rate value (10) and divide the result by 3.
Let's cover all the above in a simple formula,
(AverageRating * RateCount + NewRateValue) / (RateCount + 1)
So (2.5 * 2 + 10) / (2 + 1) = 5.
Calculate the average on the server-side (not in your database) and store the average in the AverageRating column and the rate count in the RateCount column.
Simple, right?!
Edit
This solution doesn't require storing each rating separately as long as no review, edit or delete operations are involved. Yet, for such cases; let's assume that you've got a review with a rating of 3 that the owning user would like to modify to 4. Then, the formula to recalculate the average rating would be like this,
(AverageRating * RateCount - OldRateValue + NewRateValue) / RateCount
References
https://math.stackexchange.com/a/106314