2012-02-27 34 views
1

我有以下查询需要2个参数。SQL Server 2005 - 基于在主要查询中传递的变量的列名称

  1. YearNumber
  2. MonthNumber

在我的支点查询,我想基于@Year_Rtl变量来选择列。我需要选择去年,去年和去年的数据。由于UI上显示的数据是表格格式除以@Year_Rtl,因此我决定为其编写一个数据透视表,如下所示。

在查询中,如果我硬编码[@Year_Rtl], [@Year_Rtl - 1], [@Year_Rtl - 2][2012], [2011], [2010],它工作正常。但是,由于过去的一年可以是任何事情,我希望动态地命名列。

DECLARE @Month_Rtl int 
DECLARE @Year_Rtl int 

SET @Year_Rtl = 2012 
SET @Month_Rtl = 1 

SELECT 
    'Data 1', [@Year_Rtl], [@Year_Rtl - 1], [@Year_Rtl - 2] 
FROM 
    (SELECT [Yr_No], Qty 
    FROM dbo.Table1 t 
    WHERE (t.Col1 = 10) AND 
    (t.Col2 = '673') AND 
    ((t.Mth_No = @Month_Rtl AND t.Yr_No = @Year_Rtl) OR 
    (t.Mth_No = 12 AND t.Yr_No IN (@Year_Rtl - 1, @Year_Rtl - 2))) 
    ) p PIVOT (SUM(Qty) 
       FOR [Yr_No] IN ([@Year_Rtl], [@Year_Rtl-1], [@Year_Rtl-2]) 
      ) AS pvt 

上面的查询抛出以下错误:

错误转换数据类型为nvarchar到SMALLINT。
错误值“@Year_Rtl”在PIVOT运算符中提供。
无效的列名'@Year_Rtl - 1'。
无效的列名'@Year_Rtl - 2'。

+3

参与'PIVOT'的列不能是可变的,使用动态SQL的外部。是的,它很糟糕。这只是它的方式。 – Yuck 2012-02-27 20:07:45

+0

我能够将整个查询创建为动态SQL。想知道它是否会达到性能? – Asdfg 2012-02-27 20:22:42

+1

据我所知,动态列摆动只能通过动态SQL来实现 - 当然,它不会像存储查询计划那样具有良好的性能,但是它或者不具备查询功能。 – 2012-02-27 20:25:11

回答

1

由于您可以使用动态SQL,我会采用宏替换方法。您正在识别必须用占位符动态替换的查询区域(例如$$Year_Rtl),然后在下面计算它们的替换值。我发现它保持SQL语句易于遵循。

DECLARE @SQL NVarChar(2000); 
SELECT @SQL = N' 
    SELECT 
     ''Data 1'', [$$Year_Rtl], [$$Year_RtlM1], [$$Year_RtlM2] 
    FROM 
    (SELECT [Yr_No], Qty 
     FROM dbo.Table1 t 
     WHERE (t.Col1 = 10) AND 
     (t.Col2 = ''673'') AND 
     ((t.Mth_No = $$Month_Rtl AND t.Yr_No = $$Year_Rtl) OR 
     (t.Mth_No = 12 AND t.Yr_No IN ($$Year_RtlM1, $$Year_RtlM2))) 
    ) p PIVOT (SUM(Qty) 
       FOR [Yr_No] IN ([$$Year_Rtl], [$$Year_RtlM1], [$$Year_RtlM2]) 
       ) AS pvt'; 

SELECT @SQL = REPLACE(@SQL, '$$Year_RtlM2', @Year_Rtl - 2); 
SELECT @SQL = REPLACE(@SQL, '$$Year_RtlM1', @Year_Rtl - 1); 
SELECT @SQL = REPLACE(@SQL, '$$Year_Rtl', @Year_Rtl); 
SELECT @SQL = REPLACE(@SQL, '$$Month_Rtl', @Month_Rtl); 

PRINT @SQL; 
-- Uncomment the next line to allow the built query to execute... 
--EXECUTE sp_ExecuteSQL @SQL; 
+0

你知道我怎样才能选择多个列,并仍然得到一行?像选择[Yr_No],数量,金额。我试过,但它给了我2行,并在第一行数量为空,在其他行数量为空。 – Asdfg 2012-02-27 21:50:06

0

您需要查看Dynamic SQL Pivoting。

我推荐阅读Itzik Ben-Gan的T-SQL基础知识,他会回顾如何做到这一点。

或者,如果您不想购买此书,请尝试this article

1

由于消费代码必须根据这项计划,片状(例如,基于“位置”,而不是名字中选择列) - 为什么不通过执行DATEDIFF(year,Yr_No,@Year_Rtl)归列,工作距离那里?这些列将永远是0-1-2 ...

0

也许这将帮助:

首先获得列具有计数功能是这样的:

DECLARE @Month_Rtl int, 
     @Year_Rtl int, 
     @Year_Rtl_Start INT, 
     @cols VARCHAR(MAX), 
     @values VARCHAR(MAX) 

SET @Year_Rtl = 2012 
SET @Month_Rtl = 1 
SET @Year_Rtl_Start=2009 

;WITH Years (n) AS (
     SELECT @Year_Rtl_Start UNION ALL 
     SELECT 1 + n FROM Years WHERE n < @Year_Rtl) 
SELECT 
    @cols = COALESCE(@cols + ','+QUOTENAME(n), 
        QUOTENAME(n)), 
    @values = COALESCE(@values + ','+CAST(n AS VARCHAR(100)), 
        CAST(n AS VARCHAR(100))) 
FROM 
    Years 
ORDER BY n DESC 

变量@cols包含列这是关键点,变量@values包含IN的年份。 @Year_Rtl是结束年份,而@Year_Rtl_Start是您开始的范围。

然后声明和执行动态枢轴这样的:

DECLARE @query NVARCHAR(4000)= 
N'SELECT 
    ''Data 1'', '[email protected]+' 
FROM 
    (
     SELECT 
      [Yr_No], Qty 
     FROM 
      dbo.Table1 t 
     WHERE 
      t.Col1 = 10 
      AND t.Col2 = ''673'' 
      AND 
      (
       (
        t.Mth_No = '+CAST(@Month_Rtl AS VARCHAR(10))+' 
        AND t.Yr_No = '+CAST(@Year_Rtl AS VARCHAR(10))+' 
       ) 
       OR 
       (
        t.Mth_No = 12 
        AND t.Yr_No IN ('[email protected]+')) 
       ) 
    ) p 
    PIVOT 
    (
     SUM(Qty) 
     FOR [Yr_No] IN ('[email protected]+') 
    ) AS pvt' 

EXECUTE(@query)