Multiple joins chose latest record by date - sql

I am trying to link four tables (3 of them key to this question). I need to pull the latest payment type used from T16. T16 links to T17 via headerid, which links to A10 via pledgeid.
I have tried this a bunch of different ways. The code below is giving me the latest date for each payment type, but what I really want is just the last payment type.
SELECT DISTINCT
A10.RecordId
,A10.AccountNumber
,A01.FamilyId
,a01.FamilyMemberType
,A10.PledgeCode --Child Number
,A10.OriginalPledgeId
,A10.PledgeId
,A01.FirstName
,A01.LastName
,A10.PledgeStatus
,A10.AmountPerGift
,A10.PledgeFrequency
,t16.PaymentType
FROM
A10_AccountPledges A10
LEFT JOIN
A01_AccountMaster A01 ON a01.AccountNumber = a10.AccountNumber
LEFT JOIN
T17_RecurringDonations T17 ON T17.PledgeId = A10.PledgeId
LEFT JOIN
T16_RecurringTransactionHeaders T16 ON T16.HeaderId = T17.HeaderId
INNER JOIN
(SELECT
T17.pledgeID
,MAX(T16.LastUsedDate) as lastdate
FROM
T17_RecurringDonations T17
LEFT JOIN
T16_RecurringTransactionHeaders T16 ON T16.HeaderId = T17.HeaderId
GROUP BY
T17.pledgeID) pm ON pm.PledgeId = A10.PledgeId --and pm.lastdate = T16.LastUsedDate
WHERE
A01.[Status] = 'A'
AND a10.PledgeId = 398353 --test case

You can do this like below:
select top 1 *
from (
YOUR QUERY HERE
)
order by lastdate desc;
Notes:
YOUR QUERY HERE is a placeholder which will hold your whole query as a subquery
you need to add the selection of lastdate to your subselect in order to make sure you can order by lastdate desc
EDIT
As #spaindc explained, this idea was applied, resulting in
SELECT distinct
A10.AccountNumber
,A01.FamilyId
,a01.FamilyMemberType
,A10.PledgeCode --Child Number
,A10.OriginalPledgeId
,A10.PledgeId
,A01.FirstName
,A01.LastName
,A10.PledgeStatus
,A10.PledgeFrequency
,(SELECT top 1 T16.PaymentType
FROM T16_RecurringTransactionHeaders T16, T17_RecurringDonations T17
where T16.HeaderId = T17.HeaderId
and T17.PledgeId = A10.PledgeId
order by t16.LastUsedDate desc
) as PaymentType
FROM A10_AccountPledges A10
left join A01_AccountMaster A01 on a01.AccountNumber = a10.AccountNumber
where A01.[Status] = 'A'

Thanks to Lajos for correct answer. Here is the final code for reference.
SELECT distinct
A10.AccountNumber
,A01.FamilyId
,a01.FamilyMemberType
,A10.PledgeCode --Child Number
,A10.OriginalPledgeId
,A10.PledgeId
,A01.FirstName
,A01.LastName
,A10.PledgeStatus
,A10.PledgeFrequency
,(SELECT top 1 T16.PaymentType
FROM T16_RecurringTransactionHeaders T16, T17_RecurringDonations T17
where T16.HeaderId = T17.HeaderId
and T17.PledgeId = A10.PledgeId
order by t16.LastUsedDate desc
) as PaymentType
FROM A10_AccountPledges A10
left join A01_AccountMaster A01 on a01.AccountNumber = a10.AccountNumber
where A01.[Status] = 'A'

Related

SQL Server aggregate function without group by

