SQL Query with different aggregation for count - sql

Publisher
pub_id title_id city
1 1 NY
1 2 NY
2 3 CA
3 4 VA
Titles
title_id price genre
1 10 Horror
2 5 Bio
3 50 Science
Question:
Create a SQL query that gives
-pub_id
-# titles
-# titles with Horror genre
I have been having hard time writing this SQL query and can't figure out how to include both #titles and #titles with horror genre in the same query. I would appreciate any help on this. Thank you.
Query I have tried so far( don't know how to incude titles with horror genre):
select a.pub_id, count(a.titles)
from publisher a
left join titles b on a.title_id = b.title_id group by a.pub_id
If I use having then I won't be able to calculate the total number of titles.

use following query to achieve your results
select
pub_id,
count(*) as [titles],
SUM(CASE WHEN genre='horror' then 1 else 0 END) as [horror titles]
from Publisher a
inner join titles b on a.title_id=b.title_id
group by
pub_id
you can use CASE statements to do this

you can use this query to get expected output
select t.pub_id, COUNT(t.title_id) as title_id, t2.genre
from table1 t
inner join
table2 t2
on t.title_id = t2.title_id and t2.genre like 'Horror%'
group by t.pub_id, t2.genre
Note: change table1 and table2 names into your table name
the output is shown here

Related

SQL Selecting & Counting In the same query

thanks in advance for any help on this, I am a bit of a newbie to MS SQL and I want to do something that I think is achievable but don't have the know how.
I have a simple table called "suppliers" where I can do (SELECT id, name FROM suppliers ORDER BY id ASC)
id
name
1
ACME
2
First Stop Business Supplies
3
All in One Supply Warehouse
4
Farm First Supplies
I have another table called "products"
id
name
supplier_id
1
Item 1
2
2
Item 2
1
3
Item 3
1
4
Item 4
3
5
Item 5
2
I want to list all the suppliers and get the total amount of products for each supplier if that makes sense on the same row? I am just not sure how to pass the suppliers.id through the query to get the count.
I am hoping to get to this:
id
name
total_products
1
ACME
2
2
First Stop Business Supplies
2
3
All in One Supply Warehouse
1
4
Farm First Supplies
0
I really appreciate any help on this.
Three concepts to grasp here. Left Join, group by, and Count().
select s.id, s.name, Count(*) as total_products
from suppliers s
left join products p on s.id=p.supplier_id --the left join gets your no matches
group by s.id, s.name
left join is a join where all of the values from the first table are kept even if there are no matches in the second.
Group by is an aggregation tool where the columns to be aggregated are entered.
Count() is simply a count of transactions for the grouped columns.
Try this :-
SELECT id, name, C.total_products
FROM Suppliers S
OUTER APPLY (
SELECT Count(id) AS total_products
FROM Products P
WHERE P.supplier_id = S.id
) C

Average of specific group of values

I am trying to select the average age of renters of a specific movie for demographic purposes.
My data is similar to
Movies
movie_id movie_title
1 Spider Man
2 Avengers
3 Thor
Customers
customer_id customer_dob
1 1989-03-05
2 1994-02-12
3 2001-05-01
Customer_rentals
rental_id customer_id movie_id
1 1 1
2 1 3
3 2 2
4 2 1
5 3 1
What I would like to see is
Title Avg_Age
Spider Man 25
Avengers 26
Thor 31
I have tried the following
select m.movie_title as Title, avg(all_ages.age) as avg_age
from
movies m,
(select ((0 + convert(char(8), getdate(),112) - convert(char(8),c.customer_dob,112)) / 10000) as age
from customers c, movies m, customer_rentals cr
where m.movie_id=cr.movie_id
and cr.customer_id=c.customer_id) all_ages
group by m.movie_title
Which gives me
Title Avg_Age
Spider Man 25
Avengers 25
Thor 25
It seems to be taking the average of all ages and returning it as the average for each movie and I'm not sure why this is happening
The problem with your query is that the subquery is not properly correlated to the outer query. You are selecting again from movie (using the same alias as in the outer query - m - which is confusing), while you should be relating to the record from outer query.
This can be simplified with straight joins and aggregation:
select
m.movie_title as Title,
avg((0 + convert(char(8), getdate(),112) - convert(char(8),c.customer_dob,112)) / 10000) as avg_age
from movies m
inner join customer_rentals cr on cr.movie_id = m.movie_id
inner join customers c on c.customer_id = cr.customer_id
group by m.movie_id, m.movie_title
Note that this uses standard, explicit joins (with the on keyword) rather than implicit joins (with commas in the from clause): this old syntax from decades ago should not be used in new code.

Selecting rows that have two values in related table

I have two these two tables:
ID Name ID AuthorID Title
--------- ---------------------
1 John 1 1 Blue
2 Jack 2 1 Yellow
3 Joe 3 2 Blue
4 3 Blue
How do I select the author that has both books named Blue and Yellow. I know I can query for authors where book IN ('Blue', 'Yellow') but I'm having a hard time grouping (and requiring) both books in the final result.
Calling first table AUTHORS and second table AUTHOR_TITLE, use count distinct along with your condition of IN.
SELECT A.ID, A.NAME
FROM
AUTHORS A
INNER JOIN
AUTHOR_TITLE B
ON A.ID = B.AUTHORID
AND B.TITLE IN ('BLUE','YELLOW')
GROUP BY A.ID, A.NAME
HAVING COUNT(DISTINCT B.TITLE) = 2;
You can use aggregation and having:
select authorid
from table2
where title in ('Blue', 'Yellow')
group by authoid
having count(distinct title) = 2;
You can use an additional join to bring in the author name.

Text search of a many-to-many data relation

I know this must have been answered before here, but I simply can't find a matching question.
Using a LIKE '%keyword%', I want to search a many-to-many data relationship in a MSSQL database and reduce it to a one-to-one result set. The two tables are joined through a linking table. Here's a very simplified version of what I'm talking about:
Books:
book_ id title
1 Treasure Island
2 Poe Collected Stories
3 Invest in Treasure Islands
Categories:
category_id name
1 Children
2 Adventure
3 Horror
4 Classic
5 Money
BookCategory:
book_id category_id
1 1
1 2
1 4
2 3
2 4
3 5
What I want to do is search for a phrase in the title (e.g. '%treasure island%') and get matching Books records that contain the search string and the single highest matching Categories record that goes with each book -- I want to discard the lesser category records. In other words, I'm looking for this:
book_id title category_id name
1 Treasure Island 4 Classic
3 Invest in Treasure Islands 5 Money
Any suggestions?
Try this. Filter your lookup table, then join:
With maxCategories AS
(select book_id, max(category_id) as category_id from BookCategory group by book_id)
select Books.book_id, Books.Title, Categories.category_id, Categories.name
from Books
inner join maxCategories on (Books.book_id = maxCategories.book_id)
inner join Categories on (Categories.category_id = maxCategories.category_id)
where Books.title like '%treasure island%'
Try:
select * from
(select b.*,
c.*,
row_number() over (partition by bc.book_id
order by bc.category_id desc) rn
from Books b
join BookCategory bc on b.book_id = bc.book_id
join Categories c on bc.category_id = c.category_id
where b.name like '%treasure island%') sq
where rn=1

SQL Syntax Issue with getting sum

Ok I have two tables.
Table IDAssoc has the columnsbill_id, year, area_id.
Table Bill has the columns bill_id, year, main_id, and amount_due.
I'm trying to get the sum of the amount_due column from the bill table for each of the associated area_ids in the IDAssoc table.
I'm doing a select statement to select the sum and joining on the bill_ids. How can I set this up so it will have a single row for each of the associated bills in each area_id from the assoc table. There may be three or four bill_ids associated with each area_id and I need those summed for each and returned so I can use this select in another statement. I have a group by set up for the area_id but it still is returning each row and not summing them up for each area_id. I have the year and main_id specified already in the where clause to return the data that I want, but I can't get the sum to work properly. Sorry I'm still learning and I'm not sure how to do this. Thanks!
Edit- Basically the query I'm trying so far is basically just like the one posted below:
select a.area_id, sum(b.amount_due)
from IDAssoc a
inner join Bill b
on a.bill_id = b.bill_id
where Bill.year = 2006 and bill.bill_id = 11111
These are just arbitrary numbers.
The data this is returning is like this:
amount_due - area_id
.05 1003
.15 1003
.11 1003
65 1004
55 1004
I need one row returned for each area_id with the amount_due summed. The area_id is only in the assoc table and not in the bill table.
select a.area_id, sum(b.amount_due)
from IDAssoc a
inner join Bill b
on a.bill_id = b.bill_id
where b.year = 2006 and b.bill_id = 11111
group by a.area_id
You might want to change inner join to left join if one IDAssoc can have many or no Bill:
select a.area_id, coalesce(sum(b.amount_due),0)
from IDAssoc a
left join Bill b
on a.bill_id = b.bill_id
where b.year = 2006 and b.bill_id = 11111
group by a.area_id
You are missing the GROUP BY clause:
SELECT a.area_id, SUM(b.amount_due) TotalAmount
FROM IDAssoc a
LEFT JOIN Bill b
ON a.bill_id = b.bill_id
GROUP BY a.area_id