Listing columns from multiple tables in sql - sql

I've got a database which is formed like this:
BOOKTYPES ( book_type_id (PK), book_type )
CATEGORIES ( category_id (PK), category )
PUBLISHERS ( publisher_id (PK), publisher, speciality, country )
BOOKS ( book_id (PK), title, publisher_id (FK), published_year,
purchase_price, category_id (FK),purchase_date, pages,
book_type_id(FK) )
AUTHORS ( author_id (PK), first_name, last_name, pseudonym )
AUTHORSHIP ( author_id (PK), book_id (PK) )
Now, what I need help with is listing category and the number of books that contain that category. This means that I need to retrieve the category from CATEGORIES, and category_id from books. The problem I'm facing with this is that category_id already exists inside categories, and that isn't the one that I want to retrieve.
I'd really appreciate some help with this since it's been picking my brain for quite the while now.

listing category and the number of books that contain that category
That looks like a simple aggregate query on books:
SELECT category_id, COUNT(*) count_of_books FROM books GROUP BY category_id
If you want the category name as well, you can JOIN on categories. You do disambiguate the column names by prefixing them with the table they belong to (or the table alias):
SELECT c.category, COUNT(*)
FROM books b
INNER JOIN categories c ON c.category_id = b.category_id
GROUP BY c.category_id, c.category
To filter on a given count of categories, you can use a HAVING clause:
SELECT c.category, COUNT(*)
FROM books b
INNER JOIN categories c ON c.category_id = b.category_id
GROUP BY c.category_id, c.category
HAVING COUNT(*) > 5

Related

Is there a better way to find the most popular title in a 'self-linked' table of user posts?

I have this (simplified for space) table schema with user posts and related comments:
create table
tbl_post (
id integer primary key,
title text not null,
content text not null,
post_id integer null
);
where tbl_post.post_id is an (int) comment id associated with given tbl_post.id,
or null if tbl_post.id row is main, authored title (namely not a comment).
I'm using this sqlite query to figure out the most popular title in posts table (criteria is how many comments relates to it...):
select
title
from
tbl_post
where
id = (
select
post_id
from (
select
post_id, count(post_id) as tot
from
tbl_post
where
ifnull(post_id, '') != ''
group by
post_id
order by
tot desc
limit 1
)
);
which looks quite bulky to me having those two nested select statements. I would like to make the query simpler (shorter, potentialy faster) somehow. Thanks.
How about a self-join?
SELECT p.Id p.title, p.content, COUNT(c.Id) AS nbOfComments
FROM tbl_post p
LEFT JOIN tbl_post c ON p.Id = c.post_id
WHERE p.post_id IS NULL
GROUP BY p.Id, p.title, p.content

complex sql query from 4 tables

I am developing an online travel guide with a lot of hotels. Each hotel belongs to a specific category, has a lot room types and each of hotel room has different price per season. I want to make a complex query from 4 tables in order to get the total number of hotels per hotels category where the minimum price of each hotel rooms is between 2 values which are adjusted by a slider.
My tables look like:
Categories
id_category
category_name
Hotels
id_hotel
hotel_name
category_id
......
hotels_room_types
id_hotels_room_type
hotel_id
room_type_id
......
hotels_room_types_seasons
hotels_room_types_id
season_id
price
......
for example some values of category_name are: Hotels, apartments, hostels
I would like my results table to have two fields like the following:
Hotels 32
apartments 0
hostels 5
I tried the following query but it returns the total number of all hotels per category, not the number of hotels where the minimum price of their rooms is between the price range.
SELECT c.category_name, count( DISTINCT id_hotel ) , min( price ) min_price
FROM categories c
LEFT JOIN hotels w ON ( c.id_category = w.category_id )
LEFT JOIN (
hotels_room_types
INNER JOIN hotels_room_types_seasons ON hotels_room_types.id_hotels_room_types = hotels_room_types_seasons.hotels_room_types_id)
ON w.id_hotel = hotels_room_types.hotel_id
GROUP BY c.category_name
HAVING min_price >=10 AND min_price <=130
Could anyone help me how to write the appropriate query?
Thanks!!!
SELECT Categories.Name, COUNT(DISTINCT ID_Hotel) [Count]
FROM Hotels
INNER JOIN Categories
ON Category_ID = ID_Category
INNER JOIN
( SELECT Hotel_ID, MIN(Price) [LowestPrice]
FROM hotels_room_types
INNER JOIN hotels_room_types_seasons
ON id_hotels_room_type = hotels_room_types_id
-- CONSIDER FILTERING BY SEASON HERE
GROUP BY Hotel_ID
) price
ON price.Hotel_ID = Hotels.ID_Hotel
WHERE LowestPrice BETWEEN 10 AND 130 -- OR WHATEVER YOUR PARAMETERS ARE
GROUP BY Categories.Name
I have no idea what RDBMS you are using but I do not know any where your query would work. The problem you were having with the Min Price (I assume) is because you are applying the logic after grouping by category, so you are counting all hotels where the category has a lowest price between 10 and 130, not where the hotel has a room with the lowest price between 10 and 130.
select
c.Category_name,
count(*) NumHotels
from
( select distinct
byRoomType.hotel_id
from
hotels_room_types_seasons bySeason
join hotels_room_types byRoomType
on bySeason.hotels_room_types_id = byRoomType.id_hotels_room_type
where
bySeason.Price between LowPriceParameter and HighPriceParameter
) QualifiedHotels
join Hotels
on QualifiedHotels.hotel_id = Hotels.id_hotel
join Categories c
on category_id = c.id_category

