none
Consulta dinámica de PIVOT que devuelve una Tabla a través de una funcion RRS feed

  • 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)
    GO

    Esto 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,

    martes, 23 de diciembre de 2014 14:49

Respuestas

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

    martes, 23 de diciembre de 2014 16:26
  • Juan Carlos, por qué contener esa consulta en una función? 

    martes, 23 de diciembre de 2014 16:42
  • 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

    miércoles, 24 de diciembre de 2014 16:14
  • 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

    miércoles, 24 de diciembre de 2014 16:18
  • 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.
    miércoles, 24 de diciembre de 2014 16:27
  • 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

    jueves, 25 de diciembre de 2014 10:49
  • 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,

    lunes, 29 de diciembre de 2014 15:18
  • 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


    lunes, 29 de diciembre de 2014 15:26
  • 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, 

    lunes, 29 de diciembre de 2014 15:48
  • 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ú


    lunes, 29 de diciembre de 2014 15:49
  • 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

    lunes, 29 de diciembre de 2014 16:02
  • 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,

    lunes, 29 de diciembre de 2014 16:25
  • 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?.



    lunes, 29 de diciembre de 2014 16:36
  • 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,

    lunes, 29 de diciembre de 2014 17:03
  • Hola Jesús,

    Te agradezco el tiempo y la paciencia que me haz brindado.

    Muchas gracias.

    Saludos,

    lunes, 29 de diciembre de 2014 17:06
  • Juan Carlos, ¿para qué quieres vaciar los resultados a una tabla?. Si deseas conocer los registros sólo tendrás que invocar al procedimiento.
    lunes, 29 de diciembre de 2014 17:50
  • 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,


    martes, 30 de diciembre de 2014 14:18
  • 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.
    martes, 30 de diciembre de 2014 14:32
  • 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
    
    
    


    martes, 30 de diciembre de 2014 15:31
  • 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,

    martes, 30 de diciembre de 2014 16:07
  • Gracias Juan Carlos, esto ayudará mucho. En este momento estoy saliendo de la oficina, a mi regreso pondré atención en apoyarte.
    martes, 30 de diciembre de 2014 16:11
  • 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.

    miércoles, 31 de diciembre de 2014 0:11

  • 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

    jueves, 1 de enero de 2015 8:54
  • 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,

    viernes, 2 de enero de 2015 5:00
  • 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,

    viernes, 2 de enero de 2015 5:06
  • 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


    viernes, 2 de enero de 2015 9:37
  • 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,


    lunes, 5 de enero de 2015 18:20
  • 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

    lunes, 5 de enero de 2015 23:20
  • 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

    martes, 6 de enero de 2015 22:23
  • 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

    miércoles, 7 de enero de 2015 18:23
  • 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

    miércoles, 7 de enero de 2015 18:59
  • Hola Jesús,

    Disculpa la ausencia por el foro, es por los trabajos del día a día.

    Quisiera probar tu solución con mis estructuras, si es posible que nos puedas brindar tu código.

    Saludos,

    viernes, 9 de enero de 2015 14:59
  • Jesús,

    Te comprendo ya que de momento tenemos que mantener al sistema actual VB6 y de la mano tenemos el proyecto de cambiarlo de plataforma (NET) y también la actualización de estructuras a nivel de BD.

    Saludos,

    viernes, 9 de enero de 2015 15:01
  • 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


    viernes, 9 de enero de 2015 17:23
  • El código está disponible en mi DropBox aquí :

    PivotProject.zip



    Jesús López


    EntityLite a lightweight, database first, micro orm


    sábado, 10 de enero de 2015 8:52
  • Hola Jesús,

    Muchas gracias por tu ayuda, me ha servido de mucho. Definitivamente es otra forma de ver las cosas.

    Saludos,

    Juan Carlos

    jueves, 15 de enero de 2015 13:40