SQL Server数据透视表中的预定义列名称

时间:2011-01-16 04:46:02

标签: sql sql-server stored-procedures pivot-table

前几天我在StackOverflow(stackoverflow.com/questions/4663698/how-can-i-display-a-consolidated-version-of-my-sql-server-table)中打开了一个主题。那时我需要有关如何在数据透视表上显示数据的帮助。从我在论坛中获得的帮助,我的研究使我进入了关于动态SQL的页面:www.sommarskog.se/dynamic_sql.html。然后它引导我使用Itzik Ben-Gan创建的这个令人敬畏的SQL脚本,它将创建一个存储过程,完全按照我想要的方式输出数据透视表:sommarskog.se/pivot_sp.sp

好吧,差不多。我需要在此存储过程中进行一处更改。我没有从SPROC中的@on_cols变量中提取动态列名,而是需要输出表以简单的ASC顺序保存通用列名。可能是,例如,col1,col2,col3,col4 ......动态列名称对我来说是个问题。所以我需要按照它们出现的顺序用它们的索引命名。我已经尝试过各种改变这个伟大的SQL脚本的东西,但是它不起作用。

我没有粘贴代码中的代码,因为它太长了,但链接above会让我们在那里。

任何帮助表示赞赏。非常感谢你

1 个答案:

答案 0 :(得分:1)

您需要此辅助函数或类似的东西将分隔列表(列名称)拆分为行

CREATE function dbo.values2table
(
@values varchar(max),
@separator varchar(3),
@limit int -- set to -1 for no limit
) returns @res table (id int identity, [value] varchar(max))
as
begin
declare @value varchar(50)
declare @commapos int, @lastpos int
set @commapos = 0
select @lastpos = @commapos, @commapos = charindex(@separator, @values, @lastpos+1)
while @commapos > @lastpos and @limit <> 0
begin
    select @value = substring(@values, @lastpos+1, @commapos-@lastpos-1)
    if @value <> '' begin
        insert into @res select ltrim(rtrim(@value))
        set @limit = @limit-1
    end
    select @lastpos = @commapos, @commapos = charindex(@separator, @values, @lastpos+1)
end
select @value = substring(@values, @lastpos+1, len(@values))
if @value <> '' insert into @res select ltrim(rtrim(@value))
return
end
GO

然后将pivot_sp proc更改为以下内容 (SQL Server支持多行字符串,因此我删除了所有“+ @newline +”s)

CREATE PROC dbo.pivot_sp
  @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'MAX', -- Aggregate function.
  @agg_col  AS NVARCHAR(MAX),  -- Column to aggregate.
  @generic  AS bit = 0,
  @debug    AS bit = 1
AS

-- 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 = 
      STUFF(
        (SELECT N'','' +  quotename(
           CAST(pivot_col AS sysname)
           )  AS [text()]
         FROM (SELECT DISTINCT '
           + @on_cols + N' AS pivot_col
               FROM' + @query + N') AS DistinctCols
         ORDER BY pivot_col
         FOR XML PATH(''''))
        ,1, 1, N'''');'

  IF @debug = 1
     PRINT @sql

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

  DECLARE @colsout nvarchar(max)
  if @generic = 1
    select @colsout = coalesce(@colsout,'') + value + '] as col'+right(id,10)
    from dbo.values2table(left(@cols, LEN(@cols)-1), '],[', -1) X
  else
    select @colsout = @cols

  -- Create the PIVOT query
  SET @sql = N'
    SELECT ' + @on_rows + ',' + @colsout + '
      FROM (SELECT '
              + @on_rows
              + N', ' + @on_cols + N' AS pivot_col'
              + N', ' + @agg_col + N' AS agg_col
          FROM ' + @query + N')' +
              + N' AS PivotInput
      PIVOT(' + @agg_func + N'(agg_col)
        FOR pivot_col IN(' + @cols + N')) AS PivotOutput;'

    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
GO

以下是一个示例,您应该可以从任何SQL Server数据库运行。

exec dbo.pivot_sp @query = '
select o.name object_name, c.system_type_id, c.name
from sys.columns c
inner join sys.objects o on o.object_id=c.object_id
where o.is_ms_shipped = 0',
@on_rows  = 'object_name',
@on_cols  = 'system_type_id',
@agg_func = N'COUNT', -- Aggregate function.
@agg_col  = 'name',
@generic=1,
@debug=1