Left Join same table multiple times getting only the max value each join - sql

I have the following query which returns multiple rows per join but I need to return only the row with the highest id
Any ideas how I can do this without sub-queries?
SELECT cp.RefId, work.PhoneNumber AS work, work.id AS work_id, home.PhoneNumber AS home, home.id AS home_id
FROM Contacts cp
LEFT JOIN OtherPhoneNumber work ON cp.ZoneId = work.ZoneId AND work.PhoneNumber_Type = 'W' AND work.OwnerType = 'C' AND work.OwnerRefId = cp.RefId
LEFT JOIN OtherPhoneNumber home ON cp.ZoneId = home.ZoneId AND home.PhoneNumber_Type = 'H' AND home.OwnerType = 'C' AND home.OwnerRefId = cp.RefId
WHERE cp.ZoneId = '123123'
This returns something like:
RefId work work_id home home_id
QWERTY1234 01234523423 1739092 01234563232 1818181
QWERTY1234 01234523423267196 1739093 01234563232 1818181
I only want:
RefId work work_id home home_id
QWERTY1234 01234523423267196 1739093 01234563232 1818181

One method is to extract the ids for the home and work numbers, and then join back to the original tables:
SELECT cp.RefId, work.PhoneNumber AS work, work.id AS work_id,
home.PhoneNumber AS home, home.id AS home_id
FROM Contacts cp LEFT JOIN
(SELECT o..OwnerRefId, o.zoneId,
MAX(CASE WHEN o..PhoneNumber_Type = 'W' THEN w.id END) as workid,
MAX(CASE WHEN o..PhoneNumber_Type = 'H' THEN w.id END) as homeid
FROM OtherPhoneNumber o
WHERE w.OwnerType = 'C'
GROUP BY o.OwnerRefId, o..zoneId
) wh
ON cp.RefId = w.OwnerRefId LEFT JOIN
OtherPhoneNumber work
ON work.id = wh.workid LEFT JOIN
OtherPhoneNumber home
ON home.id = wh.homeid
WHERE cp.ZoneId = '123123';
EDIT:
In SQL Server, you can do this using OUTER APPLY:
select cp.RefId, work.PhoneNumber AS work, work.id AS work_id,
home.PhoneNumber AS home, home.id AS home_id
from Contacts cp outer apply
(select top 1 o.*
from OtherPhoneNumber o
where o.PhoneNumber_Type = 'W' AND o.OwnerType = 'C' AND
o.OwnerRefId = cp.RefId AND o.ZoneId = cp.ZoneId
order by o.id desc
) work outer apply
(select top 1 o.*
from OtherPhoneNumber o
where o.PhoneNumber_Type = 'H' AND o.OwnerType = 'C' AND
o.OwnerRefId = cp.RefId AND o.ZoneId = cp.ZoneId
order by o.id desc
) home
where cp.ZoneId = '123123';
This is probably the fastest approach, with the right indexes: Contacts(ZoneId, RefId) and OtherPhoneNumber(ZoneId, OwnerRefId, PhoneNumber_Type, OwnerType, id).

Okay you can try this -
;WITH myCTE
AS
(
SELECT
cp.RefId
,work.PhoneNumber AS work
,work.id AS work_id
,home.PhoneNumber AS home
,home.id AS home_id
,ROW_NUMBER() OVER (PARTITION BY cp.RefId, work.PhoneNumber, work.id, home.PhoneNumber, home.id ORDER BY cp.RefId) AS RowNum
FROM Contacts cp
LEFT JOIN OtherPhoneNumber work
ON cp.ZoneId = work.ZoneId
AND work.PhoneNumber_Type = 'W'
AND work.OwnerType = 'C'
AND work.OwnerRefId = cp.RefId
LEFT JOIN OtherPhoneNumber home
ON cp.ZoneId = home.ZoneId
AND home.PhoneNumber_Type = 'H'
AND home.OwnerType = 'C'
AND home.OwnerRefId = cp.RefId
WHERE cp.ZoneId = '123123'
)
SELECT
*
FROM myCTE
WHERE RowNum = 1

You can use Outer Apply
SELECT cp.RefId, W.work, W.work_id, H.home, H.home_id
FROM Contacts cp
Outer Apply
(
Select Top 1 work.Id as work_id, work.PhoneNumber as work
From OtherPhoneNumber work
Where work.ZoneId = cp.ZoneId AND work.PhoneNumber_Type = 'W' AND work.OwnerType = 'C' AND work.OwnerRefId = cp.RefId
Order By Work.Id Desc
) W
Outer Apply
(
Select Top 1 home.Id as home_id, home.PhoneNumber as Home
From OtherPhoneNumber home
Where home.ZoneId = cp.ZoneId AND home.PhoneNumber_Type = 'H' AND home.OwnerType = 'C' AND home.OwnerRefId = cp.RefId
Order By H.Id Desc
) H
WHERE cp.ZoneId = '123123'

