SQL usando MIN y JOIN (CI4)

NickNack

Hola! A ver si alguien puede arrojar un poco de luz sobre mi problema - llevo unas horas dándome cabezazos con el código y no entiendo por qué no consigo sacar el resultado que busco.

Tengo dos tablas:

Tabla Empresas
ID, blahblahblah, UltimaActualizacion

Tabla Datos
IDEMPRESA, blahblahblah, Fecha, Precio

Estoy intentando sacar el precio mínimo de hoy de la tabla datos siempre y cuando la ultima actualización de la empresa sea hoy.
Nota: UltimaActualizacion es un timestamp mientras que Fecha es un date

Esta es mi SQL en Codeigniter 4:
($dia es un string formato YYYY-MM-DD)

$minPrice = $this->select('e.UltimaActualizacion')
->selectMin('d.Precio')
->from('datos as d', true)
->join('empresas as e', 'd.IDEMPRESA = e.ID AND DATE(e.UltimaActualizacion)='.$dia, 'left')
->where('d.Fecha', $dia)
->where('d.Precio IS NOT NULL')
->find();

Obtengo el precio bien, pero los datos relativos a la empresa son lo primero que encuentra. Intuyo que es un fallo tonto sumado a mi ineptitud con el SQL.

Agradecería si alguien menos inepto que yo puede arrojar un poco de luz a este problema ya que me está amargando el día. Muchas gracias!

sh31k

si no te he entendido mal quieres sacar el precio mínimo de un valor pero solo si el campo se ha actualizado hoy no ?

son dos where anidados y ya xD

->join('empresas', 'd.idempresa = e.id')
->where('d.Fecha', $hoy)
->where('e.UltimaActualizacion', $hoy)

1 respuesta
NickNack

#2 Quiero sacar el precio mínimo de entre todos los datos de hoy, que cumplan con el requisito que la empresa también esté actualizada adía de hoy.

He probado quitando el join y añadiendo la condición con otro where pero sigo teniendo el mismo problema, el precio minimo sale correcto pero la parte relativa a la empresa me esta devolviendo otra que no tiene nada que ver. Busco el ID resultante en la tabla de datos y el registro no tiene nada que ver con el precio mostrado.

1 respuesta
sh31k

#3 Claro, el join tienes que hacerlo, y luego aplicas el where a los dos campos, en el ORM de codeigniter creo recordar que por defecto la anidación de wheres es con AND no con OR.

select (min(x))
from ( tabla A )
join ( tabla B, tablaA.ID = tablaB.ID )
where (tablaA.date = hoy)
where (tablaB.lastupdate = hoy)

y debería funcionarte

PD: El join no debería ser LEFT.

1 1 respuesta
NickNack

#4

$minPrice = $this->select('d.ID, d.Fecha, d.IDEmpresa, e.UltimaActualizacion')
->selectMin('d.Precio')
->from('datos as d', true)
->join('empresas as e','d.IDEmpresa = e.ID')
->where('d.Fecha', $dia)
->where('DATE(e.UltimaActualizacion)', $dia)
->where('d.Precio IS NOT NULL')
->find();

Devuelve el precio correcto (el mínimo de precio para hoy de todos los registros de la tabla de datos) y datos de la empresa, pero cuando voy a comprobar el ID en la tabla de datos encuentro una fila con la fecha de hoy, pero con un precio completamente diferente.

    {
        "ID": "242509", -> Esta fila el precio es 3.XXX
        "Fecha": "2022-03-30", -> Correcto
        "IDEmpresa": "6", -> La empresa esta actualizada a día de hoy
        "UltimaActualizacion": "2022-03-30 14:05:26", -> Correcto
        "Precio": "2.285" -> Precio correcto pero no se corresponde con el ID arriba mencionado
    }

PS: Mil gracias por la ayuda

1 respuesta
sh31k

#5

$minPrice = $this->select('d.ID, d.Fecha, d.IDEmpresa, e.ID as priceID, e.UltimaActualizacion')
->selectMin('d.Precio')
->from('datos as d', true)
->join('empresas as e','d.IDEmpresa = e.ID')
->where('d.Fecha', $dia)
->where('DATE(e.UltimaActualizacionPrecios)', $dia)
->where('d.Precio IS NOT NULL')
->find();

1 respuesta
NickNack

#6 Nada tio, si es que yo también pienso que debería estar bien... Voy a detallar un poco más las tablas a ver si es que me he saltado algo importante. (Estaba simplificando un poco para que no fuese confuso)

Tabla Empresas:
ID, IDEmpresa, Nombre, UltimaActualizacion
1, 222, Empresa A, 2022-03-30 14:05:26
2, 3565, Empresa B, 2022-03-29 14:05:26
3, 4451, Empresa C, 2022-03-30 14:05:26

Tabla Datos
ID, IDEmpresa, Fecha, Precio
1, 222, 2022-03-29, 1.5
2, 222, 2022-03-30, 1.6
3, 3565, 2022-03-30, 1.8
4, 4451, 2022-03-30, 2.5

Como puedes ver el IDEmpresa no es una PK, y los ID de las tablas son los PK. Esto es así por que patata (se que esta mal)

Cuando hago la búsqueda debería devolverme

ID(datos): 2
IDEmpresa: 222
UltimaActualizacion: 2022-03-30 14:05:26
Precio: 1.6

Pero me esta devolviendo:

