One to many relationship, find groups that contain only the specified values - sql

So lets say I have a table
ID Car
1 Polo
2 Jetta
And a one-many relationship like
Id Driver Car
1 Jason 1
2 Fred 1
2 Fred 2
3 Batman 1
4 Frodo 2
How can I get records from car that Only has Fred and Frodo driving it?
I.e. the query needs to return only car 2.

Assuming that only has Fred and Frodo driving it means Fred, Frodo and no other, you would write:
SELECT car
FROM car_driver
GROUP BY car
HAVING COUNT(CASE WHEN driver IN ('Fred', 'Frodo') THEN 1 END) = 2
AND COUNT(*) = 2

You can use the set operator INTERSECT:
SELECT car FROM drivers WHERE driver = 'Fred'
INTERSECT
SELECT car FROM drivers WHERE driver = 'Frodo'
db<>fiddle

maybe something like this
select c.car
from cardriver cd join cars c
on c.id=cd.car and cd.Driver in ('Fred','Frodo')
group by c.car
having count(distinct cd.Driver)=2
db fiddle here

Related

How to show id with exactly three different cases in M:m table SQL

Im looking for a soulutin to this problem, where this would be the table:
ID
cars
1
volvo
1
volvo
1
Benz
2
toyota
2
Benz
1
NULL
2
volvo
3
Benz
and the outcome would be like this
ID
2
because ID:2 is the only one that has exactly 3 different cars, but others donĀ“t.
how is this posible, and how is it in general?
You can use group by and having:
select id
from t
group by id
having count(distinct cars) = 3;

Filter DB2 Group based on criteria

I have a data set for multiple versions of a record. In the structure below:
ID Version Date ActionedBy
1 1 20/08/2018 James
1 2 20/08/2018 Samuel
2 1 20/08/2018 Tom
3 1 20/08/2018 Tom
3 2 20/08/2018 Tom
3 1 20/08/2018 Dave
4 1 20/08/2018 Tom
4 2 20/08/2018 Mike
5 1 20/08/2018 Dave
I need a query to return all records that Tom has actioned but not include the ones actioned by Dave. So my result should be:
ID
2
4
I've grouped on ID but am struggling with the Having clause or sub query to not include records with Dave.
My query is:
Select * from events where ActionedBy='Tom' Group By ID Having "NOT SURE WHAT GOES HERE"
One solution could be
SELECT ID FROM events e
WHERE ActionedBy = 'Tom'
AND NOT EXISTS (SELECT 1 FROM events WHERE ActionedBy = 'Dave' AND ID = e.ID)
You do not necessarily need the GROUP BY if Tom occurs only once in a single ID. If not just add it....
I would use group by with a having clause:
select id
from t
where actionedby in ('Tom', 'Dave')
having min(actionedby) = 'Tom' and max(actionedby) = 'Tom';
Actually min() is sufficient in this case, because 'Tom' > 'Dave'.

Oracle find common value in two different columns

If I have a structure like this:
CREATE TABLE things (
id,
personA varchar2,
personB varchar2,
attribute ...,
)
And I want to find, for a given attribute, if I have at least 1 common person for all my things, how would I go about it?
So if my data is (and it could be more than 2 per attribute):
1, John, Steve, Apple
2, Steve, Larry, Apple
3, Paul, Larry, Orange
4, Paul, Larry, Orange
5, Chris, Michael, Tomato
6, Steve, Larry, Tomato
For Apple, Steve is my common person, For Orange both Paul and Larry are, and for Tomato I have no common people. I don't need a query that returns all of these at once, however. I have one of these attributes and want 0, 1, or 2 rows depending on what kind of commonality I have. I've been trying to come up with something but can't quite figure out.
This will give you your common person / attribute list. I ran it against your sample data and got the expected result. Hope it's at least pointing in the right direction :)
WITH NormNames AS (
SELECT PersonA AS Person, Attribute FROM things
UNION ALL SELECT PersonB AS Person, Attribute FROM things
)
SELECT
Person, Attribute, COUNT(*)
FROM NormNames
GROUP BY Person, Attribute
HAVING COUNT(*) >= 2
If you're on 11gR2 you could also use the unpivot operator to avoid the self-join:
select person, attribute
from (
select *
from things
unpivot (person for which_person in (persona as 'A', personb as 'B'))
)
group by person, attribute
having count(*) > 1;
PERSON ATTRIBUTE
---------- ----------
Steve Apple
Paul Orange
Larry Orange
3 rows selected.
Or to just the the people who match the attribute, which I think is what the end of your question is looking for:
select person
from (
select *
from things
unpivot (person for which_person in (persona as 'A', personb as 'B'))
)
where attribute = 'Apple'
group by person, attribute
having count(*) > 1;
PERSON
----------
Steve
1 row selected.
The unpivot translates columns into rows. Run on its own it transforms your original six rows into twelve, replacing the original persona/personb columns with a single person and an additional column indicating which column the new row was formed from, which we don't really care about here:
select *
from things
unpivot (person for which_person in (persona as 'A', personb as 'B'));
ID ATTRIBUTE W PERSON
---------- ---------- - ----------
1 Apple A John
1 Apple B Steve
2 Apple A Steve
2 Apple B Larry
3 Orange A Paul
3 Orange B Larry
4 Orange A Paul
4 Orange B Larry
5 Tomato A Chris
5 Tomato B Michael
6 Tomato A Steve
6 Tomato B Larry
12 rows selected.
The outer query is then doing a simple group.
Here's one method.
It implements an unpivot method by cross-joining to a list of numbers (you could use the unpivot method Alex uses) and then joins the result set, hopefully with a hash join for added goodness.
with
row_generator as (
select 1 counter from dual union all
select 2 counter from dual),
data_generator as (
select
attribute,
id ,
case counter
when 1 then persona
when 2 then personb
end person
from
things,
row_generator)
select
t1.attribute,
t1.person
from
row_generator t1,
row_generator t2
where
t1.attribute = t2.attribute and
t1.person = t2.person and
t1.id != t2.id;