Related

How to select only unique values from this table based on criteria?

I want to select only the first record from each group. So basically I only want the data for first processing date column and don't want the rest of the date for each product.
My current code as below
SELECT
C.AccountID as ABN_ACC ,
C.ProductSymbol,
C.ProductShortName,
S.ReactorProduct AS REC_PROD,
C.CurrencyCode AS PROD_CCY,
C.CountryOfPayment AS PAYCONTRY,
C.DividendValueCur AS PAYCCY,
C.DividendValue AS DIV_AMNT,
CONCAT ((IIF (C.QuantitySettledNoTax_LS = 'S' ,(-1*C.QuantitySettledNoTax),C.QuantitySettledNoTax )),(IIF(C.QuantitySettledTax_LS = 'S',(-1*C.QuantitySettledTax),QuantitySettledTax))) AS DIVQTY ,
C.ExdividendDate AS EXDATE,
C.Recorddate AS RECDATE,
C.DividendPayDate AS PAYDATE
From "LiquidCDW". [Staging].[CA_CorporateActions] AS C
RIGHT JOIN "LiquidCDW".[Staging].[POS_SettledPositions] AS P ON C.Recorddate = P.ProcessingDate AND C.AccountID = p.AccountID AND C.ProductSymbol = P.ProductSymbol AND C.DividendValueCur = P.CurrencyCode
INNER JOIN "LiquidCDW".[Transformation].[Mapping_Product_ABNReactor] AS S ON C.ProductSymbol = S.ReactorProduct AND S.ValidTo IS NULL AND S.ProductType = 'E' AND c.DividendValueCur = S.Currency
where C.ExdividendDate >= '20210201'
ORDER by C.ProductSymbol , C.CurrencyCode
One approach uses ROW_NUMBER:
WITH cte AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY C.ProductSymbol ORDER BY processingDate) rn,
C.AccountID AS ABN_ACC,
C.ProductSymbol,
C.ProductShortName,
S.ReactorProduct AS REC_PROD,
C.CurrencyCode AS PROD_CCY,
C.CountryOfPayment AS PAYCONTRY,
C.DividendValueCur AS PAYCCY,
C.DividendValue AS DIV_AMNT,
CONCAT((IIF(C.QuantitySettledNoTax_LS = 'S',
(-1*C.QuantitySettledNoTax), C.QuantitySettledNoTax)),
(IIF(C.QuantitySettledTax_LS = 'S',
(-1*C.QuantitySettledTax),QuantitySettledTax))) AS DIVQTY,
C.ExdividendDate AS EXDATE,
C.Recorddate AS RECDATE,
C.DividendPayDate AS PAYDATE
FROM [LiquidCDW].[Staging].[CA_CorporateActions] AS C
RIGHT JOIN "LiquidCDW".[Staging].[POS_SettledPositions] AS P
ON C.Recorddate = P.ProcessingDate AND
C.AccountID = p.AccountID AND
C.ProductSymbol = P.ProductSymbol AND
C.DividendValueCur = P.CurrencyCode
INNER JOIN [LiquidCDW].[Transformation].[Mapping_Product_ABNReactor] AS S
ON C.ProductSymbol = S.ReactorProduct AND
S.ValidTo IS NULL AND
S.ProductType = 'E' AND
C.DividendValueCur = S.Currency
WHERE
C.ExdividendDate >= '20210201'
)
SELECT *
FROM cte
WHERE rn = 1
ORDER BY ProductSymbol, CurrencyCode;

Select sql statement for Mysql

