Find diff between three tables - sql

I'm trying to find the difference between three tables, a, b, and c. I'd like to join all three, showing nulls where each table does not have records.
Each table has two columns that I would like to join on, lets say first and last name.
So far, this is what I'm doing.
Select * from a
FULL OUTER JOIN b on
a.firstName = b.firstName AND a.lastname = b.lastname
FULL OUTER JOIN c on
(c.firstName = a.firstName and c.lastName = a.LastNAME) OR
(c.firstName = b.firstName AND c.lastname = b.LastName)
I'm not confident that the logic in my output is correct. Is there a better way of doing this? In english, the first join joins A and B, including records in each table that don't exist in the other table. The second join joins A+B to C, joining C to either A or B if it matches.

You may want:
select firstname, lastname, sum(in_a), sum(in_b), sum(in_c)
from ((select firstname, lastname, 1 as in_a, 0 as in_b, 0 as in_c
from a
) union all
(select firstname, lastname, 0 as in_a, 1 as in_b, 0 as in_c
from b
) union all
(select firstname, lastname, 0 as in_a, 0 as in_b, 1 as in_c
from c
)
) abc
group by firstname, lastname;
This should each firstname/lastname pair with information about how many times the pair is in each table.
I prefer the union all/group by approach for this over left join for several reasons:
Without the using clause, the on clause gets a bit tricky as you add more tables.
Minimal use of coalesce().
Better handles duplicates within each table.
Handles NULL values for the keys.

Related

fetching duplicate tuples in Oracle using SQL

Here are the two table structures
I have two tables where I am trying to fetch some duplicate records based on some condition like where a.fname=b.fname and a.phone_no<>b.phone_no
But also I need to include other column 'address' which is in table 2 and introduce the same condition for duplicate checking for address as well.
SELECT
"Fname"||' '||"Lname" AS "Customer_Name",
COUNT(*) AS "Countof"
FROM "S_CONTACT" A
WHERE EXISTS (
SELECT 1
FROM "S_CONTACT" B
WHERE A."PHONE" != B."PHONE"
AND A."Fname" = B."Fname"
AND A."EMAIL"=B."EMAIL"
AND A."Lname"=B."Lname"
AND "DOB" IS NULL
)
GROUP BY "Fname","Lname","EMAIL"
HAVING count(*) >1;
The above sql gives me a list of customers with duplicate names and email.
But I do not know how to introduce the column address in this sql which is from different table t2
If you are trying to find duplicates in one table:
select c.fname, c.lname, c.email
from contacts c
group by c.fname, c.lname, c.email
having min(c.phone) <> max(c.phone);
If you want to count null as a different value, then use:
having min(c.phone) <> max(c.phone) or count(c.phone) <> count(*)
You can do the same thing on the second table:
select c.fname, c.lname, c.email
from second_table c
group by c.fname, c.lname, c.email
having min(c.address) <> max(c.address) or count(c.address) <> count(*)
If you need the results in the same result set, then use union or union all or some similar mechanism.

Catch multiple types of data in SQL Server

