Multiple joins to the same table - sql

I have two tables Profile_Appointments and Profile.
Each record in the profile table has a Profile_key and a Profile_Type_Key in addition to other profile data - first name, last name, etc.
Rep's have Profile_Type 4, Customer's have Profile_Type 6
PROFILE TABLE
Profile_Key Profile_Type_key First_Name Last_Name
1234 4 John Smith
8765 6 Mike Jones
The Profile_Appointment table holds two records for each appointment one with the Rep's
Profile_Key the other with the customer's Profile_Key
Appointment_Key Profile_Key
10 1234
10 8765
The appointment_key refers to the Appointment table.
I need make a query that results in one record for each appointment and has both the rep and customer's data from the Profile table
THE RESULT I WANT
Appointment_Key Profile_Key Rep Profile_Key Customer
10 1234 8765
This is the query that's not working...
select appointment_key, p.profile_key as Rep, p2.profile_key as Customer, p.firs_name,p2.first_name from profile_appointment pa
join profile p
on p.profile_key = pa.profile_key
join profile p2
on p2.profile_key = pa.profile_key
where p.profile_type_key = '4' or p2.profile_type_key = '6'
What I get is:
Appointment_Key Rep Customer Rep Customer
10 1234 1234 John John
I can't figure out what I'm missing.
Thanks.

SELECT a1.appointment_key, a1.profile_key rep, a2.profile_key customer
FROM appointment a1
INNER JOIN profile p1 ON a1.profile_key = p1.profile_key AND p1.profile_type_key = 4
INNER JOIN appointment a2 ON a1.appointment_key = a2.appointment_key
INNER JOIN profile p2 ON a2.profile_key = p2.profile_key AND p2.profile_type_key = 6
EDIT: You might find nested selects more readable:
SELECT reps.appointment_key, reps.profile_key rep, customers.profile_key customer
FROM (
SELECT appointment.*
FROM appointment
INNER JOIN profile ON appointment.profile_key = profile.profile_key AND profile_type_key = 4
) reps
INNER JOIN (
SELECT appointment.*
FROM appointment
INNER JOIN profile ON appointment.profile_key = profile.profile_key AND profile_type_key = 6
) customers ON reps.appointment_key = customers.appointment_key

You need to satisfy both conditions so you need AND instead of OR
select pa.appointment_key, p.profile_key as Rep, p2.profile_key as Customer,
p.first_name as RepName,p2.first_name as CustomerName
from profile_appointment pa
join profile_appointment pa2 on pa.appointment_key = pa2.appointment_key
join profile p on p.profile_key = pa.profile_key and p.profile_type_key = '4'
join profile p2 on p2.profile_key = pa2.profile_key and p2.profile_type_key = '6'

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

Find records for students missing a class

I have two tables, one that flags a user as having passed a course, and a list of courses per job code. I'm trying to query to return a record for all users that are missing classes.
Here are the tables:
Attended
--------
empid jobcode classcode grade
555 1 100 A
555 1 101 A
444 2 200 A
JobClassCode
--------
jobcode classcode
1 100
1 101
1 102
2 100
2 200
3 300
3 301
I started with this query to find classes with a missing user:
select * from attended at
right outer join jobcodeclass jc on at.jobcode = jc.jobcode and at.classcode = jc.classcode
I then tried to take that to build a correlated subquery, but I don't see a way to return both the user ID and missing course ID:
select * from jobcodeclass oq where classcode in (select jc.classcode from attended at
right outer join jobcodeclass jc on at.jobcode = jc.jobcode
and at.classcode = jc.classcode and jc.jobcode = oq.jobcode
and oq.classcode = jc.classcode and empid is null)
Generate all the possible classes that each employee needs by joining on the jobcode. The see which ones the student attended:
select ej.empid, ej.jobcode, jss.classcode
from (select distinct empid, jobcode from attended) ej join
JobClassCode jcc
on jcc.jobcode = ej.jobcode left join
attended a
on a.empid= e.empid and a.jobcode = ej.jobcode and
a.classcode = jcc.classcode
where a.empid is null;
If you just need the employees, use select distinct ej.empid.

Most popular pairs of shops for workers from each company

