Join ruins the select - sql

I have two tables that contains People who are working at the company and their Employment information (so People is one table, Employment is another).
The People table contains information on where the person lives, emergency contact, phone number bla bla bla. The Employment table contains information on where he works, closest boss and more.
These tables have been corrupted and now contains a few duplicates by misstake. Now in both tables there is a Person id, but the employment id is only located in Employment. I want both numbers on all people that have been duplicated.
This works perfectly:
SELECT DISTINCT
pp.Personid,
pp.Firstname,
pp.Lastname,
pp.Address,
FROM People pp
JOIN People pp2
ON pp.Firstname = pp2.Firstname
AND pp.Lastname = pp2.Lastname
AND pp.Address = pp2.Address
AND pp.Personid <> pp2.Personid
ORDER BY pp.Firstname, pp.Lastname, pp.Personid
returning the following values (but does not include Employment number as you can see):
1001 Carl Johnsson Bigstreet 1
1002 Carl Johnsson Bigstreet 1
1003 Carl Johnsson Bigstreet 1
1010 Andrew Wilkinsson Smallstreet 2
1011 Andrew Wilkinsson Smallstreet 2
Now, to add the employment id I join in that table like this:
SELECT DISTINCT
pp.Personid,
e.Employmentid,
pp.Firstname,
pp.Lastname,
pp.Address,
FROM People pp
JOIN People pp2
ON pp.Firstname = pp2.Firstname
AND pp.Lastname = pp2.Lastname
AND pp.Address = pp2.Address
AND pp.Personid <> pp2.Personid
JOIN Employment e on pp.Personid = e.Personid
ORDER BY pp.Firstname, pp.Lastname, pp.Personid
And everything goes to h**l in a handbasket with the following result:
1001 1111 Carl Johnsson Bigstreet 1
1001 1111 Carl Johnsson Bigstreet 1
1001 1111 Carl Johnsson Bigstreet 1
1010 1234 Andrew Wilkinsson Smallstreet 2
1010 1234 Andrew Wilkinsson Smallstreet 2
As you can see I get both Personid and Employmentid but now I only get one of each (repeated the correct number of times) so I don't have all the different Personid and Employmentid in my list.
Why?
What happened with my join that crashed the party?

Ok, let's make some sample data;
CREATE TABLE #People (PersonID int, FirstName varchar(50), LastName varchar(50), Address1 varchar(50))
INSERT INTO #People (PersonID, FirstName, LastName, Address1)
VALUES
('1','Mike','Hunt','Cockburn Crescent')
,('2','Mike','Hunt','Cockburn Crescent')
,('3','Mike','Hunt','Cockburn Crescent')
,('4','Connie','Lingus','Dyke Close')
,('5','Connie','Lingus','Dyke Close')
,('6','Eric','Shun','Tickle Avenue')
,('7','Ivana','Humpalot','Bottom Street')
CREATE TABLE #Employment (PersonID int, EmploymentID int)
INSERT INTO #Employment (PersonID, EmploymentID)
VALUES
('1','10')
,('2','11')
,('3','12')
,('4','13')
,('5','14')
,('6','15')
,('7','16')
I'd do the first query differently, if you work out the duplicates in a sub-select it would be easier, you'll then be able to join to the employment table with no problems;
SELECT pp.PersonID
,em.EmploymentID
,pp.FirstName
,pp.LastName
,pp.Address1
FROM #People pp
JOIN (
SELECT FirstName
,LastName
,Address1
,COUNT(1) records
FROM #People
GROUP BY FirstName
,LastName
,Address1
HAVING COUNT(1) > 1
) pp2 ON pp.FirstName = pp2.FirstName
AND pp.LastName = pp2.LastName
AND pp.Address1 = pp2.Address1
LEFT JOIN #Employment em ON pp.PersonID = em.PersonID
Remember to clean up the temp tables;
DROP TABLE #People
DROP TABLE #Employment