I have a table (Task) like this:
Task Table
and I need answer like this:
TaskResult
I am doing the first query like this:
select
StudentID, AdmissionID, EnquiryID, EnquiryDetailsID
from
Task
where
TaskUser = 0 and BranchID = 1
If I'm getting studentID then I create second query in loop for searching the student first name and last name.
elseif I'm getting EnquiryID then I create second query in loop for searching the Enquiry first name and last name.
elseif I'm getting AdmissionID then I create second query in loop for searching the Admission guys first name and last name.
elseif I'm getting EnquiryDetailsID then I create second query in loop for searching the EnquiryDetails first name and last name.
So it creates loop in a loop and I get heavy load time on the page.
I need to combine both queries into one query. So page won't be loading.
I only have two elements i.e. taskUser and BranchID.
Please help me!! Thanks in advance !!!
So - it looks like you have an oddly organized task table, and as a result, you're going to have to do mildly weird things to query right. According to your description, a row in the task table contains either a studentId, an admissionId, an enquiryId, or an enquiryDetailId. This isn't an optimal way to do this...but I understand that sometimes you have to get by with what you have.
So, to get the names, you have to join to the source of the names...and assuming they're all over the place, in related tables, you could do something like:
select
t.StudentID,t.AdmissionID,t.EnquiryID,t.EnquiryDetailsID,x.FirstName,x.LastName
from Task t inner join Student s on t.StudentId = s.Id
union all
select
t.StudentID,t.AdmissionID,t.EnquiryID,t.EnquiryDetailsID,x.FirstName,x.LastName
from Task t inner join Admission a on t.AdmissionId = a.Id
union all
select
t.StudentID,t.AdmissionID,t.EnquiryID,t.EnquiryDetailsID,x.FirstName,x.LastName
from Task t inner join Enquiry e on t.EnquiryId = e.Id
union all
select
t.StudentID,t.AdmissionID,t.EnquiryID,t.EnquiryDetailsID,x.FirstName,x.LastName
from Task t inner join EnquiryDetail d on t.EnquiryDetailId = d.Id
...or, you can accomplish the same thing kinda inside-out:
select
t.StudentID,
t.AdmissionID,
t.EnquiryID,
t.EnquiryDetailsID,
x.FirstName,
x.LastName
from
Task t
inner join
(
select 's' source, Id, FirstName, LastName from Student union all
select 'a' source, Id, FirstName, LastName from Admission union all
select 'e' source, Id, FirstName, LastName from Enquiry union all
select 'd' source, Id, FirstName, LastName from EnquiryDetail
) as x
on
( t.StudentId = x.Id and x.source = 's' )
or
( t.AdmissionId = x.Id and x.source = 'a' )
or
( t.EnquiryId = x.Id and x.source = 'e' )
or
( t.EnquiryDetailId = x.Id and x.source = 'd' )
where
t.TaskUser=0 and t.BranchID=1
Use LEFT JOIN with COALESCE like this:
--not tested
select StudentID, AdmissionID, EnquiryID, EnquiryDetailsID,
COALESCE(s.name, e.name, d.name, ed.name) as name, etc.
from Task t
left join student s on s.id = t.studentID
left join Enquiry e on e.id = t.EnquiryID
left join Admission d on d.id = t.AdmissionID
left join EnquiryDetails ed on ed.id = t.EnquiryDetailsID
where TaskUser=0 and BranchID=1

SQL JOIN on one field or the other

Trying to order a family by father's name or, if there is no father, then the mother's name where the names are in a separate "person" table, something like:
SELECT DISTINCT family.myid FROM family
JOIN person
ON family.father_id = person.myid OR
family.mother_id = person.myid
ORDER BY person.surname,
person.given_name;
In this version, the families without fathers end up unsorted at the bottom. Would like families without fathers to appear in the order by the mother's name. Sqlite SQL will suffice.
Basically, you need a separate join for the fathers and the mothers:
select f.*
from family f left join
person d
on f.father_id = d.myid left join
person m
on f.mother_id = m.myid
order by (case when d.myid is null then m.surname else d.surname end),
(case when d.myid is null then m.given_name else d.given_name end);
Because a value could be missing, this should be a left join.
COALESCE should work
ORDER BY COALESCE(NULLIF(b.surname, ''), c.surname),
COALESCE(NULLIF(b.given_name, ''), c.given_name)

SQL join two columns if one is blank

