PDA

Ver la Versión Completa : Como realizar consulta SQL para validar si un numero no forma parte de un rango


darkamerico
19-04-2016, 18:59:41
Saludos amigos, estoy implementando un sistema de Horarios para mi Facultad, ya casi esta todo hecho; estoy usando el componente TPlanner de TMS Component Pack para XE10, el detalle de los horarios los tengo en una tabla llamada horario_detalle la cual tiene la siguiente estructura:

http://s31.postimg.org/5dfuuq64b/explicacion0.jpg

Algunos datos de la tabla mencionada:

http://s31.postimg.org/4rdkcrnuj/explicacion1.jpg

El problema con todo sistema horarios es evitar que se asignen cursos que se crucen con otros previamente asignados y ese es el tema aqui, les explico:

http://s31.postimg.org/h7kfoa6bf/explicacion.jpg

Lo que necesito es mejorar la consulta sql que estoy usando para validar los casos de cruce:

SELECT
horario_detalle.id_horariodet,
horario_detalle.posIniGrid,
horario_detalle.posFinGrid
FROM
horario_detalle
INNER JOIN curso ON horario_detalle.idcurso_fk = curso.id_curso
WHERE
horario_detalle.idsemestre_fk = :IDSemestre AND
horario_detalle.idcurr_fk = :IDCurr AND
horario_detalle.idfacultad_fk = :IDFacultad AND
horario_detalle.iddptoacad_fk = :IDDpto AND
horario_detalle.iddia = :IDDia AND
horario_detalle.posIniGrid BETWEEN :PosIni AND :PosFin AND
curso.ciclo = :Ciclo



Muchas Gracias por su atencion

elrayo76
19-04-2016, 21:32:13
Te paso parte del wehere que usaba yo en su momento para validar algo similar a lo tuyo. Yo lo hacia por fechas pero se puede itilizar con cualquier tipo de datos numéricos.


AND ( ( inCampoDesde <= CampoDesde
AND inCampoHasta >= CampoDesde
)

OR ( inCampoDesde >= CampoDesde
AND inCampoHasta <= CampoHasta
)

OR ( inCampoDesde <= CampoHasta
AND inCampoHasta >= CampoHasta
)

OR ( inCampoDesde <= CampoDesde
AND inCampoHasta >= CampoHasta
)


Todo lo que comienza con 'in' lo debes reemplazar por lo que se ingresa desde tu aplicación. El resto son los campos de tu tabla.

Analiza lo que te paso y cualquier duda consultas.

Saludos,
El Rayo

PD: Si luego ven los administradores que esto le puede servir a muchos mas, lo pueden pasar como truco.

elrayo76
19-04-2016, 21:37:09
Olvide comentar que lo que puse en el mensaje anterior va a reemplazar al BETWEEN, ya que este no valida si solo se superpone una parte del rango, luego lo demas puede quedar como esta. Además yo esto lo usaba en consultas de Oracle.

Saludos,
El Rayo

darkamerico
19-04-2016, 22:14:15
Un caluroso saludo Rayo, mira puse en ejecución el código que me diste y sintácticamente funciona:

SELECT
horario_detalle.id_horariodet,
horario_detalle.posIniGrid,
horario_detalle.posFinGrid
FROM
horario_detalle
INNER JOIN curso ON horario_detalle.idcurso_fk = curso.id_curso
WHERE
horario_detalle.idsemestre_fk = 1 AND
horario_detalle.idcurr_fk = 1 AND
horario_detalle.idfacultad_fk = 1 AND
horario_detalle.iddptoacad_fk = 1 AND
horario_detalle.iddia = 1 AND
((horario_detalle.posIniGrid>=0 AND horario_detalle.posFinGrid<=2)
OR
(horario_detalle.posIniGrid>=0 AND horario_detalle.posFinGrid<=2)) AND
curso.ciclo = 10


Aquí asumo que un horario nuevo se quiere instalar el iddia=1 (Martes) entre las horas 0 (7:00am) a 2 (9:00am) cuando Ya existe un horario asignado a esa hora, y si, efectivamente arroja el id_horariodet correspondiente al horario asignado, pero que pasa si alguien quiere instalar un horario en iddia=1 entre las horas 1 (8:00am) y 3 (10:00am) donde existe cruce igualmente?


Saludos y Gracias

mamcx
19-04-2016, 22:45:28
Curiosamente un motor sql deberia hacer esto super simple (ya que esto es manejo basico de relaciones), pero desafortunadamente todo depende de las abilidades programaticas de cada motor.

Esta es la forma basica (ej en python):


>>> x = range(1,10)
>>> y = range(8,20)
>>> xs = set(x)
>>> xs.intersection(y)
set([8, 9])


El problema es como generar la lista de fechas en memoria, y eso depende de tu motor. La idea es hacer algo como:


SELECT Fecha FROM FuncionGeneradora(Inicio, Fin)
-- 1 registro por cada hora entre inicio y fin


Y de ahi usas sql estandar para la interseccion

elrayo76
19-04-2016, 23:19:59
Amigo darkamerico, te comento lo que hace lo que puse en el SQL.

1) Mira que no se crucen los datos estando los valores ingresados fuera de los rangos existentes o sean iguales a los límites.
2) Mira que no se crucen los datos estando los valores ingresados dentro del rango existente.
3) Mira que el rango tenga el desde dentro del rango existente pero el hasta puede estar dentro o fuera
4) Lo mismo que el anterior pero alrevez, el desde puede estar fuera pero el hasta dentro.

