My method only requires 9 reads versus your 15. Almost half the reads required only.Here is your execution plan |--Nested Loops(Left Outer Join, WHERE:([tempdb].[dbo].[#People].[PersonID] as [p].[PersonID]=[tempdb].[dbo].[#PeopleFields].[PersonID] as [pf2].[PersonID])) |--Nested Loops(Left Outer Join, WHERE:([tempdb].[dbo].[#People].[PersonID] as [p].[PersonID]=[tempdb].[dbo].[#PeopleFields].[PersonID] as [pf1].[PersonID])) | |--Clustered Index Scan(OBJECT:([tempdb].[dbo].[#People] AS [p]), ORDERED FORWARD) | |--Clustered Index Scan(OBJECT:([tempdb].[dbo].[#PeopleFields] AS [pf1]), WHERE:([tempdb].[dbo].[#PeopleFields].[FieldDefID] as [pf1].[FieldDefID]=(1))) |--Clustered Index Scan(OBJECT:([tempdb].[dbo].[#PeopleFields] AS [pf2]), WHERE:([tempdb].[dbo].[#PeopleFields].[FieldDefID] as [pf2].[FieldDefID]=(2)))
Here is mine |--Nested Loops(Inner Join, OUTER REFERENCES:([pf].[PersonID])) |--Stream Aggregate(GROUP BY:([pf].[PersonID]) DEFINE:([Expr1004]=MAX([Expr1006]), [Expr1005]=MAX([Expr1007]))) | |--Compute Scalar(DEFINE:([Expr1006]=CASE WHEN [tempdb].[dbo].[#PeopleFields].[FieldDefID] as [pf].[FieldDefID]=(1) THEN [tempdb].[dbo].[#PeopleFields].[FieldValue] as [pf].[FieldValue] ELSE NULL END, [Expr1007]=CASE WHEN [tempdb].[dbo].[#PeopleFields].[FieldDefID] as [pf].[FieldDefID]=(2) THEN [tempdb].[dbo].[#PeopleFields].[FieldValue] as [pf].[FieldValue] ELSE NULL END)) | |--Clustered Index Scan(OBJECT:([tempdb].[dbo].[#PeopleFields] AS [pf]), ORDERED FORWARD) |--Clustered Index Seek(OBJECT:([tempdb].[dbo].[#People] AS [p]), SEEK:([p].[PersonID]=[tempdb].[dbo].[#PeopleFields].[PersonID] as [pf].[PersonID]) ORDERED FORWARD)
And here is the sample codeCREATE TABLE #People (PersonID TINYINT, PersonType VARCHAR(10))INSERT #PeopleSELECT 1, 'Consultant' UNION ALLSELECT 2, 'Partner'CREATE UNIQUE CLUSTERED INDEX IX_People ON #People (PersonID)CREATE TABLE #PeopleFields (PersonID TINYINT, FieldDefID TINYINT, FieldValue VARCHAR(7))INSERT #PeopleFieldsSELECT 1, 3, 'X' UNION ALLSELECT 2, 3, 'Y' UNION ALLSELECT 1, 1, 'John' UNION ALLSELECT 1, 2, 'Smith' UNION ALLSELECT 2, 1, 'Alice' UNION ALLSELECT 2, 2, 'Johnson'CREATE UNIQUE CLUSTERED INDEX IX_PeopleFields ON #PeopleFields (PersonID, FieldDefID)SELECT p.PersonID, p.PersonType, pf1.FieldValue AS 'First Name', pf2.FieldValue AS 'Last Name'FROM #People p LEFT JOIN #PeopleFields pf1 ON p.PersonID = pf1.PersonID AND pf1.FieldDefID = 1LEFT JOIN #PeopleFields pf2 ON p.PersonID = pf2.PersonID AND pf2.FieldDefID = 2ORDER BY p.PersonID, p.PersonTypeSELECT p.PersonID, p.PersonType, MAX(CASE WHEN pf.FieldDefID = 1 THEN pf.FieldValue END) AS FirstName, MAX(CASE WHEN pf.FieldDefID = 2 THEN pf.FieldValue END) AS LastNameFROM #People AS pINNER JOIN #PeopleFields AS pf ON pf.PersonID = p.PersonIDGROUP BY p.PersonID, p.PersonTypeORDER BY p.PersonID, p.PersonType DROP TABLE #People, #PeopleFields
Peter LarssonHelsingborg, Sweden