2010-11-26 61 views
0

我試圖做一個PIVOT查詢出與結構的工作臺:SQL Server 2008中PIVOT - 如何控制列內容

DECLARE @workingData TABLE 
(
    location VARCHAR(20), 
    name VARCHAR(50), 
    sales_type VARCHAR(20), 
    local_id VARCHAR(15), 
    house_description VARCHAR(40), 
    sales_order VARCHAR(10), 
    order_year INT, 
    amount NUMERIC(14,0) 
) 

名稱列包含兩個值之一:「 name1'或'name2'。這些必須在分組中使用,如下所述。

我希望它成爲一個表中的列:

location 
sales_type 
local_id 
house_description 
sales_order 
name1_2007 
name2_2007 
name1_2008 
name2_2008 
name1_2009 
name2_2009 
name1_2010 
name2_2010 

我嘗試這樣做:

SELECT 
    location, sales_type, local_id, house_description, sales_order, 
    MAX([1]) AS [name1_2007], MAX([2]) AS [name2_2007], 
    MAX([3]) AS [name1_2008], MAX([4]) AS [name2_2008], 
    MAX([5]) AS [name1_2009], MAX([6]) AS [name2_2009], 
    MAX([7]) AS [name1_2010], MAX([8]) AS [name2_2010], 
    '2010' As [Base Year] 
FROM (
    SELECT location, sales_type, local_id, house_description, sales_order, order_year, name, amount 
    ,ROW_NUMBER() OVER (PARTITION BY location, sales_type, local_id, sales_order 
    ORDER BY order_year, name) AS seq 
    FROM @workingData 
    ) AS SourceTable 
PIVOT 
(
    MAX(amount) 
    FOR seq IN ([1], [2], [3], [4], [5], [6], [7], [8], [9]) 
) AS PivotTable 
GROUP BY 
location, sales_type, local_id, house_description, sales_order 

而且這種近乎作品! ;)但我的價值觀沒有放在正確的列。如果特定位置存在特定值,則sales_type,local_id,house_description和sales_order始終在[1]列中輸出。但這應該確定它是name1還是name2及其order_year

我知道我看到的是我的row_number操作的直接結果,即只有一個條目存在時,seq列將被計算爲1。所以也許我是以這種錯誤的方式攻擊?

有人可以解決這個問題嗎?

+0

如果從上面不清楚。我想支持在單個透視列中僅具有值的條目,例如name1_2009列。其他名稱和名稱2列必須設置爲NULL。 – Alex 2010-11-26 12:31:22

回答

3

儘管您只有Name1和Name2 ATM,但這是一個典型的動態數據透視示例。

創建以下SP:

CREATE PROC [dbo].[pivotsp] 
    @query AS NVARCHAR(MAX),     -- The query, can also be the name of a table/view. 
    @on_rows AS NVARCHAR(MAX),     -- The columns that will be regular rows. 
    @on_cols AS NVARCHAR(MAX),     -- The columns that are to be pivoted. 
    @agg_func AS NVARCHAR(257) = N'SUM',   -- Aggregate function. 
    @agg_col AS NVARCHAR(MAX),     -- Column to aggregate. 
    @output AS NVARCHAR(257) = N'',    -- Table for results 
    @debug AS bit = 0       -- 1 for debugging 
AS 

-- Example usage: 
-- exec pivotsp 
--   'select * from vsaleshistory', 
--   'market,marketid,family,familyid,Forecaster,Forecasterid,product,productid', 
--   'month', 
--   'sum', 
--   'ku', 
--   '##sales' 

-- Input validation 
IF @query IS NULL OR @on_rows IS NULL OR @on_cols IS NULL 
    OR @agg_func IS NULL OR @agg_col IS NULL 
BEGIN 
    RAISERROR('Invalid input parameters.', 16, 1); 
    RETURN; 
END 

-- Additional input validation goes here (SQL Injection attempts, etc.) 

