The static SQL you would need would be along the lines of:
SELECT pvt.ItemUID,
Property1 = pvt.[1],
Property2 = pvt.[2],
Property3 = pvt.[3]
FROM ( SELECT ItemUID,
PropertyUID,
RowNum = ROW_NUMBER() OVER(PARTITION BY ItemUID ORDER BY PropertyUID)
FROM ItemProperty
) AS ip
PIVOT
( MAX(PropertyUID)
FOR RowNum IN ([1], [2], [3])
) AS pvt;
This simply assigns each propertyUID a row number, then pivots based on this. the MAX(PropertyUID)
is needed for the pivot function, but could just as easily be MIN
since each combination of ItemUID
and RowNum
is unique there will only ever be one propertyUID to choose from.
If it is a varied number of properties then you will need to use dynamic SQL to do the pivoting, the end result will be similar, but you will need to use the maximum number of properties to create the SQL:
DECLARE @pvt NVARCHAR(MAX),
@Cols NVARCHAR(MAX),
@SQL NVARCHAR(MAX),
@MaxProperties INT;
-- GET MAXIMUM NUMBER OF PROPERTIES FOR A SINGLE ITEM
SELECT TOP 1 @MaxProperties = COUNT(*)
FROM ItemProperty
GROUP BY ItemUID
ORDER BY COUNT(*) DESC;
-- CREATE A STRING LIKE '[1],[2],[3]...' TO USE INSIDE PIVOT
SET @pvt = STUFF((SELECT TOP (@MaxProperties) ',' + QUOTENAME(ROW_NUMBER() OVER(ORDER BY object_id))
FROM sys.all_objects
FOR XML PATH('')), 1, 1, '');
-- CREATE A STRING LIKE 'Property1 = pvt.[1],Property2 = pvt.[2]...' TO USE IN SELECT
SET @Cols = STUFF((SELECT TOP (@MaxProperties) ',Property' + CAST(ROW_NUMBER() OVER(ORDER BY object_id) AS VARCHAR(10))
+ ' = pvt.' + QUOTENAME(ROW_NUMBER() OVER(ORDER BY object_id))
FROM sys.all_objects
FOR XML PATH('')), 1, 1, '');
-- BUILD SQL TO USE
SET @SQL = 'SELECT pvt.ItemUID,' + @Cols + '
FROM ( SELECT ItemUID,
PropertyUID,
RowNum = ROW_NUMBER() OVER(PARTITION BY ItemUID ORDER BY PropertyUID)
FROM ItemProperty
) AS ip
PIVOT
( MAX(PropertyUID)
FOR RowNum IN (' + @pvt + ')
) AS pvt;';
-- EXECUTE THE SQL THAT HAS BEEN DYNAMICALLY BUILT
EXECUTE sp_executesql @SQL;
Example on SQL Fiddle
N.B. I would usually reccommend using FOR XML PATH(''), TYPE
and the XQuery method .value
to create the SQL, but since it is just integers being concatenated I know there are no special characters so there is no need to worry about these not being escaped properly e.g.
SET @Cols = STUFF((SELECT TOP (10) ',Property' + CAST(ROW_NUMBER() OVER(ORDER BY object_id) AS VARCHAR(10))
+ ' = pvt.' + QUOTENAME(ROW_NUMBER() OVER(ORDER BY object_id))
FROM sys.all_objects
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
EDIT
If you want to limit this to a single item then you just need to add a couple of WHERE clauses (to get the count to being with, and also when executing the dynamic sql):
DECLARE @ItemUID INT = 1;
DECLARE @pvt NVARCHAR(MAX),
@Cols NVARCHAR(MAX),
@SQL NVARCHAR(MAX),
@MaxProperties INT;
-- GET MAXIMUM NUMBER OF PROPERTIES FOR A SINGLE ITEM
SELECT @MaxProperties = COUNT(*)
FROM ItemProperty
WHERE ItemUID = @ItemUID
GROUP BY ItemUID
ORDER BY COUNT(*) DESC;
-- CREATE A STRING LIKE '[1],[2],[3]...' TO USE INSIDE PIVOT
SET @pvt = STUFF((SELECT TOP (@MaxProperties) ',' + QUOTENAME(ROW_NUMBER() OVER(ORDER BY object_id))
FROM sys.all_objects
FOR XML PATH('')), 1, 1, '');
-- CREATE A STRING LIKE 'Property1 = pvt.[1],Property2 = pvt.[2]...' TO USE IN SELECT
SET @Cols = STUFF((SELECT TOP (@MaxProperties) ',Property' + CAST(ROW_NUMBER() OVER(ORDER BY object_id) AS VARCHAR(10))
+ ' = pvt.' + QUOTENAME(ROW_NUMBER() OVER(ORDER BY object_id))
FROM sys.all_objects
FOR XML PATH('')), 1, 1, '');
-- BUILD SQL TO USE
SET @SQL = 'SELECT pvt.ItemUID,' + @Cols + '
FROM ( SELECT ItemUID,
PropertyUID,
RowNum = ROW_NUMBER() OVER(PARTITION BY ItemUID ORDER BY PropertyUID)
FROM ItemProperty
WHERE ItemUID = @ItemUID
) AS ip
PIVOT
( MAX(PropertyUID)
FOR RowNum IN (' + @pvt + ')
) AS pvt;';
-- EXECUTE THE SQL THAT HAS BEEN DYNAMICALLY BUILT
EXECUTE sp_executesql @SQL, N'@ItemUID INT', @ItemUID;