I think you should try this
SELECT DISTINCT
ep.Personid,
ep.Employementid,
ep.FirstName,
ep.LastName,
ep.Address
FROM Person P join
(SELECT
pp.Personid,
e.Employmentid,
pp.Firstname,
pp.Lastname,
pp.Address,
from PP
JOIN Employment e on pp.Personid = e.Personid ) ep
on
P.Firstname = ep.Firstname
AND P.Lastname = ep.Lastname
AND P.Address = ep.Address
AND P.Personid <> ep.Personid
ORDER BY P.Firstname, P.Lastname, P.Personid
Sir please Check and reply to me

Your code should work and I am unable to reproduce your issue using data I have made up. The outcome you are seeing suggests to me that there are multiple person id's for carl johnsson in the employment table and that the employmentid is different - even though it looks the same in the output.
Can you supply your table definitions and sample data?

Related

Common records from SQL table

I have 3 tables called CompanyInfo , IDInfo and PersonalInfo.
The objective is to fetch P.[First Name], P.[Last Name], I.PAN, C.DOB.
I can put left join or right join to cater this but the sequence of tables may change as it is an input to some tool and user may enter the table names in any sequence like user1 mentions "CompanyInfo, IDInfo, PersonalInfo", user2 mentions "IDInfo, PersonalInfo, CompanyInfo" and so on.
Table data is as below, Is there a way I can fetch the data through single SQL query (may be union). Say for if I want to fetch the data for ID = 4, I should get:
FirstName LastName PAN DOB
User Four UAN44444 NULL
If ID = 3
FirstName LastName PAN DOB
User THree NULL 1987-12-08
CompanyInfo Table
ID Company OfficialEmail EmpID DOB Department Joining Date Status
1 RBS userone#rbs.co.uk UK222222 1980-11-15 HR 2012-11-20 Inactive
3 Infosys userthree#infy.com IN333333 1987-12-08 Admin 2016-08-18 Inactive
5 IBM userfive#us.ibm.com US55555 1986-03-26 Finance 2014-06-26 Active
10 Samsung userten#samsung.com SK101010 1988-04-04 Admin 2013-04-07 Active
IDInfo Table
ID UAN TIN PAN
2 UAN22222 TIN222222 PAN22222
4 UAN44444 TIN444444 PAN44444
5 UAN55555 TIN555555 PAN55555
PersonalInfo Table
ID FirstName LastName PhoneNumber City Address ZipCode Email
1 User One +44-7432564125 London United Kingdom RG231FDT userone#gmail.com
2 User Two +91-987654321 New Delhi India 110006 usertwo#gmail.com
3 User Three +44-782136425 Guildford United Kingdom GUI74DS userthree#gmail.com
4 User Four +1-230156428 Atlanta United States GA 30337 userfour#gmail.com
5 User Five +1-650324152 Houston United States TX 77077 userfive#gmail.com
6 User Six +91-8885552223 Mumbai India 400012 usersix#gmail.com
7 User Seven +91-9998887771 Bangalore India 560021 userseven#gmail.com
Simple LEFT JOIN will resolve your problem.
SELECT P.FirstName, P.LastName, I.PAN, C.DOB
FROM PersonalInfo P
LEFT JOIN IDInfo I ON I.ID = P.ID
LEFT JOIN CompanyInfo C ON C.ID = P.ID
WHERE P.ID = #YourInput
You can use left join and right join:
SELECT P.FirstName, P.LastName, I.PAN, C.DOB. FROM IDInfo i LEFT JOIN PersonalInfo p ON p.ID = i.ID LEFT JOIN CompanyInfo c ON c.ID = i.ID WHERE i.ID = 3
Creating table variables to hold data of 3 table
DECLARE #CompanyInfo TABLE
(ID INT,Company NVARCHAR(50),OfficialEmail NVARCHAR(50),EmpID NVARCHAR(50),DOB NVARCHAR(50),Department NVARCHAR(50),[Joining Date] NVARCHAR(50),[Status] NVARCHAR(50))
DECLARE #IDInfo TABLE
(ID INT,UAN NVARCHAR(50),TIN NVARCHAR(50),PAN NVARCHAR(50))
DECLARE #PersonalInfo TABLE
(ID INT,FirstName NVARCHAR(50),LastName NVARCHAR(50),PhoneNumber NVARCHAR(50),City NVARCHAR(50),[Address] NVARCHAR(50),ZipCode NVARCHAR(50),Email NVARCHAR(50))
Populate data to the tables
INSERT INTO #CompanyInfo VALUES
(1,'RBS','userone#rbs.co.uk','UK222222','1980-11-15','HR','2012-11-20','Inactive'),
(3,'Infosys','userthree#infy.com','IN333333','1987-12-08','Admin','2016-08-18','Inactive'),
(5,'IBM','userfive#us.ibm.com','US55555','1986-03-26','Finance','2014-06-26','Active'),
(10,'Samsung','userten#samsung.com','SK101010','1988-04-04','Admin','2013-04-07','Active')
INSERT INTO #IDInfo VALUES
(2,'UAN22222','TIN222222','PAN22222'),
(4,'UAN44444','TIN444444','PAN44444'),
(5,'UAN55555','TIN555555','PAN55555')
INSERT INTO #PersonalInfo VALUES
(1,'User','One','+44-7432564125','London','United Kingdom','RG231FDT','userone#gmail.com'),
(2,'User','Two','+91-987654321','New Delhi','India','110006','usertwo#gmail.com'),
(3,'User','Three','+44-782136425','Guildford','United Kingdom','GUI74DS','userthree#gmail.com'),
(4,'User','Four','+1-230156428','Atlanta','United States','GA 30337','userfour#gmail.com'),
(5,'User','Five','+1-650324152','Houston','United States','TX 77077','userfive#gmail.com'),
(6,'User','Six','+91-8885552223','Mumbai','India','400012','usersix#gmail.com'),
(7,'User','Seven','+91-9998887771','Bangalore','India','560021','userseven#gmail.com')
Query to obtain the required output
SELECT
P.FirstName,
P.LastName,
I.PAN,
C.DOB
FROM
#PersonalInfo P LEFT JOIN #IDInfo I ON I.ID = P.ID
LEFT JOIN #CompanyInfo C ON C.ID = P.ID
WHERE
P.ID = 4
OUTPUT :-
FirstName LastName PAN DOB
User Four PAN44444 NULL