BEGIN TRY 
    DECLARE 
    @sql  AS NVARCHAR(MAX), 
    @cols AS NVARCHAR(MAX), 
    @newline AS NVARCHAR(2); 

    SET @newline = NCHAR(13) + NCHAR(10); 

    -- If input is a valid table or view 
    -- construct a SELECT statement against it 
    IF COALESCE(OBJECT_ID(@query, N'U'), 
       OBJECT_ID(@query, N'V')) IS NOT NULL 
    SET @query = N'SELECT * FROM ' + @query; 

    -- Make the query a derived table 
    SET @query = N'(' + @query + N') AS Query'; 

    -- Handle * input in @agg_col 
    IF @agg_col = N'*' 
    SET @agg_col = N'1'; 

    -- Construct column list 
    SET @sql = 
     N'SET @result = '         + @newline + 
     N' STUFF('           + @newline + 
     N' (SELECT N'','' + quotename(' 
        + 'CAST(pivot_col AS sysname)' + 
        + ') AS [text()]'       + @newline + 
     N'  FROM (SELECT DISTINCT(' 
        + @on_cols + N') AS pivot_col'    + @newline + 
     N'   FROM' + @query + N') AS DistinctCols' + @newline + 
     N'  ORDER BY pivot_col'       + @newline + 
     N'  FOR XML PATH(''''))'       + @newline + 
     N' ,1, 1, N'''');' 

    IF @debug = 1 
    PRINT @sql 

    EXEC sp_executesql 
    @stmt = @sql, 
    @params = N'@result AS NVARCHAR(MAX) OUTPUT', 
    @result = @cols OUTPUT; 

    IF @debug = 1 
    PRINT @cols 

    -- Create the PIVOT query 
    IF @output = N'' 
     begin 
     SET @sql = 
      N'SELECT *'           + @newline + 
      N'FROM (SELECT ' 
          + @on_rows 
          + N', ' + @on_cols + N' AS pivot_col' 
          + N', ' + @agg_col + N' AS agg_col'  + @newline + 
      N'  FROM ' + @query + N')' + 
          + N' AS PivotInput'      + @newline + 
      N' PIVOT(' + @agg_func + N'(agg_col)'    + @newline + 
      N' FOR pivot_col IN(' + @cols + N')) AS PivotOutput;' 
     end 
    ELSE 
     begin 
     set @sql = 'IF EXISTS (SELECT * FROM tempdb.sys.objects WHERE ' + 
      'name = ''' + @output + ''' AND type = N''U'') DROP TABLE tempdb.' + @output 
     EXEC sp_executesql @sql; 

     SET @sql = 
      N'SELECT * INTO ' + @output       + @newline + 
      N'FROM (SELECT ' 
          + @on_rows 
          + N', ' + @on_cols + N' AS pivot_col' 
          + N', ' + @agg_col + N' AS agg_col'  + @newline + 
      N'  FROM ' + @query + N')' + 
          + N' AS PivotInput'      + @newline + 
      N' PIVOT(' + @agg_func + N'(agg_col)'    + @newline + 
      N' FOR pivot_col IN(' + @cols + N')) AS PivotOutput;' 
     end 

    IF @debug = 1 
     PRINT @sql 

    EXEC sp_executesql @sql; 
END TRY 
BEGIN CATCH 
    DECLARE 
    @error_message AS NVARCHAR(2047), 
    @error_severity AS INT, 
    @error_state AS INT; 

    SET @error_message = ERROR_MESSAGE(); 
    SET @error_severity = ERROR_SEVERITY(); 
    SET @error_state = ERROR_STATE(); 

    RAISERROR(@error_message, @error_severity, @error_state); 

    RETURN; 
END CATCH 

現在事情變得更加容易。從輸入

1 loca namea st1 1 house1 2 2007 1234 
2 loca namea st1 1 house1 2 2007 2345 
3 loca namea st1 1 house1 2 2007 3456 
4 loca namea st1 1 house1 2 2008 6789 
5 loca namea st1 1 house1 2 2008 7890 
6 loca nameb st1 1 house1 2 2007 1234 
7 locc nameb st1 1 house1 2 2007 2345 
8 loca nameb st1 1 house1 2 2007 3456 
9 loca nameb st1 1 house1 2 2008 6789 
10 locc nameb st1 1 house1 2 2008 7890 

我們彙總的名字和年份

SELECT 
    location, sales_type, local_id, house_description, sales_order, 
    [name] + '_' + cast(order_year AS varchar(20)) as nameyear, 
    max(amount) as amount 
INTO 
    ##crosstab 
FROM 
    working 
GROUP BY 
    location, sales_type, local_id, house_description, sales_order, 
    [name] + '_' + cast(order_year AS varchar(20)) 

loca st1 1 house1 2 namea_2007 3456 
loca st1 1 house1 2 namea_2008 7890 
loca st1 1 house1 2 nameb_2007 3456 
loca st1 1 house1 2 nameb_2008 6789 
locc st1 1 house1 2 nameb_2007 2345 
locc st1 1 house1 2 nameb_2008 7890 

然後,使用pivot_sp

EXEC pivotsp 
     'select * from ##crosstab', 
     'location, sales_type, local_id, house_description, sales_order', 
     'nameyear', 
     'max', 
     'amount', 
     '##answer' 

SELECT 
    * 
FROM 
    ##answer 

我們得到

locat st local  house so namea_2007 namea_2008 nameb_2007 nameb_2008 
loca st1 1 house1 2 3456 7890 3456 6789 
locc st1 1 house1 2 NULL NULL 2345 7890 

HTH

+0

太棒了。謝謝 – Sameer 2012-03-31 01:38:08

0

謝謝。你的回答讓我意識到使用ROW_NUMBER是做動態PIVOT的錯誤方法。我改變了[1],[2]等樞軸柱的名稱,所以我現在可以通過名稱和年份的聚合精確地匹配它們。正如「傻笑者的例子很好地顯示!

我也喜歡smirkingmans程序。我可以派上用場。:)

爲了澄清這對於面臨着同樣的問題,其他用戶我列出這裏實際的SQL,使我的PIVOT查詢工作(在現實生活中我動態生成此):

SELECT 
     location, 
     sales_type, 
     local_id, 
     house_description, 
     sales_order, 
      MAX([name12007]), 
      MAX([name22007]), 
      MAX([name12008]), 
      MAX([name22008]), 
      MAX([name12009]), 
      MAX([name22009]), 
      MAX([name12010]), 
      MAX([name22010]), 
     '2010' As [BaseYear] 
    FROM (
     SELECT location, sales_type, local_id, house_description, sales_order, order_year, name, 
      ISNULL(amount,0) AS [amount], 
      (name + CONVERT(VARCHAR(MAX),order_year)) AS ColumnIdentifier 
     FROM @workingData 
     ) AS SourceTable 
    PIVOT 
    (
     MAX(amount) 
     FOR ColumnIdentifier IN ([name12007],[name22007],[name12008],[name22008], 
             [name12009],[name22009],[name12010],[name22010]) 
    ) AS PivotTable 
    GROUP BY 
     location, sales_type, local_id, house_description, sales_order 

問候 亞歷