I have one mapping table with different geographical resolutions. The user enters data into a table and I'm trying to join the data table to the mapping table with the condition that if you can join on zipcode value then join if zip isnt available then join on city.
Is is possible to do this within a join?
I would you something like this in your where clause:
WHERE a.ZipCode = ISNULL(b.ZipCode, a.ZipCode)
AND a.City = ISNULL(b.City,a.City)
Depending what your data looks like, you can use an OR in your JOIN clause
SELECT *
FROM a
JOIN b
ON a.Zip = b.Zip
OR a.City = b.City
or union two queries together
SELECT *
FROM a
JOIN b
ON a.Zip = b.Zip
UNION
SELECT *
FROM a
JOIN b
ON a.City = b.City
AND a.Zip <> b.Zip
WHERE (a.ZipCode IS NOT NULL AND b.ZipCode IS NOT NULL AND a.ZipCode = b.ZipCode)
OR (a.ZipCode IS NULL OR b.ZipCode IS NULL) AND a.City = b.City
Let's break it down:
(a.ZipCode IS NOT NULL AND b.ZipCode IS NOT NULL AND a.ZipCode = b.ZipCode)
First, we need to make sure that neither zipcode is NULL (unknown)
If we have 2 zipcodes, we can compare them, if not, we want to compare cities:
OR (a.ZipCode IS NULL OR b.ZipCode IS NULL) AND a.City = b.City
Here we make sure that at least one of the zipcodes is NULL (unknown) otherwise we would prefer to compare zipcodes. Then we compare Cities.
You could skip the NULL check in the second part and simplify the where clause to:
WHERE (a.ZipCode IS NOT NULL AND b.ZipCode IS NOT NULL AND a.ZipCode = b.ZipCode)
OR a.City = b.City
But in that case, it will not prioritize matching the ZipCode over the City, for example: If we have a city with ZipCode: 1000 and Name: Test, and another city with ZipCode: 2000, Name: Test.. it will now consider them to be a match.
When using an (INNER) JOIN, there is no difference wheter you specifiy the conditions in the JOIN or the WHERE clause, so these:
SELECT ... FROM a JOIN b ON a.id=b.a_id
SELECT ... FROM a, b WHERE a.id=b.a_id
Will produce the same result.

SQL select, 3 tables

How can I use select if I have 3 tables?
Tables:
school_subject(ID_of_subject, workplace, name)
student(ID_of_student, firstName, surname, adress)
writing(ID_of_action, ID_of_sbuject, ID_of_student)
I want to write the student's name and surname (alphabetically) who have workplace=home.
Is this possible? And how to alphabetize?
SELECT s.firstName, s.surname
FROM student S, school_subject P, writing Z
WHERE P.workplace = 'home'
AND P.ID_of_subject = Z.ID_of_subject
AND Z.ID_of_student = s.ID_of_student;
SELECT s.firstName, s.surname
FROM student S INNER JOIN writing Z
ON Z.ID_of_student = s.ID_of_student
INNER JOIN school_subject P
ON P.ID_of_subject = Z.ID_of_subject
WHERE P.workplace = 'home'
ORDER BY S.firstName, S.surname // Sort the list
To order alphabetically the result it is possible to use ORDER BY keyword. So your query becomes:
SELECT DISTINCT S.firstName, S.surname
FROM student S, school_subject P, writing Z
WHERE P.workplace = 'home' AND
P.ID_of_subject = Z.ID_of_subject AND
Z.ID_of_student = S.ID_of_student
ORDER BY S.surname, S.firstName
The DISTINCT keyword is necessary, because in writing table there are eventually more tuples given keys ID_of_subject and ID_of_student.
So this is necessary to avoid repeating firstName and surname many times.
Note that each student is identified by ID_of_student, not by firstName and surname, so as #danjok said use DISTINCT if you only want the name and surname.
If you want to select all students that satisfy your requirement (even if two or more students have the same firstName and surname), you should including ID_of_student on SELECT clause:
SELECT S.ID_of_student, S.firstName, S.surname
FROM student S
INNER JOIN writing W ON W.ID_of_student = S.ID_of_student
INNER JOIN school_subject P ON P.ID_of_subject = W.ID_of_subject
WHERE P.workplace = 'home'
ORDER BY S.firstName asc, S.surname asc