ID(datos): 3
IDEmpresa: 3565
UltimaActualizacion: 2022-03-30 14:05:26
Precio: 1.6

El precio lo devuelve bien, pero cuando voy a buscarlo en la tabla de datos, veo que el registro es ( 3, 3565, 2022-03-30, 1.8 ) lo cual no se corresponde con el precio de 1.6, que debería ser el registro ( 2, 222, 2022-03-30, 1.6 ).

En la tabla datos puede haber varios datos de diferentes empresas con el mismo precio, yo me quiero quedar con el primero que salga, no me importa tanto la empresa si no comprobar que el dato que sale es el mínimo y proviene de una empresa que está actualizada adía de hoy.

PD: Por supuesto estoy comparando el IDEmpresa en ambos casos en el join, no estoy mezclando ID con IDEmpresa.

1 respuesta
sh31k

#7 No debería pasar eso, encima no coinciden los valores, es curioso.

Podrías probar a sacar todos los datos de las dos tablas, para ver si alguno te lo está machacando:

d., e.

1 respuesta
NickNack

#8 Si dumpeo todos los datos de ambas tablas, obtengo la empresa correctamente y todos sus valores EXCEPTO el precio, que se machaca y es el MIN de la tabla y no se corresponde con el registro indicado

La query generada:

SELECT `d`.`ID`, `d`.`Fecha`, `d`.`IDEmpresa`, `e`.`UltimaActualizacion`, MIN(`d`.`Precio`) AS `Precio`
FROM `datos` as `d`
JOIN `empresas` as `e` ON `d`.`IDEmpresa` = `e`.`IDEmpresa`
WHERE `d`.`Fecha` = '2022-03-30'
AND DATE(e.UltimaActualizacion) = '2022-03-30'
AND `d`.`Precio` IS NOT NULL
mecmec

Falta el min precio en el where...

1 respuesta
NickNack

#10 Podrías elaborar un poquito más la respuesta? Perdona mi ineptitud con el SQL pero no veo donde colocar el MIN dentro del where. Gracias :pray:

MTX_Anubis

Tienes que hacer un group by, min(x) te está devolviendo el mínimo de TODAS las rows retornadas.

Además si usas mysql, el resto de campos te los agrega como le sale del cimbrel.

1 1 respuesta
NickNack

#12 Haciendo un group by del precio si me devuelve los valores correctos, pero solo si lo limito a 1. No es un poco guarra la solución así? Pregunto, no asumo. Se me ocurren otras maneras también muy sucias de sacarlo pero me gustaría hacerlo bien xD

De esta manera si quisiese hacer lo inverso, es decir sacar el precio máximo, tendría que ordenar descendente y daria igual que hiciese SelectMin o selectMax, lo cual me confunde un poco xD

2 respuestas
MTX_Anubis

#13 Pero qué necesitas devolver?

No es lo mismo necesitar el ID del dato (pudiendo tener varios datos en el día por empresa) que sólo el precio mínimo que ha tenido una empresa ese día (esta última es más fácil).

si quieres hacer lo último es algo tal que así (si necesitas sacar el id de Dato la query se complica bastante más).

select e.id, min(d.precio)
from empresas e
left join datos d on d.empresaId = e.id
where d.fecha = <...>
and e.ultimaAct = <...>
group by e.id
2 1 respuesta
jeke

#13 Yo entiendo que tienes que agrupar por empresa (o por su id) y que te de el mínimo por cada empresa. No por el precio

NickNack

#14 Necesito devolver el precio mínimo para ese día del conjunto de datos de empresas. En la tabla de datos tengo un registro por día y por empresa, donde varias empresas pueden tener el mismo precio ese día.

A su vez, cada empresa tiene una fecha de actualización, es decir, la ultima vez que el precio fue actualizado. Esto es así por que aunque tenga un valor de precio para el día de hoy en la tabla de datos no quiere decir que el valor esté actualizado ya que la empresa puede no haber actualizado su precio desde hace días pero sigue devolviendo el valor.

Se que es confuso, y que la estructura de datos es confusa, solo estoy intentando aplicar un parche a una entrada de datos de mierda para que no esté devolviendo precios que no están actualizados aunque la entrada de datos me diga que si.

Para que quede un poco más claro: Empresa A reporta el lunes 1.8, el martes 1.8, el miércoles 1.8, el jueves 1.9. Realmente el único cambio de precios es el jueves, cuando pasa a 1.9, el resto de reportes es simplemente que la empresa no ha actualizado su precio y esta devolviendo el ultimo precio que tenia (y claro, no devuelve sin un timestamp de modificación). De ahí que la empresa tenga su propio timestamp de actualización.

Soy consciente que como estoy planteando el problema a nivel interno no es la manera mas eficiente, pero supone mucho cambio y no estoy dispuesto a ello.

Dicho esto, con el group by y un limit consigo ordenar los precios y sacar el precio menor. El hecho de que esté sacando los datos de la empresa en esta query no es mas que para cerciorarme que realmente está cogiendo los datos bien y realmente la ultima actualización de la empresa es HOY. Además, necesito discriminar también las empresas por otros valores (que no esta incluido en la query que he puesto).

Me quedo con esta solución y utilizando un OrderBy ASC/DESC para sacar el mínimo y el máximo de los precios.

Gracias a todos por vuestros aportes, me han ayudado a entender por qué estaba fallando la query.

1

Usuarios habituales

  • NickNack
  • jeke
  • MTX_Anubis
  • mecmec
  • sh31k