SQL查询将行设置为列

时间:2011-07-07 15:02:07

标签: sql sql-server-2000

由于我使用的是SQL Server 2000,并且没有SQL Server 2005的数据透视表,我需要一些关于创建查询的帮助。

下面找一些样本数据。

TBLProduct
ProductID   ProductName
1           Jacket
2           Blazer
3           Chaleco

TBLFactors
FactorID    FactorName
1           Length
2           Threading
3           Wool
4       Cotton

TBLFactorInspect
ID  ProductID   FactorID    FactorValue
1   1           1           5.00
2   1           2           5.55
3   2           2           6.33
4   2           3           3.66
5   2           4           1.05

我需要有关将输出数据的查询的帮助:

ProductID   ProductName Length  Threading   Wool    Cotton
1           Jacket      5.00    5.55        -       -
2           Blazer      -       6.33        3.66    1.05
3           Chaleco     -       -           -       -

我想我还需要另一个查询,例如,只查询ProductID = 1,结果数据会将因子作为已定义值的列返回,即:

ProductID   ProductName Length  Threading
1           Jacket      5.00    5.55

任何建议都非常感谢。感谢。

更新: 如果存储过程是实现这一目标的最佳答案,那么我没有任何问题,因为我似乎认为SQL Server 2K在创建枢轴方面的限制是这里的主要障碍。

我正在尝试在存储过程解决方案中处理动态SQL,但我对动态列 - 值映射没有太大影响。

3 个答案:

答案 0 :(得分:2)

我没有方便尝试的SQL Server 2000实例,但看看这是否能让你随处可见。我有点惊讶于有条件地包含列的要求 - 我们希望构建应用程序以处理列突然出现和消失的情况,具体取决于TBLFactorInspect表中的参数和更改数据。

USE [tempdb];
GO
SET NOCOUNT ON;
GO

设定:

CREATE TABLE dbo.TBLProduct
(
    ProductID INT PRIMARY KEY, ProductName NVARCHAR(50)
);

INSERT dbo.TBLProduct(ProductID, ProductName)
SELECT 1, 'Jacket'
UNION SELECT 2, 'Blazer'
UNION SELECT 3, 'Chaleco';

CREATE TABLE dbo.TBLFactors
(
    FactorID INT PRIMARY KEY, FactorName NVARCHAR(50)
);

INSERT dbo.TBLFactors(FactorID, FactorName)
SELECT 1, 'Length'
UNION SELECT 2, 'Threading'
UNION SELECT 3, 'Wool'
UNION SELECT 4, 'Cotton';

CREATE TABLE dbo.TBLFactorInspect
(
    ID INT PRIMARY KEY, ProductID INT,
    FactorID INT, FactorValue DECIMAL(5,2)
);

INSERT dbo.TBLFactorInspect(ID, ProductID, FactorID, FactorValue)
SELECT 1,1,1,5.00 UNION SELECT 2,1,2,5.55
UNION SELECT 3,2,2,6.33 UNION SELECT 4,2,3,3.66
UNION SELECT 5,2,4,1.05;
GO

现在有些代码:

CREATE PROCEDURE dbo.GetProductPivot
    @ProductID INT = NULL
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @sql NVARCHAR(4000);
    SET @sql = N'';

    SELECT @sql = @sql + N'
        ' + QUOTENAME(FactorName) 
        + ' = MAX(CASE WHEN d.FactorID = ' 
        + RTRIM(FactorID) + ' THEN d.FactorValue END),'
    FROM
    (
        SELECT f.FactorID, f.FactorName
        FROM dbo.TBLFactorInspect AS d
        INNER JOIN dbo.TBLFactors AS f
        ON f.FactorID = d.FactorID
        WHERE (d.ProductID = @ProductID OR @ProductID IS NULL)
        GROUP BY f.FactorID, f.FactorName
    ) AS f
    ORDER BY f.FactorID;

    IF @sql = N''
    BEGIN
        SELECT ProductID, ProductName, 
            Result = 'No data in TBLFactorInspect'
            FROM dbo.TBLProduct
            WHERE ProductID = COALESCE(@ProductID, ProductID);
    END
    ELSE
    BEGIN
        SELECT @sql = N'SELECT p.ProductID, p.ProductName, ' + 
            LEFT(@sql, LEN(@sql)-1) + '
                FROM dbo.TBLProduct AS p
                LEFT OUTER JOIN dbo.TBLFactorInspect AS d
                ON p.ProductID = d.ProductID
                ' + CASE WHEN @ProductID IS NOT NULL THEN 
                ' WHERE p.ProductID = ' + RTRIM(@ProductID) ELSE '' END + '
                GROUP BY p.ProductID, p.ProductName
                ORDER BY p.ProductID;';

        EXEC sp_executeSQL @sql;
    END
