SQL SUBQUERY ON SELF - sql

I have a table with the following data:
licence_number
date_of_birth
organisation
I want to do a query where:
Get the licence_numbers and dobs in organisation1 where the same
licence numbers and dobs are in organisation2.
I know it cant be that hard, but im struggling.

You can group by license_number and date_of_birth where organization is set to either of the two interesting organizations, and count how many distinct organizations there are in a group.
If there are two out of two possible in a single group, you have a hit.
SELECT license_number, date_of_birth
FROM mytable
WHERE organisation IN ('organisation1', 'organisation2')
GROUP BY license_number, date_of_birth
HAVING COUNT(DISTINCT organisation) = 2;
...or you can use INTERSECT;
SELECT license_number, date_of_birth
FROM mytable WHERE organisation = 'organisation1'
INTERSECT
SELECT license_number, date_of_birth
FROM mytable WHERE organisation = 'organisation2'
An SQLfiddle to test both.

select
from t t0
where
organization = 'organization1'
and
exists (
select 1
from t
where
organization = 'organization2'
and
licence_number = t0.licence_number
and
date_of_birth = t0.date_of_birth
)

You can just self join the table where the licence number and the dates are the same but the organisation isn't:
SELECT DISTINCT p1.licence_number, p1.date_of_birth
FROM people p1
INNER JOIN people p2
ON p1.licence_number = p2.licence_number AND
p1.date_of_birth = p2.date_of_birth AND
p1.organisation <> p2.organisation
SQL Fiddle here

Is it a JOIN or 1 table?
Select
[licence_number],
[date_of_birth],
[organisation]
From YourTable
Where organisation1 = organisation2
--OR
Select
[licence_number],
[date_of_birth],
[organisation]
From YourTable
Where organisation1 IN ('organisation2','organisation3','organisation3')
Order By [licence_number]

Related

Subquery and normal query comes out with different results

I'm a beginner of the oracle, currently, I'm doing a question using subquery(without JOIN) and normal (with JOIN) query, but at the end, the results are different from this two query,
I can't figure out this problem, does anyone know?
The question is asking about list the dog owner details which has booked at least twice in this platform
SELECT PET_OWNER.Owner_id,Oname,OAdd,COUNT(*) AS BOOKING
FROM PET_OWNER
WHERE Owner_id IN(
SELECT Owner_id
FROM PET
WHERE PType = 'DOG' AND Pet_id IN(SELECT Pet_id FROM BOOKING))
GROUP BY PET_OWNER.Owner_id,Oname,OAdd
HAVING COUNT(*) >=2
ORDER BY PET_OWNER.Owner_id;
This subquery shows no rows selected,
SELECT PET_OWNER.Owner_id,Oname,OAdd,COUNT(*) AS BOOKING
FROM PET_OWNER,PET,BOOKING
WHERE PET_OWNER.Owner_id = PET.Owner_id AND
PET.Pet_id = BOOKING.Pet_id AND
PType = 'DOG'
GROUP BY PET_OWNER.Owner_id,Oname,OAdd
HAVING COUNT(*) >=2
ORDER BY PET_OWNER.Owner_id;
this query shows 10 records which are the correct answer for this question
I expected these two queries come out with the same result but it is not
does anyone know what is wrong with it?
can anyone show me how to convert this code to subquery?
Because duplicated join key will cause duplicatation in result.
In your case, the Owner_id should be non-unique in the PET table.
It is still possible to get the correct answer by using join. And as the owner_id in the subquery t is unique, so the execution plan should be same with the subquery version.
select p.* from Pet_Owner p
join (
select PET.Owner_id
from PET
inner join Booking on Booking.Pet_id = PET.Pet_id
where pType = 'DOG'
group by PET.Owner_id
having count(1) >= 2) t
on t.Owner_id = p.Owner_id
order by p.Owner_id
By the way, your SQL code is so old-school as it is in ANSI-89, while the join syntax is already in ANSI-92. I know many school teachers still love the old style, I hope you can read both, but only write code in ANSI-92 way.
What happen is that it will give you distinct values on your PET_OWNER.Owner_id,Oname,OAdd. So what we need is to group by owner_id first.
Here's your query. get first those owner_id with count() >= 2 as subquery
select * from Pet_Owner where Owner_id in (
select t1.Owner_id from PET_OWNER t1
inner join PET t2 on t1.Owner_id = t2.Owner_id
inner join Booking t3 on t3.Pet_id = t2.Pet_id
where pType = 'DOG'
group by t1.Owner_id
having count(1) >= 2)
order by Owner_id
not using join, nested subqueries is our only option
select * from Pet_Owner where Owner_id in (
select owner_id from Pet_Owner where Owner_id in
(select Owner_id from Pet where Pet_id in
(select Pet_id in Booking) and PType='DOG')
group by owner_id
having count(1) >= 2)
order by Owner_id
if you are trying to the # of dogs per owner:
select * from Pet_Owner where Owner_id in (
select Owner_id from Pet where Pet_id in
(select Pet_id in Booking) and PType='DOG'
group by owner_id
having count(1) >= 2)
) order by Owner_id

