Estoy creando una aplicación fastAPI y tengo una consulta complicada que trato de evitar como múltiples consultas individuales en las que concateno los resultados. Tengo las siguientes tablas que tienen claves foráneas:
CHANGE_LOG: cambio_id change_id | original (FK ROSTER.shift_id) | new (FK ROSTER.shift_id) | change_type (FK CONFIG_CHANGE_TYPES)
PLANTILLA: shift_id | shift_type (FK CONFIG_SHIFT_TYPES) | shift_start | shift_end | user_id (FK USERS)
CONFIG_CHANGE_TYPES: change_type_id | change_type_name
CONFIG_SHIFT_TYPES: shift_type_id | shift_type_name
USUARIOS: user_id | user_name
FK= Clave foránea
Necesito devolver la siguiente información: user_name , change_type_name y shift_start shift_end y shift_type_name para aquellos cuyo shift_id coincida con el original o el nuevo en la fila CHANGE_LOG.
El problema es que la tabla CHANGE_LOG puede tener tanto original como nuevo, solo un original pero no nuevo, o solo un nuevo pero no original. Pero como el usuario puede seleccionar algunas opciones de los cuadros desplegables antes de enviar la solicitud, también necesito poder incluir un filtro para destacar:
El problema es que no puedo encontrar una manera de garantizar el nombre de usuario para cada fila sin inspeccionarlo después porque no sé si el nuevo u original existe o si está configurado como null .
¿Hay alguna manera en SQLalchemy de tener un filtro opcional en la consulta donde pueda decir si el original existe, use eso para obtener el ID de usuario, pero si no, use el nuevo para obtener el ID de usuario? Además, si tengo una consulta que definitivamente encuentra aquellos con turnos originales y nuevos, nunca encontrará aquellos con solo uno de ellos, ya que los criterios nunca coincidirán.
También he leído este y otros similares, y aunque resolverán el problema de establecer condicionalmente algunos de los filtros, no soluciona el problema de los valores nulos parciales que no devuelven nada en absoluto, en lugar de la mitad de los datos. Este parece resolver ese problema, pero no tengo idea de cómo implementarlo.
Sé que es complicado, así que avíseme si he hecho un mal trabajo al explicar la pregunta.
Ordenado. La solución fue usar la opción de outerjoin . Estoy seguro de que la sintaxis puede ser más elegante que mi solución si me comprometo adecuadamente a agregar relaciones al definir cada clase, pero lo que termino es explícito y creo que hace que sea más fácil de leer... al menos para mí.
Dado que estoy usando algunas tablas más de una vez en la misma consulta para obtener información diferente, era importante crear un alias para esas, de lo contrario, terminé con un conflicto (qué 'user_id' quería, no está claro). Para aquellos que juegan en casa, esta es mi solución general:
new=aliased(ROSTER) original=aliased(ROSTER) o_name=aliased(CONFIG_SHIFT_TYPES) n_name=aliased(CONFIG_SHIFT_TYPES) pd.read_sql( db.query( CHANGE_LOG.change_id, CHANGE_LOG.created, CONFIG_CHANGE_TYPES.change_name, o_name.shift_name.label('original_type'), n_name.shift_name.label('new_type'), OPERATORS.operator_name ) .outerjoin(original, original.shift_id==CHANGE_LOG.original_shift) .outerjoin(new, new.shift_id==CHANGE_LOG.new_shift) .outerjoin (CONFIG_CHANGE_TYPES,CONFIG_CHANGE_TYPES.change_id==CHANGE_LOG.change_type) .outerjoin(CONFIG_SHIFT_TYPES, CONFIG_SHIFT_TYPES.shift_id==new.roster_shift_id) .outerjoin(o_name, o_name.shift_id==original.roster_shift_id) .outerjoin(n_name, n_name.shift_id==new.roster_shift_id) .outerjoin(USERS, or_(USERS.operator_id==original.user_id, USERS.user_id==new.user_id) ).statement, engine)