I have this bit of sql
SELECT pd.id, pd.title, pd.fname, pd.lname, pd.DOB, pd.phone_home, pd.phone_biz, pd.phone_contact, pd.phone_cell, pd.sex, pd.email, pd.pid, f.form_id, ldf1.field_value AS autoleaxis, ldf2.field_value AS autolecyl
FROM patient_data pd
LEFT JOIN forms f ON pd.pid = f.pid
LEFT JOIN `lbf_data` ldf1 ON ldf1.form_id = f.form_id
LEFT JOIN `lbf_data` ldf2 ON ldf1.form_id = ldf2.form_id
WHERE (
f.form_name = 'Opthalmology'
AND ldf1.field_id = 'autoleaxis'
AND ldf2.field_id = 'autolecyl'
)
OR (
f.form_id IS NULL
AND ldf1.field_id IS NULL
AND ldf2.field_id IS NULL
)
ORDER BY pd.pid
Which is for a MySQL Database. It works ok except it is returning more than one record per person in some cases as they have more than one form_id. How do I restrict it so that it only returns the records for highest form_id that the person has or form_id set to null if there isn't one. Thanks
You can use subquery in LEFT JOIN, as with:
SELECT *
FROM patient_data pd
LEFT JOIN
(
select max(form_id) as form_id, pid, form_name
from forms as f2
) as f
ON pd.pid = f.pid
LEFT JOIN `lbf_data` ldf1 ON ldf1.form_id = f.form_id
LEFT JOIN `lbf_data` ldf2 ON ldf1.form_id = ldf2.form_id
WHERE (
f.form_name = 'Opthalmology'
AND ldf1.field_id = 'autoleaxis'
AND ldf2.field_id = 'autolecyl'
)
OR (
f.form_id IS NULL
AND ldf1.field_id IS NULL
AND ldf2.field_id IS NULL
)
ORDER BY pd.pid
You can test in here

Show unique value for id, based on latest createby date