SQL Query: Using AND/OR in the WHERE clause

I'm currently working on a project where I have a list of dental patients, and I'm supposed to be displaying all the patients who have two specific procedure codes attached to their profiles. These patients must have BOTH procedure codes, not one or the other. At first I thought I could accomplish this by using a basic AND statement in my WHERE clause, like so.
SELECT [stuff]
FROM [this table]
WHERE ProcedureCode = 'ABC123' AND ProcedureCode = 'DEF456';
The query of course returns nothing because the codes are entered individually and you can't have both Procedure 1 and Procedure 2 simultaneously.
I tried switching the "AND" to "OR" just out of curiosity. Of course I'm now getting results for patients who only have one code or the other, but the patients who have both are showing up too, and the codes are displayed as separate results, like so:
Patient ID Last Name First Name Code Visit Date
1111111 Doe Jane ABC123 11-21-2015
5555555 Smith John ABC123 12-08-2015
5555555 Smith John DEF456 12-08-2015
My SQL is pretty rusty these days. I'm trying to think of a way to filter out patients like Jane Doe and only include patients like John Smith who have both procedure codes. Ideas?
ADDING INFO BASED ON CHRISTIAN'S ANSWER:
This is what the updated query looks like:
SELECT PatientID, LastName, FirstName, Code, VisitDate
FROM VisitInfo
WHERE PatientID IN
(
SELECT PatientID
FROM VisitInfo
WHERE Code = 'ABC123' OR Code = 'DEF456'
GROUP BY PatientID
HAVING COUNT(*) > 1
)
AND (Code = 'ABC123' OR Code = 'DEF456');
So I'm still getting results like the following where a patient is only showing one procedure code but possibly multiple instances of it:
Patient ID Last Name First Name Code Visit Date
1111111 Doe Jane ABC123 11-02-2015
1111111 Doe Jane ABC123 11-21-2015
5555555 Smith John ABC123 12-08-2015
5555555 Smith John DEF456 12-08-2015
5555555 Smith John ABC123 12-14-2015
9999999 Jones Mike DEF456 11-22-2015
9999999 Jones Mike DEF456 12-06-2015
Even though Jane Doe and Mike Jones have 2 results, they're both the same code, so we don't want to include them. And even though John Smith still has 2 of the same code, his results also include both codes, so we want to keep him and other patients like him.
ANOTHER UPDATE:
I just learned that I now need to include a few basic demographic details for the patients in question, so I've joined my VisitInfo table with a PatientInfo table. The updated query looks like this:
SELECT v.PatientID, v.LastName, v.FirstName, v.Code, v.VisitDate, p.DateOfBirth, p.Gender, p.PrimaryPhone
FROM VisitInfo v JOIN PatientInfo p ON v.PatientID = p.PatientID
WHERE v.PatientID IN
(
SELECT PatientID
FROM VisitInfo
WHERE Code = 'ABC123' OR Code = 'DEF456'
GROUP BY PatientID
HAVING COUNT(*) > 1
)
AND (Code = 'ABC123' OR Code = 'DEF456');
I wasn't sure if the new JOIN would affect anyone's answers...
SELECT *
FROM TABLE
WHERE Patient_ID IN
(
SELECT Patient_ID
FROM TABLE
WHERE Code = 'ABC123' OR Code = 'DEF456'
GROUP BY Patient_ID
HAVING COUNT(*) = 2
)
AND (Code = 'ABC123' OR Code = 'DEF456')
UPDATE 1:
As a patient can have multiple ´procedure codes´, this way will work better:
SELECT *
FROM TABLE T1
WHERE EXISTS (SELECT 1
FROM TABLE T2
WHERE T1.Patient_ID = T2.Patient_ID
AND T2.Code = 'ABC123')
AND EXISTS (SELECT 1
FROM TABLE T2
WHERE T1.Patient_ID = T2.Patient_ID
AND T2.Code = 'DEF456')
AND T1.Code IN ('ABC123','DEF456')
There's a bunch of ways to skin this particular cat - here's another one:
WITH ABC123 AS (SELECT DISTINCT PATIENTID
FROM VISITINFO
WHERE PROCEDURECODE = 'ABC123'),
DEF456 AS (SELECT DISTINCT PATIENTID
FROM VISITINFO
WHERE PROCEDURECODE = 'DEF456')
SELECT v.PATIENTID, v.LASTNAME, v.FIRSTNAME, v.PROCEDURECODE, v.VISITDATE,
p.DateOfBirth, p.Gender, p.PrimaryPhone
FROM VISITINFO v
INNER JOIN ABC123 a
ON a.PATIENTID = v.PATIENTID
INNER JOIN DEF456 d
ON d.PATIENTID = v.PATIENTID
INNER JOIN PatientInfo p
ON v.PatientID = p.PatientID
WHERE v.PROCEDURE_CODE IN ('ABC123', 'DEF456')
ORDER BY v.PATIENTID, v.VISITDATE, v.PROCEDURECODE;
What we're doing here is using a couple of CTE's to get the PATIENTID's for each patient who has the the procedures in question performed. We then start with all records in VISITINFO and inner join those with the two CTE's. Because an INNER JOIN requires that matching information exist in the tables on both sides of the join this has the effect of retaining only the visits which match the information in both of the CTE's, based on the join criteria which in this case is the PATIENTID.
Best of luck.
Select the records with the two codes, but use COUNT OVER to count the distinct codes per patient. Only keep those records with a count of 2 codes for the patient, i.e. patients with both codes.
select patient_id, last_name, first_name, code, visit_date
from
(
select mytable.*, count(distinct code) over (partition by patient_id) as cnt
from mytable
where code in ('ABC123','DEF456')
) data
where cnt = 2;