I want to include tcon.Inductive_Injection_Hours, tcon.Capacitive_Injection_Hours without applying group by. How can I do that?
SELECT
bp.Serial_Number,
tcon.Serial_Number AS ConverterSerialNumber,
MAX(tcon.Time_Stamp) AS DateStamp,
tcon.Inductive_Injection_Hours,
tcon.Capacitive_Injection_Hours
FROM
dbo.Bypass AS bp
INNER JOIN
dbo.Converter AS c ON bp.Bypass_ID = c.Bypass_ID
INNER JOIN
dbo.Converter_Tel_Data AS tcon ON c.Converter_ID = tcon.Converter_ID
WHERE
(bp.Site_ID = 7)
GROUP BY
bp.Serial_Number, tcon.Serial_Number,
tcon.Inductive_Injection_Hours, tcon.Capacitive_Injection_Hours
ORDER BY
ConverterSerialNumber
I have figured it out.
select [data].Serial_Number,Time_Stamp,Inductive_Injection_Hours,Capacitive_Injection_Hours,b.Serial_Number from Converter_Tel_Data as [data]
inner join dbo.Converter AS c On [data].Converter_ID = c.Converter_ID
inner join dbo.Bypass as b on c.Bypass_ID = b.Bypass_ID
WHERE
(Time_Stamp = (SELECT MAX(Time_Stamp) FROM Converter_Tel_Data WHERE Converter_ID = [data].Converter_ID)) And ([data].Site_ID=7)
ORDER BY [data].Serial_Number
You can use row_number - either in a CTE/derived table or using a trick with TOP 1.
Select Top 1 With Ties
bp.Serial_Number
, tcon.Serial_Number AS ConverterSerialNumber
, tcon.Time_Stamp AS DateStamp
, tcon.Inductive_Injection_Hours
, tcon.Capacitive_Injection_Hours
From dbo.Bypass AS bp
Inner Join dbo.Converter AS c On bp.Bypass_ID = c.Bypass_ID
Inner Join dbo.Converter_Tel_Data AS tcon ON c.Converter_ID = tcon.Converter_ID
Where bp.Site_ID = 7
Order By
row_number() over(Partition By bp.Serial_Number Order By tcon.Time_Stamp desc)
This should return the latest row from the tconn table for each bp.Serial_Number.

having an and condition on multiple rows - sql server

i have the following query where i'm trying to select one value based on a condition
select distinct tblItemTypePropValue.ItemType_Fkey, tblItemPrice.price, dbo.fnGetDiscountItemType(tblItemTypePropValue.ItemType_Fkey) as finalPrice
from tblItemTypePropValue
inner join tblItemPrice
on tblItemTypePropValue.ItemType_Fkey = tblItemPrice.itemType_fkey
where Value_Fkey in (2097,2131)
and tblitemtypepropvalue.ItemType_Fkey in (select id from tblItemType where Item_Fkey = 12241)
the problem is i n the where in value_fkey in (2097,2131)
is there a way to write an and condition on multiple rows where value_fkey = 2097 and = 2131.
and also, can it be done without a join since my parameters inside the in can vary in count.
sample result
itemtype_fkey price finalPrice
6191 100 93
so the query will only return the ItemType_Fkey that has both Value_Fkeys specified in the query
Value
thanks,
You can do two INNER JOINs and check for the existence of the 2097 and 2131 keys on them.
Being INNER JOINS, only the Prices having both of those keys will be returned.
select distinct P.ItemType_Fkey, P.price,
dbo.fnGetDiscountItemType(P.ItemType_Fkey) as finalPrice
from tblItemType T
inner join tblItemPrice P on P.ItemType_Fkey = T.id
inner join tblItemTypePropValue V1 on V1.ItemType_Fkey = P.ItemType_Fkey and V1.Value_Fkey = 2097
inner join tblItemTypePropValue V2 on V2.ItemType_Fkey = P.ItemType_Fkey and V2.Value_Fkey = 2104
where T.Item_Fkey = 12241
select distinct tblItemTypePropValue.Value_Fkey,tblItemTypePropValue.ItemType_Fkey, tblItemPrice.price, dbo.fnGetDiscountItemType(tblItemTypePropValue.ItemType_Fkey) as finalPrice
from tblItemTypePropValue
inner join tblItemPrice
on tblItemTypePropValue.ItemType_Fkey = tblItemPrice.itemType_fkey
where ((Value_Fkey = 2097) or (Value_Fkey = 2131))
and tblitemtypepropvalue.ItemType_Fkey in (select id from tblItemType where Item_Fkey = 12241)
You probably want those items where rows for both Value_Fkey exist like this
SELECT
v.ItemType_Fkey,
max(p.price), -- max/min/avg?
dbo.fnGetDiscountItemType(v.ItemType_Fkey) AS finalPrice
FROM tblItemTypePropValue AS v
INNER JOIN tblItemPrice AS p
ON v.ItemType_Fkey = p.itemType_fkey
WHERE Value_Fkey IN (2097,2131) --- list of values
AND v.ItemType_Fkey
IN ( SELECT id
FROM tblItemType
WHERE Item_Fkey = 12241
)
GROUP BY v.ItemType_Fkey
HAVING COUNT(DISTINCT Value_Fkey) = 2 -- number of values in list

