none
seleccionar solo 1 registro de tabla vinculada RRS feed

  • Pregunta

  • estimados:

    Un problema que considero sencillo, pero no puedo resolverlo ... :(
    Detallo:
    Tengo 2 tablas: - productos (id,descripcion)

                            - precios (idProducto,Precio,FechaDelPrecio)
    Con lo cual, en la tabla 'precios' puedo tener N veces repetido un IdProducto.
    Necesito seleccionar productos con el precio que tuvieron en determinada fecha o hasta una determinada fecha.
    Ejemplo de tablas mínimo:
    Productos:
    1,Jabón liquido
    2,Esponja
    3,Peine

    precios:
    1,20.50,20/08/2018
    1,22.00,12/09/2019
    1,31.27,30/11/2019
    2,7.40,10/07/2017
    2,11.25,27/08/2019
    2,15.30,10/09/2019
    2,19.00,30/11/2019
    3,3.5,02/04/2018
    3,5.00,06/08/2018
    3,7.68,30/09/2019

    La formulación de la consulta sería (ej.):
    "Lista completa de precios de productos al 31/10/2019"

    SELECT s.ID ,s.Descripcion ,p.precio
      FROM Productos s
      INNER JOIN (
      SELECT MAX(fechaprecio) as fecha,precio,IDProducto FROM 
      precios WHERE FechaPrecio < '2019-11-01'
      GROUP BY codsalida,precio) as p
      ON S.id=p.IDProducto
      ORDER BY descripcion

    Pero me devuelve todos los registros de la tabla precios... Podrían ayudarme?

    Muchas gracias 
    saludos

    miércoles, 12 de febrero de 2020 22:12

Respuestas

  • Hola BGuidi:

    Puedes hacerlo del siguiente modo:

    Create table Precios (idProducto int, precio decimal (5,2), fechaPrecio date)
    Create table Productos (id int, descripcion varchar(100))
    go
    set dateformat dmy
    insert into Precios (idProducto, precio, fechaPrecio)
    values
    (1,20.50,'20/08/2018'),
    (1,22.00,'12/09/2019'),
    (1,31.27,'30/11/2019'),
    (2, 7.40,'10/07/2017'),
    (2,11.25,'27/08/2019'),
    (2,15.30,'10/09/2019'),
    (2,19.00,'30/11/2019'),
    (3, 3.5, '02/04/2018'),
    (3, 5.00,'06/08/2018'),
    (3, 7.68,'30/09/2019');
    go
    insert into Productos (id, descripcion)
    values
    (1,'Jabón líquido'),
    (2,'Esponja'),
    (3,'Peine');
    go

    Hasta aquí el escenario planteado.

    select [p].[id]
         , [p].[descripcion]
         , [o].[precio]
    from 
         Productos p
         cross apply
    (
        select top (1) [fechaprecio] as [fecha]
                     , [precio]
                     , [IDProducto]
        FROM 
             precios
               WHERE [FechaPrecio] < '2019-11-01'
                     and [Precios].[idProducto] = [p].[id]
               order by [fechaPrecio] desc
    ) as o;
    

    Utilizando el operador apply con top(1) 

    Pero podrías reducir mucho el código creando una función para que lo haga.

    CREATE FUNCTION dbo.fnGetPriceProduct 
    (@idProducto int, @dateMax date, @cant int)
    Returns table
    return
    select top (@cant) [fechaprecio] as [fecha]
                     , [precio]
                     , [IDProducto]
        FROM 
             precios
               WHERE [FechaPrecio] <= @dateMax
                     and [Precios].[idProducto] = @idProducto 
               order by [fechaPrecio] desc
    GO

    Y la llamada quedaría así.

    SELECT fn.idProducto, P.descripcion, fn.precio
    FROM Productos P
    CROSS APPLY dbo.fnGetPriceProduct(P.id, '20191031',1) as fn

    Así te serviría para cualquier otra fecha, y cantidad de precios por producto.

    Salida

    • Marcado como respuesta BGuidi miércoles, 18 de marzo de 2020 15:52
    jueves, 13 de febrero de 2020 5:56
  • Te pido mil disculpas por no responder antes. Tuve un tema familiar que me obligó a viajar y este tema quedó absolutamente de lado.
    He probado tu solución, que es muy elegante y funciona perfecto. Muchisimas gracias por tu tiempo y colaboración.
    Saludos
    • Marcado como respuesta BGuidi miércoles, 18 de marzo de 2020 15:52
    miércoles, 18 de marzo de 2020 15:52

Todas las respuestas

  • Pues utiliza SELECT TOP 1

    IIslas Master Consultant SQL Server

    miércoles, 12 de febrero de 2020 22:33
  • Hola BGuidi:

    Puedes hacerlo del siguiente modo:

    Create table Precios (idProducto int, precio decimal (5,2), fechaPrecio date)
    Create table Productos (id int, descripcion varchar(100))
    go
    set dateformat dmy
    insert into Precios (idProducto, precio, fechaPrecio)
    values
    (1,20.50,'20/08/2018'),
    (1,22.00,'12/09/2019'),
    (1,31.27,'30/11/2019'),
    (2, 7.40,'10/07/2017'),
    (2,11.25,'27/08/2019'),
    (2,15.30,'10/09/2019'),
    (2,19.00,'30/11/2019'),
    (3, 3.5, '02/04/2018'),
    (3, 5.00,'06/08/2018'),
    (3, 7.68,'30/09/2019');
    go
    insert into Productos (id, descripcion)
    values
    (1,'Jabón líquido'),
    (2,'Esponja'),
    (3,'Peine');
    go

    Hasta aquí el escenario planteado.

    select [p].[id]
         , [p].[descripcion]
         , [o].[precio]
    from 
         Productos p
         cross apply
    (
        select top (1) [fechaprecio] as [fecha]
                     , [precio]
                     , [IDProducto]
        FROM 
             precios
               WHERE [FechaPrecio] < '2019-11-01'
                     and [Precios].[idProducto] = [p].[id]
               order by [fechaPrecio] desc
    ) as o;
    

    Utilizando el operador apply con top(1) 

    Pero podrías reducir mucho el código creando una función para que lo haga.

    CREATE FUNCTION dbo.fnGetPriceProduct 
    (@idProducto int, @dateMax date, @cant int)
    Returns table
    return
    select top (@cant) [fechaprecio] as [fecha]
                     , [precio]
                     , [IDProducto]
        FROM 
             precios
               WHERE [FechaPrecio] <= @dateMax
                     and [Precios].[idProducto] = @idProducto 
               order by [fechaPrecio] desc
    GO

    Y la llamada quedaría así.

    SELECT fn.idProducto, P.descripcion, fn.precio
    FROM Productos P
    CROSS APPLY dbo.fnGetPriceProduct(P.id, '20191031',1) as fn

    Así te serviría para cualquier otra fecha, y cantidad de precios por producto.

    Salida

    • Marcado como respuesta BGuidi miércoles, 18 de marzo de 2020 15:52
    jueves, 13 de febrero de 2020 5:56
  • Deleted
    jueves, 13 de febrero de 2020 11:48
  • hola. Antes que nada perdón por no responder pero tuve que viajar de urgencia y no pude ocuparme de este tema. Te agradezco mucho la atención, pero usar Top 1 no me resolvía la consulta.
    saludos
    miércoles, 18 de marzo de 2020 15:35
  • Estimado: antes que nada mil disculpas por no responder antes, pero una urgencia familiar me obligó a viajar y este tema quedó postergado.
    Es impecable la solución que me propones. MUchas gracias por tu atención.
    saludos
    • Marcado como respuesta BGuidi miércoles, 18 de marzo de 2020 15:52
    • Desmarcado como respuesta BGuidi miércoles, 18 de marzo de 2020 15:52
    miércoles, 18 de marzo de 2020 15:47
  • Te pido mil disculpas por no responder antes. Tuve un tema familiar que me obligó a viajar y este tema quedó absolutamente de lado.
    He probado tu solución, que es muy elegante y funciona perfecto. Muchisimas gracias por tu tiempo y colaboración.
    Saludos
    • Marcado como respuesta BGuidi miércoles, 18 de marzo de 2020 15:52
    miércoles, 18 de marzo de 2020 15:52
  • De nada. Un placer poder aportar.
    miércoles, 18 de marzo de 2020 18:23