Возможно ли вообще иметь в MSSQL функцию с табличным значением, которая принимает атрибут и генерирует связанный оператор SQL с функцией Pivot?

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
6
Jan de Jager 22 Июн 2009 в 15:23

4 ответа

Лучший ответ

К сожалению, нет, за исключением хранимых процедур, SQL Server отслеживает определение таблицы вывода всех представлений, функций и т. Д. Таким образом, столбцы и типы не могут динамически изменяться на входе.

По этой причине я никогда не считал, что оператор PIVOT может быть полезным. Обычно единственный способ получить изменяющиеся данные столбца - это рассматривать их как XML.

По какой причине вы его переворачиваете? Обычно это проблема пользовательского интерфейса, поэтому я бы рекомендовал делать это в пользовательском интерфейсе, SSRS и т. Д.

6
Joel Mansford 22 Июн 2009 в 11:27

Я выполнил следующую процедуру для динамического поворота всех значений в таблице.

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
1
MgSam 15 Авг 2017 в 17:16

Вот кое-что, что я сделал, чтобы приспособить такую ​​функцию (как представление, но функция будет следовать той же методологии). Мы вставляли его в конец каждого сценария установки, чтобы при внесении любых изменений в поворачиваемую таблицу они отражались в повторно созданном представлении.

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]
1
Brandon Boone 16 Дек 2011 в 16:05

Функция с табличным значением всегда должна возвращать указанную схему, поэтому это невозможно.

3
cjk 22 Июн 2009 в 11:27