How to improve performance of left join in AX Query - sql

I'm trying to create a query to fetch dynamics AX salestable records with their history log records in a left join.
If I filter on the salesid I naturally get the results in a split second, but if I leave out the salesid it takes forever even though SalesID SN16129492 is the only 'valid' record for 2017/03/08?:
Declare #Bedrijf NVARCHAR(3) = 'dat';
Declare #leverdatum_van DATE = convert(datetime, '2017/03/08');
Declare #leverdatum_tot DATE = convert(datetime, '2017/03/08');
Declare #bedrag decimal = 1;
SELECT SUM(SALESLINE.LINEAMOUNT) AS waarde, SALESTABLE.CUSTACCOUNT, SALESTABLE.SALESID as ordernummer, CUSTTABLE.NAME as klantnaam,
MAX(SalesTable.createdBy) as ingevoerd_door, log.dader, log.veldnummer, log.huidigestatus, log.vorigestatus
FROM SALESLINE INNER JOIN
SALESTABLE ON SALESLINE.SALESID = SALESTABLE.SALESID AND SALESLINE.DATAAREAID = SALESTABLE.DATAAREAID
INNER JOIN CUSTTABLE ON
SALESTABLE.DATAAREAID=CUSTTABLE.DATAAREAID AND SALESTABLE.CUSTACCOUNT=CUSTTABLE.ACCOUNTNUM
LEFT JOIN
(select dbo.CONPEEK(CAST(dbo.CONPEEK(data, 2) AS varbinary(8000)), 2) AS DADER,
dbo.CONPEEK(CAST(dbo.CONPEEK(data, 5) AS varbinary(8000)), 1) AS VELDNUMMER,
dbo.CONPEEK(CAST(dbo.CONPEEK(data, 5) AS varbinary(8000)), 2) AS HUIDIGESTATUS,
dbo.CONPEEK(CAST(dbo.CONPEEK(data, 5) AS varbinary(8000)), 3) AS VORIGESTATUS, LOGRECID
from SYSDATABASELOG
where TABLE_=366 AND DATAAREAID=#bedrijf
) log
ON SALESTABLE.DATAAREAID=#bedrijf AND SALESTABLE.RECID=log.LOGRECID
WHERE (SALESLINE.DATAAREAID = #bedrijf) AND SALESTABLE.SALESTYPE=4 --retourorder
AND (SALESTABLE.SHIPPINGDATEREQUESTED between #leverdatum_van and #leverdatum_tot)
--AND (Salestable.SALESID = 'SN16129492')
AND (SALESTABLE.SalesOrderStatus in (0,1,2,3,4,5))
GROUP BY SALESTABLE.CUSTACCOUNT, SALESTABLE.SALESID, CUSTTABLE.NAME, log.dader, log.veldnummer, log.huidigestatus, log.vorigestatus
HAVING (SUM(ABS(SALESLINE.LINEAMOUNT))> #bedrag)
Is there a way to improve performance / redesign this query?

You must explicitly include dataareaid AND partition in conditions for all tables (you can either add it as a filter, or in join conditions), even if you just have a single partition.
By default, AX2012 implicitly adds the dataareaid and partition fields to all indexes created in the AOT as the first two fields.
As we found, SQL server is notoriously bad with handling these kinds of indexes, apart from the fact that it can't just "skip" the first level if you don't provide it explicitly (partition), it is also really bad at estimating index statistics (SQL server does a histogram for the first field in the index, and assumes the rest of the fields in the index have a normal distribution).

This is not an answer, but I couldn't write code so long in a comment.
The result could be different, but it's just to help us understand if a part of your query degrade performance (GROUP BY). I moved part of group fields outside.
Can you try this and see if it is "going forever" (using your words)?
SELECT A.*
,log.dader
,log.veldnummer
,log.huidigestatus
,log.vorigestatus
FROM (
SELECT SUM(SALESLINE.LINEAMOUNT) AS waarde
,SALESTABLE.CUSTACCOUNT
,SALESTABLE.SALESID AS ordernummer
,CUSTTABLE.NAME AS klantnaam
,MAX(SalesTable.createdBy) AS ingevoerd_door
,SALESTABLE.RECID /* ADDED THIS */
FROM SALESLINE
INNER JOIN SALESTABLE ON SALESLINE.SALESID = SALESTABLE.SALESID AND SALESLINE.DATAAREAID = SALESTABLE.DATAAREAID
INNER JOIN CUSTTABLE ON SALESTABLE.DATAAREAID = CUSTTABLE.DATAAREAID AND SALESTABLE.CUSTACCOUNT = CUSTTABLE.ACCOUNTNUM
WHERE SALESLINE.DATAAREAID = #bedrijf
AND SALESTABLE.SALESTYPE = 4 --retourorder
AND SALESTABLE.SHIPPINGDATEREQUESTED BETWEEN #leverdatum_van AND #leverdatum_tot
--AND (Salestable.SALESID = 'SN16129492')
AND SALESTABLE.SalesOrderStatus IN (0, 1, 2, 3, 4, 5)
GROUP BY SALESTABLE.CUSTACCOUNT
,SALESTABLE.SALESID
,CUSTTABLE.NAME
,SALESTABLE.RECID /* ADDED THIS */
HAVING SUM(ABS(SALESLINE.LINEAMOUNT)) > #bedrag
) A
LEFT JOIN (SELECT dbo.CONPEEK(CAST(dbo.CONPEEK(data, 2) AS VARBINARY(8000)), 2) AS DADER
,dbo.CONPEEK(CAST(dbo.CONPEEK(data, 5) AS VARBINARY(8000)), 1) AS VELDNUMMER
,dbo.CONPEEK(CAST(dbo.CONPEEK(data, 5) AS VARBINARY(8000)), 2) AS HUIDIGESTATUS
,dbo.CONPEEK(CAST(dbo.CONPEEK(data, 5) AS VARBINARY(8000)), 3) AS VORIGESTATUS
,LOGRECID
FROM SYSDATABASELOG
WHERE TABLE_ = 366
AND DATAAREAID = #bedrijf) log ON SALESTABLE.RECID = log.LOGRECID

By adding filters for createddatetime for bot left and right table I've decreased processing time from 'forever' to < 30 seconds for a period of one month.
Optimized Query:
Declare #Bedrijf NVARCHAR(3) = 'dat';
Declare #leverdatum_van DATETIME = convert(datetime, '2017/03/01');
Declare #leverdatum_tot DATETIME = convert(datetime, '2017/04/01');
Declare #bedrag decimal = 1;
SELECT SUM(SALESLINE.LINEAMOUNT) AS waarde, SALESTABLE.CUSTACCOUNT, SALESTABLE.SALESID as ordernummer, CUSTTABLE.NAME as klantnaam,
MAX(SalesTable.createdBy) as ingevoerd_door, log.dader, log.veldnummer, log.huidigestatus, log.vorigestatus
FROM SALESLINE
INNER JOIN SALESTABLE
ON SALESLINE.SALESID = SALESTABLE.SALESID AND SALESLINE.DATAAREAID = SALESTABLE.DATAAREAID
INNER JOIN CUSTTABLE
ON SALESTABLE.DATAAREAID=CUSTTABLE.DATAAREAID AND SALESTABLE.CUSTACCOUNT=CUSTTABLE.ACCOUNTNUM
LEFT JOIN (
select dbo.CONPEEK(CAST(dbo.CONPEEK(data, 2) AS varbinary(8000)), 2) AS DADER,
dbo.CONPEEK(CAST(dbo.CONPEEK(data, 5) AS varbinary(8000)), 1) AS VELDNUMMER,
dbo.CONPEEK(CAST(dbo.CONPEEK(data, 5) AS varbinary(8000)), 2) AS HUIDIGESTATUS,
dbo.CONPEEK(CAST(dbo.CONPEEK(data, 5) AS varbinary(8000)), 3) AS VORIGESTATUS, LOGRECID
from SYSDATABASELOG
where TABLE_=366 AND DATAAREAID=#bedrijf
and createddatetime between #leverdatum_van and #leverdatum_tot
) log
ON SALESTABLE.DATAAREAID=#bedrijf AND SALESTABLE.RECID=log.LOGRECID
WHERE (SALESLINE.DATAAREAID = #bedrijf) AND SALESTABLE.SALESTYPE=4 --retourorder
AND (SALESTABLE.createddatetime between #leverdatum_van and #leverdatum_tot)
--AND (Salestable.SALESID = 'SN16129492')
AND (SALESTABLE.SalesOrderStatus in (0,1,2,3,4,5))
GROUP BY SALESTABLE.CUSTACCOUNT, SALESTABLE.SALESID, CUSTTABLE.NAME, log.dader, log.veldnummer, log.huidigestatus, log.vorigestatus
HAVING (SUM(ABS(SALESLINE.LINEAMOUNT))> #bedrag)
More clustered index seeks in the new execution plan:
Thnx for helping me out ;)

Related

Why is SQL doing an inner join where an outer join is needed

I have two tables which I want to "outer" join (and then fetch) using SQL. The exact SQL query (in question) is:
SELECT
LEFT(a.cusip, 6) AS cusip6,
a.date, a.prc, a.ret, a.vol, a.spread, a.shrout,
b.epsf12, (b.seqq-b.pstkq) / b.cshoq AS bps
FROM
crsp.msf a
FULL JOIN
compa.fundq b ON (LEFT(a.cusip, 6) = LEFT(b.cusip, 6)
AND a.date = b.datadate)
WHERE
(b.datadate BETWEEN '2010-01-01' and '2015-12-31')
AND (a.date BETWEEN '2010-01-01' and '2015-12-31')
AND (b.cshoq > 0)
This returns 670'293 rows.
But when I fetch the two datasets separately and (outer) join them through R-merge(), I get 1'182'093 rows. The two separate queries I use are:
SELECT
LEFT(cusip, 6) AS cusip6, date, prc, ret, vol, spread, shrout
FROM
crsp.msf
WHERE
date BETWEEN '2010-01-01' and '2015-12-31'
SELECT
LEFT(cusip, 6) AS cusip6, datadate AS date, epsf12,
(seqq-pstkq)/cshoq AS bps
FROM
compa.fundq
WHERE
datadate BETWEEN '2010-01-01' and '2015-12-31'
AND cshoq > 0
And then I merge (outer join) using:
merge(x = data_1, y = data_2, by.x = c("cusip6", "date"), by.y = c("cusip6", "date"), all = T)
This returns 1'182'093 rows which is correct. So my original (first) SQL query is in fact performing an "inner join" when I explicitly specify an outer join. The below R-merge() returned 670'293 rows re-validating that the fetched data from SQL is indeed an inner join.
merge(x = data_1, y = data_2, by.x = c("cusip6", "date"), by.y = c("cusip6", "date"))
What am I doing wrong with my SQL query?
Because the WHERE clause is applied after the JOINs. At this point there are NULL values (as a result of 'failed' JOINs), and those rows fail the WHERE clause.
If you want an OUTER JOIN and a filter, put the filter in the JOIN or a sub-query.
SELECT
LEFT(a.cusip, 6) AS cusip6,
a.date, a.prc, a.ret, a.vol, a.spread, a.shrout,
b.epsf12, (b.seqq-b.pstkq) / b.cshoq AS bps
FROM
(SELECT * FROM crsp.msf WHERE date BETWEEN '2010-01-01' and '2015-12-31') a
FULL JOIN
(SELECT * FROM compa.fundq WHERE datadate BETWEEN '2010-01-01' and '2015-12-31' AND cshoq > 0) b
ON LEFT(a.cusip, 6) = LEFT(b.cusip, 6)
AND a.date = b.datadate

Progress SQL Column cannot be found or is not specified for query

I'm writing SQL against a Progress 10.2B07 database and am getting the following error "Column 'OUTERINVOICEHEADER.MEMBERID' cannot be found or is not specified for query (13865).
Here is the query:
select concat(substring(OuterInvoiceHeader.sold_to_cust_nbr, 1, 6) + '-', OuterInvoiceHeader.sold_to_cust_seq) as MemberID,
sum(OuterInvoiceHeader.net_weight) as TotalInvoicePounds,
sum(OuterInvoiceHeader.net_weight / 2000) as TotalTons,
sum(OuterInvoiceHeader.invoice_amt) as InvoiceAmount,
sum(InvoiceSurcharges.Surcharge) as Surcharges,
sum(OuterInvoiceHeader.invoice_amt - InvoiceSurcharges.Surcharge) as Total,
sum(Returns.qty_received) as PoundsReturned
from AXS.PUB.ivc_header OuterInvoiceHeader
inner join
(select m.invoice_nbr, sum(m.extension) Surcharge from AXS.PUB.ivc_mchgs m
inner join
AXS.PUB.ivc_header h
on h.invoice_nbr = m.invoice_nbr
group by m.invoice_nbr) InvoiceSurcharges
on OuterInvoiceHeader.invoice_nbr = InvoiceSurcharges.invoice_nbr
left outer join
(select concat(substring(ReturnHeader.ship_to_nbr, 1, 6)+'-',InnerInvoiceHeader.sold_to_cust_seq) as ReturnMemberID,
ReturnHeader.invoice_nbr as ReturnInvoiceNum,
qty_received
from AXS.PUB.return_hdr ReturnHeader
inner join
AXS.PUB.ivc_header InnerInvoiceHeader
on ReturnHeader.invoice_nbr = InnerInvoiceHeader.invoice_nbr
inner join AXS.PUB.return_line ReturnLine
on ReturnHeader.claim_nbr = ReturnLine.claim_nbr
where ReturnInvoiceNum = '0001010914'
group by ReturnMemberID, ReturnInvoiceNum, qty_received) Returns
on OuterInvoiceHeader.MemberID = Returns.ReturnMemberID
--on OuterInvoiceHeader.invoice_nbr = Returns.ReturnInvoiceNum
where OuterInvoiceHeader.sold_to_cust_nbr = '000837' and OuterInvoiceHeader.invoice_date between '06/01/2016' and '06/30/2016' and OuterInvoiceHeader.invoice_status = '5804' and OuterInvoiceHeader.invoice_type='5601'
group by MemberID
The problem is in the left join; the commented out on clause "on OuterInvoiceHeader.invoice_nbr = Returns.ReturnInvoiceNum" will work if uncommented. The "on OuterInvoiceHeader.MemberID = Returns.ReturnMemberID" clause gives me the error.
What I don't understand is that both of these reference a column in the top SELECT statement, the only difference is that one is a concatenation and the other is not.
I hope that I just can't see the forest for the trees here and the answer is simple, so if anyone has any suggestions or questions I'm all ears.
try this:
I replaced the references to the alias MemberID to be the actual concatinated columns CONCAT(SUBSTRING(OuterInvoiceHeader.sold_to_cust_nbr, 1, 6)+'-', OuterInvoiceHeader.sold_to_cust_seq)
SELECT CONCAT(SUBSTRING(OuterInvoiceHeader.sold_to_cust_nbr, 1, 6)+'-', OuterInvoiceHeader.sold_to_cust_seq) AS MemberID
, SUM(OuterInvoiceHeader.net_weight) AS TotalInvoicePounds
, SUM(OuterInvoiceHeader.net_weight / 2000) AS TotalTons
, SUM(OuterInvoiceHeader.invoice_amt) AS InvoiceAmount
, SUM(InvoiceSurcharges.Surcharge) AS Surcharges
, SUM(OuterInvoiceHeader.invoice_amt - InvoiceSurcharges.Surcharge) AS Total
, SUM(Returns.qty_received) AS PoundsReturned
FROM AXS.PUB.ivc_header OuterInvoiceHeader
INNER JOIN
(SELECT m.invoice_nbr
, SUM(m.extension) Surcharge
FROM AXS.PUB.ivc_mchgs m
INNER JOIN AXS.PUB.ivc_header h ON h.invoice_nbr = m.invoice_nbr
GROUP BY m.invoice_nbr) InvoiceSurcharges ON OuterInvoiceHeader.invoice_nbr = InvoiceSurcharges.invoice_nbr
LEFT OUTER JOIN
(SELECT CONCAT(SUBSTRING(ReturnHeader.ship_to_nbr, 1, 6)+'-', InnerInvoiceHeader.sold_to_cust_seq) AS ReturnMemberID
, ReturnHeader.invoice_nbr AS ReturnInvoiceNum
, qty_received
FROM AXS.PUB.return_hdr ReturnHeader
INNER JOIN AXS.PUB.ivc_header InnerInvoiceHeader ON ReturnHeader.invoice_nbr = InnerInvoiceHeader.invoice_nbr
INNER JOIN AXS.PUB.return_line ReturnLine ON ReturnHeader.claim_nbr = ReturnLine.claim_nbr
WHERE ReturnInvoiceNum = '0001010914'
GROUP BY ReturnMemberID
, ReturnInvoiceNum
, qty_received) Returns ON CONCAT(SUBSTRING(OuterInvoiceHeader.sold_to_cust_nbr, 1, 6)+'-', OuterInvoiceHeader.sold_to_cust_seq) = Returns.ReturnMemberID
--on OuterInvoiceHeader.invoice_nbr = Returns.ReturnInvoiceNum
WHERE OuterInvoiceHeader.sold_to_cust_nbr = '000837'
AND OuterInvoiceHeader.invoice_date BETWEEN '06/01/2016' AND '06/30/2016'
AND OuterInvoiceHeader.invoice_status = '5804'
AND OuterInvoiceHeader.invoice_type = '5601'
GROUP BY CONCAT(SUBSTRING(OuterInvoiceHeader.sold_to_cust_nbr, 1, 6)+'-', OuterInvoiceHeader.sold_to_cust_seq);
Basically you need to keep in mind the order which SQL statements are executed:
FROM clause
WHERE clause
GROUP BY clause
HAVING clause
SELECT clause
ORDER BY clause
That's a computed column alias and thus the error. You should consider using the entire expression rather like
on concat(substring(OuterInvoiceHeader.sold_to_cust_nbr, 1, 6) + '-', OuterInvoiceHeader.sold_to_cust_seq) = Returns.ReturnMemberID
Instead of on OuterInvoiceHeader.MemberID = Returns.ReturnMemberID. As well, change any other place where you are using the same alias. You can and should use that alias only in a outer query and not in the same query.

SQL - SELECT subquery AS BIT value for EXIST check

I have a problem.
I'm trying to get a BIT value to check whether a person has entered the building last night between 10pm to midnight. When I run the subquery code by itself, it gives me the results I need. As I'm working with SSRS2008 I need for all the results to be in the same stored procedure.
So the problem is, it gives me the bit values somewhat right, for the ones that are obviously false, it gives false, for the ones that are obviously true, it gives true. But for the ones in the middle (the day shift, who leave at 23) it gives the results somewhat random..
Does anyone have a clue?
SELECT DISTINCT TOP 200
Events.LoggedTime,
PTUsers.Name,
PTDoors.PTDoorsID,
PTUsers.AccessLevel,
CAST(CASE
WHEN EXISTS (SELECT Events.LoggedTime
FROM Events
INNER JOIN PTUsers AS PTUsers_1 ON Events.GlobalIndex1 = PTUsers.GlobalRecord
INNER JOIN PTDoors AS PTDoors_1 ON Events.RecordIndex2 + 1 = PTDoors.Address
WHERE (DATEPART(day, Events.LoggedTime) = DATEPART(day, GETDATE() - 1))
AND (DATEPART(hour, Events.LoggedTime) IN (22, 23))
AND (PTDoors_1.PTDoorsID = 14)) THEN 1 ELSE 0 END AS BIT) AS Night
FROM
Events
INNER JOIN
PTUsers ON Events.GlobalIndex1 = PTUsers.GlobalRecord
INNER JOIN
PTDoors ON Events.RecordIndex2 + 1 = PTDoors.Address
WHERE
(PTUsers.Panel = 0)
AND (PTDoors.Panel = 0)
AND (PTDoors.PTDoorsID = 14)
AND (DATEPART(day, Events.LoggedTime) = DATEPART(day, GETDATE()) - 1)
AND (PTUsers.AccessLevel IN (3))
ORDER BY
Events.LoggedTime DESC
#lrd i did the corrections you suggested,
thanks for pointing out the table aliases :)
i removed the cast, so now i get the BIT column. but now the problem is that i get all "1"'s as results.
What baffles me is the that subquery works as it should as a query on it's own. it goes back a day, and displays the entries on that door in the given timeframe.
now i'm trying to compare that information for the same person, meaning - i see a person in the list, arriving yesterday at 6am, check if that person also arrived the day before that between 22 & midnight and return a bit value to display that.
SELECT DISTINCT TOP 200 Events.LoggedTime, PTUsers.Name, PTDoors.PTDoorsID, PTUsers.AccessLevel, CASE WHEN EXISTS
(SELECT Events.LoggedTime
FROM Events INNER JOIN
PTUsers AS PTUsers_1 ON Events.GlobalIndex1 = PTUsers_1.GlobalRecord INNER JOIN
PTDoors AS PTDoors_1 ON Events.RecordIndex2 + 1 = PTDoors_1.Address
WHERE (DATEPART(day, Events.LoggedTime) = DATEPART(day, GETDATE() - 1)) AND (DATEPART(hour, Events.LoggedTime) IN (22, 23)) AND
(PTDoors_1.PTDoorsID = 14)) THEN 1 ELSE 0 END AS BIT
FROM Events INNER JOIN
PTUsers ON Events.GlobalIndex1 = PTUsers.GlobalRecord INNER JOIN
PTDoors ON Events.RecordIndex2 + 1 = PTDoors.Address
WHERE (PTUsers.Panel = 0) AND (PTDoors.Panel = 0) AND (PTDoors.PTDoorsID = 14) AND (DATEPART(day, Events.LoggedTime) = DATEPART(day, GETDATE())
- 1) AND (PTUsers.AccessLevel IN (3))
ORDER BY Events.LoggedTime DESC
I don't think you need a CAST because you are explicitly defining Night as a BIT By setting the result to EXISTS(), which is a bit. I removed the query that was incorrect.
I see your problem. You are not using the correct table alias for your join constraint in your subquery.
It should be:
INNER JOIN PTUsers AS PTUsers_1 ON Events.GlobalIndex1 = PTUsers_1.GlobalRecord
INNER JOIN PTDoors AS PTDoors_1 ON Events.RecordIndex2 + 1 = PTDoors_1.Address
Also,check and make sure you are using the correct values in your join. I would change the following for a test.
FROM Events Events_2
INNER JOIN PTUsers AS PTUsers_1 ON Events_2.GlobalIndex1 = PTUsers_1.GlobalRecord
INNER JOIN PTDoors AS PTDoors_1 ON Events_2.RecordIndex2 + 1 = PTDoors_1.Address

Using results of JOIN statement to join on the results of another JOIN statement

I have the following 2 Join Statements:
--Get Total Hrs
DECLARE #BeginDate datetime, #EndDate datetime
set #BeginDate = '01-01-2013'
set #EndDate = '12-31-2013'
BEGIN
SELECT F.Type, E.Product, SUM(F.Hours * E.Amount) AS 'Total Hours'
FROM Hours H
INNER JOIN Equipment E
ON F.SN = E.SN
WHERE (F.Date BETWEEN #BeginDate AND #EndDate)
GROUP BY F.Type, E.Product
ORDER BY Product ASC
END
--Get Number of Unscheduled Removals
DECLARE #BeginDate1 datetime, #EndDate1 datetime
set #BeginDate1 = '01-01-2013'
set #EndDate1 = '12-31-2013'
BEGIN
SELECT LEFT(dbo.fn_GetPartName(R.PartID),CHARINDEX('-',dbo.fn_GetPartName(R.PartID), 1) - 1) AS 'Part No',
Count(s.status) AS NumberUnscheduledRemovals
FROM Repair R
INNER JOIN Conversion C
ON R.Performed = C.Performed
AND R.Confirmed = C.Confirmed
INNER JOIN Status S
ON C.StatusID = S.StatusID
WHERE (R.Received BETWEEN #BeginDate1 AND #EndDate1)
AND (S.Status = 'UNSCHEDULED')
GROUP BY LEFT(dbo.fn_GetPartName(R.PartID),CHARINDEX('-',dbo.fn_GetPartName(R.PartID), 1) - 1)
ORDER BY LEFT(dbo.fn_GetPartName(R.PartID),CHARINDEX('-',dbo.fn_GetPartName(R.PartID), 1) - 1) ASC
END
Both queries have results including part numbers (these have the same values). I want to INNER JOIN the results from both queries on the resulting part numbers. have been trying for a while but cant seem to get the syntax right to do it.
Use a temp table using CREATE TABLE #TempPartNum1 & #TempPartNum2.
Grab all the relevant data from the first two queries and put them in the temp tables, then join the temp tables.
You could also use a CTE ("Common Table Expression"):
;WITH QueryOne AS (
... put your first query here
), QueryTwo AS (
... put your second query here
) SELECT blah blah blah
FROM QueryOne INNER JOIN QueryTwo ON foo = bar
CTEs are very handy for things like this.

Slow TSQL query with nested selects

I have written a query, but appears to run quite slowly in our live environment.
The query contains nested select statements which I believe to slow down execution.
Does anyone have any tips of how I could re-write the query to improve the execution speed.
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '20140101'
SET #EndDate = '20140101'
SELECT a.applicationid,
a.ourreference,
(SELECT MAX(e.CreateDateTime) FROM tblEmail AS e WHERE( e.ApplicationID = a.ApplicationID) AND (ISNULL(e.StandardEmailID,0) <> 3)) AS 'LastEmail',
(SELECT MAX(l.CreateDateTime) FROM tblLetter AS l WHERE l.ApplicationID = a.ApplicationID AND l.ExternalDocumentTypeID IS NULL) AS 'LastInternalLetter',
(SELECT MAX(l.CreateDateTime) FROM tblLetter AS l WHERE l.ApplicationID = a.ApplicationID AND l.ExternalDocumentTypeID IS NOT NULL) AS 'LastExternalLetter'
INTO #Temp
FROM tblapplication AS a
WHERE (a.LogDate BETWEEN #StartDate AND #EndDate + '23:59:59')
AND (a.BusinessSourceID NOT IN (11, 16))
AND (a.ApplicationStatusID = 100)
SELECT *
FROM #Temp AS tem
WHERE ((LastEmail < LastExternalLetter) AND (LastInternalLetter < LastExternalLetter))
Cheers everyone
edit- Sorry I should say what the query is for. I am trying to see if our customers have sent us a letter and we haven't responded to them with a letter/email
select *
from
(
Select a.applicationId, a.ourreference,
max(e.CreateDateTime) LastEmail,
max(li.CreateDateTime) LastInternalLetter,
max(le.CreateDateTime) LastExternalLetter
from tblApplication a
left outer join tblEmail e on e.ApplicationID = a.ApplicationID
left outer join tblLetter li on li.ApplicationId = a.ApplicationId
left outer join tblLetter le on le.ApplicationId = a.ApplicationId
WHERE (a.LogDate BETWEEN #StartDate AND #EndDate + '23:59:59')
AND (a.BusinessSourceID NOT IN (11, 16))
AND (a.ApplicationStatusID = 100)
and (e.StandardEmailID <> 3 or e.StandardEmailId is null)
group by a.ApplicationId, a.OurReference
)x
WHERE ((LastEmail < LastExternalLetter) AND (LastInternalLetter < LastExternalLetter))
This gets rid of the temporary table. Remember, with SQL wherever you have a Table you can substitute a query (in Selects anyway). This should make it a bit more efficient, but you want to look at the Query Plans and see what's holding it up.
Cheers -