I am using the following SQL code to JOIN a few tables, but need to show unique values for the s.incidentid based on the latest s.createdate.
I know I need to use a Sub query with Maxdate, but I am not sure on the correct syntax.
This is my first query with multiple joins and I am struggling to get my head round it.
Here is my code:
SELECT
s.incidentid,
u.internalid as AssignedTo,
u.fullname as AssignedTo_FullName,
s.createby as AssignedBy,
u2.fullname as AssignedBy_FullName,
s.createdate as AssignedTime,
i.[description],
i.fix,
st.[description] as [Status],
(SELECT (CASE WHEN u.internalid = s.createby THEN 'Yes' ELSE 'No' END) as SelfAssigned),
d.d1,
d.d2,
d.d3,
d.d4,
d.d5
FROM dbo.IncidentServiceLevelAgreement s
JOIN dbo.UserAll u on u.userid = s.userid
JOIN dbo.UserAll u2 on u2.internalid = s.createby
JOIN dbo.IncidentAll i on s.incidentid = i.incidentid
JOIN dbo.[Status] st on i.statusid = st.statusid
JOIN dbo.flatdiagnosis d on i.actualdiagnosisid = d.diagnosisid
WHERE (s.groupId = '4954' and s.incidentServiceLevelAgreementTypeID = '9')
ORDER BY AssignedTime DESC
Any help greatly appreciated.
The easiest is to use a CTE and the ROW_NUMBER function:
WITH CTE AS
(
SELECT RN = ROW_NUMBER() OVER ( PARTITION BY incidentid
ORDER BY createdate DESC ),
s.Incidentid,
u.Internalid AS AssignedTo,
u.Fullname AS AssignedTo_FullName,
s.Createby AS AssignedBy,
u2.Fullname AS AssignedBy_FullName,
s.Createdate AS AssignedTime,
i.[Description],
i.Fix,
st.[Description] AS [Status],
SelfAssigned = CASE WHEN u.Internalid = s.Createby
THEN 'Yes' ELSE 'No' END,
d.D1,
d.D2,
d.D3,
d.D4,
d.D5
FROM dbo.Incidentservicelevelagreement s
JOIN dbo.Userall u
ON u.Userid = s.Userid
JOIN dbo.Userall u2
ON u2.Internalid = s.Createby
JOIN dbo.Incidentall i
ON s.Incidentid = i.Incidentid
JOIN dbo.[Status] st
ON i.Statusid = st.Statusid
JOIN dbo.Flatdiagnosis d
ON i.Actualdiagnosisid = d.Diagnosisid
WHERE ( s.Groupid = '4954'
AND s.Incidentservicelevelagreementtypeid = '9' )
)
SELECT * FROM CTE WHERE RN = 1
ORDER BY AssignedTime DESC
(instead of SELECT * list all columns explicitly, I didn't feel like it)

SQL - left join generate duplicates

I have code to select some applications but LEFT JOIN is creating duplicates.
Question: How to get rid of duplicates generated by
LEFT JOIN
attachments as att
ON
(a.ssn = att.ssn AND att.doc_type = 'id_copy' AND att.source = 'cpt3')
My Work:
I am considering SELECT DISTINCT or GROUP BY but, can't figure out where to put them.
This is the query:
SELECT
a.*, c.code, l.who, l.locked_time, att.id AS attnew,
( SELECT
COUNT(*)
FROM
applications as a2
WHERE
a2.approved = 'Y'
AND
a2.paid = 'Y'
AND
a2.paid_back = 'Y'
AND
a2.ssn = a.ssn
) AS previous_apps
FROM
applications as a
LEFT JOIN
locked_by as l
USING(id)
LEFT JOIN
campaign_codes as c
ON
c.id = a.campaign
LEFT JOIN
attachments as att
ON
(a.ssn = att.ssn AND att.doc_type = 'id_copy' AND att.source = 'cpt3')
WHERE
a.closed='N'
AND
a.paid = 'N'
ORDER BY
a.arrived_date
DESC
Use GROUP BY
to avoid duplicates. In your case:
...
WHERE
a.closed='N'
AND
a.paid = 'N'
GROUP BY
a.id
ORDER BY
a.arrived_date
DESC
If you want to make an one to one relation to avoid duplicates and att.id is the same for each att.ssn or you need only one (MAX,MIN,..ANY?) att.id. Try this:
LEFT JOIN
(SELECT ssn,
MAX(id) as id,
FROM attachments
WHERE doc_type = 'id_copy' AND source = 'cpt3'
GROUP BY ssn
)as att
ON
(a.ssn = att.ssn)

Query for logistic regression, multiple where exists

A logistic regression is a composed of a uniquely identifying number, followed by multiple binary variables (always 1 or 0) based on whether or not a person meets certain criteria. Below I have a query that lists several of these binary conditions. With only four such criteria the query takes a little longer to run than what I would think. Is there a more efficient approach than below? Note. tblicd is a large table lookup table with text representations of 15k+ rows. The query makes no real sense, just a proof of concept. I have the proper indexes on my composite keys.
select patient.patientid
,case when exists
(
select c.patientid from tblclaims as c
inner join patient as p on p.patientid=c.patientid
and c.admissiondate = p.admissiondate
and c.dischargedate = p.dischargedate
where patient.patientid = p.patientid
group by c.patientid
having count(*) > 1000
)
then '1' else '0'
end as moreThan1000
,case when exists
(
select c.patientid from tblclaims as c
inner join patient as p on p.patientid=c.patientid
and c.admissiondate = p.admissiondate
and c.dischargedate = p.dischargedate
where patient.patientid = p.patientid
group by c.patientid
having count(*) > 1500
)
then '1' else '0'
end as moreThan1500
,case when exists
(
select distinct picd.patientid from patienticd as picd
inner join patient as p on p.patientid= picd.patientid
and picd.admissiondate = p.admissiondate
and picd.dischargedate = p.dischargedate
inner join tblicd as t on t.icd_id = picd.icd_id
where t.descrip like '%diabetes%' and patient.patientid = picd.patientid
)
then '1' else '0'
end as diabetes
,case when exists
(
select r.patientid, count(*) from patient as r
where r.patientid = patient.patientid
group by r.patientid
having count(*) >1
)
then '1' else '0'
end
from patient
order by moreThan1000 desc
I would start by using subqueries in the from clause:
select q.patientid, moreThan1000, moreThan1500,
(case when d.patientid is not null then 1 else 0 end),
(case when pc.patientid is not null then 1 else 0 end)
from patient p left outer join
(select c.patientid,
(case when count(*) > 1000 then 1 else 0 end) as moreThan1000,
(case when count(*) > 1500 then 1 else 0 end) as moreThan1500
from tblclaims as c inner join
patient as p
on p.patientid=c.patientid and
c.admissiondate = p.admissiondate and
c.dischargedate = p.dischargedate
group by c.patientid
) q
on p.patientid = q.patientid left outer join
(select distinct picd.patientid
from patienticd as picd inner join
patient as p
on p.patientid= picd.patientid and
picd.admissiondate = p.admissiondate and
picd.dischargedate = p.dischargedate inner join
tblicd as t
on t.icd_id = picd.icd_id
where t.descrip like '%diabetes%'
) d
on p.patientid = d.patientid left outer join
(select r.patientid, count(*) as cnt
from patient as r
group by r.patientid
having count(*) >1
) pc
on p.patientid = pc.patientid
order by 2 desc
You can then probably simplify these subqueries more by combining them (for instance "p" and "pc" on the outer query can be combined into one). However, without the correlated subqueries, SQL Server should find it easier to optimize the queries.
Example of left joins as requested...
SELECT
patientid,
ISNULL(CondA.ConditionA,0) as IsConditionA,
ISNULL(CondB.ConditionB,0) as IsConditionB,
....
FROM
patient
LEFT JOIN
(SELECT DISTINCT patientid, 1 as ConditionA from ... where ... ) CondA
ON patient.patientid = CondA.patientID
LEFT JOIN
(SELECT DISTINCT patientid, 1 as ConditionB from ... where ... ) CondB
ON patient.patientid = CondB.patientID
If your Condition queries only return a maximum one row, you can simplify them down to
(SELECT patientid, 1 as ConditionA from ... where ... ) CondA