Pongo ejemplo con tu caso

Inicio = 2 / Fin = 4

Ingresan:

Inicio = 1 / Fin = 5 --> Primera validacion de mi SQL

Inicio = 2 / Fin = 5 --> Segunda validacion de mi SQL

Inicio = 2 / Fin = 4 --> Tercera validacion de mi SQL

Inicio = 1 / Fin = 4 --> Cuarta Validacion de mi SQL

Con este ejemplo creo que te debería quedar mas claro porque todas esas validaciones. Creeme que fue analizado mucho de hacerlo asi por tema de performance y no hay otra forma de hacerlo.


mamcx, como haces en tu código para validar si solo el inicio esta dentro del rango y el fin esta fuera o viceversa?. Por lo que entendi no lo puede hacer, dime si me equivoco.

Saludos,
El Rayo

mamcx
20-04-2016, 00:06:59
Lo que digo es que si se el motor que usa puede generar fechas en memoria puede usar sql estandar para hacer el chequeo. Osea, es armar una tabla en memoria con registros como:


12-01-01 1pm
12-01-01 2pm
12-01-01 3pm

darkamerico
20-04-2016, 00:11:58
La consulta implementada quedó como sigue:

SELECT
horario_detalle.id_horariodet,
horario_detalle.posIniGrid,
horario_detalle.posFinGrid
FROM
horario_detalle
INNER JOIN curso ON horario_detalle.idcurso_fk = curso.id_curso
WHERE
horario_detalle.idsemestre_fk = :IDSemestre AND
horario_detalle.idcurr_fk = :IDCurr AND
horario_detalle.idfacultad_fk = :IDFacultad AND
horario_detalle.iddptoacad_fk = :IDDpto AND
horario_detalle.iddia = :IDDia AND
((:PosIni <= horario_detalle.posIniGrid AND :PosFin >= horario_detalle.posFinGrid)
OR
(:PosIni >= horario_detalle.posIniGrid AND :PosFin <= horario_detalle.posFinGrid)
OR
(:PosIni <= horario_detalle.posFinGrid AND :PosFin >= horario_detalle.posFinGrid)
OR
(:PosIni <= horario_detalle.posIniGrid AND :PosFin >= horario_detalle.posFinGrid)) AND
curso.ciclo = :Ciclo


Solo una observacion final: Si Existe un horario de hora=0 (7:00am) a hora=2 (9:00am) y quiero asignar un horario nuevo de 9:00am a 11:00am no me permite.

Un abrazo

elrayo76
20-04-2016, 02:40:29
Porque no te deja asignar el horario que quieres?. No se que es lo que te está devolviendo la consulta completa que pusiste.

Igualmente fijate que te equivocaste en un dato de la consulta. Te pongo tal cual como debes copiarla y pegarla.


((:PosIni <= horario_detalle.posIniGrid AND :PosFin >= horario_detalle.posIniGrid)
OR
(:PosIni >= horario_detalle.posIniGrid AND :PosFin <= horario_detalle.posFinGrid)
OR
(:PosIni <= horario_detalle.posFinGrid AND :PosFin >= horario_detalle.posFinGrid)
OR
(:PosIni <= horario_detalle.posIniGrid AND :PosFin >= horario_detalle.posFinGrid))


Saludos,
El Rayo

darkamerico
20-04-2016, 14:29:04
Grande Rayo, la cosa funcionó!!!!

elrayo76
20-04-2016, 14:48:32
Fue un gusto haberte ayudado, ya que a mi me han ayudado muchas veces.

Saludos,
El Rayo