I've got 2 tables, one with sales and one with companies:
Sales Table
Transaction_Id Shop_id Sale_date Client_ID
92356 24234 11.09.2018 12356
92345 32121 11.09.2018 32121
94323 24321 11.09.2018 21231
94278 45321 11.09.2018 42123
Company table
Client_ID Company_name
12345 ABC
13322 ABC
32321 BCD
22221 BCD
What I want to achieve is distinct count of Clients from each Company for each pair of shops(Clients who had at least 1 transaction in both of shops) :
Shop_Id_1 Shop_id_2 Company_name Count(distinct Client_id)
12356 12345 ABC 31
12345 14278 ABC 23
14323 12345 BCD 32
14278 12345 BCD 43
I think that I have to use self join, but my queries even with filter for one week is killing DB, any thoughts on that? I'm using Microsoft SQL server 2012.
Thanks
I think this is a self-join and aggregation, with a twist. The twist is that you want to include the company in each sales record, so it can be used in the self-join:
with sc as (
select s.*, c.company_name
from sales s join
companies c
on s.client_id = c.client_id
)
select sc1.shop_id, sc2.shop_id, sc1.company_name, count(distinct sc1.client_id)
from sc sc1 join
sc sc2
on sc1.client_id = sc2.client_id and
sc1.company_name = sc2.company_name
group by sc1.shop_id, sc2.shop_id, sc1.company_name;
I think there are some issues with your question. I interpreted it as such that the company table contains the shop ID's, not the ClienId's.
First you can create a solution to get the shops as rows for each company. Here I chose a maximum of 5 shops per company. Don't forget the semicolon in the previous statement before the cte's.
WITH CTE_Comp AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY CompanyName ORDER BY ShopID) AS RowNumb
FROM Company AS C
)
SELECT C1.ShopID,
C2.ShopID AS ShopID_2,
C3.ShopID AS ShopID_3,
C4.ShopID AS ShopID_4,
C5.ShopID AS ShopID_5,
C1.CompanyName
INTO ShopsByCompany
FROM CTE_Comp AS C1
LEFT JOIN CTE_Comp AS C2 ON C1.CompanyName= C2.CompanyName AND RowNumb = 2
LEFT JOIN CTE_Comp AS C2 ON C1.CompanyName= C3.CompanyName AND RowNumb = 3
LEFT JOIN CTE_Comp AS C2 ON C1.CompanyName= C4.CompanyName AND RowNumb = 4
LEFT JOIN CTE_Comp AS C2 ON C1.CompanyName= C5.CompanyName AND RowNumb = 5
WHERE C1.RowNumb = 1
After that, in a few steps, I think you could get the desired result:
WITH ClientsPerShop AS
(
SELECT ShopID,
COUNT (DISTINCT ClientID) AS TotalClients
FROM Sales
GROUP BY ShopID
)
, ClienstsPerCompany AS
(
SELECT CompanyName,
SUM (TotalClients) AS ClientsPerComp
FROM Company AS C
INNER JOIN ClientsPerShop AS CPS ON C.ShopID = CPS.ShopID
GROUP BY CompanyName
)
SELECT *
FROM ClienstsPerCompany AS CPA
INNER JOIN ShopsByCompany AS SBC ON SBC.CompanyName = CPA.CompanyName
Hopefully this will bring you closer to your solution, best of luck!

Listing data that share same data

I have three tables:
SN | Table Name | Primary Key | Foreign Key | Attribute
-----------------------------------------------------------------
1 | salesArea | salesAreaID | | areaDescription
2 | store | storeID | salesAreaID |
3 | salesPerson | salesPersonID | storeID | salesPersonName
I am trying to get the salesPersonName(s) of anyone that is in the same salesArea as a certain salesPerson.
For instance, a salesPerson named David works in storeID 23, which is in the NE salesArea.
There is another salesPerson named Bob working in storeID 34, which is also in the NE salesArea.
My query so far is...
SELECT salesPersonName,
salesPersonID,
st.salesAreaID,
areaDescription,
sp.storeId
FROM salesperson as sp, salesArea as sa, store as st
I am confused on how to get it get the storeID of 'David' and then retrieve what the salesArea is. Then with that salesArea, retrieve all salesPersons with that salesArea.
Salesperson table only gives the storeID, but then store gives the salesArea for the storeIDs.
Point in the right direction would be nice, join clause? some fancy where with a group by?
I am assuming:
Areas have stores
stores have salespersons
salespersons have names
So, we first want to find the area ID of the salesperson passed:
SELECT sp.SalesPersonID, st.SalesAreaID
FROM salesperson as sp
INNER JOIN store as st on sp.StoreID = st.StoreID
WHERE sp.SalesPersonName = 'David'
Now, we could use the results of this query to then execute the second part of the question, which is "get the other people in a given salesarea". If, for example, the above query returned SalesAreaID = 23 and SalesPersonID = 10, our second query would look like the following:
SELECT sp.SalesPersonID, sp.SalesPersonName, st.StoreID
FROM stores st
INNER JOIN salespersons sp on st.StoreID = sp.StoreID
WHERE st.SalesAreaID = 23 and sp.SalesPersonID <> 10
But, we can combine the two queries to form a single one that does the same thing:
SELECT sp2.SalesPersonID, sp2.SalesPersonName, st2.StoreID
FROM salesperson as sp
-- Join to stores to get the areaID of the salesperson passed (multiplicity 1)
INNER JOIN store as st on sp.StoreID = st.StoreID
-- self join to store to get all stores in that area (multiplicity 1 x Stores[area])
INNER JOIN store as st2 on st.SalesAreaID = st2.SalesAreaID
-- get all salespersons assigned to all stores in the area (multiplicity 1 x salespersons[area])
INNER JOIN salesperson as sp2
on st2.StoreID = sp2.StoreID
-- only return "other" salespersons
and sp2.SalesPersonID <> sp.SalesPersonID
WHERE sp.SalesPersonName = 'David'
Et voilĂ , we have a query giving all other salespersons in a the same area as a passed salesperson.

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