Principales respuestas
Consulta dinámica de PIVOT que devuelve una Tabla a través de una funcion

Pregunta
-
Hola con todos,
Mi duda es como puedo crear un función que me devuelva un tabla pero de una consulta dinámica con el operador PIVOT. Básicamente utilizo la consulta dinámica por que no se cuantas columnas me devolverá la operación con PIVOT.
Les adjunto la consulta que he tratado de crear como función.
CREATE FUNCTION FN_ListaPorTipo
(@CTPL_Codigo INT)
RETURNS TABLE
AS
SET NOCOUNT ON
DECLARE @Lista NVARCHAR(MAX), @Consulta NVARCHAR(MAX)
SELECT @Lista = STUFF((SELECT ', ' + QUOTENAME(AT.ATTI_Desc)
FROM ATRIBUTO_TIPOS AT
INNER JOIN DET_TIPOS_PLA_ATRIBUTO_TIPO DTPAT
ON AT.ATTI_Codigo = DTPAT.ATTI_Codigo
WHERE DTPAT.CTPL_Codigo = @CTPL_Codigo
GROUP BY AT.ATTI_Codigo, AT.ATTI_Desc
ORDER BY AT.ATTI_Codigo
FOR XML PATH('')), 1, 2, '')
--SELECT @Lista
SELECT @Consulta = 'SELECT ' + @Lista + '
FROM (SELECT AT.ATTI_Codigo, AT.ATTI_Desc
FROM ATRIBUTO_TIPOS AT
INNER JOIN DET_TIPOS_PLA_ATRIBUTO_TIPO DTPAT
ON AT.ATTI_Codigo = DTPAT.ATTI_Codigo
WHERE DTPAT.CTPL_codigo = ' + CONVERT(VARCHAR, @CTPL_Codigo) + ') P
PIVOT (MAX(ATTI_Codigo) FOR ATTI_Desc IN (' + @Lista + ')) PVT'
--SELECT @Consulta
RETURN (EXECUTE SP_EXECUTESQL @Consulta)
GOEsto no me funciona debido a que solo debería de tener una instrucción SELECT para una función que me devuelva una tabla sin haberla declarado, el problema radica que para un tipo me puede devolver una tabla con 2 campos, para otro 5 campos, entonces no puedo hacer una declaración de una tabla que no se cuantos campos me devolverá.
Por otro lado, si hay otro tipo de solución estoy abierto a seguirlos.
La versión de SQL es SQL Server 2008 R2 con SP2 en ingles.
Saludos,
Respuestas
-
El código está disponible en mi DropBox aquí :
Jesús López
EntityLite a lightweight, database first, micro orm- Editado Jesús López sábado, 10 de enero de 2015 8:54 x
- Marcado como respuesta Juan Carlos Alemán Cuadros jueves, 15 de enero de 2015 13:38
Todas las respuestas
-
No es permitido usar SQL dinamico en funciones del usuario.
Te es de algun beneficio usar un procedimiento almacenado?
AMB
Some guidelines for posting questions...
AYÚDANOS A AYUDARTE, guía básica de consejos para formular preguntas -
Juan Carlos, por qué contener esa consulta en una función?
- Editado Willams Morales martes, 23 de diciembre de 2014 16:43
-
Hola AMB,
Ya lo cambie a un Procedimiento pero tengo el mismo problema o quizás no se como orientarlo, ya que cuando ejecuto el procedimiento me puede devolver una tabla con 2 campos o 5 campos dependiendo del parámetro que le pase.
Se que puedo crear la tabla e insertar a esta el resultado del procedimiento, pero no se como podría prever la cantidad de campos que me devuelve el procedimiento.
Saludos,
Juan Carlos
-
Hola Williams,
Básicamente por que deseo utilizarla en procedimientos para devolver los datos que deseo, se que una opción es poner mi código en cada procedimiento, pero siempre trato de reutilizar funciones para no estar repitiendo lo mismo en cada procedimiento, cosa que si en algún momento tengo que cambiar algo no tengo que modificar varios procedimientos solo la función.
Saludos,
Juan Carlos
-
Juan Carlos, usualmente el objetivo de las columnas dinámicas es el de generar informes donde el origen de datos "tal cual" es consumido por un contenedor que crea las columnas dependiendo del origen. No estoy seguro que uso posterior haces a ese conjunto de datos, quizá si nos comentas un poco de ello podríamos apoyarte.
-
Yo te voy a dar otro enfoque completamente diferente. Le voy a dar completamente la vuelta.
En mi humilde opinión, lo del Pivot, no es más que una cuestión de presentación, y de eso, de la presentación, debería encargarse la aplicación cliente, no creo que sea una responsabilidad del servidor de de base de datos. Es más, no creo que el operador PIVOT sea ni siquiera necesario.
Hacer la rotación de las columnas en la aplicación cliente es muy fácil, hay componente en c# que lo hace de forma limpia y elegante. El código fuente de ese componente y una explicación completa sobre PIVOT la puedes encontrar en un artículo de CodeProject que escribí no hace mucho:
Client Side Multi-Column Dynamic Pivot
Jesús López
EntityLite a lightweight, database first, micro orm- Propuesto como respuesta Miguel.Vazquez.MSDNModerator viernes, 26 de diciembre de 2014 20:21
-
Hola Williams,
Te comento un poco lo que hemos hecho.
Tenemos algunas tablas donde definimos los siguiente: nombre del tipo, por ejemplo países, sexo, etc. otra es nombre de los atributos que tiene casa tipos, como código, nombre, etc. y finalmente otra donde almaceno los datos como tal es decir, los nombre de los países, y los tipos de sexos.
Si lo hiciera por tablas independientes tendría algo así:
- Para un tipo países, tendríamos: codigopais, nombrepais, codigopaisinstituciongobierno.
- Para un tipo sexo, tendríamos: codigosexo, nombresexo, descripcioncortasexo.
Entonces lo que hicimos fue juntar estas tablas y crear una tabla de atributos, donde tenemos, código, nombre, descripcioncorta, codigoinstituciongobierno, luego estos atributos de los asignamos a los tipos de datos que necesitamos, mas o menos así: para el tipo países, le asignamos los atributos código, nombre y codigoinstituciongobierno, para el tipo sexo le asignamos código, nombre, descripcioncorta; todos estos son registros en distintas tablas y finalmente tenemos una tabla donde ponemos los datos de los tipos de países y tipos de sexo, pero todos estos ya a este nivel llegan pro relaciones.
Entonces cuando presentamos los valores a nivel de reportes o mantenimientos en el aplicativo, tengo que traerme los nombre de los atributos asignados a cada tipo, es por ello que esto se maneja de forma dinámica ya que para uno puedo tener 2 atributos y para otro puedo tener 5 atributos.
Espero me puedas ayudar.
Saludos,
-
Eso que haces se parece mucho al modelo EAV (Entity Attribute Value). Si bien, en algunos casos puede llegar a ser un modelo aceptable, normalmente no lo es, sino que conlleva muchos más problemas que ventajas, como estás pudiendo comprobar tú mismo.
Yo te recomendaría que volvieras a crear esas tablas, en vez de intentar tenerlo todo en una.
No solo tienes problemas con presentar los datos. Tu entidad que debería estar en una sóla fila y está en varias. No digamos cuando tienes que hacer consultas un poco más complicadas, se vuelve un infierno, te lo aseguro.
Hay muchos artículos por ahí hablando del tema. Pero este me ha gustado especialmente:
OTLT and EAV: the two big design mistakes all beginners make
Jesús López
EntityLite a lightweight, database first, micro orm- Propuesto como respuesta Miguel.Vazquez.MSDNModerator lunes, 29 de diciembre de 2014 15:30
- Editado Jesús López lunes, 29 de diciembre de 2014 15:48 x
-
Hola Jesús,
Si bien comprendo bien lo que me dices, el tema va por que en un sistema de estos tipos de pequeñas tablas auxiliares puedes llegar a tener cientos incluso miles de tablas que solo almacenan en mucho caso dos tipos de datos; es por ello, que lo llevamos a este tipo de estructura para poderlo solventar; sin embargo la idea original era meterlo en una función y/o procedimiento que me permita luego reutilizarlo para no escribirlo en cada procedimiento o función, pero, si no es posible hacerlo probare poniéndolo en cada procedimiento y función que necesite, esperare un tiempo para ver si alguien tiene alguna sugerencia.
Gracias por los aportes compartidos, si tuvieses algo más que me puedas brindar te lo agradecería mucho.
Saludos,
-
Juan Carlos, al igual que Jesús no estoy de acuerdo en como has modelado tus tablas. Y básicamente el enredo lo veo cuando quieras saber el significado de un valor en una relación, haces confuso el modelo y las consultas se cargarán de JOIN's de manera innecesaria.
Estoy de acuerdo en el uso de estás tablas que contengan varios tipos, pero sin caer en el abuso. Por ejemplo, la tabla países no las contendría en esas tablas. Las uso básicamente para crear listas como sexo, días de la semana, etc. Pero sólo tengo 2 tablas: CabListas y DetListas, y como regla general que contengan la misma cantidad de columnas, que generalmente son Código, Descripción y Descripción Corta.
Aún así, si ya no puedes cambiar el modelo, no creo que sea una buena idea mostrar todos los atributos y valores en un sólo informe ya que como bien mencionas las cabeceras son distintas, no sólo en cantidad sino en descripción. Creo que a lo mucho que podrías llegar es a manejar el informe por tipo mostrando en la grilla sólo sus valores, ajustando de manera correcta para ello la cantidad de columnas y su descripción.
EDITO:
Finalmente, para mostrar lo que te menciono, en Perú tenemos el organismo SUNAT que nos proporciona "n" tablas con distintas columnas y significados, sin importar como es la estructura de nuestras tablas finalmente mostramos la información por tipo y construimos la cantidad de columnas y su descripción también por tipo, de la siguiente manera:
-----------------------------------------------------------------------------------------------------
Espero haberte ayudado con mi sugerencia, si resolvió tu problema no olvides marcarla como respuesta.
Willams Morales P.
Arequipa - Perú- Editado Willams Morales lunes, 29 de diciembre de 2014 16:03
-
Juan Carlos,
Si yo entiendo cuales son las ventajas de esos modelos, pero los inconvenientes superan con creces las ventajas. Sólo vuelve a mirar tu intento de función FN_ListaPorTipo, es muy compleja, SQL dinámico, XML, etc. Hacer cosas sencillas debería se sencillo, pero el modelo que usas provoca que hacer cosas sencillas se convierta en un infierno de programación y de ineficiencia.
Nos pides que te ayudemos, pero yo me resisto a ayudarte a seguir por el mal camino, lo siento.
Jesús López
EntityLite a lightweight, database first, micro orm -
Hola Williams,
Te comento como historia, comenzamos con una tabla, pero de esta tabla llegamos a tener hasta 400 relaciones con distintas tablas del sistema (ya que de acuerdo a documentación solo como máximo se debe de tener 256 si mal no recuerdo, el problema es que no ya nos permitía eliminar registros), en algunos casos con una tabla teníamos mas de diez relaciones contra esta tabla, por ello luego pasamos a dos mas o menos con la estructura que indicas, pero igual caímos en algo similar, aparte que de acuerdo a la legislación nos iban pidiendo mas datos adicionales con un código que lo solicita el gobierno, pero es un dato que nosotros ya los tenemos en el sistema, así que tenemos que ir adicionando ciertos atributos a datos que ya teníamos, y así fue como nació la idea que les trate de explicar lineas arriba.
Por otro lado, no es para presentar un informe con los distintos tipos de atributos, sino mas bien que cada vez que quiera llamar un dato de estos pequeños mantenimientos le pase un parámetro y me devuelva lo que necesito y lo pueda reutilizar en distintos sitios, si luego tengo que cambiar algo solo lo haré en un solo sitio, esa era la idea originalmente.
Si tienes alguna idea, aunque suene media descabellada me la puedes comentar y total la puedo probar.
Saludos,
-
De acuerdo, veo que tu consulta inicial se encamina a ello y el problema es que no puedes contener en funciones sql dinámico. Se te sugirió contenerlo en un procedimiento y mencionaste tener problemas. ¿Qué problemas has tenido?.
- Editado Willams Morales lunes, 29 de diciembre de 2014 16:39
-
Hola Williams,
El problema con un procedimiento es que si el resultado lo pongo dentro de una tabla, no se cuantos campos me devolverá la tabla, o es que estoy equivocado.
create table @Tabla(campo1 int, campo2 varchar(100), campo3 varchar(100))
insert into @Tabla
exec sp_procedimiento(@parametro)
go
Si no me he equivocado mas o menos es así como se insertan en una tabla el resultado de un procedimiento, o es que hay otra forma de hacerlo.
Saludos,
-
-
-
Hola Williams,
Lo deseo en una tabla para unirlo con la tabla donde tengo los datos almacenados y poderlos juntar. Esto básicamente donde solo tengo identificadores con los distintos tipos de datos (atributos) que se configuro por cada pequeño grupo de datos que tengo.
Saludos,
-
Juan Carlos, me ayudaría mucho para comprenderte y resolver este problema a la brevedad el que me hagas llegar un modelo con las tablas implicadas (las 3 tablas que hablas más una donde hagas uso) y también involucres está nueva tabla que quieres crear para entender que es lo que quieres lograr.
-
Hola Williams,
Te paso la información, bueno en realidad no son 3 tablas sino son 5.
Creación de tablas e imagen de relaciones.
CREATE TABLE [dbo].[CAB_TIPOS_PLA]( [CTPL_Codigo] [smallint] IDENTITY(1,1) NOT NULL, [CTPL_Desc] [varchar](100) NOT NULL, [CTPL_Activo] [bit] NOT NULL, [CTPL_FecCrea] [smalldatetime] NOT NULL, [CTPL_UsrCrea] [varchar](20) NOT NULL, [CTPL_FecMod] [smalldatetime] NULL, [CTPL_UsrMod] [varchar](20) NULL, [CTPL_recursivo] [bit] NULL, [CTPL_codigo_padre] [smallint] NULL, CONSTRAINT [PK_CAB_TIPOS_PLA] PRIMARY KEY CLUSTERED ([CTPL_Codigo] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO ALTER TABLE [dbo].[CAB_TIPOS_PLA] WITH CHECK ADD CONSTRAINT [FK_CAB_TIPOS_PLA_CAB_TIPOS_PLA] FOREIGN KEY([CTPL_codigo_padre]) REFERENCES [dbo].[CAB_TIPOS_PLA] ([CTPL_Codigo]) GO ALTER TABLE [dbo].[CAB_TIPOS_PLA] CHECK CONSTRAINT [FK_CAB_TIPOS_PLA_CAB_TIPOS_PLA] GO ALTER TABLE [dbo].[CAB_TIPOS_PLA] ADD CONSTRAINT [DF_CAB_TIPOS_PLA_CTVT_FecCrea] DEFAULT (getdate()) FOR [CTPL_FecCrea] GO CREATE TABLE [dbo].[ATRIBUTO_TIPOS]( [ATTI_Codigo] [smallint] IDENTITY(1,1) NOT NULL, [ATTI_Desc] [varchar](100) NOT NULL, [ATTI_Activo] [bit] NOT NULL, [ATTI_Dato] [tinyint] NOT NULL, [ATTI_FecCrea] [smalldatetime] NOT NULL, [ATTI_UsrCrea] [varchar](20) NOT NULL, [ATTI_FecMod] [smalldatetime] NULL, [ATTI_UsrMod] [varchar](20) NULL, [ATTI_Fijo] [bit] NULL, CONSTRAINT [PK_ATRIBUTO_TIPOS] PRIMARY KEY CLUSTERED ([ATTI_Codigo] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO ALTER TABLE [dbo].[ATRIBUTO_TIPOS] ADD CONSTRAINT [DF_DET_TIPOS_VTA_ATTI_FecCrea] DEFAULT (getdate()) FOR [ATTI_FecCrea] GO CREATE TABLE [dbo].[DET_TIPOS_PLA_ATRIBUTO_TIPO]( [CTPL_codigo] [smallint] NOT NULL, [ATTI_Codigo] [smallint] NOT NULL, [DTAT_Obligatorio] [bit] NOT NULL, [DTAT_FecCrea] [smalldatetime] NOT NULL, [DTAT_UsrCrea] [varchar](20) NOT NULL, [DTAT_FecMod] [smalldatetime] NULL, [DTAT_UsrMod] [varchar](20) NULL, [DTAT_eliminado] [bit] NULL, [DTAT_CTPL_codigo] [smallint] NULL, [DTAT_Tabla] [varchar](50) NULL, CONSTRAINT [PK_DET_TIPOS_PLA_ATRIBUTO_TIPO] PRIMARY KEY CLUSTERED ([CTPL_codigo] ASC, [ATTI_Codigo] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO ALTER TABLE [dbo].[DET_TIPOS_PLA_ATRIBUTO_TIPO] WITH CHECK ADD CONSTRAINT [FK_DET_TIPOS_PLA_ATRIBUTO_TIPO_ATRIBUTO_TIPOS] FOREIGN KEY([ATTI_Codigo]) REFERENCES [dbo].[ATRIBUTO_TIPOS] ([ATTI_Codigo]) GO ALTER TABLE [dbo].[DET_TIPOS_PLA_ATRIBUTO_TIPO] CHECK CONSTRAINT [FK_DET_TIPOS_PLA_ATRIBUTO_TIPO_ATRIBUTO_TIPOS] GO ALTER TABLE [dbo].[DET_TIPOS_PLA_ATRIBUTO_TIPO] WITH CHECK ADD CONSTRAINT [FK_DET_TIPOS_PLA_ATRIBUTO_TIPO_CAB_TIPOS_PLA] FOREIGN KEY([CTPL_codigo]) REFERENCES [dbo].[CAB_TIPOS_PLA] ([CTPL_Codigo]) GO ALTER TABLE [dbo].[DET_TIPOS_PLA_ATRIBUTO_TIPO] CHECK CONSTRAINT [FK_DET_TIPOS_PLA_ATRIBUTO_TIPO_CAB_TIPOS_PLA] GO CREATE TABLE [dbo].[DET_TIPOS_PLA]( [DTPL_Codigo] [smallint] IDENTITY(1,1) NOT NULL, [CTPL_Codigo] [smallint] NOT NULL, [DTPL_Desc] [varchar](100) NOT NULL, [DTPL_Activo] [bit] NOT NULL, [DTPL_FecCrea] [smalldatetime] NOT NULL, [DTPL_UsrCrea] [varchar](20) NOT NULL, [DTPL_FecMod] [smalldatetime] NULL, [DTPL_UsrMod] [varchar](20) NULL, [DTPL_Codigo_Padre] [smallint] NULL, CONSTRAINT [PK_DET_TIPOS_PLA] PRIMARY KEY CLUSTERED ( [DTPL_Codigo] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO ALTER TABLE [dbo].[DET_TIPOS_PLA] WITH CHECK ADD CONSTRAINT [FK_DET_TIPOS_PLA_CAB_TIPOS_PLA] FOREIGN KEY([CTPL_Codigo]) REFERENCES [dbo].[CAB_TIPOS_PLA] ([CTPL_Codigo]) GO ALTER TABLE [dbo].[DET_TIPOS_PLA] CHECK CONSTRAINT [FK_DET_TIPOS_PLA_CAB_TIPOS_PLA] GO ALTER TABLE [dbo].[DET_TIPOS_PLA] WITH CHECK ADD CONSTRAINT [FK_DET_TIPOS_PLA_DET_TIPOS_PLA] FOREIGN KEY([DTPL_Codigo_Padre]) REFERENCES [dbo].[DET_TIPOS_PLA] ([DTPL_Codigo]) GO ALTER TABLE [dbo].[DET_TIPOS_PLA] CHECK CONSTRAINT [FK_DET_TIPOS_PLA_DET_TIPOS_PLA] GO CREATE TABLE [dbo].[ATRIBUTO_TIPOS_DET_TIPOS_PLA]( [ATDT_Codigo] [smallint] IDENTITY(1,1) NOT NULL, [DTPL_Codigo] [smallint] NOT NULL, [CTPL_Codigo] [smallint] NOT NULL, [ATTI_Codigo] [smallint] NOT NULL, [ATDT_Numerico] [decimal](12, 2) NULL, [ATDT_Texto] [varchar](max) NULL, [ATDT_Fecha] [smalldatetime] NULL, [ATDT_Tipo] [smallint] NULL, [ATDT_FecCrea] [smalldatetime] NOT NULL, [ATDT_UsrCrea] [varchar](20) NOT NULL, [ATDT_FecMod] [smalldatetime] NULL, [ATDT_UsrMod] [varchar](20) NULL, CONSTRAINT [PK_ATRIBUTO_TIPOS_DET_TIPOS_PLA_1] PRIMARY KEY CLUSTERED ( [ATDT_Codigo] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO ALTER TABLE [dbo].[ATRIBUTO_TIPOS_DET_TIPOS_PLA] WITH CHECK ADD CONSTRAINT [FK_ATRIBUTO_TIPOS_DET_TIPOS_PLA_DET_TIPOS_PLA] FOREIGN KEY([DTPL_Codigo]) REFERENCES [dbo].[DET_TIPOS_PLA] ([DTPL_Codigo]) GO ALTER TABLE [dbo].[ATRIBUTO_TIPOS_DET_TIPOS_PLA] CHECK CONSTRAINT [FK_ATRIBUTO_TIPOS_DET_TIPOS_PLA_DET_TIPOS_PLA] GO ALTER TABLE [dbo].[ATRIBUTO_TIPOS_DET_TIPOS_PLA] WITH CHECK ADD CONSTRAINT [FK_ATRIBUTO_TIPOS_DET_TIPOS_PLA_DET_TIPOS_PLA_ATRIBUTO_TIPO] FOREIGN KEY([CTPL_Codigo], [ATTI_Codigo]) REFERENCES [dbo].[DET_TIPOS_PLA_ATRIBUTO_TIPO] ([CTPL_codigo], [ATTI_Codigo]) GO ALTER TABLE [dbo].[ATRIBUTO_TIPOS_DET_TIPOS_PLA] CHECK CONSTRAINT [FK_ATRIBUTO_TIPOS_DET_TIPOS_PLA_DET_TIPOS_PLA_ATRIBUTO_TIPO] GO
-
Hola Williams,
Te explico un poco que tipos de datos van en cada tabla:
CAB_Tipos_PLA, Aquí se crean los padres de cada uno de los estos pequeños mantenimientos, los que engloban todos los listados que vendrán después.
ATTIBUTO_TIPOS, Aquí se crean cada uno de los atributos que tendrán los pequeños mantenimientos, como por ejemplo Código, Descripción, etc.
DET_TIPOS_PLA_ATRIBUTO_TIPO, Aquí se asignan los atributos de cada pequeño mantenimiento.
DET_TIPOS_PLA, Aquí se almacena la primera incidencia del pequeño mantenimiento, por lo general debido para manejar un estado de este pequeño mantenimiento.
ATRIBUTO_TIPOS_DET_TIPOS_PLA, Aquí es donde se almacenan los datos de cada uno de datos de estos pequeños mantenimientos que tengan asignados.
La tabla que va hacer relación con el resto de tablas es ATRIBUTO_TIPOS_DET_TIPOS_PLA.
Si necesitas algo más coméntame.
Saludos,
-
-
Juan Carlos, modelo revisado y entendido (*), discrepo en algunas cosas pero finalmente lo entiendo y considero que este hilo no es para modificar tu modelo sino para buscar una solución a tu problema.
(*)Explicame un poco mas de la tabla DET_TIPOS_PLA con que objetivo fue creada? Si fuese posible dime un ejemplo de registro que almacenas ahí.
Ahora, te agradeceré que seas claro y puntual para indicarme exactamente que es lo que deseas obtener. Mencionas que deseas una tabla pero no mencionas la estructura de esa tabla y el objetivo de su creación. Con el modelo en mente me será más fácil entender lo que requieras.
-
Si tienes alguna idea, aunque suene media descabellada me la puedes comentar y total la puedo probar.
Saludos,
Te voy a proponer una idea. Está claro que no vas a cambiar el modelo a pesar de los problemas que te está causando, y también está claro que no puedes crear una función tabular porque las funciones tabulares no admiten sql dinámico. El procedimiento almacenado tampoco es una solución porque no conoces a priori las columnas que va a tener y por tanto no puedes reutilizar el procedimiento.
Sin embargo sí que hay algo que puedes reutilizar, se trata de la propia sentencia sql. No puedes reutilizar el conjunto de registros, pero sí la sentencia sql en sí. Lo que te estoy proponiendo es una función o un procedimiento que devuelva la instrucción sql como una cadena que te sirva como base para construir otras instrucciones SQL. Así tendrías resuelto el problema de la reutilización.
Otra cosa que podrías probar es a cambiar el enfoque. Los datos los tienes guardados en un modelo EAV pero para trabajar con ellos los transformas en un modelo clásico haciendo PIVOT, lo cual resulta en una gran complicación. La idea es no hacer PIVOT, sino trabajar diréctamente con el modelo EAV. Si has elegido ese modelo, llévalo hasta sus últimas consecuencias, abrázalo completamente, no lo transformes en el modelo clásico de filas para procesar la información. Otra cuestión es la presentación de los datos, eso lo puede hacer la aplicación cliente. La aplicación cliente recibe los datos en modelo EAV y los transforma en modelo clásico de filas para presentarlos al usuario. Este problema está resuelto en el artículo de CodeProject.
Client Side Multi-Column Dynamic Pivot
Jesús López
EntityLite a lightweight, database first, micro orm -
Hola Williams,
Disculpa la demora pero por fin de año no pude contestarte, ahora te explico lo que me indicas.
La tabla DET_TIPOS_PLA solo tiene el fin de almacenar en un solo registro si la lista de registro esta activo o no, ya que por regla tratamos de no borrar la información de la BD sino mas bien desactivarla, es por ello que esta tabla tiene una columna de Activo, por ejemplo:
Retomando el ejemplo de los Paises, que este lo creo en la tabla CAB_TIPOS_PLA y le asigno los atributos de Codigo, Descripcion, DescripcionCorta, Campo1 y Campo2, entonces, cuando creo el campo almaceno los datos de en la tabla ATRIBUTOS_TIPOS_DET_TIPOS_PLA aqui generare 5 registros por cada pais que registre en la base de datos, pero para indicar que estos 5 registros no estan activos es que utilizo la tabla DET_TIPOS_PLA que es donde manejo el estado de esos 5 registros.
Lo que deseo obtener es los datos como si estos estuvieran en una hoja de calculo (mas o menos), para el ejemplo de Paises, siempre y cuando le pase como parametro que deseo la informacion de paises.
De cabecera tendria:
Codigo, Descripcion, DescripcionCorta, Campo1, Campo2
1, Perú, PE, 2014-12-15, CCCC
2, Chile, CH, 2014-10-20, DDDD, etc.
Saludos,
-
Hola Jesús,
Si he visto lo que haz publicado en CodeProject, no lo he revisado muy bien, pero en otro hilo vi una respuesta que diste y otro enfoque que hiciste a la solución clásica que se puede dar generalmente.
Una limitante es la plataforma en que se tiene el sistema que es VB6, pero dame un poco de tiempo para analizarlo un poco más.
Gracias por las opciones brindadas.
Saludos,
-
Lo del VB6 es una lata sí. Será más difícil, pero no imposible, igual que yo tomo un data reader y construyo un data table pivoteando las columnas en c#, tú puedes en VB6, a partir de un recordset construir un nuevo recordset con las columnas pivoteadas.
No sé si recordarás que puedes crear un recordset desde cero, sin base de datos, definirle las columnas y rellenarlo de datos todo por código VB6.0
Jesús López
EntityLite a lightweight, database first, micro orm
- Editado Jesús López viernes, 2 de enero de 2015 9:38 x
- Propuesto como respuesta Miguel.Vazquez.MSDNModerator viernes, 2 de enero de 2015 23:09
-
Hola Jesús,
Si se como hacer lo que comentas, y he realizado algunas pruebas y si funciona solo que demora mucho, pero al fin y al cabo funciona.
Estoy probando que la función me devuelva una cadena y esta ejecutarla luego, deseamos ver la diferencia de tiempos.
Ya comento como me fue.
Saludos,
-
Juan Carlos,
Si muestras el código VB6.0, seguro que podemos hacer que vaya mucho más deprisa. El pivoteo que yo hago en c# va muy rápido (tanto como una carga de un datatable con un data adapter) y seguro que puede hacerse igual de eficiente (o parecido) en VB 6.0.
Quizá podríamos traducir (back port) el código que tengo en c# basado en datareader y datatable en código VB6.0 con recordsets. Seguro que será más o menos igual de eficiente.
Jesús López
EntityLite a lightweight, database first, micro orm -
Juan Carlos,
He trasladado (back ported) el código c# del artículo de CodeProject a VB6.0 con recordset. Aún ni lo he ejecutado así que seguro que tiene algunos problemas. Pero mañana seguramente estará listo. Te tendré informado.
Jesús López
EntityLite a lightweight, database first, micro orm -
Juan Carlos,
Tengo el código de pivote dinámico de múltiple columnas del lado del cliente listo en Visual Basic 6.0
Data La siguiente consulta llamada "ProductSale_Year" en la base de datos de ejemplo NWind.mdb:
SELECT P.ProductID, P.ProductName, DatePart('yyyy',Orders.OrderDate) AS [Year], Round( Sum(OD.Quantity*OD.UnitPrice*(1-OD.Discount)), 2) AS Sales, Count(*) AS Orders FROM Products AS P LEFT JOIN ([Order Details] AS OD LEFT JOIN Orders ON OD.OrderID = Orders.OrderID) ON P.ProductID = OD.ProductID GROUP BY P.ProductID, P.ProductName, DatePart('yyyy',Orders.OrderDate);
Y el siguiente código VB 6.0 en un formulario:
Dim WithEvents SalesPivotTransform As PivotTransform Dim WithEvents YearPivotTransform As PivotTransform Private Sub Form_Load() Dim UnpivotedColumnNames() As String Dim pvTransforms() As PivotTransform ReDim UnpivotedColumnNames(0 To 1) ReDim pvTransforms(0 To 1) Dim cn As New ADODB.Connection cn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Projects\PivotProject\Nwind.mdb" cn.CursorLocation = adUseClient cn.Open Dim cmd As New ADODB.Command Set cmd.ActiveConnection = cn cmd.CommandText = "SELECT * FROM ProductSale_Year ORDER BY ProductName, ProductID, Year" Dim rst As New ADODB.Recordset Set rst.source = cmd rst.Open cmd, , adOpenStatic, adLockReadOnly Set rst.ActiveConnection = Nothing cn.Close Set SalesPivotTransform = New PivotTransform SalesPivotTransform.PivotColumnName = "Year" SalesPivotTransform.ValueColumnName = "Sales" Set YearPivotTransform = New PivotTransform YearPivotTransform.PivotColumnName = "Year" YearPivotTransform.ValueColumnName = "Orders" Set pvTransforms(0) = SalesPivotTransform Set pvTransforms(1) = YearPivotTransform UnpivotedColumnNames(0) = "ProductName" UnpivotedColumnNames(1) = "ProductID" Dim pvDef As New PivotDef pvDef.PivotTransforms = pvTransforms pvDef.UnpivotedColumnNames = UnpivotedColumnNames Dim builder As New PivotBuilder Dim pvrst As ADODB.Recordset Set pvrst = builder.Build(pvDef, rst) Set Me.DataGrid1.DataSource = pvrst End Sub Private Sub SalesPivotTransform_PivotedColumnNameRequested(e As PCNRequestedEventArgs) e.PivotColumnName = "Sales" & CStr(e.PivotColumnValue) End Sub Private Sub YearPivotTransform_PivotedColumnNameRequested(e As PCNRequestedEventArgs) e.PivotColumnName = "Orders" & CStr(e.PivotColumnValue) End Sub
Esto es lo que sale:
Y te aseguro que va como un tiro de rápido.
¿Te interesa o no?
Jesús López
EntityLite a lightweight, database first, micro orm -
A pesar de que sentía cierta añoranza, te aseguro que volver a programar en VB 6.0 no ha sido una experiencia placentera. Ya casi no me acordaba... Ahora me doy perfectamente cuenta de la enorme diferencia que hay entre VB6.0 y programar en c#, un lenguaje moderno y potente, con una .NET Fx que tiene una estupenda base class library.
Jesús López
EntityLite a lightweight, database first, micro orm -
-
-
Dame tu dirección de correo electrónico y te envío el proyecto por email.
o escríbeme a jesuslpm arroba hotmail.com
Jesús López
EntityLite a lightweight, database first, micro orm
- Editado Jesús López viernes, 9 de enero de 2015 17:24 x
-
El código está disponible en mi DropBox aquí :
Jesús López
EntityLite a lightweight, database first, micro orm- Editado Jesús López sábado, 10 de enero de 2015 8:54 x
- Marcado como respuesta Juan Carlos Alemán Cuadros jueves, 15 de enero de 2015 13:38
-