We have a vendor-supplied application. It is under support and we are talking to their developers (we have their largest database size by an order of magnitude), but in the meantime, we have the following query that is run thousands of times a day, with the only parts changing the ABCDEFG12345
in the where
clause. Almost everything in the where's are customisable, as it is the query generated by their master job search.
There are about 500,000 records in the dJobs table, and similar numbers of records in the joined tables:
select * from (
select top 20 * from (
select top 20
( --Start Count
select count(*) from dJobs
left join dClients on cltClientID = jobClientId
left join dJobStatus ON jbsID = jobJobStatus
where ((jobSupervisor= -1.00000000 or jobCoordinator= -1.00000000 ) OR 1=1 )
and
(
(jobjobStatus=0 OR (0=0 AND 0=0))
AND (jbsType=0 OR (0=0 AND 0=0))
AND (jobPriority=0 OR (0=0 AND 0=0))
AND (jobEventID=0 OR (0=0 AND 0=0))
AND (jobSiteCode='' OR (''='' AND ''=''))
AND (jobjobStatus IN (
select jbsid from djobstatusgroupmapping where jsgid = 0
)
OR (0=0 AND 0=0))
AND jobDateCreated BETWEEN '2004-06-10' AND '2014-06-10' AND jobBookedDate BETWEEN '1901-01-01 00:00:00' AND '2024-06-10 23:59:00'
and
(('ABCDEFG12345'<>''
AND
(
(1 = 1 AND
(
jobWorkOrderNo like '%ABCDEFG12345%' OR
jobSiteName like '%ABCDEFG12345%' OR
jobSiteLocationBuilding like '%ABCDEFG12345%' OR
jobSiteAddress like '%ABCDEFG12345%' OR
jobSiteSuburb like '%ABCDEFG12345%' OR
jobSitePostcode like '%ABCDEFG12345%' OR
jobWorkToDo like '%ABCDEFG12345%' OR
jobSiteClient like '%ABCDEFG12345%' OR
jobSiteContact like '%ABCDEFG12345%' OR
jobSiteContactPhone like '%ABCDEFG12345%' OR
jobSiteContactPhone2 like '%ABCDEFG12345%'
)
)
OR
(1 = 1 AND
(
cltClientName like '%ABCDEFG12345%' OR
cltDivision like '%ABCDEFG12345%' OR
cltAddress1 like '%ABCDEFG12345%' OR
cltAddress2 like '%ABCDEFG12345%' OR
rtrim(cltAddress1)+' '+rtrim(cltAddress2) like '%ABCDEFG12345%' OR
cltSuburb like '%ABCDEFG12345%' OR
cltPostCode like '%ABCDEFG12345%' OR
cltState like '%ABCDEFG12345%' OR
cltTelephone like '%ABCDEFG12345%' OR
cltContact like '%ABCDEFG12345%' OR
cltMobile like '%ABCDEFG12345%' OR
cltEmail like '%ABCDEFG12345%'
)
)
)
)
OR 'ABCDEFG12345'='')
) --End Count
) as vRecCount,
jobID,
jobWorkOrderNo,
jobSequence,
jobSiteClient,
jobSiteStreetNumber,
jobSiteAddress,
jobSiteSuburb,
jobSiteState,
jobSitePostcode,
jobDateofLoss,
jobDateofLossTime,
jobDateCreated,
jobTargetDate,
jobClientID,
jobPriority,
jobJobStatus,
jobEventID,
regFullname,
case
when (not jobSupervisor= -1.00000000 ) and jobCoordinator= -1.00000000 then 1 else
case when (jobSupervisor= -1.00000000 ) and (jobCoordinator=0) then 2 else
case when (jobSupervisor= -1.00000000 ) and (not jobCoordinator= -1.00000000 ) then 3 else 0
end end end as 'Coordinator',
jobSupervisor,
jobBookedDate,
jobLat,
jobLong,
jobClaimAssistRequest,
jobBooked,
jobSiteContact,
jobSiteContactPhone2,
jobPCM
from dJobs
left join dClients on cltClientID = jobClientId
left join dUsers on regUserId = jobCoordinator
left join dJobStatus ON jbsID = jobJobStatus
where ((jobSupervisor= -1.00000000 or jobCoordinator= -1.00000000 ) OR 1=1 )
and
(
(jobjobStatus=0 OR (0=0 AND 0=0))
AND (jobPriority=0 OR (0=0 AND 0=0))
AND (jbsType=0 OR (0=0 AND 0=0))
AND (jobEventID=0 OR (0=0 AND 0=0))
AND (jobSiteCode='' OR (''='' AND ''=''))
AND (jobjobStatus IN (
select jbsid from djobstatusgroupmapping where jsgid = 0
)
OR (0=0 AND 0=0))
)
AND jobDateCreated BETWEEN '2004-06-10' AND '2014-06-10' AND jobBookedDate BETWEEN '1901-01-01 00:00:00' AND '2024-06-10 23:59:00'
and
(('ABCDEFG12345'<>''
AND
(
(1 = 1 AND
(
jobWorkOrderNo like '%ABCDEFG12345%' OR
jobSiteName like '%ABCDEFG12345%' OR
jobSiteLocationBuilding like '%ABCDEFG12345%' OR
jobSiteAddress like '%ABCDEFG12345%' OR rtrim(jobSiteStreetNumber)+' '+rtrim(jobSiteAddress) LIKE '%ABCDEFG12345%' OR
jobSiteSuburb like '%ABCDEFG12345%' OR
jobSitePostcode like '%ABCDEFG12345%' OR
jobWorkToDo like '%ABCDEFG12345%' OR
jobSiteClient like '%ABCDEFG12345%' OR
jobSiteContact like '%ABCDEFG12345%' OR
jobSiteContactPhone like '%ABCDEFG12345%' OR
jobSiteContactPhone2 like '%ABCDEFG12345%'
)
)
OR
(1 = 1 AND
(
cltClientName like '%ABCDEFG12345%' OR
cltDivision like '%ABCDEFG12345%' OR
cltAddress1 like '%ABCDEFG12345%' OR
cltAddress2 like '%ABCDEFG12345%' OR
rtrim(cltAddress1)+' '+rtrim(cltAddress2) like '%ABCDEFG12345%' OR
cltSuburb like '%ABCDEFG12345%' OR
cltPostCode like '%ABCDEFG12345%' OR
cltState like '%ABCDEFG12345%' OR
cltTelephone like '%ABCDEFG12345%' OR
cltContact like '%ABCDEFG12345%' OR
cltMobile like '%ABCDEFG12345%' OR
cltEmail like '%ABCDEFG12345%'
)
)
)
)
OR 'ABCDEFG12345'='')
order by jobid desc , jobDateCreated desc
) as newtbl order by jobid asc , jobDateCreated asc
) as newtbl_2 order by jobid desc, jobDateCreated desc
The SQL Server Database Tuning Advisor has no suggestions for me - so it looks like the developers have done a good job at creating appropriate indexes and statistics for the query.
Until they can refactor things at their end, which could take 6+ months, what's left for us to do? Just throw more money at the SQL server (SSDs, more RAM, perhaps Enterprise Edition for partitioning)?
Breakdown of transaction cost is:
- Total CPU Time (ms.): 12,237.70
- # Total Logical IO: 2,331,089
- # Avg. Logical IO: 2,331,089.00
- # Logical Reads: 2,331,089
SQL Server 2008 R2 Standard running inside a VMWare Virtual Machine.
Interesting part of the execution plan below. Most time spent on a clustered index scan.
Sorry for the delay; the following stats were taking from our staging server, which has 1/10th the number of records as production, but is also on lower class hardware. Execution Plan XML, STATISTICS IO
:
Table 'dClients'. Scan count 5, logical reads 705780, physical reads 9, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'dJobs'. Scan count 10, logical reads 23386, physical reads 16, read-ahead reads 10601, lob logical reads 186928, lob physical reads 578, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'dUsers'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
STATISTICS IO
output and post the full XML plan. That nested loops table scan ofdClients
(which is a heap!) is highly suspect. There's a row misestimate somewhere here, for sure, and there's no way the percentages are accurate.HASH
join hintdClients
and you'll see a massive improvement in execution time. Beyond that, we'll need to see the schema and index definitions to make better recommendations.