sql - retrieve only first record using join - sql-server

I have three tables: Employee, Contact, Employee_Contact
Employee:
EMP_ID, FIRST_NAME, LAST_NAME...
CONTACT:
CONTACT_ID, TELEPHONE, MOBILE...
EMPLOYEE_CONTACT:
EMP_ID, CONTACT_ID
Employee and Contact table is mapped with Employee_Contact table.
So I want to retrieve all Employees with Employee's only first contact, I tried this query but it is retrieving all the contacts related to that employee. Please check below query and let me know in case of any mistakes.
SELECT EMP.FirstName
,DEPT.Description AS Department
,CNT.Mobile
,CNT.Telephone
FROM MstEmp EMP
LEFT OUTER JOIN Department DEPT ON EMP.DeptID = DEPT.Department_ID
LEFT OUTER JOIN Employee_Contact EC ON EMP.EMP_ID = EC.EMP_ID
OUTER APPLY (
SELECT TOP 1 *
FROM Contact CONT
WHERE EC.Contact_ID = CONT.Contact_ID
) CNT

Use Top keyword retrieve top one record.
SELECT TOP 1 EMP.FirstName, DEPT.Description AS Department, CNT.Mobile,
CNT.Telephone
FROM MstEmp EMP
LEFT OUTER JOIN Department DEPT on EMP.DeptID = DEPT.Department_ID
LEFT OUTER JOIN Employee_Contact EC ON EMP.EMP_ID = EC.EMP_ID
OUTER APPLY(SELECT TOP 1 * FROM Contact CONT WHERE EC.Contact_ID =
CONT.Contact_ID)CNT

Try this one:
SELECT EMP.FirstName
,DEPT.Description AS Department
,t.Mobile
,t.Telephone
FROM MstEmp EMP
LEFT OUTER JOIN Department DEPT ON EMP.DeptID = DEPT.Department_ID
LEFT OUTER JOIN Employee_Contact EC ON EMP.EMP_ID = EC.EMP_ID
LEFT OUTER JOIN Contact CONT ON EC.Contact_ID = (
SELECT MIN(c.Contact_ID) c_id
FROM Contact c
WHERE CONT.Contact_ID = c.Contact_ID
) t

Give it a try:
select * from (
select E.[emp_id],
E.[first_name],
E.[last_name],
C.*,
RANK() over (partition by (E.[emp_id], E.[first_name], E.[last_name]) order by (select null)) [RN]
from Employee [E]
join employee_contact [EC] on E.[emp_id] = EC.[emp_id]
join contact [C] on EC.[contact_id] = C.[contact_id]
) A where RN = 1

You can use ROW_NUMBER() and get the first record for all the employees and then join it with the other tables.
SELECT EMP.FirstName,
DEPT.Description AS Department,
c.Mobile,
c.Telephone
FROM MstEmp EMP
LEFT OUTER JOIN Department DEPT ON EMP.DeptID = DEPT.Department_ID
INNER JOIN (
SELECT *,ROW_NUMBER() OVER (PARTITION BY EMP_ID ORDER BY EMP_ID) AS RN
FROM Employee_Contact ) EC ON EMP.EMP_ID = EC.EMP_ID
INNER JOIN Contact c ON c.Contact_ID=EC.Contact_ID
WHERE EC.RN=1

Related

Rookie mistake with join

