Select element where condition is met on all joined elements - sql

I found it hard labeling this question with an appropriate title but maybe thats because i dont know how to accomplish my goal.
I have the following database layout:
table: folders
columns: folderId
table: files
columns: fileId, authorId
table: file_relation
columns: folderId, fileId
table: authors
columns: authorId, surname, firstName
What i try to achieve is listing all folder that only contain files from author X Y (surname = X, firstName = Y). My approach was like this:
SELECT f.folderId FROM `folders` f
INNER JOIN `file_relation` rel ON rel.folderId = f.folderId
INNER JOIN `files` fls ON fls.fileId = rel.fileId
INNER JOIN `authors` a ON a.authorId = fls.authorId
WHERE a.surname = X and a.firstName = Y;
Now that lists me all the folders where there are files from said author. But how do i now restrict them to only get listed if there is no other file?

It's better to use aggregate function instead of subquery. The query will look like this:
SELECT f.folderId,
COUNT(fls.fileId) as filesCount,
SUM(a.surname = 'X' and a.firstName = 'Y') as authorFilesCount
FROM `folders` f
INNER JOIN `file_relation` rel ON rel.folderId = f.folderId
INNER JOIN `files` fls ON fls.fileId = rel.fileId
INNER JOIN `authors` a ON a.authorId = fls.authorId
GROUP BY f.folderId
HAVING filesCount = authorFilesCount;

Related

SQL, Show all entries where count from different tables is unqeual

I have the following two SELECTs:
Declare #S_PWO varchar(300)
Declare #S_O varchar(300)
#S_PWO =
(
SELECT COUNT(PWO.Attr1)
FROM Person P
JOIN PersonInOrg PIO
ON P.UID_Person = PIO.UID_Person
JOIN Org O
ON O.UID_Org = PIO.UID_Org
JOIN PersonWantsOrg PWO
ON PIO.Attr2 = PWO.Attr3
WHERE
O.Attr4 like '%STRING%' AND
P.UID_Person = 'XXXXXXXXX'
)
#S_O =
(
SELECT Count(O.CCC_DisplayName)
FROM Person P
JOIN PersonInOrg PIO
ON P.UID_Person = PIO.UID_Person
JOIN Org O
ON O.UID_Org = PIO.UID_Org
JOIN PersonWantsOrg PWO
ON PIO.Attr2 = PWO.Attr3
WHERE
O.Attr4 like '%STRING%' AND
P.UID_Person = 'XXXXXXXXX'
)
For the person XXXXXXXXX they may result in an equal count, or in an unequal count.
I now need to find all persons where the count of those two Selects is unqueal.
How do i do that?
Your using the same Join/Where in both Selects, only the Count differs.
Simply combine both into one, Group By UID_Person and apply Having:
SELECT P.UID_Person, Count(PWO.Attr1) AS cntAttr1, Count(O.CCC_DisplayName) AS cntDisplayName
FROM Person P
JOIN PersonInOrg PIO
JOIN PersonInOrg PIO
ON P.UID_Person = PIO.UID_Person
JOIN Org O
ON O.UID_Org = PIO.UID_Org
JOIN PersonWantsOrg PWO
ON PIO.Attr2 = PWO.Attr3
WHERE
O.Attr4 LIKE '%STRING%'
GROUP BY P.UID_Person
HAVING Count(PWO.Attr1) <> Count(O.CCC_DisplayName)

Query to search for the songs to contain all the requested tags

