【问题标题】:SQL Function that returns dynamically pivotted data返回动态透视数据的 SQL 函数
【发布时间】:2010-11-04 19:50:11
【问题描述】:

是否有可能在 MSSQL 中有一个表值函数,它接受一个属性并使用 Pivot 函数生成关联的 SQL 语句?

CREATE FUNCTION dbo.fnPivot (@EntityTypeID int)
RETURNS TABLE
AS
BEGIN
    DECLARE @SQL varchar(MAX);
    DECLARE @COLS varchar(MAX);

    select @COLS=coalesce(@COLS+',','')+'['+Name+']'from c_EntityAttribute WHERE EntityTypeID = @EntityTypeID;

    SET @SQL = 'SELECT * FROM (SELECT EntityInstanceID, AttributeName, Value FROM v_EntityElementData WHERE EntityTypeID = 1) as s';
    SET @SQL = @SQL + 'PIVOT ( MIN(Value) FOR AttributeName IN (' + @COLS + ') ) AS p';

    RETURN EXEC sp_ExecuteSQL @SQL ;
END

【问题讨论】:

  • 我已经设法通过在存储过程中构建视图(使用 PIVOT)来获得我想要的灵活性,可以安排在必要时重现视图。如果有人想要 PROC,请告诉我。

标签: sql dynamic pivot


【解决方案1】:

很遗憾,除了存储过程之外,SQL Server 会跟踪所有视图、函数等输出的表定义。因此,列和类型不能在输入时动态更改。

出于这个原因,我从未发现 PIVOT 运算符很有用。通常,获取不同列数据的唯一方法是将其视为 XML。

您出于什么原因旋转它?这通常是 UI 问题,因此我建议在 UI 或 SSRS 等处进行。

【讨论】:

  • 问题是系统依赖于 EAV 数据结构,但我正在尝试构建一个不需要太多维护的动态报告层。可能会返回到基于 EAV 元生成静态表的存储过程。感谢您的快速回复。
  • 我做了类似的事情,并使用 SSRS 中的 Matrix / Tablix 功能在渲染上进行旋转 - 工作非常整洁
  • 我不同意这是 UI 问题。这就像说所有数据都是 UI 问题。 RDBS 最适合对数据进行切片和切块。否则,您必须通过网络向应用程序层发送更多信息,以便 UI 小部件或其他类似部件可以对其进行操作。我绝对不希望在线上有 50000 条记录,以便 UI 可以进行透视视图。
【解决方案2】:

表值函数必须始终返回指定的模式,所以这是不可能的。

【讨论】:

    【解决方案3】:

    为了适应这种特性,我做了一些事情(作为一个视图,但函数将遵循相同的方法)。我们将它放在每个安装脚本的末尾,因此当对要旋转的表进行任何更改时,它们将在重新生成的视图中被拾取。

    IF EXISTS (SELECT *
           FROM   sys.objects
           WHERE  object_id = Object_id(N'[dbo].[vwYourView]')
                  AND type IN ( N'V' ))
    BEGIN
      DROP VIEW [dbo].[vwYourView]
    END
    GO                            
    
    Declare @cols VARCHAR(MAX)
    
    Select @cols = COALESCE(@cols + ',[' + YourColumn+ ']',
                                     '[' + YourColumn+ ']')
                                     FROM YourTable
                                     Order By YourColumn
    
    DECLARE @query VARCHAR(MAX)
    Set @query =
    N'
    Create View vwYourView
    AS
    Select * FROM YourTable
    Pivot (
        MAX(YourVal)
        FOR YourColumn IN( '+
            @cols
        +')
    ) AS pvt
    '
    
    Execute(@query)
    
    Select * FROM [vwYourView]
    

    【讨论】:

      【解决方案4】:

      我已经制定了以下过程来动态旋转表中的所有值。

      CREATE OR ALTER PROC dbo.spDynamicPivot (
        @Query NVARCHAR(MAX)              --The query to pivot
      , @AggregateFunction NVARCHAR(MAX)  --The aggregation function surrounding the column name to be pivoted
      , @ValueColumn NVARCHAR(MAX)        --The value column from which to create columns
      , @ResultTable NVARCHAR(MAX) = NULL --An optional table name into which the results will be stored. Note, this unfortunately will not work with #temp tables.
      ) AS BEGIN
      
        SET XACT_ABORT ON;
        SET NOCOUNT ON;
      
        DECLARE @cols NVARCHAR(MAX);
      
        DECLARE @sql NVARCHAR(MAX) = 'SELECT @cols = ISNULL(@cols + '','', '''') + ''['' + Val + '']'' FROM (SELECT DISTINCT Val = ' + @ValueColumn + ' FROM (' + @Query + ') a) b;';
        DECLARE @paramDef NVARCHAR(MAX) = '@cols NVARCHAR(MAX) OUTPUT';
      
        EXEC sp_executesql @sql, @paramDef, @cols = @cols OUTPUT;
      
        DECLARE @resultSetSql NVARCHAR(MAX) = IIF(ISNULL(@ResultTable, '') <> '', 'INTO ' + @ResultTable + ' ', '');
        SET @sql = 'SELECT * ' + @resultSetSql + 'FROM (' + @Query + ') q PIVOT( ' + @AggregateFunction + ' FOR ' + @ValueColumn + ' IN (' + @cols + ') ) p';
      
        EXEC sp_executesql @sql;
      
      END
      

      用法:

      DROP TABLE IF EXISTS #t;
      CREATE TABLE #t (Name VARCHAR(50), Age INT, NetWorth FLOAT);
      INSERT INTO #t VALUES 
      ('Bill', 50, 250),
      ('Barb', 17, 15),
      ('Fred', 25, 30),
      ('Bill', 25, 100),
      ('Kahless', 90000, 4E10) 
      
      --Displaying results directly
      EXEC dbo.spDynamicPivot @Query = 'SELECT * FROM #t', @AggregateFunction = 'AVG(NetWorth)', @ValueColumn = 'Name'
      
      --Or writing them into a table for additional use
      DROP TABLE IF EXISTS tempdb.dbo.MyTable;
      EXEC dbo.spDynamicPivot @Query = 'SELECT * FROM #t', @AggregateFunction = 'AVG(NetWorth)', @ValueColumn = 'Name', @ResultTable = 'tempdb.dbo.MyTable'
      
      SELECT * FROM tempdb.dbo.MyTable
      

      【讨论】:

        猜你喜欢
        • 2016-05-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-25
        • 2014-10-16
        • 2010-12-28
        • 2014-07-26
        • 1970-01-01
        相关资源
        最近更新 更多