Selecting rows that have two values in related table - sql

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.

Related

Summarize Null Values in Table with Group By

I have two tables:
Person(ID, Name)
Sports(person_ID, Sport)
The Problem: Sport can have NULL values. And if that is the case then if I group by ID the sport should be NULL.
SELECT p.ID, p.Name, s.Sport
FROM Person p
INNER JOIN Sports s ON p.ID=s.person_id
GROUP BY p.ID
Without the Group By the table looks like this:
p.ID p.Name s.Sport
1 tom soccer
1 tom NULL
2 lisa golf
2 lisa soccer
3 tim golf
3 tim NULL
What I want now:
1 tom NULL
2 lisa golf
3 tim NULL
But what I get:
1 tom soccer
2 lisa golf
3 tim golf
I've tried subselects and ifs but I couldn't get anything to work. Thanks in advance!
Here is a query which should generate your expected result set, though as #jarlh has pointed out, it isn't clear why Lisa should play golf over soccer.
SELECT
p.ID,
p.Name,
CASE WHEN COUNT(CASE WHEN s.Sport IS NULL THEN 1 END) > 0
THEN NULL ELSE MIN(s.Sport) END AS Sport
FROM Person p
INNER JOIN Sports s
ON p.ID = s.person_id
GROUP BY
p.ID,
p.name;
Note that I group by both the ID and name, which would be required on many databases (though perhaps not SQLite).
you can't manage the NULL value with aggreagtion function as MIN()
but you could try
SELECT p.ID, p.Name, min(ifnull(s.Sport,''))
FROM Person p
INNER JOIN Sports s ON p.ID=s.person_id
GROUP BY p.ID, p.name
Assuming the version of SQLLite you are using supports row_number(), please try below, you can set a row_number to 1 if you order by s.sport ASC, then select the first row for each category. If there is NULL, it should locate at the top row of each category via this query. You don't need to use group by:
;with cte as (
select p.ID, p.Name, s.Sport,
ROW_NUMBER() OVER (PARTITION BY p.ID ORDER BY s.Sport ASC) AS rn
FROM Person p INNER JOIN Sports s ON p.ID=s.person_id
)
select *
from cte
where rn=1
You can do this with a correlated subquery, avoiding the join in the outer query:
select p.*,
(select s.sport
from sports s
where s.personId = p.id
order by (s.sport is null) desc, s.sport asc
) as min_sport
from person p;
This may prove useful under some circumstances. With an index on sports(personid, sport), it might be faster than the group by, depending on the data (lots of people, few sports per person).
Also, this is slightly different from your query because it returns all people, even those with no sports.

SQL Query with different aggregation for count

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

Joining two tables and display data from first table

I have two tables tblPatient, tblDropDowns
tblpatient:
firstname gender patienttype
anil 1 3
Satheesh 1 4
Vinod 1 4
Shashikanth 1 3
Srimani 2 3
Thanuja 2 4
Nandini 2 4
Vishu 2 3
and
tblDropdowns:
id Name
1 Male
2 Female
3 Inpatient
4 Outpatient
Now i want to display the patient table with gender and patient type as their significant names are connected to dropdown table.
result table:
firstname gender patienttype
anil male inpatient
satheesh male outpatient
vinod male outpatient
please help me out..
thanks
anil
In general it would be better avoid storing different things in the same table. However, you could join with subqueries that only contain the relevant records.
SELECT firstname, gender.Name AS gender, patienttype.Name As patienttype
FROM tblPatient p
INNER JOIN (SELECT id, Name
FROM tblDropdowns
WHERE id IN (1, 2)) gender
ON p.gender = gender.id
INNER JOIN (SELECT id, Name
FROM tblDropdowns
WHERE id > 2) patienttype
ON p.patienttype = patienttype.id
Please try out this.
select [column1], [column2] from tblpatient a, tbldropdowns b
where a.gender = b.id order by a.gender;
You could also use joins: Please refer this W3Schools SQL Link.
Hope this helps. Thanks.
Edit
Maybe this query could be your solution:
select a.firstname, b.name as 'gender', b.name as 'PatientType'
from tblpatient a, tbldropdowns b
where a.gender = b.id and a.patienttype = b.id
order by a.gender;
Thanks again. :-)
Try as follows:
SELECT firstname, name, CASE WHEN (patienttype=3) THEN 'inpatient' ELSE 'outpatient' END as patienttype_text from tblpatient INNER JOIN tbldropdowns ON gender = id
join your "tblDropdowns" table twice in your select query.
Please refer this link to understand join,
http://www.w3schools.com/sql/sql_join_left.asp
select tP.firstname,tG.Name gender,tPT.Name patienttype
from tblPatient tP
left join tblDropDowns tG
on tG.id = tP.gender
left join tblDropDowns tPT
on tPT.id = tP.patienttype

SELECT statement to exclude results through join table relationship

Given three tables like this:
fruit: taste: fruit_taste:
id name id name id fruit_id taste_id
-- ---- -- ---- -- -------- --------
1 apple 1 sweet 1 1 1
2 lime 2 tart 2 1 2
3 banana 3 2 2
4 lemon 4 4 2
How can I SELECT only the fruit which do have not been identified by the join table as having a sweet taste? My desired results should also include 'banana', since there is not a fruit_taste identifying it as sweet.
I tried (along with a ton of other things):
SELECT *
FROM fruit
LEFT OUTER JOIN fruit_taste
ON fruit_taste.fruit_id = fruit.id
WHERE (fruit_taste.taste_id != 1 OR fruit_taste.id IS null)
which almost works, but still gives me 'apple' in my result, since my apple is a Granny Smith, and is both sweet and tart. How can I exclude 'apple' in my query? Is my only option a subquery with NOT IN?
select *
from fruit
where id not in
(select ft.fruit_id
from fruit_taste ft
inner join taste t
on ft.taste_id = t.id
where t.name = 'sweet')
Aside from using NOT IN, you can also use LEFT JOIN. To filter out fruits that have no sweet taste, the total number of sweet for every fruit should be 0.
SELECT a.ID, a.Name
FROM fruit a
LEFT JOIN fruit_taste b
ON a.id = b.fruit_id
LEFT JOIN taste c
ON b.taste_id = c.id
GROUP BY a.ID, a.Name
HAVING SUM(IFNULL(c.name = 'sweet', 0)) = 0
SQLFiddle Demo
You can use a LEFT JOIN and avoid the subquery if you add the excluded taste to the JOIN filter and then exclude matching rows. This should generally be better for performance than using NOT IN on a subquery, but YMMV.
SELECT *
FROM fruit
LEFT JOIN fruit_taste
ON fruit_taste.fruit_id = fruit.id
AND fruit_taste.taste_id = 1
WHERE fruit_taste.id IS NULL

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