Here are the tests I ran. I recreated the VIEW between each test (even if not changed) which i hope will have cleared the caches etc.
TEST #1 has LookupValue = NULL (so will need the DEFAULT lookup)
TEST #2 has LookupValue = NOT NULL (depending on the test either Includes/Excldues the lookup)
There are very few rows in my CONFIG table (and will be in PRODUCTION too), so the READS is tiny, and as such I have no real feel for which of these plans is better, any advice / comment / suggestions would be appreciated (even if "For so few rows who cares"
)
ALTER VIEW dbo.MyView
AS
SELECT
[V_ClustedIndexKey] = T.ClustedIndexKey
, [V_LookupValue] = T.CodeValue
, [V_LookupValue_DEFAULT] = CONVERT(int, CFG.cfg_Data)
FROM dbo.MyTable AS T
LEFT OUTER JOIN dbo.CONFIG AS CodeValue_CFG
ON CFG.cfg_Kind = 'INIT'
AND CFG.cfg_Key = 'CodeValue'
AND CFG.cfg_Brand = 'MyBrand'
AND CFG.cfg_ItemNo = 1
TEST #1:
V_ClustedIndexKey V_LookupValue V_LookupValue_DEFAULT
----------------- ------------- ---------------------
1447 NULL 9
Table 'CONFIG'. Scan count 0, logical reads 2
Table 'MyTable'. Scan count 0, logical reads 2
|--Compute Scalar(DEFINE:([Expr1004]=CONVERT(int,[MyDB].[dbo].[CONFIG].[cfg_Data] as [CodeValue_CFG].[cfg_Data],0)))
|--Nested Loops(Left Outer Join)
|--Clustered Index Seek(OBJECT:([MyDB].[dbo].[MyTable].[IX_ClustedIndexKey] AS [T])
, SEEK:([T].[ClustedIndexKey]=(1447)) ORDERED FORWARD)
|--Clustered Index Seek(OBJECT:([MyDB].[dbo].[CONFIG].[PK_CFG] AS [CodeValue_CFG])
, SEEK:([CodeValue_CFG].[cfg_Kind]='INIT'
AND [CodeValue_CFG].[cfg_Key]='CodeValue'
AND [CodeValue_CFG].[cfg_Brand]='MyBrand'
AND [CodeValue_CFG].[cfg_ItemNo]=(1)
) ORDERED FORWARD)
TEST #2:
Results depend on whether DeFAULT was only included if LookupValue=NULL
V_ClustedIndexKey V_LookupValue V_LookupValue_DEFAULT
----------------- ------------- ---------------------
881 5 NULL
or
881 5 9
(Same)
Add:
AND T.CodeValue IS NULL
TEST #1:
Table 'CONFIG'. Scan count 0, logical reads 2
Table 'MyTable'. Scan count 0, logical reads 2
|--Compute Scalar(DEFINE:([Expr1004]=CONVERT(int,[MyDB].[dbo].[CONFIG].[cfg_Data] as [CodeValue_CFG].[cfg_Data],0)))
|--Nested Loops(Left Outer Join, WHERE:([MyDB].[dbo].[MyTable].[CodeValue] as [T].[CodeValue] IS NULL)) <<<
|--Clustered Index Seek(OBJECT:([MyDB].[dbo].[MyTable].[IX_ClustedIndexKey] AS [T])
, SEEK:([T].[ClustedIndexKey]=(1447)) ORDERED FORWARD)
|--Clustered Index Seek(OBJECT:([MyDB].[dbo].[CONFIG].[PK_CFG] AS [CodeValue_CFG])
, SEEK:([CodeValue_CFG].[cfg_Kind]='INIT'
AND [CodeValue_CFG].[cfg_Key]='CodeValue'
AND [CodeValue_CFG].[cfg_Brand]='MyBrand'
AND [CodeValue_CFG].[cfg_ItemNo]=(1)
) ORDERED FORWARD)
TEST #2:
(same)
INNER JOIN
FROM dbo.MyTable AS T
JOIN dbo.CONFIG AS CodeValue_CFG
ON CFG.cfg_Kind = 'INIT'
AND CFG.cfg_Key = 'CodeValue'
AND CFG.cfg_Brand = 'MyBrand'
AND CFG.cfg_ItemNo = 1
TEST #1:
Table 'MyTable'. Scan count 0, logical reads 2
Table 'CONFIG'. Scan count 0, logical reads 2
|--Nested Loops(Inner Join)
|--Compute Scalar(DEFINE:([Expr1004]=CONVERT(int,[MyDB].[dbo].[CONFIG].[cfg_Data] as [CodeValue_CFG].[cfg_Data],0)))
| |--Clustered Index Seek(OBJECT:([MyDB].[dbo].[CONFIG].[PK_CFG] AS [CodeValue_CFG])
, SEEK:([CodeValue_CFG].[cfg_Kind]='INIT'
AND [CodeValue_CFG].[cfg_Key]='CodeValue'
AND [CodeValue_CFG].[cfg_Brand]='MyBrand'
AND [CodeValue_CFG].[cfg_ItemNo]=(1)
) ORDERED FORWARD)
|--Clustered Index Seek(OBJECT:([MyDB].[dbo].[MyTable].[IX_ClustedIndexKey] AS [T])
, SEEK:([T].[ClustedIndexKey]=(1447)) ORDERED FORWARD)
TEST #2:
Table 'MyTable'. Scan count 0, logical reads 2
Table 'CONFIG'. Scan count 0, logical reads 2
(same)
Add:
AND T.CodeValue IS NULL
TEST #1:
Table 'MyTable'. Scan count 0, logical reads 2
Table 'CONFIG'. Scan count 0, logical reads 2
|--Nested Loops(Inner Join)
|--Compute Scalar(DEFINE:([Expr1004]=CONVERT(int,[MyDB].[dbo].[CONFIG].[cfg_Data] as [CodeValue_CFG].[cfg_Data],0)))
| |--Clustered Index Seek(OBJECT:([MyDB].[dbo].[CONFIG].[PK_CFG] AS [CodeValue_CFG])
, SEEK:([CodeValue_CFG].[cfg_Kind]='INIT'
AND [CodeValue_CFG].[cfg_Key]='CodeValue'
AND [CodeValue_CFG].[cfg_Brand]='MyBrand'
AND [CodeValue_CFG].[cfg_ItemNo]=(1)
) ORDERED FORWARD)
|--Clustered Index Seek(OBJECT:([MyDB].[dbo].[MyTable].[IX_ClustedIndexKey] AS [T])
, SEEK:([T].[ClustedIndexKey]=(1447))
, WHERE:([MyDB].[dbo].[MyTable].[CodeValue] as [T].[CodeValue] IS NULL) <<<
ORDERED FORWARD)
TEST #2:
Table 'MyTable'. Scan count 0, logical reads 2
Table 'CONFIG'. Scan count 0, logical reads 2
(same)
Remove JOIN and change SELECT:
, [V_LookupValue_DEFAULT] = CASE WHEN T.CodeValue IS NOT NULL THEN NULL
ELSE
(
SELECT [V_LookupValue_DEFAULT] = CONVERT(int, CFG.cfg_Data)
FROM dbo.CONFIG AS CodeValue_CFG
WHERE CFG.cfg_Kind = 'INIT'
AND CFG.cfg_Key = 'CodeValue'
AND CFG.cfg_Brand = 'MyBrand'
AND CFG.cfg_ItemNo = 1
AND T.CodeValue IS NULL
)
END
TEST #1:
Table 'CONFIG'. Scan count 0, logical reads 2
Table 'MyTable'. Scan count 0, logical reads 2
|--Compute Scalar(DEFINE:([Expr1005]=CASE WHEN [MyDB].[dbo].[MyTable].[CodeValue] as [T].[CodeValue] IS NOT NULL THEN NULL ELSE CONVERT(int,[MyDB].[dbo].[CONFIG].[cfg_Data] as [CodeValue_CFG].[cfg_Data],0) END))
|--Nested Loops(Left Outer Join, PASSTHRU:([MyDB].[dbo].[MyTable].[CodeValue] as [T].[CodeValue] IS NOT NULL), OUTER REFERENCES:([T].[CodeValue]))
|--Clustered Index Seek(OBJECT:([MyDB].[dbo].[MyTable].[IX_ClustedIndexKey] AS [T])
, SEEK:([T].[ClustedIndexKey]=(1447)) ORDERED FORWARD)
|--Filter(WHERE:(STARTUP EXPR([MyDB].[dbo].[MyTable].[CodeValue] as [T].[CodeValue] IS NULL)))
|--Clustered Index Seek(OBJECT:([MyDB].[dbo].[CONFIG].[PK_CFG] AS [CodeValue_CFG])
, SEEK:([CodeValue_CFG].[cfg_Kind]='INIT'
AND [CodeValue_CFG].[cfg_Key]='CodeValue'
AND [CodeValue_CFG].[cfg_Brand]='MyBrand'
AND [CodeValue_CFG].[cfg_ItemNo]=(1)
) ORDERED FORWARD)
TEST #2:
Table 'Worktable'. Scan count 0, logical reads 0
Table 'MyTable'. Scan count 0, logical reads 2
(same)