将多对多关系查询返回到单行

时间:2014-08-26 11:50:36

标签: sql sql-server tsql

我想知道是否有可能。我有3张桌子:

Item             ItemProperty               Property

ItemUID          ItemUID                    PropertyUID

Name             PropertyUID                Name

...              Value                      ....

但是我想要一个查询,它返回一行中的所有内容,并将属性的名称作为列名称,如此

ItemUID        ItemName      Property1       Property2      Property2

109-2...       PostalCard    Value1          Value2         Value3

有可能吗?我希望它以这种特定格式而不是数据格式的原因是我们受限于我们的工具之一。

我想指定查询只返回1行,而不是多个项目的数据表,因为有些项目只能包含1个属性而其他项目超过5-10,会有一个“WHERE ITEMUID = @ItemUID”类型的。

架构的SQLFiddle(我希望它有效吗?)http://sqlfiddle.com/#!3/70f34/2

2 个答案:

答案 0 :(得分:1)

您需要的静态SQL将遵循:

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;

这只是为每个propertyUID分配一个行号,然后根据此进行转动。枢轴函数需要MAX(PropertyUID),但MIN可以很容易ItemUID,因为RowNumDECLARE @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; 的每个组合都是唯一的,只有一个属性可供选择从

如果它是不同数量的属性,那么您将需要使用动态SQL来进行透视,最终结果将类似,但您需要使用最大数量的属性来创建SQL:

FOR XML PATH(''), TYPE

<强> Example on SQL Fiddle

N.B。我通常会建议使用.value和XQuery方法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, ''); 来创建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;

修改

如果你想将它限制为单个项目,那么你只需要添加几个WHERE子句(以获得计数,以及执行动态sql时):

{{1}}

答案 1 :(得分:0)

您必须以这种方式使用Pivoting,您可以将行转换为列。见this示例