SQL Performance Tuning for query with lots of table joins - sql

I have some SQL, and I have been trying to make it as fast as possible. However, because we have tables with a large amount of data, the SQL takes a long time to run.
More info:
PostgreSQL 9.2.24 on x86_64-redhat-linux-gnu, compiled by gcc (GCC)
4.8.5 20150623 (Red Hat 4.8.5-28), 64-bit
Question
Are there any pointers that I can use to make this faster?
SELECT DISTINCT
trip.travel_order_num as order_num,
min(contact.fname || ' ' || contact.sname) as traveller,
min(agentcontact.fname || ' ' || agentcontact.sname) as agent,
min(employee.staff_code) as staff_code,
trip.projectnumber,
min(lob.name) as cost_centre,
trip.start_date,
trip.end_date,
DATE(trip.end_date) - DATE(trip.start_date) + 1 as number_of_days,
DATE(trip.start_date) - DATE(reqcreationdate) as days_requested_in_advance,
trip.created as tripcreationdate,
trip.recoverablecost,
tripstatus.name AS status,
trip.company_id,
trip.eventtypes,
trip.trip_reason,
min(to_char(timetofirstbooking.earliestbookingdate - trip.reqcreationdate, 'FMDD HH24:MI:SS')) as dates_taken_to_book,
min(COALESCE(paidaccommodation.paid_accomtot, 0.00)) AS paid_accomtot,
min(COALESCE(booked_accommodation_total, 0.00)) as booked_accommodation_total,
min(COALESCE(null, 0.00)) AS paid_cartot,
min(COALESCE(bookedcarhire.booked_car_hire_total, 0.00)) as booked_car_hire_total,
min(COALESCE(paidflightfare.allstatuses_flighttot, 0.00)) AS allstatuses_flighttot,
min(COALESCE(paidflightfare.savings, 0.00)) AS flightsavings_tot,
min(COALESCE(paidflightfare.publishedfare, 0.00)) AS flightpublishedfare_tot,
min(COALESCE(tripservicefeepaid.feeamount, 0.00)) as servicefee,
min(COALESCE(tripchangeservicefeepaid.feeamount, 0.00)) as changeservicefee,
min(COALESCE(otheritemspaid.amount, 0.00)) as otherfee,
CASE
WHEN trip.requester_personid IS NOT NULL THEN min(requestercontact.fname || ' ' || requestercontact.sname)
WHEN trip.requester_personid IS NULL THEN min(agentcontact.fname || ' ' || agentcontact.sname)
END as requester,
CASE
WHEN min(tas.lastname) IS NOT NULL AND min(tas.staffcode) IS NOT NULL THEN string_agg(DISTINCT(CONCAT(tas.firstname,' ',tas.lastname, ' (',tas.staffcode,')')),', ')
WHEN min(tas.lastname) IS NOT NULL THEN string_agg(DISTINCT(CONCAT(tas.firstname,' ',tas.lastname)),', ')
ELSE ''
END as approver,
min(bob.approvalRequestedDate) as approvalrequesteddate,
max(bob.apprConcDate) as approvalconclusiondate,
extract(EPOCH from min(timetoapprove))/3600 as timetoapprove
FROM TRIP
LEFT JOIN TRIPTRAVELLERMAP ON triptravellermap.tripid = trip.trip_id
LEFT JOIN FLIGHTFARE ON flightfare.triptravid = triptravellermap.triptravid
LEFT JOIN TRAVELAGENT ON travelagent.agentid = trip.agent_id
LEFT JOIN CONTACT as agentcontact ON agentcontact.member_id = travelagent.memberid
LEFT JOIN ITEM ON item.itemid = trip.itemid
LEFT JOIN LOB ON lob.lobid = item.lobid
LEFT JOIN TRIPSTATUS ON tripstatus.statusid = trip.status
LEFT JOIN PERSON_CONTACT ON person_contact.person_personid = triptravellermap.personid
LEFT JOIN CONTACT ON contact.contact_id = person_contact.personaldetails_contact_id
LEFT JOIN PERSON_EMPLOYEE ON person_employee.person_personid = triptravellermap.personid
LEFT JOIN EMPLOYEE ON employee.employee_id = person_employee.employmentdetails_employee_id
LEFT JOIN TRIPREQUIREMENTSMAPPING ON triprequirementsmapping.tripid = trip.trip_id
LEFT JOIN TRIPREQUIREMENTS ON triprequirements.uri = triprequirementsmapping.corporatetravelrequesturi
LEFT JOIN PERSON_CONTACT AS requesterperson ON requesterperson.person_personid = trip.requester_personid
LEFT JOIN CONTACT AS requestercontact ON requestercontact.contact_id = requesterperson.personaldetails_contact_id
LEFT JOIN trip_approver_snapshot tas ON trip.trip_id=tas.trip_id
/* PAID ACCOMMODATION */
LEFT JOIN (SELECT trip.trip_id, SUM(lineitems.amount) as paid_accomtot
FROM TRIP
INNER JOIN RESERVATION ON reservation.trip_id = trip.trip_id
INNER JOIN ACCOMMODATION ON accommodation.reservationid = reservation.reservationid
INNER JOIN LINEITEMS ON lineitems.invoiceid = accommodation.invoiceid
WHERE accommodation.created >= '2021-05-01' and accommodation.created <= ('2021-05-23')
AND accommodation.resstatus <> 2
GROUP BY trip.trip_id) AS paidaccommodation ON paidaccommodation.trip_id = trip.trip_id
/* BOOKED ACCOMMODATION */
LEFT JOIN (SELECT trip.trip_id, SUM(accommodation.totalaftertax) as booked_accommodation_total
FROM TRIP
INNER JOIN RESERVATION ON reservation.trip_id = trip.trip_id
INNER JOIN ACCOMMODATION ON accommodation.reservationid = reservation.reservationid
WHERE accommodation.created >= '2021-05-01' AND accommodation.created <= '2021-05-23'
AND accommodation.invoiceid IS NULL AND accommodation.resstatus <> 2
GROUP BY trip.trip_id) AS bookedaccommodation ON bookedaccommodation.trip_id = trip.trip_id
/* PAID CARRENTAL */
LEFT JOIN (SELECT trip.trip_id, SUM(carrental.rental_price) as booked_car_hire_total
FROM TRIP
INNER JOIN RESERVATION ON reservation.trip_id = trip.trip_id
INNER JOIN CARRENTAL ON carrental.reservationid = reservation.reservationid
WHERE carrental.created >= '2021-05-01' AND carrental.created <= '2021-05-23' AND carrental.status <> 2
GROUP BY trip.trip_id) AS bookedcarhire ON bookedcarhire.trip_id = trip.trip_id
/* PAID FLIGHT DETAILS */
LEFT JOIN ( SELECT trip.trip_id, SUM(flightfare.totalfare) as allstatuses_flighttot, SUM(flightfare.savings) as savings, SUM(flightfare.publishedfare) as publishedfare, MIN(flightfare.created) as earliestbooking
FROM TRIP
INNER JOIN TRIPTRAVELLERMAP ON triptravellermap.tripid = trip.trip_id
INNER JOIN FLIGHTFARE ON flightfare.triptravid = triptravellermap.triptravid
WHERE flightfare.created >= '2021-05-01' AND flightfare.created <= '2021-05-23'
AND (flightfare.status <> 2 OR (flightfare.status = 2 AND flightfare.ticketed = 1))
AND NOT(flightfare.ticketed = 0 AND flightfare.tickettimelimit < CURRENT_TIMESTAMP)
GROUP BY trip.trip_id ) AS paidflightfare ON paidflightfare.trip_id = trip.trip_id
/* TIME TO FIRST BOOKING */
LEFT JOIN (SELECT cc.trip_id, MIN(earliest_booked_date) as earliestbookingdate
FROM(
(SELECT trip.trip_id, MIN(flightfare.created) as earliest_booked_date
FROM TRIP
INNER JOIN TRIPTRAVELLERMAP ON triptravellermap.tripid = trip.trip_id
INNER JOIN FLIGHTFARE ON flightfare.triptravid = triptravellermap.triptravid
GROUP BY trip.trip_id )
UNION
(SELECT trip.trip_id, MIN(carrental.created) as earliest_booked_date
FROM TRIP
INNER JOIN RESERVATION ON reservation.trip_id = trip.trip_id
INNER JOIN CARRENTAL ON carrental.reservationid = reservation.reservationid
GROUP BY trip.trip_id)
UNION
(SELECT trip.trip_id, MIN(accommodation.created) as earliest_booked_date
FROM TRIP
INNER JOIN RESERVATION ON reservation.trip_id = trip.trip_id
INNER JOIN ACCOMMODATION ON accommodation.reservationid = reservation.reservationid
GROUP BY trip.trip_id)
) as cc GROUP BY cc.trip_id) AS timetofirstbooking ON timetofirstbooking.trip_id = trip.trip_id
/* TRIP FEES */
LEFT JOIN (SELECT trip.trip_id, SUM(tripfee.feeamount + tripfee.feeamountvat) as feeamount
FROM TRIP
INNER JOIN TRIPFEE ON tripfee.tripid = trip.trip_id
GROUP BY trip.trip_id) AS tripservicefeepaid ON tripservicefeepaid.trip_id = trip.trip_id
/* TRIP CHANGE FEES */
LEFT JOIN (SELECT trip.trip_id, SUM(tripfee.feeamount + tripfee.feeamountvat) as feeamount
FROM TRIP
INNER JOIN TRIPFEE ON tripfee.tripid = trip.trip_id
WHERE tripfee.tripfeetype = 9
GROUP BY trip.trip_id) AS tripchangeservicefeepaid ON tripchangeservicefeepaid.trip_id = trip.trip_id
/* OTHER ITEMS PAID */
LEFT JOIN (SELECT trip.trip_id, SUM(otheritems.amount + otheritems.vat) as amount
FROM TRIP
INNER JOIN OTHERITEMS ON otheritems.tripid = trip.trip_id
GROUP BY trip.trip_id) AS otheritemspaid ON otheritemspaid.trip_id = trip.trip_id
LEFT JOIN (
SELECT requested,max(conclusiondate) as apprConcDate , max(requestedDate) as approvalRequestedDate, max(conclusiondate)- max(requestedDate) as timetoapprove
FROM approvalsubmission
left join triprequirements on approvalsubmission.requested=triprequirements.uri
where approvalsubmission.conclusion='APPROVED'
group by requested) as bob on bob.requested = triprequirements.uri
WHERE
trip.trip_id IN (SELECT DISTINCT(trip_id) FROM reservation WHERE reservation.created >= ('2021-05-01') AND reservation.created <= ('2021-05-23'))
--AND LOWER(employee.staff_code) LIKE LOWER('$P!{staffCode')
AND trip.status <> 2
AND trip.company_id = 6632
--AND $P!{costCentreCompanySwitcher}
group by trip.trip_id, tripstatus.name
ORDER BY min(lob.name)