I believe I have made a rookie mistake with the last join but I cannot see what it is. The code worked until I needed to add the last entry recorded in the DONCOMMS table. (This table contains all the communications for each donor) Now I get 3 duplicate lines for every entry from the first table.
The original report shows me all the donor pledges from DON0010 and links the area description and donor from the tables AREA and DON0001 respectively. I now want to bring out the latest communication found in DONCOMMS for each pledge found in DON0010.
select
pl.[PLEDGE_NO]
,d1.[DONOR_NO]
,rtrim(pl.DONOR) DONOR
,d1.[CONTACT_NAME]
,d1.[AREA1]
,a1.[DESCRIPTION]
,pl.[AMOUNT]
,pl.[AMOUNT_RECVD]
,pl.[TYPE]
,ISNULL(t2.[COMM_TYPE],'') AS COMM_TYPE
,ISNULL(t2.[REFERENCE],'') AS REFERENCE
,ISNULL(t2.[DETAIL],'') AS DETAIL
,replace(isnull(convert(varchar, t2.[DATE], 103),''),'01/01/1753','') COMMS_DATE
,ISNULL(t2.[CONTACT],'') AS CONTACT
from don0010 pl
left join DON0001 d1 on pl.[DONOR] = d1.[DONOR]
left join AREA1 a1 on d1.[AREA1] = a1.[AREA]
**left join (Select * from doncomms dc where dc.DATE = (SELECT MAX(DATE) FROM doncomms WHERE DONOR_NO = dc.DONOR_NO) and ENTITY = 'C') t2
on t2.[DONOR_NO] = d1.[DONOR_NO]**
select
pl.[PLEDGE_NO]
,d1.[DONOR_NO]
,rtrim(pl.DONOR) DONOR
,d1.[CONTACT_NAME]
,d1.[AREA1]
,a1.[DESCRIPTION]
,pl.[AMOUNT]
,pl.[AMOUNT_RECVD]
,pl.[TYPE]
,ISNULL(t2.[COMM_TYPE],'') AS COMM_TYPE
,ISNULL(t2.[REFERENCE],'') AS REFERENCE
,ISNULL(t2.[DETAIL],'') AS DETAIL
,replace(isnull(convert(varchar, t2.[DATE], 103),''),'01/01/1753','') COMMS_DATE
,ISNULL(t2.[CONTACT],'') AS CONTACT
from don0010 pl
left join DON0001 d1 on pl.[DONOR] = d1.[DONOR]
left join AREA1 a1 on d1.[AREA1] = a1.[AREA]
outer apply
(
select top 1 with ties dc.*
from doncomms dc
where dc.DONOR_NO = d1.DONOR_NO and dc.ENTITY = 'C'
order by dc.DATE desc
) t2
The problem usually is you have several rows with DATE = MAX(DATE)
Try this query alone to find out if same DONOR_NO appear more than once
Select DONOR_NO, count(*)
from doncomms dc
where dc.DATE = (SELECT MAX(DATE)
FROM doncomms
WHERE DONOR_NO = dc.DONOR_NO)
and ENTITY = 'C'
GROUP BY DONOR_NO
In that case is better if you use a CTE syntaxis and ROW_NUMBER()
WITH cte as (
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY DONOR_NO ORDER BY DATE DESC) as rn
FROM doncomms
WHERE ENTITY = 'C'
) T
WHERE T.rn = 1
)
select *
from don0010 pl
left join DON0001 d1 on pl.[DONOR] = d1.[DONOR]
left join AREA1 a1 on d1.[AREA1] = a1.[AREA]
left join cte as t2
on t2.[DONOR_NO] = d1.[DONOR_NO]
Thank you Juan, I just needed to add the field names to run as a view.
WITH cte as (
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY DONOR_NO ORDER BY DATE DESC) as rn
FROM doncomms
WHERE ENTITY = 'C'
) T
WHERE T.rn = 1
)
select pl.[PLEDGE_NO]
,d1.[DONOR_NO]
,rtrim(pl.DONOR) DONOR
,d1.[CONTACT_NAME]
,d1.[AREA1]
,a1.[DESCRIPTION]
,pl.[AMOUNT]
,pl.[AMOUNT_RECVD]
,pl.[TYPE]
,pl.[SOURCE]
,d1.[POST_CODE] as DONOR_POSTCODE
,replace(isnull(convert(varchar, pl.PLEDGE_DATE, 103),''),'01/01/1753','') PLEDGE_DATE
,replace(isnull(convert(varchar, pl.[EVENT_DATE], 103),''),'01/01/1753','') EVENT_DATE
,rtrim(pl.DESCRIPTION1) DESCRIPTION1
,rtrim(pl.DESCRIPTION2) DESCRIPTION2
,pl.[COMPLETE]
,pl.[APPLICATION]
,rtrim(pl.COMMENT1) COMMENT1
,rtrim(pl.COMMENT2) COMMENT2
,pl.[MEMO]
,ISNULL(t2.[ENTERED_BY] , '') AS ENTERED_BY
,ISNULL(t2.[COMM_TYPE],'') AS COMM_TYPE
,ISNULL(t2.[REFERENCE],'') AS REFERENCE
,ISNULL(t2.[DETAIL],'') AS DETAIL
,replace(isnull(convert(varchar, t2.[DATE], 103),''),'01/01/1753','') COMMS_DATE
,ISNULL(t2.[CONTACT],'') AS CONTACT
from don0010 pl
left join DON0001 d1 on pl.[DONOR] = d1.[DONOR]
left join AREA1 a1 on d1.[AREA1] = a1.[AREA]
left join cte as t2
on t2.[DONOR_NO] = d1.[DONOR_NO]