How to implement/improve/ make faster this query with joins

Please bear with me I am not skilled in SQL:
I have three tables
1) Notifications - stores all my data
2) GroupTable - Has the names of groups and related id
3) GroupUser - this table maps Uname and Udob to a group from GroupTable.
Now before I fetch records from Notifications I want to check the GroupTable for GroupID take this GroupID and look in GroupUser for all the records in this GroupID (Names,DOB as these are unique) Once I get this data I want to fetch records from Notifications table for the Names and DOB's in ascending order of the date:
So far I have the following query, it works fine just that I am not satisfied and I think this can be improved:
SELECT
*
FROM
(SELECT
*
FROM Notifications
WHERE
DateToNotify < '2016-03-24' AND
NotificationDateFor IN
(SELECT gu.Name
FROM GroupUser AS gu
INNER JOIN GroupTable AS gt ON
gu.GroupID = gt._id AND
gt.GroupName = "Groupn"
) AND
DOB IN
(SELECT gu.DOB
FROM GroupUser AS gu
INNER JOIN GroupTable AS gt ON
gu.GroupID = gt._id AND
gt.GroupName = "Groupn"
)
) as T
ORDER BY
SUBSTR(DATE('NOW'), 0) > SUBSTR(DateToNotify, 0)
, SUBSTR(DateToNotify, 0)
I don't think that you would get this faster with joins instead of the IN clauses. It can be that re-writing would not even change the execution plan, because the dbms tries to access the data in the optimal way anyhow.
It seems a bit strange that you don't look for group users matching name and dob, but only ensure that there are group users matching the name and - possibly other - group users matching the dob. But as you say that the query works fine as is, okay.
EDIT: Okay, according to your comment you actually want groupuser matches on both name and dob. So what you are looking for would be
AND (NotificationDateFor, DOB) IN (SELECT gu.Name, gu.DOB FROM ...)
But SQLite doesn't support this beautiful syntax (Oracle is the only dbms I know of that does).
So you either join or use EXISTS.
With JOIN:
select distinct n.*
from notifications n
join
(
select name, dob
from groupuser
where groupid = (select _id from grouptable where groupname = 'groupn')
) as gu on n.notificationdatefor = gu.name and n.dob = gu.dob
where n.datetonotify < '2016-03-24'
order by date('now') > n.datetonotify, n.datetonotify;
With EXISTS:
select *
from notifications n
where datetonotify < '2016-03-24'
and exists
(
select *
from groupuser gu
where gu.groupid = (select _id from grouptable where groupname = 'groupn')
and gu.name = n.notificationdatefor
and gu.dob = n.dob
)
order by date('now') > n.datetonotify, n.datetonotify;

SQL query for finding row with same column values that was created most recently

If I have three columns in my MySQL table people, say id, name, created where name is a string and created is a timestamp.. what's the appropriate query for a scenario where I have 10 rows and each row has a record with a name. The names could have a unique id, but a similar name none the less. So you can have three Bob's, two Mary's, one Jack and 4 Phil's.
There is also a hobbies table with the columns id, hobby, person_id.
Basically I want a query that will do the following:
Return all of the people with zero hobbies, but only check by the latest distinct person created, if that makes sense. Meaning if there is a Bob person that was created yesterday, and one created today.. I only want to know if the Bob created today has zero hobbies. The one from yesterday is no longer relevant.
select pp.id
from people pp, (select name, max(created) from people group by name) p
where pp.name = p.name
and pp.created = p.created
and id not in ( select person_id from hobbies )
SELECT latest_person.* FROM (
SELECT p1.* FROM people p1
WHERE NOT EXISTS (
SELECT * FROM people p2
WHERE p1.name = p2.name AND p1.created < p2.created
)
) AS latest_person
LEFT OUTER JOIN hobbies h ON h.person_id = latest_person.id
WHERE h.id IS NULL;
Try This:
Select *
From people p
Where timeStamp =
(Select Max(timestamp)
From people
Where name = p.Name
And not exists
(Select * From hobbies
Where person_id = p.id))

Help with query

