Domanda

È possibile avere una funzione con valori di tabella in MSSQL che accetta un attributo e genera un'istruzione SQL associata con la funzione 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
È stato utile?

Soluzione

Sfortunatamente no, ad eccezione delle procedure memorizzate, SQL Server tiene traccia della definizione della tabella dell'output di tutte le viste, funzioni, ecc. Pertanto le colonne e i tipi non possono cambiare dinamicamente sull'input.

Per questo motivo non ho mai trovato utile l'operatore PIVOT. Generalmente l'unico modo per ottenere dati di colonne variabili è di trattarli come XML.

Per quale motivo lo stai ruotando? Di solito si tratta di un problema relativo all'interfaccia utente, quindi consiglierei di farlo nell'interfaccia utente o SSRS ecc.

Altri suggerimenti

Una funzione con valori di tabella deve sempre restituire uno schema specificato, quindi questo non è possibile.

Ecco qualcosa che ho fatto per accogliere questo tipo di funzionalità (come vista, ma una funzione seguirebbe la stessa metodologia). Lo abbiamo bloccato alla fine di ogni script di installazione, quindi quando venivano apportate modifiche alla tabella che veniva ruotata, venivano raccolte nella vista rigenerata.

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]

Ho eseguito la seguente procedura per ruotare dinamicamente tutti i valori in una tabella.

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

Utilizzo:

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top