I am making a search engine for songs using tags, and I have trouble building the SQL query that will list all the songs that match with the tags.
The database looks like this:
http://i.imgur.com/5zmfAz8.png
Songs have many Tags through an intermediate table(SongTags).
Let's have a population as example:
Tags:
Electro, Instrumental, Energetic, Melancholic, Vocal, Rock
Songs:
SongA (Electro, Melancholic, Vocal)
SongB (Instrumental, Melancholic, Rock)
SongC (Energetic, Vocal)
The search should return the songs that contains ALL the tags requested.
Search1:
"Vocal" returns: SongA, SongB
Search2:
"Vocal", "Energetic" returns: SongC
Search3:
"Vocal", "Energetic", "Electro" returns: nothing
I see how to do a search on 1 tag, but not on multiple tags.
For exemple for the Search1, I know that this would work:
SELECT * FROM "songs"
INNER JOIN "song_tags" ON "song_tags"."song_id" = "songs"."id"
INNER JOIN "tags" ON "tags"."id" = "song_tags"."tag_id"
WHERE "tags"."name" = 'Vocal'
But then I have no idea on how I could execute Search2, because I need the Song to contain both "Vocal" and "Energetic".
Edit:
I am using PostgreSQL
SELECT songs.*, COUNT(songs.id) AS total FROM "songs"
INNER JOIN "song_tags" ON "song_tags"."song_id" = "songs"."id"
INNER JOIN "tags" ON "tags"."id" = "song_tags"."tag_id"
WHERE "tags"."name" IN ('Vocal', 'Energetic')
GROUP BY songs.id
HAVING total = 2
If you want to get songs that match all the tags you are provided, I'd use subselects. They'll be a little bit slow, but it'll give you an all-matched solution:
SELECT songs.* FROM songs
WHERE songs.song_id IN
(SELECT song_tags.song_id FROM song_tags st JOIN tags t ON st.tag_id = t.id AND t.name = 'VOCAL')
AND songs.song_id IN (SELECT song_tags.song_id FROM song_tags st JOIN tags t ON st.tag_id = t.id AND t.name = 'ENERGETIC')
If your SQL server supports the INTERSECT command, you could do this:
SELECT songs.* FROM songs
WHERE songs.song_id IN
(SELECT song_tags.song_id FROM song_tags st JOIN tags t ON st.tag_id = t.id AND t.name = 'VOCAL'
INTERSECT
SELECT song_tags.song_id FROM song_tags st JOIN tags t ON st.tag_id = t.id AND t.name = 'ENERGETIC')
which I think is a cooler way to do it.
Hope that helps. :)

What kind of query do I need?

I have a table called physical_exam_tool_and_body_parts that has:
id physical_exam_tool_id body_part_id
the body_parts table has: name_tid
the physical_exam_tools table has: name_tid
The translation table looks like:
id lang text
I'm trying to query:
SELECT physical_exam_tool_text,
body_part_text
FROM physical_exam_tool_and_body_parts
WHERE translation.lang = 'fr'
I want the names of the body part and physical exam tool for the lang 'fr'. How can I do this. I'm new to joins.
body_parts and physical_exam_tools tables have:
id name_tid
name_tid is the id in the translation table. The translation table has id lang text. So the primary key for translations is a composite key (id,lang).
In physical_exam_body_part_and_tool the id's in that are just the ids (foreign keys) for the body_parts and physical_exam_tools table.
Join to the Translation Table twice where lang = 'fr'.
SELECT t.[text] AS [ExamTool], t2.[text] AS [BodyPart]
FROM physical_exam_tool_and_body_parts as p
inner join physical_exam_tool as pet
on p.physical_exam_tool_id = pet.physical_exam_tool_id
and t.lang = 'fr'
inner join translation as t
on pet.name_tid = t.id
inner join body_parts as b
on p.body_part_id = b.body_part_id
inner join translation as t2
on b.name_tid = t2.id
and t2.lang = 'fr'
Something like this.
SELECT PHYSICAL_EXAM_TOOL_TEXT,
BODY_PART_TEXT
FROM PHYSICAL_EXAM_TOOL_AND_BODY_PARTS A
INNER JOIN BODY_PARTS B
ON A.BODY_PART_ID = B.ID
INNER JOIN TRANSLATION C
ON C.ID = B.NAME_TID
WHERE C.LANG = 'FR'

SQL same postal code