Joining multiple tables with a single query

Student
student_id FirstName LastName
---------------------------------------------------
1 Joe Bloggs
2 Alan Day
3 David Johnson
Student_Course
course_id student_id courseName
---------------------------------------------------
1 1 Computer Science
2 1 David Beckham Studies
3 1 Geography
1 3 Computer Science
3 3 Geography
Student_clubs
club_id student_id club_name club_count
---------------------------------------------------
1 1 Footbal 10
2 1 Rugby 10
3 1 Syncronized Swimming 10
4 3 Tennis 15
In the above example, student with id = 1 takes 3 course and is part of 3 clubs.
If i was to find out which courses a student is involved in or which club the student is part of i can do it but i will need to run two queries. Is it possible to run a single query against the
tables listed above so that the results come out like this:
Output
student_id FirstName Student_associated_courses Student_associated_clubs
---------------------------------------------------------------------------
1 Joe 1,2,3 Football, Rugby, Syncronized swimming
3 David 1,3 Tennis
Is it possible to get the above output with just one query? I am using JDBC to get the data so i am trying to see if i can avoid multiple trips to get the necessary data.
use GROUP_CONCAT with DISTINCT in MySQL
SELECT a.student_ID, a.firstname,
GROUP_CONCAT(DISTINCT b.course_ID),
GROUP_CONCAT(DISTINCT c.club_name)
FROM student a
INNER JOIN student_Course b
ON a.student_id = b.student_ID
INNER JOIN student_clubs c
ON a.student_ID = c.student_ID
GROUP BY a.student_ID, a.firstname
See SQLFiddle Demo
Try it like this:
SELECT *
FROM Student s JOIN
(SELECT sc."student_id", listagg(sc."course_id", ',')within group(ORDER BY sc."course_id")
FROM Student_Course sc
GROUP BY sc."student_id") s_course ON s."student_id"=s_course."student_id"
JOIN (SELECT sl."student_id", listagg(sl."club_name", ',')within GROUP(ORDER BY sl."club_name")
FROM Student_clubs sl
GROUP BY sl."student_id") s_club ON s."student_id"=s_club."student_id"
The "catch" is that LISTAGG doesn't work with DISTINCT keyword
Here is a fiddle

Limiting records of combinations from 2 columns

looking for some help limiting the results while querying combinations between 2 columns. Here's an example of the kind of table I am working with..
id name group state
1 Bob A NY
2 Jim A NY
3 Dan A NY
4 Mike A FL
5 Tim B NY
6 Sam B FL
7 Brad B FL
8 Glen B FL
9 Ben C FL
I am trying to display all records of all combinations of "group" and "state", but limiting to displaying only up to 2 records for each combination. The result should look like the following..
id name group state
1 Bob A NY
2 Jim A NY
4 Mike A FL
5 Tim B NY
6 Sam B FL
7 Brad B FL
9 Ben C FL
Thanks for the help.
Assuming you always want the two rows for each group and state combination with the lowest id
SELECT *
FROM (SELECT a.*,
row_number() over (partition by group, state
order by id asc) rnk
FROM your_table a)
WHERE rnk <= 2
Of course, since group is a reserved word, I assume your column is actually named something else... You'd need to adjust my query to use the correct column name.