Can't order query correctly

A while ago I requested help to code a LEFT JOIN filtering in a particular way that the result postition the desired value in the first row.
Need to retrieve table's last inserted/updated record with some exclusions
The thing now is that there are many cases which are mixing data. The scenario is that on the same table we have 2 values that we need to organize on different columns. The PO_ID is unique, but can have 1 or more values on the other tables, and for this particular case 1 PO_ID has 3 SHIP_ID_CUS values. We only need 1 PO_ID per row (no duplicates) that is way we used the MAX() and GROUP BY.
Here is a piece of the code that I think cause issues.
select
z.po_id,
max(scdc.ship_id) as ship_id_cdc,
max(lscdc.ship_evnt_cd) as last_event_cdc,
max(lscdc.ship_evnt_tms) as event_tms_cdc,
max(scus.SHIP_ID) as ship_id_cus,
max(lscus.ship_evnt_cd) as last_event_cus,
max(lscus.ship_evnt_tms) as event_tms_cus
from TABLE.A z
left join (select distinct po_id, iltc.ship_id, s.ship_to_loc_code from TABLE.B iltc inner join TABLE.C s on iltc.ship_id=s.ship_id and iltc.ship_to_loc_code=s.ship_to_loc_code and s.ship_to_ctry<>' ') AS A ON z.po_id = a.po_id
left JOIN TABLE.C scus ON A.SHIP_ID = scus.SHIP_ID AND A.SHIP_TO_LOC_CODE = scus.SHIP_TO_LOC_CODE and scus.loc_type = 'CUS' AND DAYS(scus.shipment_tms)+10 >= DAYS(z.ship_tms)
left JOIN TABLE.C scdc ON A.SHIP_ID = scdc.SHIP_ID AND A.SHIP_TO_LOC_CODE = scdc.SHIP_TO_LOC_CODE and scdc.loc_type = 'CDC' AND DAYS(scdc.shipment_tms)+10 >= DAYS(z.ship_tms)
left join
( select ship_id_856, ship_to_loc_cd856, ship_evnt_cd, ship_evnt_tms, carr_tracking_num, event_srv_lvl
, row_number() over(partition by ship_id order by updt_job_tms desc) as RN
FROM TABLE.D
WHERE LEFT(ship_evnt_cd, 1) <> '9') lscus
ON lscus.ship_id_856=scus.ship_id and scus.ship_to_loc_code=lscus.ship_to_loc_cd856 and lscus.rn = 1
left join
( select ship_id_856, ship_to_loc_cd856, ship_evnt_cd, ship_evnt_tms, carr_tracking_num, event_srv_lvl
, row_number() over(partition by ship_id order by updt_job_tms desc) as RN
FROM TABLE.D
WHERE LEFT(ship_evnt_cd, 1) <> '9') lscdc
ON lscdc.ship_id_856=scdc.ship_id and lscdc.ship_to_loc_cd856=scdc.ship_to_loc_code and lscdc.rn = 1
WHERE
z.po_id = 'T1DLDC'
GROUP BY z.po_id
By searching that condition we get the following result
The problem is that if we search directly on the TABLE.D, the last event that we need (with last update record tms) is another one (X1) and somehow the date is incorrect.
What is even more weird, is that if we search for the ship_id_cus on the original query, we get the correct code but still with a wrong date...
WHERE
--z.po_id = 'T1DLDC'
scus.ship_id = 'D30980'
GROUP BY z.po_id
I tried other logic changes like modifying the left joins to search on a subquery.
left JOIN ( select * from TABLE.C order by updt_job_tms desc) scus ON A.SHIP_ID = scus.SHIP_ID AND A.SHIP_TO_LOC_CODE = scus.SHIP_TO_LOC_CODE and scus.loc_type = 'CUS' AND DAYS(scus.shipment_tms)+10 >= DAYS(z.ship_tms)
But this is also giving the same exact results by searching either by po_id or ship_id_cus
Any ideas or comment will be much appreciated.
Thanks
------------------------------------UPDATE-----------------------------------
Adding the result of the LEFT JOIN with the row_partition() including all the ship_id_cus for that po_id, and all the codes with the tms. None match here.
Based on all these, it should be the last ship_id_cus with X1 event/tms. If we exclude also the ones starting with 9, we would get the following result.
(I am not applying here ordering by ship_id_cus, which already described before that did not work either the way I implemented)
If you have a table: TBL1
ID APPROVED APPROVER DATE_APPROVED
====== ======== ======== =============
ABC Y JOE 2019-01-13
ABC N ZACK 2018-12-23
ABC N SUE 2019-02-23
And you do SQL:
SELECT ID, MAX(APPROVED) AS APPROVAL
,MAX(APPROVER) AS APPROVED_BY , MAX(DATE_APPROVED) AS APPROVED_ON
FROM TBL1 GROUP BY ID
you will get result:
ID APPROVAL APPROVED_BY APPROVED_ON
====== ======== =========== =============
ABC Y ZACK 2019-02-23
which is correct to the code but is NOT what you want
Try the following:
SELECT T1.ID, T1.APPROVED, T1.APPROVER, T1.DATE_APPROVED
FROM TBL1 AS T1
INNER JOIN (SELECT ID, MAX(DATE_APPROVED) AS APPROVED_ON
FROM TBL1 GROUP BY ID
) AS T2
ON T1.ID =T2.ID
AND T1.DATE_APPROVED = T2.APPROVED_ON
Result:
ID APPROVED APPROVER DATE_APPROVED
====== ======== ======== =============
ABC N SUE 2019-02-23