In a Many to Many relationship, how can records collectively owned be selected

I'm sure I'm missing the correct terminology here, but for this example many-to-many relationship:
A user has many books. A book is owned by many users.
What's the best way to see all books owned by both of two users?
Right now I'm just using this:
SELECT * FROM books
WHERE book_id IN
(SELECT book_id FROM user_has_book WHERE user_id = 'joe' AND book_id IN
(SELECT book_id FROM user_has_book WHERE user_id = 'frank'))
I know that can't be the best way to do this - right? I'm using MySQL
SELECT b.*
FROM books b
INNER JOIN
(SELECT book_id, COUNT(*) as cnt
FROM user_has_book
WHERE user_id IN ('Frank', 'Joe')
GROUP BY book_id
HAVING cnt=2)x ON (x.book_id = b.book_id)

SQL one-to-many relationship, how to select parent containing unique set of children

I have a routine one-to-many schema, similar to this simple example:
PERSON
person_id (PK)
PERSON_TRAIT
person_id (FK)
trait_id (FK)
quantity
TRAIT
trait_id (PK)
name
//other attributes
Given a set of traits ("friendly, funny"), how would I return the associated person_id and quantity recordset.
At first glance I was tempted to use this but it's not this simple:
select person_id, quantity
from trait t
inner join person_trait pt on t.trait_id = pt.trait_id
where name in ('friendly', 'funny')
This isn't correct because I could have a person that contains those traits plus more ("friendly, funny, skinny") and it would be returned.
To take it a step further, if there isn't a person that contains all of the traits exactly, how would I aggregate the traits from multiple different persons and return a recordset of those person_id and quantity values?
Using SQL Server 2005.
This will give you a list of people who have only the two traits indicated.
SELECT person_id, quantity
FROM PERSON_TRAIT
WHERE person_id IN
(
SELECT person_id
FROM PERSON_TRAIT pt
LEFT OUTER JOIN TRAIT t ON pt.trait_id = t.trait_id
AND t.name IN ('friendly','funny')
GROUP BY person_id
HAVING COUNT(*) = COUNT(t.trait_id)
)
Try:
select person_id, sum(quantity)
from trait t
inner join person_trait pt on t.trait_id = pt.trait_id
where name in ('friendly', 'funny')
having count(distinct name) = 2
Change the having number to be the number of distinct names to be selected - so if 'friendly', 'funny' and 'clever' are required, change the having clause to be having count(distinct name) = 3.

Counting children in another table for each row

I have two tables: CATEGORY and SUBCATEGORY
Table Structure for CATEGORY
category_id int(11)
category_name varchar(250)
category_status enum('0', '1')
Table Structure for SUBCATEGORY
subcategory_id int(10)
subcategory_name varchar(255)
status enum('0', '1')
For example there is a single CATEGORY named .NET and it has entries in SUBCATEGORY like ASP.NET, VB.NET, C#.NET . In this case I need to get the count of CATEGORY as 1 and the count of SUBCATEGORY as 3 using MySQL.
How can I accomplish this?
Well, you can do it with a subquery. However, you'll need to add a category_id column to the subcategory table, so that we know what subcategories go with which categories. Then, you can get what you want with the following query:
select
category_name,
1 as CategoryCount,
(select count(*) from subcategory where category_id = c.category_id) as SubCategoryCount
from
category c
Since we can assume category count is one and there's more than likely a key constraint on category_id between the two tables, this will work as well:
select c.category_id, count(c.category_id)
from category c
inner join subcategory s on (c.category_id = s.category_id)
group by c.category_id
SELECT COUNT(*) FROM CATEGORY;
SELECT COUNT(*) FROM SUB_CATEGORY;
I don't believe that's exactly what you're going for, but that's all you're really gonna get without a foreign key.