END
GO

一些证据表明它不使用参数或特定产品ID:

EXEC dbo.GetProductPivot;
EXEC dbo.GetProductPivot @ProductID = 1;
EXEC dbo.GetProductPivot @ProductID = 2;
EXEC dbo.GetProductPivot @ProductID = 3;
GO

现在让我们添加一个新因素并证明它继续有效:

INSERT dbo.TBLFactors(FactorID, FactorName)
    SELECT 5, 'Tubing';

INSERT dbo.TBLFactorInspect(ID, ProductID, FactorID, FactorValue)
SELECT 6,1,5,2.75;
GO
EXEC dbo.GetProductPivot;
EXEC dbo.GetProductPivot @ProductID = 1;
GO

清理:

DROP TABLE dbo.TBLProduct, dbo.TBLFactors, dbo.TBLFactorInspect;
GO
DROP PROCEDURE dbo.GetProductPivot;
GO

答案 1 :(得分:1)

如果没有动态SQL,您需要明确说明最终会使用哪些列。 (在SQL Server 2000中,只有在您已经知道列名称等的情况下才能进行透视。如果需要动态数量的列,使用动态名称,SQL Server 2000无法在不编写动态SQL的情况下执行此操作)

此外,根据评论,非规范化数据通常最好在GUI中完成,而不是在数据库本身上完成。

但这应该做你要求的......

SELECT
  tblProduct.ProductID,
  tblProduct.ProductName,
  MAX(CASE WHEN tblFactorInspect.FactorID = 1 THEN tblFactorInspect.FactorValue END) AS Length,
  MAX(CASE WHEN tblFactorInspect.FactorID = 2 THEN tblFactorInspect.FactorValue END) AS Threading,
  MAX(CASE WHEN tblFactorInspect.FactorID = 3 THEN tblFactorInspect.FactorValue END) AS Wool,
  MAX(CASE WHEN tblFactorInspect.FactorID = 4 THEN tblFactorInspect.FactorValue END) AS Cotton
FROM
  tblProduct
LEFT JOIN
  tblFactorInspect
    ON  tblFactorInspect.ProductID = tblProduct.ProductID
GROUP BY
  tblProduct.ProductID,
  tblProduct.ProductName

添加WHERE tblProduct.ProductID = 1也会提出您要求的问题的后半部分。

答案 2 :(得分:1)

我的第一个想法是使用这样的左连接:(一切都是显式的,但在tblFactors中使用有限数量的行)

SELECT
  tblProduct.ProductID,
  tblProduct.ProductName,
  ISNULL(tblLength.FactorValue, '-') AS [Length],
  ISNULL(tblThreading.FactorValue, '-') AS Threading,
  ISNULL(tblWool.FactorValue, '-') AS Wool,
  ISNULL(tblCotton.FactorValue, '-') AS Cotton
FROM
  tblProduct
LEFT JOIN tblFactorInspect tblLength
    ON  tblLength.ProductID = tblProduct.ProductID AND FactorID = 1
LEFT JOIN tblFactorInspect tblThreading
    ON  tblThreading.ProductID = tblProduct.ProductID AND FactorID = 2
LEFT JOIN tblFactorInspect tblWool
    ON  tblWool.ProductID = tblProduct.ProductID AND FactorID = 3
LEFT JOIN tblFactorInspect tblCotton
    ON  tblCotton.ProductID = tblProduct.ProductID AND FactorID = 4