SQL Server : select values from table if values in referenced table is equal

I have two tables: units
name | departament | count | unit_manager
unit_1 | dep_1 | 100 | nick
unit_2 | dep_1 | 200 | john
and employees:
full_name | unit | position | salary | phone_number
mary | unit_2 | clerk | 100 | 123-45
nick | unit_2 | manager | 200 | 24-56
Employees table has a foreign key unit that refers to the name column in units table.
I need to write a SQL query to select all units, in which all employees, except unit manager have the same position.
How can I do this?
All that I can find out - is the way how I can select units that have similar values from employees table
SELECT u.*
FROM employee s1, employee s2
INNER JOIN units u ON s2.unit = u.name
WHERE s1.full_name <> s2.full_name
AND s1.position = s2.position
Try this:
SELECT u.*
FROM employee s1
INNER JOIN employee s2 ON s1.urID = s2.urID
INNER JOIN units u ON s2.unit = u.name
WHERE s1.full_name <> s2.full_name
AND s1.position = s2.position
If you are not returning the data you want try:
SELECT u.*
FROM employee s1
INNER JOIN employee s2 ON s1.urID = s2.urID
LEFT OUTER JOIN units u ON s2.unit = u.name
WHERE s1.full_name <> s2.full_name
AND s1.position = s2.position
To get a better handle on how to do join correctly see Jeff Atwoods article on Join types
I had to take a bit of a guess as to what you wanted, but try this...
Your test data was insufficient to test the results, so I added some rows...
This will return unit_1, because all the employees in unit_1 are clerks, but not unit_2, because 1 of the employees in unit_2 is 'not a clerk' (excluding the manager)
create table #unit (name varchar(20), departament varchar(20), counta varchar(20), unit_manager varchar(20))
insert into #unit values('unit_1','dep_1','100','nick');
insert into #unit values('unit_2','dep_1','200','john');
create table #employees(full_name varchar(20), unit varchar(20), position varchar(20))
insert into #employees values('nick','unit_1','manager');
insert into #employees values('mary1.1','unit_1','clerk');
insert into #employees values('mary1.2','unit_1','clerk');
insert into #employees values('mary1.3','unit_1','clerk');
insert into #employees values('mary1.4','unit_1','clerk');
insert into #employees values('john','unit_2','manager');
insert into #employees values('mary2.1','unit_2','clerk');
insert into #employees values('mary2.2','unit_2','not a clerk');
insert into #employees values('mary2.3','unit_2','clerk');
insert into #employees values('mary2.4','unit_2','clerk');
SELECT u.name AS Unit, COUNT(*) as Employees, COUNT(DISTINCT e1.Position) as Positions
FROM #unit u
JOIN #employees e1 on u.name = e1.unit AND u.unit_manager <> e1.full_name
GROUP by u.name
HAVING COUNT(DISTINCT e1.Position) = 1
if you want to see a list of all the units, and how many distinct positions they have, do this
SELECT u.name AS Unit, COUNT(*) as Employees, COUNT(DISTINCT e1.Position) as Positions
FROM #unit u
JOIN #employees e1 on u.name = e1.unit AND u.unit_manager <> e1.full_name
GROUP by u.name

