2009-06-22 16 views
6

¿Es posible tener una función de tabla con valores en MSSQL que tome un atributo y genere una declaración SQL asociada con función Pivot?SQL Función que devuelve datos pivotados dinámicamente

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 
+0

He conseguido que la flexibilidad que quería mediante la construcción de mis puntos de vista (usando pivote) en un procedimiento almacenado que puede ser programado para reproducir las vistas cuando sea necesario. Avíseme si alguien quiere el PROC. –

Respuesta

6
Desafortunadamente no

, con la excepción de los procedimientos almacenados de SQL Server realiza un seguimiento de la definición de la tabla de la salida de todas las vistas, funciones, etc. Por lo tanto las columnas y los tipos no pueden cambiar de forma dinámica en la entrada.

Por esta razón, nunca he encontrado que el operador PIVOT sea útil. En general, la única forma de obtener datos variables de columna es tratarlos como XML.

¿Por qué razón lo estás pivotando? Esto suele ser un problema de UI, por lo que recomendaría hacerlo en la interfaz de usuario o SSRS, etc.

+0

El problema es que el sistema se basa en una estructura de datos EAV, pero estoy tratando de crear una capa dinámica de informes que no requiere demasiado mantenimiento. Probablemente regrese a los procesos almacenados que generan las tablas estáticas basadas en el meta de EAV. Gracias por la rápida respuesta. –

+2

Hago similar y uso la funcionalidad de Matrix/Tablix dentro de SSRS para pivotarlo en el procesamiento: funciona muy bien –

+2

No estoy de acuerdo con que esto sea una preocupación de UI. Eso es como decir que todos los datos son una preocupación de UI. El RDBS es el más adecuado para cortar y cortar datos. De lo contrario, debe enviar mucho más por cable a la capa de la aplicación para que el widget de la interfaz de usuario o algo así pueda manipularlo. De ninguna manera quiero 50000 registros en el cable para que la IU pueda hacer una vista de pivote. – Wade

1

Una función con valores de tabla siempre debe devolver un esquema específico, por lo que no es posible.

1

He aquí algo que hice para acomodar este tipo de característica (como una vista, pero una función seguiría la misma metodología). Lo pegamos al final de cada script de instalación para que cuando se realizaran cambios en la tabla pivoteen, se recojan en la vista regenerada.

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

Realicé el siguiente procedimiento para pivotar dinámicamente todos los valores en una tabla.

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 

Uso:

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 
Cuestiones relacionadas