如何处理从数据库中选择动态属性

时间:2013-03-02 15:00:23

标签: sql sql-server tsql pivot

我设计了一个小数据库的想法是我有一个表产品,每个产品都有它的类别和动态属性列表(这些属性在另一个表中):

ProductCategories{ProductCategoryId, Name}
Products{ProductId, Name, ProductCategoryId}

我在这里绑定某个类别的产品。现在,每个ProductCategory都与另一个表相关,我可以在其中添加该类别的属性:

CategoryProperties{CategoryPropertyId, ProductCategoryId, Name}

每个属性的值都在此表中:

ProductPropertyValues{ProductId, CategoryPropertyId, Value}

问题出在我执行查询时:

select p.Name as ProductName, m.Name as Manufacturer, pc.Name as Category
from products p
left join dbo.Manufacturers m
on p.ManufactureId = m.ManufactureId
left join dbo.ProductCategories pc
on p.ProductCategoryId = pc.ProductCategoryId

这给我的结果如下:

| some product name | RedBull | Can |

但我希望获得产品类别的所有相关属性,这就是问题所在。当我尝试这个查询时:

select p.Name as ProductName, m.Name as Manufacturer, pc.Name as Category, cp.Name,  ppv.Value
from products p
left join dbo.Manufacturers m
on p.ManufactureId = m.ManufactureId
left join dbo.ProductCategories pc
on p.ProductCategoryId = pc.ProductCategoryId

left join dbo.CategoryProperties cp
on pc.ProductCategoryId = cp.ProductCategoryId

left join dbo.ProductPropertyValues ppv
on p.ProductId = ppv.ProductId

在结果中查询而不是一行之后,我得到'121'结果。 我不知道我做错了什么。 我希望得到如下结果:

| Product name      | Manufacturer | Category | prop1 | prop2     | prop3| Prop4          |      
| some product name | RedBull      | Can      | 34    | something | 45.6 | something else |

我做错了什么或者这是不可能的?
据我所知,我在这里得到一些交叉加入。 enter image description here

enter image description here enter image description here

2 个答案:

答案 0 :(得分:2)

听起来您正在尝试PIVOT数据。这将获取行值并将其转换为列。

如果您要转换一定数量的属性,那么您可以将查询硬编码为类似于此的内容:

select *
from
(
  select p.Name as ProductName, 
    m.Name as Manufacturer, 
    pc.Name as Category, 
    cp.Name,
    ppv.Value
  from products p
  left join dbo.Manufacturers m
    on p.ManufactureId = m.ManufactureId
  left join dbo.ProductCategories pc
    on p.ProductCategoryId = pc.ProductCategoryId
  left join dbo.CategoryProperties cp
    on pc.ProductCategoryId = cp.ProductCategoryId
  left join dbo.ProductPropertyValues ppv
    on p.ProductId = ppv.ProductId
    and cp.CategoryPropertyId = ppv.CategoryPropertyId
) src
pivot
(
  max(value)
  for Name in ([Code], [Data], etc)
) piv

如果您的数值未知,则会变得更加困难。您将不得不使用动态SQL来执行此操作。我向您展示了一个静态版本,因为它更容易从硬编码版本转移到动态解决方案。

动态版本的关键是获取列名称。完整的脚本将类似于:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(name) 
                    from dbo.CategoryProperties
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT ProductName, Manufacturer, Category,' + @cols + ' from 
             (
                select p.Name as ProductName, 
                  m.Name as Manufacturer, 
                  pc.Name as Category, 
                  cp.Name,  
                  ppv.Value
                from products p
                left join dbo.Manufacturers m
                  on p.ManufactureId = m.ManufactureId
                left join dbo.ProductCategories pc
                  on p.ProductCategoryId = pc.ProductCategoryId
                left join dbo.CategoryProperties cp
                  on pc.ProductCategoryId = cp.ProductCategoryId
                left join dbo.ProductPropertyValues ppv
                  on p.ProductId = ppv.ProductId
                  and cp.CategoryPropertyId = ppv.CategoryPropertyId
            ) x
            pivot 
            (
                max(value)
                for Name in (' + @cols + ')
            ) p '

execute(@query)

答案 1 :(得分:1)

要回答有关交叉加入的问题,我认为问题在于您没有将ProductPropertyValues绑定回CategoryProperties。因此,对于每个ProductPropertyValues行的所有类别,您都会获得CategoryProperties的所有属性。

现在,只要获得你想要的格式......这就是所谓的“旋转”,因为你想要一个垂直列表(所有属性)并“转动”它,水平渲染它。这很难动态执行,因为虽然SQL Server确实有PIVOT命令,但它要求您知道在编码时用于列名的所有值。如果您知道您的属性被称为“Prop 1”,“Prop 2”等,那么这将起作用,我认为名称因产品而异。

您如何确定将所有属性放在一行中?它可以做到,但它会变得复杂。