Complicated table join

I thought I had a good grasp on table joins but there is one problem here I can't figure out.
I am trying to track the progress of students on specifically required courses. Some students are required to complete an exact list of courses before further qualification.
Tables (simplified):
students
--------
id INT PRIMARY KEY
name VARCHAR(50)
student_courses
---------------
student_id INT PRIMARY KEY
course_id TINYINT PRIMARY KEY
course_status TINYINT (Not done, Started, Completed)
steps_done TINYINT
total_steps TINYINT
date_created DATETIME
date_modified DATETIME
courses
-------
id TINYINT PRIMARY KEY
name VARCHAR(50)
I want to insert a list of required courses, for example 5 different courses in the courses table and then select a specific student and get list of all the courses required, whether a row exists for that course in the student_courses table or not.
I guess I could insert all rows from the courses table in the student_courses table for each student, but I don't want that because not all students need to do these courses. And what if new courses are added later.
I just want a result which is something like this:
students table:
id name
--- ------------------
1 George Smith
2 Dana Jones
3 Maria Cobblestone
SELECT * FROM students (JOIN bla bla bla - this is the point where I'm lost...)
WHERE students.id = 1
Result:
id name course_id courses.name course_status steps_done
--- ------------------ --------- ------------ ------------- ----------
1 George Smith 1 Botany Not started 0
1 George Smith 2 Biology NULL NULL
1 George Smith 3 Physics NULL NULL
1 George Smith 4 Algebra Completed 34
1 George Smith 5 Sewing Started 2
If the course_status or steps_done is NULL it means that no row exists for this student for this course in the student_courses table.
The idea is then using this in MS Access (or some other system) and have the row automatically inserted in the student_courses table once you enter a value in the NULL field.
You can't just use an outer join to do this, you need to create a list of all students/classes combinations that you're interested in first, then use that list in a LEFT JOIN. Can be done in a cte/subquery using CROSS JOIN:
;WITH cte AS (SELECT DISTINCT s.id Student_ID
,s.name
,c.id Course_ID
,c.name Class_Name
FROM Students s
CROSS JOIN Courses c)
SELECT cte.*,sc.status
FROM cte
LEFT JOIN student_courses sc
ON cte.course_id = sc.course_id
Can also use a subquery if needs to be done in Access (not 100% on syntax in Access):
SELECT sub.*,sc.status
FROM (SELECT DISTINCT s.id Student_ID
,s.name
,c.id Course_ID
,c.name Class_Name
FROM Students s
CROSS JOIN Courses c
) AS sub
LEFT JOIN student_courses sc
ON sub.course_id = sc.course_id
Demo: SQL Fiddle
You want a left outer join. The first table is from the courses table and is used for the required courses (defined in the where clause).
select s.id, s.name, c.id, c.name, c.course_status, c.steps_done
from (courses as c left join
student_courses as sc
on sc.course_id = c.id and
sc.student_id = 1
) left join
students as s
on sc.student_id = s.id
where c.id in (<list of required courses>)
order by s.id, c.id;
I think I have all the "Access"isms in there.
Actually, the above will be missing the student name when s/he is missing a course. The following is more correct:
select s.id, s.name, c.id, c.name, c.course_status, c.steps_done
from (courses as c left join
student_courses as sc
on sc.course_id = c.id and
sc.student_id = 1
) cross join
students as s
on s.id = 1
where c.id in (<list of required courses>)
order by s.id, c.id;

In SQL how do I write a query to return 1 record from a 1 to many relationship?

Let's say I have a Person table and a Purchases table with a 1 to many relationship. I want to run a single query that returns this person and just their latest purchase. This seems easy but I just can't seem to get it.
select p.*, pp.*
from Person p
left outer join (
select PersonID, max(PurchaseDate) as MaxPurchaseDate
from Purchase
group by PersonID
) ppm
left outer join Purchase pp on ppm.PersonID = pp.PersonID
and ppm.MaxPurchaseDate = pp.PurchaseDate
where p.PersonID = 42
This query will also show the latest purchase for all users if you remove the WHERE clause.
Assuming you have something like a PurchaseDate column and want a particular person (SQL Server):
SELECT TOP 1 P.Name, P.PersonID, C.PurchaseDescription FROM Persons AS P
INNER JOIN Purchases AS C ON C.PersonID = P.PersonID
WHERE P.PersonID = #PersonID
ORDER BY C.PurchaseDate DESC
Many Databases preform the "Limit or Top" command in different ways. Here is a reference http://troels.arvin.dk/db/rdbms/#select-limit and below are a few samples
If using SQL Server
SELECT TOP 1
*
FROM Person p
INNER JOIN Purchases pc on pc.PersonID = P.PersonID
Order BY pc.PurchaseDate DESC
Should work on MySQL
SELECT
*
FROM Person p
INNER JOIN Purchases pc on pc.PersonID = P.PersonID
Order BY pc.PurchaseDate DESC
LIMIT 1
Strictly off the top of my head!...If it's only one record then...
SELECT TOP 1 *
FROM Person p
INNER JOIN Purchases pu
ON p.ID = p.PersonId
ORDER BY pu.OrderDate
WHERE p.ID = *thePersonYouWant*
otherwise...
SELECT TOP 1 *
FROM Person p
INNER JOIN
(
SELECT TOP 1 pu.ID
FROM Purchases pu
ON pu.PersonID = p.Id
ORDER BY pu.OrderDate
) sq
I think! I haven't got access to a SQL box right now to test it on.
Without knowing your structure at all, or your dbms, you would order the results descending by the purchase date/time, and return only the first joined record.
Try TOP 1 With an order by desc on date. Ex:
CREATE TABLE #One
(
id int
)
CREATE TABLE #Many
(
id int,
[date] date,
value int
)
INSERT INTO #One (id)
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3
INSERT INTO #Many (id, [date], value)
SELECT 1, GETDATE(), 1 UNION ALL
SELECT 1, DATEADD(DD, 1 ,GETDATE()), 3 UNION ALL
SELECT 1, DATEADD(DD, -1 ,GETDATE()), 0
SELECT TOP 1 *
FROM #One O
JOIN #Many M ON O.id = M.id
ORDER BY [date] DESC
If you want to select the latest purchase for each person, that would be:
SELECT PE.ID, PE.Name, MAx(PU.pucrhaseDate) FROM Persons AS PE JOIN PURCHASE as PU ON PE.ID = PU.Person_ID
If you want to have all persons also those who have no purchases, you need to use LEFT JOIN.
I think you need one more table called Items for example.
The PERSONS table would uniquely define each person and all their attributes, while the ITEMS table would uniquely define each items and their attributes.
Assume the following:
Persons |Purchases |Items
PerID PerName |PurID PurDt PerID ItemID |ItemID ItemDesc ICost
101 Joe Smith |201 101107 101 301 |301 Laptop 500
|202 101107 101 302 |302 Desktop 699
102 Jane Doe |203 101108 102 303 |303 iPod 199
103 Jason Tut |204 101109 101 304 |304 iPad 499
|205 101109 101 305 |305 Printer 99
One Person Parent may tie to none, one or many Purchase Child.
One Item Parent may tie to none, one or many Purchase Child.
One or more Purchases Children will tie to one Person Parent, and one Item Parent.
select per.PerName as Name
, pur.PurDt as Date
, itm.ItemDesc as Item
, itm.ICost as Cost
from Persons per
, Purchases pur
, Items itm
where pur.PerID = per.PerID -- For that Person
and pur.ItemID = itm.ItemID -- and that Item
and pur.PurDt = -- and the purchase date is
( Select max(lst.PurDt) -- the last date
from Purchases lst -- purchases
where lst.PerID = per.PerID ) -- for that person
This should return:
Name Date Item Cost
Joe Smith 101109 Ipad 499
Joe Smith 101109 Printer 99
Jane Doe 101108 iPod 199