SQL statement merge two rows into one

In the results of my sql-statement (SQL Server 2016) I would like to combine two rows with the same value in two columns ("study_id" and "study_start") into one row and keep the row with higest value in a third cell ("Id"). If any columns (i.e. "App_id" or "Date_arrival) in the row with higest Id is NULL, then it should take the value from the row with the lowest "Id".
I get the result below:
Id study_id study_start Code Expl Desc Startmonth App_id Date_arrival Efter_op Date_begin
167262 878899 954 4.1 udd.ord Afbrudt feb 86666 21-06-2012 N 17-08-2012
180537 878899 954 1 Afsluttet Afsluttet feb NULL NULL NULL NULL
And I would like to get this result:
Id study_id study_start Code Expl Desc Startmonth App_id Date_arrival Efter_op Date_begin
180537 878899 954 1 Afsluttet Afsluttet feb 86666 21-06-2012 N 17-08-2012
My statement looks like this:
SELECT dbo.PopulationStam_V.ELEV_ID AS id,
dbo.PopulationStam_V.PERS_ID AS study_id,
dbo.STUDIESTARTER.STUDST_ID AS study_start,
dbo.Optagelse_Studiestatus.AFGANGSARSAG AS Code,
dbo.Optagelse_Studiestatus.KORT_BETEGNELSE AS Expl,
ISNULL((CAST(dbo.Optagelse_Studiestatus.Studiestatus AS varchar(20))), 'Indskrevet') AS 'Desc',
dbo.STUDIESTARTER.OPTAG_START_MANED AS Startmonth,
dbo.ANSOGNINGER.ANSOG_ID as App_id,
dbo.ANSOGNINGER.ANKOMSTDATO AS Data_arrival',
dbo.ANSOGNINGER.EFTEROPTAG AS Efter_op,
dbo.ANSOGNINGER.STATUSDATO AS Date_begin
FROM dbo.INSTITUTIONER
INNER JOIN dbo.PopulationStam_V
ON dbo.INSTITUTIONER.INST_ID = dbo.PopulationStam_V.SEMI_ID
LEFT JOIN dbo.ANSOGNINGER
ON dbo.PopulationStam_V.ELEV_ID = dbo.ANSOGNINGER.ELEV_ID
INNER JOIN dbo.STUDIESTARTER
ON dbo.PopulationStam_V.STUDST_ID_OPRINDELIG = dbo.STUDIESTARTER.STUDST_ID
INNER JOIN dbo.UDD_NAVNE_T
ON dbo.PopulationStam_V.UDDA_ID = dbo.UDD_NAVNE_T.UDD_ID
INNER JOIN dbo.UDDANNELSER
ON dbo.UDD_NAVNE_T.UDD_ID = dbo.UDDANNELSER.UDDA_ID
LEFT OUTER JOIN dbo.PERSONER
ON dbo.PopulationStam_V.PERS_ID = dbo.PERSONER.PERS_ID
LEFT OUTER JOIN dbo.POSTNR
ON dbo.PERSONER.PONR_ID = dbo.POSTNR.PONR_ID
LEFT OUTER JOIN dbo.KønAlleElevID_V
ON dbo.PopulationStam_V.ELEV_ID = dbo.KønAlleElevID_V.ELEV_ID
LEFT OUTER JOIN dbo.Optagelse_Studiestatus
ON dbo.PopulationStam_V.AFAR_ID = dbo.Optagelse_Studiestatus.AFAR_ID
LEFT OUTER JOIN dbo.frafaldsmodel_adgangsgrundlag
ON dbo.frafaldsmodel_adgangsgrundlag.ELEV_ID = dbo.PopulationStam_V.ELEV_ID
LEFT OUTER JOIN dbo.Optagelse_prioriteterUFM
ON dbo.Optagelse_prioriteterUFM.cpr = dbo.PopulationStam_V.CPR_NR
AND dbo.Optagelse_prioriteterUFM.Aar = dbo.frafaldsmodel_adgangsgrundlag.optagelsesaar
LEFT OUTER JOIN dbo.frafaldsmodel_stoettetabel_uddannelser AS fsu
ON fsu.id_uddannelse = dbo.UDDANNELSER.UDDA_ID
AND fsu.id_inst = dbo.INSTITUTIONER.INST_ID
AND fsu.uddannelse_aar = dbo.frafaldsmodel_adgangsgrundlag.optagelsesaar
WHERE dbo.STUDIESTARTER.STUDIESTARTSDATO > '2012-03-01 00:00:00.000'
AND (dbo.Optagelse_Studiestatus.AFGANGSARSAG IS NULL
OR dbo.Optagelse_Studiestatus.AFGANGSARSAG NOT LIKE '2.7.4')
AND (dbo.PopulationStam_V.INDSKRIVNINGSFORM = '1100'
OR dbo.PopulationStam_V.INDSKRIVNINGSFORM = '1700')
GROUP BY dbo.PopulationStam_V.ELEV_ID,
dbo.PopulationStam_V.PERS_ID,
dbo.STUDIESTARTER.STUDST_ID,
dbo.Optagelse_Studiestatus.AFGANGSARSAG,
dbo.Optagelse_Studiestatus.KORT_BETEGNELSE,
dbo.STUDIESTARTER.OPTAG_START_MANED,
Studiestatus,
dbo.ANSOGNINGER.ANSOG_ID,
dbo.ANSOGNINGER.ANKOMSTDATO,
dbo.ANSOGNINGER.EFTEROPTAG,
dbo.ANSOGNINGER.STATUSDATO
I really hope somebody out there can help.
Many ways, this will work:
WITH subSource AS (
/* Your query here */
)
SELECT
s1.id,
/* all other columns work like this:
COALESCE(S1.column,s2.column)
for example: */
coalesce(s1.appid,s2.appid) as appid
FROM subSource s1
INNER JOIN subSource s2
ON s1.study_id =s2.study_id
and s1.study_start = s2.study_start
AND s1.id > s2.id
/* I imagine some other clauses might be needed but maybe not */
The rest is copy paste

SQL Union Query

SELECT pv.PropertyID, COUNT(pv.VisitID) AS InitialVisit
FROM tblPAppointments pa INNER JOIN tblPropertyVisit pv ON pv.AppID = pa.AppID
WHERE pv.Status = 0
GROUP BY pv.PropertyID
UNION ALL
SELECT jv.PropertyID, COUNT(jv.JobVistID) AS JobVisit
FROM tblPAppointments pa INNER JOIN tblJobVisits jv ON jv.AppID = pa.AppID
WHERE jv.VisitStatus = 1
GROUP BY jv.PropertyID
I need to get InitialVisit count and JobVisit count in two separate columns.above query returns just two columns (PropertyID,InitialVisit).
Use a NULL as a placeholder for the column that there won't be any output for:
SELECT pv.PropertyID,
COUNT(pv.VisitID) AS InitialVisit,
NULL AS jobvisit
FROM tblPAppointments pa
JOIN tblPropertyVisit pv ON pv.AppID = pa.AppID
WHERE pv.Status = 0
GROUP BY pv.PropertyID
UNION ALL
SELECT jv.PropertyID,
NULL AS initialvisit,
COUNT(jv.JobVistID) AS JobVisit
FROM tblPAppointments pa
JOIN tblJobVisits jv ON jv.AppID = pa.AppID
WHERE jv.VisitStatus = 1
GROUP BY jv.PropertyID
This will return three columns. The column alias is necessary in the first query, but not in the second -- I aliased both to make it clear what is happening.
Be aware that using NULL like this in SQL Server will require you to use CAST/CONVERT on the NULL for data types other than INT because SQL Server defaults the NULL to an INT data type (as odd as that is).
An alternate query that doesn't use UNION:
SELECT x.propertyid,
COUNT(y.visitid) AS initialvisit,
COUNT(z.jobvisitid) AS jobvisit
FROM (SELECT pv.propertyid
FROM TBLPROPERTYVISIT pv
WHERE EXISTS (SELECT NULL
FROM TBLAPPOINTMENTS a
WHERE a.appid = pv.appid)
UNION
SELECT jv.propertyid
FROM TBLJOBVISIT jv
WHERE EXISTS (SELECT NULL
FROM TBLAPPOINTMENTS a
WHERE a.appid = jv.appid)) x
LEFT JOIN TBLPROPERTYVISIT y ON y.propertyid = x.propertyid
LEFT JOIN TBLJOBVISIT z ON z.propertyid = x.propertyid
GROUP BY x.propertyid
No need for a UNION at all. And you don't use tblPAppointments either
Edited to allow for no rows in one of the tables. Still one row output though
SELECT
ISNULL(pv2.PropertyID, jv2.PropertyID),
ISNULL(pv2.InitialVisit, 0),
ISNULL(jv2.JobVisit, 0)
FROM
(
SELECT pv.PropertyID, COUNT(pv.VisitID) AS InitialVisit
FROM tblPropertyVisit pv
WHERE pv.Status = 0
GROUP BY pv.PropertyID
) pv2
FULL OUTER JOIN
(
SELECT jv.PropertyID, COUNT(jv.JobVistID) AS JobVisit
FROM tblJobVisits jv
WHERE jv.VisitStatus = 1
GROUP BY jv.PropertyID
) jv2 ON pv2.PropertyID = jv2.PropertyID