Database: SQL Server 2005
I’m using a function that creates a comma separated list using the COALESCE function.
ALTER FUNCTION [dbo].[func_Codes](@CustID int, @GroupID int)
returns varchar(1000) as
BEGIN
DECLARE @List varchar(1000)
SELECT @List = COALESCE(@List + ',', '') + Code
FROM dbo.vw_CustBillingInfo
WHERE dbo.vw_CustBillingInfo.CustID = @CustID
AND dbo.vw_CustBillingInfo.GroupID = @GroupID
RETURN @List
END
The view it calls uses the following setup:
SELECT <columns>
FROM (SELECT <columns>
FROM Customer
INNER JOIN Codes ON dbo.GetRootCode(Customer.Code) = Codes.SpecialCode
OR (IsNumeric(Customer.Code) = 0 AND Substring(Customer.Code,2,3) = Codes.SpecialCode)
UNION
SELECT <columns>
FROM Customer
INNER JOIN Codes ON dbo.GetRootCode(Customer.Code2) = Codes.SpecialCode
OR (IsNumeric(Customer.Code2) = 0 AND Substring(Customer.Code2,2,3) = Codes.SpecialCode)
UNION
<Repeat a few more similar unions>
The dbo.GetRootCode(code) scalar function does some substring operations on the column to pull out a special part of the varchar column.
If I take the query out of the view I can improve performance by creating a temp table and storing the values of GetRootCode(Customer.Code), IsNumeric(Customer.Code), Substring(Customer.Code,2,3), etc for each code used. This doesn’t seem like the best approach but it shows that optimizations can be done. Also a view can’t use a temp table anyway.
Instead of the function to create the comma separated list I tried an xml variation I saw multiple places online.
STUFF((
SELECT ','+Code FROM dbo.vw_CustBillingInfo WHERE dbo.vw_CustBillingInfo.CustID = C.CustID AND dbo.vw_CustBillingInfo.GroupID = C.GroupID FOR XML PATH('')
), 1, 1, '')
However, while this worked, performance was fairly awful. It added about 20 to 30 seconds to the execution time of the query
This leads me back to trying to optimize the view directly for better performance. IO statistics shows the first select of the view to be:
(20 row(s) affected)
Table 'Worktable'. Scan count 1, logical reads 42920, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Customer'. Scan count 1, logical reads 269, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Codes'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
This takes over a second to execute. Add that up over all unions and then this view is called twice over the execution path, and the time greatly adds up.
I’m stuck on the best way to optimize the INNER JOIN of the view to increase the speed and lessen the IO required. Any suggestions would be greatly appreciated.
You are using TWO non-sargable
JOINconditions perJOIN.So you are getting at a minimum a table scan per
JOIN, possible two depending on your implementation.The really short answer is:
JOINon UDFsJOINusing any other functions (likeSUBSTRING).There is almost NO WAY to optimize this. SQL doesn’t know what the output will be til it runs the function, so it runs it against each and every row.