select count over partition by

I am learning window functions in sql server. I am using AdventrueWorks2012 database for practice. I want to calculate total number of sales and purchases for each item in the store.
The classic solution can be like
SELECT ProductID,
Quantity,
(SELECT Count(*)
FROM AdventureWorks.Purchasing.PurchaseOrderDetail
WHERE PurchaseOrderDetail.ProductID = p.ProductID) TotalPurchases,
(SELECT Count(*)
FROM AdventureWorks.Sales.SalesOrderDetail
WHERE SalesOrderDetail.ProductID = p.ProductID) TotalSales
FROM (SELECT DISTINCT ProductID,
Quantity
FROM AdventureWorks.Production.ProductInventory) p
Trying to convert to window functions gives me wrong results:
SELECT DISTINCT d.ProductID,
Quantity,
Count(d.ProductID)
OVER(
PARTITION BY d.ProductID) TotalPurchases,
Count(d2.ProductID)
OVER(
PARTITION BY d2.ProductID) TotalSales
FROM (SELECT DISTINCT ProductID,
Quantity
FROM AdventureWorks.Production.ProductInventory) p
INNER JOIN AdventureWorks.Purchasing.PurchaseOrderDetail d
ON p.ProductID = d.ProductID
INNER JOIN AdventureWorks.Sales.SalesOrderDetail d2
ON p.ProductID = d2.ProductID
ORDER BY d.ProductID
Why this is wrong? How can I correct it?
You should change INNER JOIN to LEFT JOIN
Because when you inner join, result will miss productid which from ProductInventory table does not have PurchaseOrderDetail or SalesOrderDetail.

Get Max(id) from one to many table

I know this question was asked many times but I was tring and trying without success
I have one to many relationship between two tables and some more inner-joins to get more data.
Here is my query:
SELECT
ShopOffer.OfferID,
ShopOffer.OfferMessage,
Shop.ID,
Shop.Name,
Shop.Phone,
[User].Name,
[User].UserID,
ShopOfferStatus.Name AS StatusName,
BlockedShopInUser.IsBlocked
FROM
ShopOffer
INNER JOIN ShopOfferStatus ON ShopOffer.ShopOfferStatusID = ShopOfferStatus.ShopOfferStatusID
INNER JOIN Shop ON ShopOffer.ShopID = Shop.ShopID
INNER JOIN UserRequest ON ShopOffer.UserRequestID = UserRequest.UserRequestID
INNER JOIN [User] ON UserRequest.UserID = [User].UserID
INNER JOIN BlockedShopInUser ON Shop.ShopID = BlockedShopInUser.ShopID AND [User].UserID = BlockedShopInUser.UserID
Each shop can create many offers. In that query I would like to get only the last offer for each shop.
Thanks.
Here is a way:
;WITH LastShopOffer AS
(
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY ShopID ORDER BY OfferID DESC)
FROM ShopOffer
)
SELECT
SO.OfferID,
SO.OfferMessage,
S.ID,
S.Name,
S.Phone,
U.Name,
U.UserID,
SOS.Name AS StatusName,
B.IsBlocked
FROM ( SELECT *
FROM LastShopOffer
WHERE RN = 1) SO
INNER JOIN ShopOfferStatus SOS
ON SO.ShopOfferStatusID = SOS.ShopOfferStatusID
INNER JOIN Shop S
ON SO.ShopID = S.ShopID
INNER JOIN UserRequest UR
ON SO.UserRequestID = UR.UserRequestID
INNER JOIN [User] U
ON UR.UserID = U.UserID
INNER JOIN BlockedShopInUser B
ON S.ShopID = B.ShopID
AND U.UserID = B.UserID;
I think you have to start with Shop and then perform a CROSS APPLY on the TOP 1 record from ShopOffer:
SELECT
ShopOffer.OfferID,
ShopOffer.OfferMessage,
Shop.ID,
Shop.Name,
Shop.Phone,
[User].Name,
[User].UserID,
ShopOfferStatus.Name AS StatusName,
BlockedShopInUser.IsBlocked
FROM Shop
CROSS APPLY (
SELECT TOP 1 OfferID, OfferMessage, ShopOfferStatusID, UserRequestID
FROM ShopOffer AS s
WHERE s.ShopID = Shop.ShopID
ORDER BY s.OfferID DESC
) ShopOffer
INNER JOIN ShopOfferStatus ON ShopOffer.ShopOfferStatusID = ShopOfferStatus.ShopOfferStatusID
INNER JOIN UserRequest ON ShopOffer.UserRequestID = UserRequest.UserRequestID
INNER JOIN [User] ON UserRequest.UserID = [User].UserID
INNER JOIN BlockedShopInUser ON Shop.ShopID = BlockedShopInUser.ShopID AND [User].UserID = BlockedShopInUser.UserID