Fixed:
remove the status from the group by clause:
max(tripstatus.name) AS status,
...
group by trip.trip_id

Related

Probably subquerys and not joins

This big query below returns me 2860 records and its correct number. I'm getting. The thing is that I need to add to this query invoice lines and make the same thing as I did with sale_order_lines "sum(l.price_subtotal / COALESCE(cr.rate, 1.0)) AS price_subtotal". I need to get the sum of price_subtotal of invoice lines.
so my first thought was to join tables like this.
JOIN sale_order_invoice_rel so_inv_rel on (so_inv_rel.order_id = s.id )
JOIN account_invoice inv on (inv.id = so_inv_rel.invoice_id and inv.state in ('open','paid'))
JOIN account_invoice_line ail on (inv.id = ail.invoice_id)
and then
sum(ail.price_subtotal / COALESCE(cr.rate, 1.0)) as price_subtotal
but after the first JOIN number of lines, I'm selecting is changing and even if I joins are done the numbers are way off basically I get 5x2860. So probably I need to make some subquery but at this point, I don't know how and asking for help.
WITH currency_rate AS (
SELECT r.currency_id,
COALESCE(r.company_id, c.id) AS company_id,
r.rate,
r.name AS date_start,
( SELECT r2.name
FROM res_currency_rate r2
WHERE r2.name > r.name AND r2.currency_id = r.currency_id AND (r2.company_id IS NULL OR r2.company_id = c.id)
ORDER BY r2.name
LIMIT 1) AS date_end
FROM res_currency_rate r
JOIN res_company c ON r.company_id IS NULL OR r.company_id = c.id
)
SELECT min(l.id) AS id,
l.product_id,
l.color_id,
l.product_size_id,
t.uom_id AS product_uom,
sum(l.product_uom_qty / u.factor * u2.factor) AS product_uom_qty,
sum(l.qty_delivered / u.factor * u2.factor) AS qty_delivered,
sum(l.qty_invoiced / u.factor * u2.factor) AS qty_invoiced,
sum(l.qty_to_invoice / u.factor * u2.factor) AS qty_to_invoice,
sum(l.price_total / COALESCE(cr.rate, 1.0)) AS price_total,
l.price_unit / COALESCE(cr3.rate, 1.0) AS price_total_by_cmp_curr,
sum(l.price_subtotal / COALESCE(cr.rate, 1.0)) AS price_subtotal,
count(*) AS nbr,
s.date_order AS date,
s.state,
s.partner_id,
s.user_id,
s.company_id,
date_part('epoch'::text, avg(date_trunc('day'::text, s.date_order) - date_trunc('day'::text, s.create_date))) / (24 * 60 * 60)::numeric(16,2)::double precision AS delay,
t.categ_id,
s.pricelist_id,
s.project_id AS analytic_account_id,
s.team_id,
p.product_tmpl_id,
partner.country_id,
partner.commercial_partner_id
FROM sale_order_line l
JOIN sale_order s ON l.order_id = s.id
JOIN res_partner partner ON s.partner_id = partner.id
LEFT JOIN product_product p ON l.product_id = p.id
LEFT JOIN product_template t ON p.product_tmpl_id = t.id
LEFT JOIN product_uom u ON u.id = l.product_uom
LEFT JOIN product_uom u2 ON u2.id = t.uom_id
LEFT JOIN res_company rc ON rc.id = s.company_id
LEFT JOIN product_pricelist pp ON s.pricelist_id = pp.id
LEFT JOIN currency_rate cr ON cr.currency_id = pp.currency_id AND cr.company_id = s.company_id AND cr.date_start <= COALESCE(s.date_order::timestamp with time zone, now()) AND (cr.date_end IS NULL OR cr.date_end > COALESCE(s.date_order::timestamp with time zone, now()))
LEFT JOIN currency_rate cr3 ON cr.currency_id = rc.currency_id AND cr.company_id = s.company_id AND cr.date_start <= COALESCE(s.date_order::timestamp with time zone, now()) AND (cr.date_end IS NULL OR cr.date_end > COALESCE(s.date_order::timestamp with time zone, now()))
GROUP BY l.product_id, t.uom_id, t.categ_id, s.date_order, s.partner_id, s.user_id, s.state, s.company_id, s.pricelist_id, s.project_id, s.team_id, l.color_id, cr3.rate, l.price_unit, l.product_size_id, p.product_tmpl_id, partner.country_id, partner.commercial_partner_id;
You can add the part that could be in a subquery to the with statement you already have to avoid the increase in number of lines, like so:
WITH currency_rate AS (
SELECT r.currency_id,
COALESCE(r.company_id, c.id) AS company_id,
r.rate,
r.name AS date_start,
( SELECT r2.name
FROM res_currency_rate r2
WHERE r2.name > r.name AND r2.currency_id = r.currency_id AND (r2.company_id IS NULL OR r2.company_id = c.id)
ORDER BY r2.name
LIMIT 1) AS date_end
FROM res_currency_rate r
JOIN res_company c ON r.company_id IS NULL OR r.company_id = c.id
)
, order_line_subtotal AS(
SELECT so_inv_rel.order_id, sum(ail.price_subtotal) as price_subtotal
FROM sale_order_invoice_rel so_inv_rel
JOIN account_invoice inv on (inv.id = so_inv_rel.invoice_id and inv.state in ('open','paid'))
JOIN account_invoice_line ail on (inv.id = ail.invoice_id)
GROUP BY so_inv_rel.order_id )
SELECT min(l.id) AS id,
....
From there it should be straightforward to add to the query without increasing the number of rows (since from the joins you already have a row for each order before the aggregation / group by.

Having count returning zero rows when it should return something

SELECT
P.P_DESC AS P,
D.PD_NAME AS D,
D.GPE_NBR AS GPE,
D.GPE_GEN_NM,
D.GPE_2_ ,
D.GPE_4c ,
SUBSTR (TRIM(C.FILL_DATE__ID),1,6),
C.fill_Date__ID,
Prod.PD_DESC ,
DMC.M_A_NBR,
DMC.MAD_NBR ,
DMC.FT_NAME,
DMC.LA_NAME,
DDB.DATE_DATE,
CAST((CURRENT_DATE - DDB.DATE_DATE)/365.25 as Int) M_AGE_TODAY,
RXDOC.PB_FT_NAME || ' ' || RXDOC.PB_LA_NAME ,
RXDOC.PB_ID,
DT.D_TYPE_DESC ,
CASE WHEN SDL.GEPE IS NOT NULL THEN 'Y' ELSE 'N' END AS FLAG,
COUNT (C.PHR_C_ID) AS CCount,
SUM (C.AMT_PAID) AS Spend
FROM O_PHAR_C C
INNER JOIN _D D ON C.D__ID=D.D__ID
INNER JOIN _P P
ON C.P__ID = P.P__ID
AND C.P__ID = 00001
INNER JOIN _M_CURR DMC ON C.PB_M_CURR_ID = DMC.M_CURR_ID
INNER JOIN _M DM ON DM.M__ID = DMC.M__ID
INNER JOIN _DATE DDB ON DDB.DATE__ID = DM.BIRTH_DATE__ID
INNER JOIN _M_ELIG_CURR DMEC ON C.PB_M_ELIG_CURR_ID = DMEC.M_ELIG_CURR_ID
LEFT OUTER JOIN BA_PROD_LAB_OWN.specialtyDList SDL
ON D.GPE_NBR = SDL.GPE
AND SDL.EFF_END_DATE >= CURRENT_DATE
LEFT OUTER JOIN _PD Prod ON DMEC.PD__ID = Prod.PD__ID
LEFT OUTER JOIN _RX_PB RXDOC ON C.PB__ID=RXDOC.PB__ID
LEFT OUTER JOIN _PHAR_D_TYPE DT ON C.PHAR_D_TYPE__ID = DT.PHAR_D_TYPE__ID
WHERE C.fill_date__ID BETWEEN 20170201 AND 20170228
AND C.RE_I = 'N'
AND C.sR__ID IN (96,13,203)
GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14, 15, 16, 17, 18, 19
HAVING COUNT(*) >= 10;
I want to use this query to return instances of the C.PHR_C_ID that are more than or equal to 10. However, when I limit my select statement to that column I get the desired result but when I add all these columns to it 'having count' returns 0. There are more than zero records; I think there is something wrong with the query.
The problem is that when you add in your fields and GROUP BY them, you get a count of records for that distinct list of fields. You can probably switch to a QUALIFY statement instead. This will sum up the results by distinct C.PHR_C_ID ignoring the other fields, and then return any fields for that distinct C.PHR_C_ID which has a count(*) >= 10.
SELECT
P.P_DESC AS P,
D.PD_NAME AS D,
D.GPE_NBR AS GPE,
D.GPE_GEN_NM,
D.GPE_2_ ,
D.GPE_4c ,
SUBSTR (TRIM(C.FILL_DATE__ID),1,6),
C.fill_Date__ID,
Prod.PD_DESC ,
DMC.M_A_NBR,
DMC.MAD_NBR ,
DMC.FT_NAME,
DMC.LA_NAME,
DDB.DATE_DATE,
CAST((CURRENT_DATE - DDB.DATE_DATE)/365.25 as Int) M_AGE_TODAY,
RXDOC.PB_FT_NAME || ' ' || RXDOC.PB_LA_NAME ,
RXDOC.PB_ID,
DT.D_TYPE_DESC ,
CASE
WHEN SDL.GEPE IS NOT NULL THEN 'Y'
ELSE 'N'
END AS FLAG,
COUNT(*) OVER (PARTITION BY C.PHR_C_ID) AS CCount,
C.AMT_PAID AS Spend
FROM O_PHAR_C C
INNER JOIN _D D
ON C.D__ID=D.D__ID
INNER JOIN _P P
ON C.P__ID = P.P__ID
AND C.P__ID = 00001
INNER JOIN _M_CURR DMC
ON C.PB_M_CURR_ID = DMC.M_CURR_ID
INNER JOIN _M DM
ON DM.M__ID = DMC.M__ID
INNER JOIN _DATE DDB
ON DDB.DATE__ID = DM.BIRTH_DATE__ID
INNER JOIN _M_ELIG_CURR DMEC
ON C.PB_M_ELIG_CURR_ID = DMEC.M_ELIG_CURR_ID
LEFT OUTER JOIN BA_PROD_LAB_OWN.specialtyDList SDL
ON D.GPE_NBR = SDL.GPE
AND SDL.EFF_END_DATE >= CURRENT_DATE
LEFT OUTER JOIN _PD Prod
ON DMEC.PD__ID = Prod.PD__ID
LEFT OUTER JOIN _RX_PB RXDOC
ON C.PB__ID=RXDOC.PB__ID
LEFT OUTER JOIN _PHAR_D_TYPE DT
ON C.PHAR_D_TYPE__ID = DT.PHAR_D_TYPE__ID
WHERE
C.fill_date__ID BETWEEN 20170201 AND 20170228
AND C.RE_I = 'N'
AND C.sR__ID IN (96,13,203)
QUALIFY COUNT(*) OVER (PARTITION BY C.PHR_C_ID) >= 10;

SQL - charges and payments in same column; how to break it out

I have a SQL query that is pulling data from multiple tables (I have 11 joins)
There is an "ARTransaction" table which contains charges, payments, adjustments, etc. and there is a "transactionCodeID" column inside of that which describes the type of transaction.
I am trying to select a lot of data, but need two separate columns(the ones with comments above them), one for charges and one for payments. Is there a way to achieve this without using the where clause? I tried to use a nested select statement but it returned the same value for every single row (the total amount)
I am attaching the query below - thanks in advance! Also I am fairly new to data retrieval so if anything else looks wonky any other advice is greatly appreciated.
SELECT CONVERT(varchar(10),sl.ServiceDtFrom, 101) AS 'srvdate'
, f.Alias AS 'svc dprtmnt'
, CASE WHEN pc.Alias IS NULL
THEN po.Name
ELSE pc.Description END AS 'svc dept grp'
, COUNT(clm.ID) AS 'clm cnt'
, COUNT(p.ID) AS 'ptnt count'
/* 1 */
, SUM(ar.Amount) AS 'all chgs' --ONLY CHARGES (tt.ID IN(1,2))
, SUM(sl.Units + sl.TimeUnits + sl.PhysicalStatusUnits) AS 'chg units sum'
/* 2 */
, SUM(ar.Amount) AS 'net pmt' --ONLY PAYMENTS (tt.ID IN(3,4,9,10,11,12,20,21))
FROM ARTransaction ar WITH (NOLOCK)
LEFT JOIN ServiceLine sl WITH (NOLOCK)
ON ar.ServiceLineID = sl.ID
LEFT JOIN Incident i WITH (NOLOCK)
ON sl.IncidentID = i.ID
LEFT JOIN HealthCareFacility f WITH (NOLOCK)
ON i.FacilityID = f.ID
LEFT JOIN ProvOrgFacility poc WITH (NOLOCK)
ON poc.FacilityID = f.ID
LEFT JOIN ProfitCenter pc WITH (NOLOCK)
ON poc.ProfitCenterID = pc.ID
LEFT JOIN ProviderOrganization po WITH (NOLOCK)
ON i.ProvOrgID = po.ID
LEFT JOIN Claim clm WITH (NOLOCK)
ON i.ID = clm.IncidentID
LEFT JOIN Person p WITH (NOLOCK)
ON i.PatientID = p.ID
LEFT JOIN TransactionCode tc WITH (NOLOCK)
ON ar.TransactionCodeID = tc.ID
LEFT JOIN TransactionType tt WITH (NOLOCK)
ON tc.TransactionTypeID = tt.ID
WHERE i.IsReversed <> 1
AND sl.ServiceDtFrom IS NOT NULL
GROUP BY
sl.ServiceDtFrom, f.Alias
, po.Name, pc.Alias, pc.Description
ORDER BY 1,3,2
You can use CASE statements to achieve this:-
SELECT CONVERT(varchar(10),sl.ServiceDtFrom, 101) AS 'srvdate'
, f.Alias AS 'svc dprtmnt'
, CASE WHEN pc.Alias IS NULL
THEN po.Name
ELSE pc.Description END AS 'svc dept grp'
, COUNT(clm.ID) AS 'clm cnt'
, COUNT(p.ID) AS 'ptnt count'
/* 1 */
, SUM(case when tt.ID IN(1,2) then ar.Amount else 0 end) AS 'all chgs' --ONLY CHARGES (tt.ID IN(1,2))
, SUM(sl.Units + sl.TimeUnits + sl.PhysicalStatusUnits) AS 'chg units sum'
/* 2 */
, SUM(case when tt.ID IN(3,4,9,10,11,12,20,21) then ar.Amount else 0 end) AS 'net pmt' --ONLY PAYMENTS (tt.ID IN(3,4,9,10,11,12,20,21))
FROM ARTransaction ar WITH (NOLOCK)
LEFT JOIN ServiceLine sl WITH (NOLOCK)
ON ar.ServiceLineID = sl.ID
LEFT JOIN Incident i WITH (NOLOCK)
ON sl.IncidentID = i.ID
LEFT JOIN HealthCareFacility f WITH (NOLOCK)
ON i.FacilityID = f.ID
LEFT JOIN ProvOrgFacility poc WITH (NOLOCK)
ON poc.FacilityID = f.ID
LEFT JOIN ProfitCenter pc WITH (NOLOCK)
ON poc.ProfitCenterID = pc.ID
LEFT JOIN ProviderOrganization po WITH (NOLOCK)
ON i.ProvOrgID = po.ID
LEFT JOIN Claim clm WITH (NOLOCK)
ON i.ID = clm.IncidentID
LEFT JOIN Person p WITH (NOLOCK)
ON i.PatientID = p.ID
LEFT JOIN TransactionCode tc WITH (NOLOCK)
ON ar.TransactionCodeID = tc.ID
LEFT JOIN TransactionType tt WITH (NOLOCK)
ON tc.TransactionTypeID = tt.ID
WHERE i.IsReversed <> 1
AND sl.ServiceDtFrom IS NOT NULL
GROUP BY

using a subquery as a column that's using another column in the 'where' clause

I probably messed that title up! So I have a column called "programID" and I want another column to tell me the most recent date an order was placed using the programID. I think I'd need a subcolumn but the problem is how do I use the row's programID so that it matches the programID for the same row?
DECLARE #ClientIDs varchar(4) = 6653;
DECLARE #ProgramStatus char(1) = 'Y'; -- Y, N, or R
SELECT
rcp.RCPID AS ProgramID
, rcp.RCPName AS Program
, rcp.RCPActive AS ProgramStatus
, aa.AACustomCardInternalReview AS VCCP
, aa.AAJobNo AS AAJobNo
, aas.AASName AS AAStatus
, rcp.RCPOpsApproved AS OpsApproved
, clt.CltID AS ClientID
, rcp.RCPSignatureRequired AS SignatureRequired
, st.STEnumValue AS DefaultShipType
, rcp.RCPShipMethodOverrideType AS ShipTypeOverride
,aa.AANetworkProgramID
,(Select max(cdconfirmationdate) from carddet where ) --can't figure this part out
FROM
RetailerCardProgram rcp WITH (NOLOCK)
INNER JOIN ClientRetailerMap crm WITH (NOLOCK)
ON crm.CRMRetailerID = rcp.RCPRetailerID
INNER JOIN Client clt WITH(NOLOCK)
ON clt.CltID = crm.CRMCltID
LEFT JOIN AssociationApproval aa WITH (NOLOCK)
ON aa.AARetailerID = rcp.RCPRetailerID
AND aa.AABin = rcp.RCPBin6
AND aa.AAFrontOfPlasticTemplateID = rcp.RCPFOCTemplateID
AND aa.AABackOfPlasticTemplateID = rcp.RCPBOCTemplateID
AND ISNULL(aa.AACardID, 0) = ISNULL(rcp.RCPDefaultPlasticCardID, 0)
-- AND LOWER(rcp.RCPName) NOT LIKE '%do not use%' -- Needed for AA Job Number 1594
LEFT JOIN AssociationApprovalStatus aas WITH (NOLOCK)
ON aas.AASID = aa.AAAssociationApprovalStatusID
LEFT JOIN OpenLoopAssociation ola WITH (NOLOCK)
ON ola.OLAID=rcp.RCPOLAID
LEFT JOIN ClientCardProgramMap ccpm WITH (NOLOCK)
ON ccpm.CCPMCardProgramID = rcp.RCPID
AND ccpm.CCPMClientID = clt.CltID
LEFT JOIN TippingModule tm WITH (NOLOCK)
ON tm.TMid = rcp.RCPTippingModuleID
LEFT JOIN GiftCardTemplate fgt WITH (NOLOCK)
ON fgt.gtid = rcp.RCPFOCTemplateID
AND fgt.GTPage='P'
LEFT JOIN GiftCardTemplate bgt WITH (NOLOCK)
ON bgt.gtid = rcp.RCPBOCTemplateID
AND bgt.GTPage='PB'
LEFT JOIN Card c WITH (NOLOCK)
ON c.CardID = rcp.RCPDefaultCarrierID
LEFT JOIN CardType ct WITH (NOLOCK)
ON ct.CTID = c.CardTypeID
LEFT JOIN RetailerCardProgramTCSheetMap rtm1 WITH (NOLOCK)
ON rtm1.RTMRCPID = rcp.RCPID
AND rtm1.RTMInsertOrder = 1
LEFT JOIN RetailerCardProgramTCSheetMap rtm2 WITH (NOLOCK)
ON rtm2.RTMRCPID = rcp.RCPID
AND rtm2.RTMInsertOrder = 2
LEFT JOIN RetailerCardProgramTCSheetMap rtm3 WITH (NOLOCK)
ON rtm3.RTMRCPID = rcp.RCPID
AND rtm3.RTMInsertOrder = 3
LEFT JOIN RetailerCardProgramTCSheetMap rtm4 WITH (NOLOCK)
ON rtm4.RTMRCPID = rcp.RCPID
AND rtm4.RTMInsertOrder = 4
LEFT JOIN TCSheet i1 WITH (NOLOCK)
ON i1.TCSID = rtm1.RTMTCSID
LEFT JOIN TCSheet i2 WITH (NOLOCK)
ON i2.TCSID = rtm2.RTMTCSID
LEFT JOIN TCSheet i3 WITH (NOLOCK)
ON i3.TCSID = rtm3.RTMTCSID
LEFT JOIN TCSheet i4 WITH (NOLOCK)
ON i4.TCSID = rtm4.RTMTCSID
LEFT JOIN ShipType st WITH (NOLOCK)
ON st.STId = rcp.RCPDefaultShipTypeID
WHERE
clt.CltID IN (#ClientIDs) -- 6653 and 6657.
AND rcp.RCPActive IN (#ProgramStatus)
ORDER BY
AAJobNo
, Program
You want to join with a nested select on the table carddet. I'm inferring that RCPID is the relationship between carddet and your main table RetainerCardProgram...
SELECT rcp.RCPID AS ProgramID,
date.MAXDATE AS MaxDate,
rest of your columns...
FROM RetailerCardProgram rcp WITH (NOLOCK)
INNER JOIN (
SELECT RCPID, MAX(cdconfirmationdate) as 'MAXDATE'
FROM carddet
GROUP BY RCPID
) date on date.RCPID = rcp.RCPID
rest of query...
You may want a left join if not all IDs have a date in carddet.
Obligatory addition: Also your use of NOLOCK is a bit terrifying. See http://blogs.sqlsentry.com/aaronbertrand/bad-habits-nolock-everywhere/

Conditionally Join the Table with a Condition

I have 3 tables. BaseProducts, Products and ProductsMerchants. I need to find the count using a condition. This is my SQL,
ALTER PROCEDURE [dbo].[GetTotalProductsCount]
(
#SuperUser bit,
#MarchantId int
)
AS
BEGIN
IF(#SuperUser = 1)
BEGIN
SELECT COUNT(*) AS Total
FROM [dbo].[BaseProducts]
END
ELSE
BEGIN
SELECT COUNT(*) AS Total
FROM [dbo].[BaseProducts] BP
INNER JOIN [dbo].[Products] P ON P.BaseProductId = BP.Id
INNER JOIN ProductsMerchants PM ON PM.ProductId = P.Id
WHERE PM.MarchantId = #MarchantId;
END
END
The problem is that I need to rewrite the same query just for checking a condition. Can I make it one query?
You can do this:
SELECT COUNT(*) AS Total
FROM [dbo].[BaseProducts]
WHERE #SuperUser = 1
UNION ALL
SELECT COUNT(*) AS Total
FROM [dbo].[BaseProducts] BP
INNER JOIN [dbo].[Products] P ON P.BaseProductId = BP.Id
INNER JOIN ProductsMerchants PM ON PM.ProductId = P.Id
WHERE PM.MarchantId = #MarchantId AND #SuperUser <> 1;
Personally, I find the if form more understandable.
If the inner joins are being used for filtering and don't increase the number of rows, you could also do:
SELECT COUNT(*) AS Total
FROM [dbo].[BaseProducts] BP
LEFT JOIN [dbo].[Products] P ON P.BaseProductId = BP.Id
LEFT JOIN ProductsMerchants PM ON PM.ProductId = P.Id
WHERE PM.MarchantId = #MarchantId OR #SuperUser = 1;
(The PM.MarchantId = #MarchantId undoes the left outer join.)
But once again, I find that the intent of the if is clearer.
Or even this:
SELECT (CASE WHEN #SuperUser = 1 THEN CNT ELSE COUNT(*) END) AS Total
FROM (SELECT COUNT(*) as CNT FROM [dbo].[BaseProducts] BP) const CROSS JOIN
[dbo].[BaseProducts] BP
INNER JOIN [dbo].[Products] P ON P.BaseProductId = BP.Id
INNER JOIN ProductsMerchants PM ON PM.ProductId = P.Id
WHERE PM.MarchantId = #MarchantId OR #SuperUser = 1;