透视来自不同表格的数据

时间:2011-12-21 22:48:20

标签: sql sql-server linq sql-server-2008 pivot


这是我的问题:
我有一个像这样的数据库:

|ID_Brand| Description
------------------------------
|1       | CoolBrandName
|2       | AlsoCoolBrandName 
|...     | ...

链接到此表我创建了一个像这样的文件表

|ID_file | ID_BRAND | DESCRIPTION | FILECONTENT | ID_CATEGORY
-------------------------------------------------------------
|1       | 1        | File1       |0x0FF0A0B2..| 1 
|2       | 1        | File2       |0x2F1A000C..| 2
|3       | 2        | File3       |0X5FB43002..| 1
|4       | 1        | File4       |0x93EEFD13..| 1
|...     |...       | ...         |...         | ...

最终使用ID_CATEGORY关联链接到上表的Category表。

|ID_CATEGORY | DESCRIPTION
--------------------------
|1           | Category1
|2           | Category2
|3           | Category3
|4           | Category4
|...         | ...

我需要在这些表上显示数据,如数据透视表,按类别分组。 例如,对于上面的数据,我需要输出如下内容:

|BrandName         | Category1 | Category2 | Category3 | Category4
------------------------------------------------------------------
|CoolBrandName     | File1     | File2     | *NULL*    | *NULL* 
|AlsoCoolBrandName | File3     | *NULL*    | *NULL*    | *NULL*
|CoolBrandName     | File4     | *NULL*    | *NULL*    | *NULL*

类别表具有固定的行数。

我尝试从上面的示例开始 http://msdn.microsoft.com/en-us/library/ms177410.aspx 但没有运气。

我需要通过SQL(我使用sql server 2008 r2)或通过Linq实现这一目标。

任何人都可以帮我解决这个问题吗?

我感谢任何建议。

提前致谢 诉

1 个答案:

答案 0 :(得分:4)

PIVOT查询类似于GROUP BY查询,但前者隐含分组。数据按所有列进行分组,但只有一列,而这一列成为聚合列。我在my other answer中详细说明了这一点。

在您的情况下,结果集应该按两个明显的列进行分组。它是品牌名称和类别名称。但这还不够,因为根据您的示例,某些文件可能属于同一品牌/类别组,您仍然希望在输出中显示每个单独的文件。所以,显然,必须有另一个标准来分组。

对我而言,第三个标准最明显的选择是某种排名位置。您可以从下面的结果查询中看到,我选择根据字母顺序对文件进行排名。

所以,这是一个适合我的解决方案。首先,我的测试环境 - DDL和样本数据:

DECLARE @Brand TABLE (
  ID_Brand int IDENTITY,
  Description varchar(50)
);
DECLARE @Category TABLE (
  ID_Category int IDENTITY,
  Description varchar(50)
);
DECLARE @File TABLE (
  ID_File int IDENTITY,
  ID_Brand int,
  FileContent varbinary(max) DEFAULT (CAST(NEWID() AS varbinary)),
  Description varchar(50),
  ID_Category int
);

INSERT INTO @Brand (Description) VALUES
  ('CoolBrandName'),
  ('AlsoCoolBrandName');
INSERT INTO @Category (Description) VALUES
  ('Category1'),
  ('Category2'),
  ('Category3'),
  ('Category4');
INSERT INTO @File (ID_Brand, ID_Category, Description) VALUES
  (1, 1, 'File1'),
  (1, 2, 'File2'),
  (2, 1, 'File3'),
  (1, 1, 'File4');

这是获取所需输出的查询:

WITH ranked AS (
  SELECT
    *,
    rnk = ROW_NUMBER() OVER (PARTITION BY ID_Brand, ID_Category ORDER BY Description)
  FROM @File
),
joined AS (
  SELECT
    BrandName    = b.Description,
    CategoryName = c.Description,
    FileName     = f.Description,
    FileRank     = f.rnk
  FROM ranked f
    INNER JOIN @Brand b    ON f.ID_Brand    = b.ID_Brand
    INNER JOIN @Category c ON f.ID_Category = c.ID_Category
)
SELECT
  BrandName,
  Category1,
  Category2,
  Category3,
  Category4
FROM joined
PIVOT (
  MAX(FileName) FOR CategoryName IN (Category1, Category2, Category3, Category4)
) p
ORDER BY Category1

输出本身如下:

BrandName          Category1  Category2  Category3  Category4
-----------------  ---------  ---------  ---------  ---------
CoolBrandName      File1      File2      NULL       NULL
AlsoCoolBrandName  File3      NULL       NULL       NULL
CoolBrandName      File4      NULL       NULL       NULL

您可以看到输出中没有第三个标准,即排名。它仍然参与分组,因为它存在于我们正在应用PIVOT子句的行集中,joined

最后一点。必须在PIVOT查询中聚合透视列的值。但是,在您的情况下,逻辑上不应该聚合数据,因为应该显示每个文件。在这种情况下,通常使用MAX()或其他一些聚合函数,保证不会扭曲函数所应用的列的值。您只需要确保每个可能的组包含不超过一个值(我们通过引入排名列来完成)。