I'm trying to make a query that looks at a single table to see if a student is in a team called CMHT and in a medic team - if they are I don't want to see the result.
I only want see the record if they're only in CMHT or medic, not both.
Would the right direction be using sub query to filter it out? I've done a search on NOT IN but how could you get to see check if its in more then 2 teams are not?
Student Team ref
1 CMHT 1
1 Medic 2
2 Medic 3 this would be in the result
3 CMHT 5 this would be in the result
So far I've done the following code would I need use a sub query or do a self join and filter it that way?
SELECT Table1.Student, Table1.Team, Table1.refnumber
FROM Table1
WHERE (((Table1.Team) In ('Medics','CMHT'))
This is Mark Byers's answer with a HAVING clause instead of a subquery:
SELECT Student, Team, ref
FROM Table1
GROUP BY Student
HAVING COUNT(Student) = 1
SELECT *
FROM students
WHERE NOT EXISTS
(
SELECT NULL
FROM students si
WHERE si.student = s.student
AND si.team = 'CMHT'
)
OR NOT EXISTS
(
SELECT NULL
FROM students si
WHERE si.student = s.student
AND si.team = 'Medic'
)
SELECT a.*
FROM Table1 a
INNER JOIN
( SELECT Student, COUNT(*) FROM Table1
GROUP BY Student
HAVING COUNT(*) = 1)b
ON (a.Student = b.Student)
how could you get to see check if its in 2 or more teams?
You can count the number of teams per student and then filter only those you want to see:
SELECT student FROM
(
SELECT student, COUNT(*) AS cnt
FROM Table1
GROUP BY student
) T1
WHERE cnt = 1
You can do it with outer join
select COALESCE(t1.Student, t2.Student) as Student,
COALESCE(t1.Team, t2.Team) as Team,
COALESCE(t1.ref, t2.ref) as ref
from
(select * from Student where Team = 'CMHT') t1
outer join
(select * from Student where Team = 'Medic') t2
on t1.Student = t2.Student
where
t1.Student is null or
t2.Student is null;

how can i rewrite a select query in this situation

Here are two table in parent/child relationship.
What i need to do is to select students with there average mark:
CREATE TABLE dbo.Students(
Id int NOT NULL,
Name varchar(15) NOT NULL,
CONSTRAINT PK_Students PRIMARY KEY CLUSTERED
(
CREATE TABLE [dbo].[Results](
Id int NOT NULL,
Subject varchar(15) NOT NULL,
Mark int NOT NULL
)
ALTER TABLE [dbo].[Results] WITH CHECK ADD CONSTRAINT [FK_Results_Students] FOREIGN KEY([Id])
REFERENCES [dbo].[Students] ([Id])
I wrote a query like this :
SELECT name , coalesce(avg(r.[mark]),0) as Avmark
FROM students s
LEFT JOIN results r ON s.[id]=r.[id]
GROUP BY s.[name]
ORDER BY ISNULL(AVG(r.[mark]),0) DESC;
But the result is that all of students with there avg mark in desc order.What i need is to restrict result set with students that have the highest average mark agaist other,i.e.if the are two students with avg mark 50 and 1 with 25 i need to display only those students with 50.If there are only one student with highest avg mark- only he must appear in result set.How can i do this in best way?
SQL Server 2005+, using CTEs:
WITH grade_average AS (
SELECT r.id,
AVG(r.mark) 'avg_mark'
FROM RESULTS r
GROUP BY r.id),
highest_average AS (
SELECT MAX(ga.avg_mark) 'highest_avg_mark'
FROM grade_average ga)
SELECT DISTINCT
s.name,
ga.avg_mark
FROM STUDENTS s
JOIN grade_average ga ON ha.id = s.id
JOIN highest_average ha ON ha.highest_avg_mark = ga.avg_mark
Non-CTE equivalent:
SELECT DISTINCT
s.name,
ga.avg_mark
FROM STUDENTS s
JOIN (SELECT r.id,
AVG(r.mark) 'avg_mark'
FROM RESULTS r
GROUP BY r.id) ga ON ha.id = s.id
JOIN SELECT MAX(ga.avg_mark) 'highest_avg_mark'
FROM (SELECT r.id,
AVG(r.mark) 'avg_mark'
FROM RESULTS r
GROUP BY r.id) ga) ha ON ha.highest_avg_mark = ga.avg_mark
If you're using a relatively new version of MS SQL server, you can use WITH to make this simple to write:
WITH T AS (
SELECT
name,
coalesce(avg(r.[mark]),0) as mark
FROM students s
LEFT JOIN results r ON s.[id]=r.[id]
GROUP BY s.[name])
SELECT name as 'ФИО', mark as 'Средний бал'
FROM T
WHERE T.mark = (SELECT MAX(mark) from T)
Is it as simple as this? For all versions of SQL Server 2000+
SELECT TOP 1 WITH TIES
name, ISNULL(avg(r.[mark]),0) as AvMark
FROM
students s
LEFT JOIN
results r ON s.[id]=r.[id]
GROUP BY
s.[name]
ORDER BY
ISNULL(avg(r.[mark]),0) DESC;
SELECT name as 'ФИО',
coalesce(avg(r.[mark]),0) as 'Средний бал'
FROM students s
LEFT JOIN results r
ON s.[id]=r.[id]
GROUP BY s.[name]
HAVING AVG(r.[mark]) >= 50
ORDER BY ISNULL(AVG(r.[mark]),0) DESC
about HAVING clause