select PERSON.fornavn, PERSON.efternavn, PERSON.postnr, POST.distrikt
from PERSON inner join POST on POST.postnr = PERSON.postnr
inner join MEDLEM on PERSON.personnr = MEDLEM.personnr
inner join FORMAND on MEDLEM.personnr = FORMAND.personnr
group by PERSON.fornavn, PERSON.efternavn, PERSON.postnr, POST.distrikt, FORMAND.afdnr
having FORMAND.afdnr = 3
order by PERSON.fornavn
I need to show the persons that live on the same postal code as the boss for department 3, but the results make no sense.
Fornavn = First name, Formand = boss, Adresse = adress, Afdeling = Department (afdnr = department nr.), Medlem = member
Tables:
PERSON: personnr, fornavn, adresse, postnr
POST: postnr, district
FORMAND: personnr, afdnr
MEDLEM: personnr, afdnr
AFDELING: afdnr, afdname
select PERSON.fornavn, PERSON.efternavn, PERSON.postnr, POST.distrikt
from PERSON inner join POST on POST.postnr = PERSON.postnr
inner join MEDLEM on PERSON.personnr = MEDLEM.personnr
inner join FORMAND on MEDLEM.personnr = FORMAND.personnr
where FORMAND.afdnr = 3
group by PERSON.fornavn, PERSON.efternavn, PERSON.postnr, POST.distrikt, FORMAND.afdnr
order by PERSON.fornavn
try with :
where FORMAND.afdnr = 3
insteed of
having FORMAND.afdnr = 3
If I understand you right ("the persons that live on the same postal code as (the boss for department 3)", and assuming there is only one boss:
select PERSON.fornavn, PERSON.efternavn, PERSON.postnr, POST.distrikt
from PERSON
inner join POST on POST.postnr = PERSON.postnr
where PERSON.postnr = (SELECT pf.postnr
FROM FORMAND f
inner join PERSON pf ON f.personnr = pf.personnr
WHERE f.afdnr = 3)
order by PERSON.fornavn
Note: Your query makes no sense given the description, unless I am missing something. You are joining PERSON through MEDLEM to FORMAND on personnr, which means you will only get the boss's record back (PERSON.personnr = FORMAND.personnr).
Note: If there is more than one boss, you can change the = in the WHERE clause to IN to get all people that live in the same post code as any of the bosses.
In case I read you wrong, and you actually mean "the (persons that live on the same postal code as the boss) for department 3", try:
select p.fornavn, p.efternavn, p.postnr, POST.distrikt
from PERSON p
inner join POST on POST.postnr = p.postnr
inner join MEDLEM on p.personnr = MEDLEM.personnr
inner join FORMAND on MEDLEM.afdnr = FORMAND.afdnr
inner join PERSON pf on FORMAND.personnr = pf.personnr AND pf.postnr = p.postnr
where MEDLEM.afdnr = 3
order by PERSON.fornavn
Note: Again, I believe your join from the question is strange. See above.

OJB ReportQuery join question

Given three tables:
People: {Person_Id, Age}
Accounts: {Account_Id, Person_Id, Account_Type}
Age_Ranges: {Age_Range_Id, Age_Range_Label, Lower_Bound, Upper_Bound}
I'm trying to represent the following SQL query using OJB (without resorting to a QueryBySQL):
select ar.Age_Range_Label, count(a.Account_Id)
from People p
inner join Accounts a on p.Person_Id = a.Person_Id
inner join Age_Ranges ar on p.Age between ar.Lower_bound and ar.Upper_Bound
where a.Account_Type = 'Chequing'
The java code I have so far looks like this:
Criteria criteria = new Criteria();
criteria.addEqualTo("Account_Type", "Chequing");
// join on Age_Ranges based on the person's Age
criteria.???
ReportQueryByCriteria q = QueryFactory.newReportQuery(Accounts.class, criteria);
q.setAttributes(new String[] {"Age_Range_Label", "count(Account_Id)"});
q.addGroupBy("Age_Range_Label");
...
But I'm unsure how to join up with the Age_Ranges table such that I can group on the Age_Range_Label column.
Any insight?