Use String Row As column

I have UserExams tables shown below
I need to display like FirstName, First, Second, Final
I've tried using PIVOT but the aggregation function don't return all StudentMarks
select *
from
(
SELECT Users.FirstName,
ExamsNames.Name,
UserExams.StudentMark
FROM Exams INNER JOIN ExamsNames ON Exams.ExamNameID = ExamsNames.ExamsNamesID
INNER JOIN UserExams ON Exams.ExamID = UserExams.ExamID
INNER JOIN Users ON UserExams.UserID = Users.UserID
)t
PIVOT
(
min(StudentMark)
for Name in ([First],[Second],[Final])
)p
The way your current query is written you are going to return one min(StudentMark) for each exam in Name. If you want to return multiple values for each exam, then you will want to include another column that will give you distinct rows - I would suggest using row_number:
select FirstName, [First],[Second],[Final]
from
(
SELECT Users.FirstName,
ExamsNames.Name,
UserExams.StudentMark,
row_number() over(partition by Users.FirstName, ExamsNames.Name
order by UserExams.StudentMark) seq
FROM Exams
INNER JOIN ExamsNames ON Exams.ExamNameID = ExamsNames.ExamsNamesID
INNER JOIN UserExams ON Exams.ExamID = UserExams.ExamID
INNER JOIN Users ON UserExams.UserID = Users.UserID
)t
PIVOT
(
min(StudentMark)
for Name in ([First],[Second],[Final])
)p

Find element in hierarchy

I'm trying to create a hierarchical query using WITH of T-SQL. What I want to do is, for example, to find if John is a boss of Ryan, directly or indirectly?
How can I write a query for this?
WITH BossOf AS(
SELECT bossId, Id From Employees
UNION ALL
SELECT b.bossId, e.Id
FROM Employees AS e
INNER JOIN BossOf b ON b.Id = e.bossId
)
SELECT * FROM BossOf
WHERE Id = 'Ryan'
Just as an example of what you can do with BossOf...
WITH BossOf AS(
SELECT bossId, Id From Employees
UNION ALL
SELECT b.bossId, e.Id
FROM Employees AS e
INNER JOIN BossOf b ON b.Id = e.bossId
)
SELECT * FROM Employees
WHERE Id IN (SELECT BossId
FROM BossOf
Where Id = 'Ryan')
Or Even
WITH BossOf AS(
SELECT bossId, Id From Employees
UNION ALL
SELECT b.bossId, e.Id
FROM Employees AS e
INNER JOIN BossOf b ON b.Id = e.bossId
)
SELECT COUNT(*) FROM BossOf
WHERE Id = 'Ryan'
AND BossId = 'John'

Resources