Sql server SQL Server 2008 PIVOT-如何控制列内容

Sql server SQL Server 2008 PIVOT-如何控制列内容,sql-server,tsql,sql-server-2008,pivot,Sql Server,Tsql,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 N

我正在尝试使用以下结构对工作表进行透视查询:

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)
)
“名称”列包含两个值之一:“名称1”或“名称2”。必须在如下所述的分组中使用这些参数

我希望它成为一个包含以下列的表:

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
这几乎奏效了!;)但是我的值没有放在正确的列中。如果某个位置、销售类型、本地id、房屋描述和销售订单存在特定值,则该值始终在[1]列中输出。但这应该根据它是
name1
还是
name2
以及它的
订单年份来确定

我知道我看到的是我的行数运算的直接结果,如果只有一个条目存在,该运算将seq列计算为1。所以也许我用错误的方式攻击它


有人能解决这个问题吗?

虽然只有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

尽管您只有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

谢谢你。您的回答让我意识到使用ROW_NUMBER进行动态透视是错误的。我从[1]、[2]等更改了透视列的名称,因此现在可以通过名称和年份的聚合精确地匹配它们。正如“笑脸人”的例子很好地说明的那样

我也喜欢斯迈金曼的程序。我可能会派上用场

为了向面临相同问题的其他用户阐明这一点,我在这里列出了使我的PIVOT查询工作的实际SQL(在现实生活中,我动态生成此SQL):

问候
亚历克斯谢谢你。您的回答让我意识到使用ROW_NUMBER进行动态透视是错误的。我从[1]、[2]等更改了透视列的名称,因此现在可以通过名称和年份的聚合精确地匹配它们。正如“笑脸人”的例子很好地说明的那样

我也喜欢斯迈金曼的程序。我可能会派上用场

为了向面临相同问题的其他用户阐明这一点,我在这里列出了使我的PIVOT查询工作的实际SQL(在现实生活中,我动态生成此SQL):

问候
Alex

如果上述情况不清楚。我希望支持在单个透视列中只有一个值的条目,例如name1_2009列。然后,必须将其他name1 og name2列设置为NULL。如果上面不清楚。我希望支持在单个透视列中只有一个值的条目,例如name1_2009列。然后,其他name1 og name2列必须设置为NULL。