Todos saben que los nuevos desarrolladores escriben funciones largas. A medida que avanza, mejora al dividir su código en partes más pequeñas y la experiencia le enseña el valor de hacerlo.
Ingrese SQL. Sí, la forma de pensar SQL sobre el código es diferente de la forma procesal de pensar sobre el código, pero este principio parece igualmente aplicable.
Digamos que tengo una consulta que toma la forma:
select * from subQuery1 inner join subQuerry2 left join subquerry3 left join join subQuery4
Usando algunas ID o fechas, etc.
Esas subconsultas son complejas y pueden contener subconsultas propias. En ningún otro contexto de programación, pensaría que la lógica para las subconsultas complejas 1-4 pertenece a mi consulta principal que las une a todas. Parece tan sencillo que esas subconsultas deberían definirse como vistas, al igual que serían funciones si estuviera escribiendo código de procedimiento.
Entonces, ¿por qué no es esa práctica común? ¿Por qué la gente suele escribir estas largas consultas SQL monolíticas? ¿Por qué SQL no fomenta el uso extensivo de vistas al igual que la programación de procedimientos alienta el uso extensivo de funciones? (En muchos entornos empresariales, crear vistas ni siquiera es algo que se pueda hacer fácilmente. Se requieren solicitudes y aprobaciones. ¡Imagínese si otros tipos de programadores tuvieran que enviar una solicitud cada vez que crean una función!)
He pensado en tres posibles respuestas:
Esto ya es común y estoy trabajando con personas sin experiencia.
Los programadores experimentados no escriben SQL complejo porque prefieren resolver problemas de procesamiento de datos con código de procedimiento
Algo más
Respuestas:
Creo que el problema principal es que no todas las bases de datos admiten expresiones de tabla comunes.
Mi empleador usa DB / 2 para muchas cosas. Las últimas versiones son compatibles con CTE, de modo que puedo hacer cosas como:
El resultado es que podemos tener nombres de tabla / campo muy abreviados y esencialmente estoy creando vistas temporales, con nombres más legibles, que luego puedo usar. Claro, la consulta se hace más larga. Pero el resultado es que puedo escribir algo que está bastante claramente separado (usando CTE de la forma en que usarías las funciones para SECAR) y terminar con un código que es bastante legible. Y debido a que puedo separar mis subconsultas y hacer que una subconsulta haga referencia a otra, no todo es "en línea". En ocasiones, escribí un CTE, luego hice que otros cuatro CTE lo hicieran referencia, luego hice que la consulta principal uniera los resultados de los últimos cuatro.
Esto se puede hacer con:
Pero va MUCHO para que el código sea más limpio, más legible, más SECO.
He desarrollado una "biblioteca estándar" de CTE que puedo conectar a varias consultas, lo que me permite comenzar mi nueva consulta. Algunos de ellos también están empezando a ser aceptados por otros desarrolladores de mi organización.
Con el tiempo, puede tener sentido convertir algunos de estos en vistas, de modo que esta "biblioteca estándar" esté disponible sin necesidad de copiar / pegar. Pero mis CTE terminan siendo ajustados, muy levemente, para diversas necesidades que no he podido hacer que un solo CTE se use TAN ANCHAMENTE, sin modificaciones, que valga la pena crear una vista.
Parecería que parte de su queja es "¿por qué no sé acerca de los CTE?" o "¿por qué mi base de datos no admite CTE?"
En cuanto a las actualizaciones ... sí, puede usar CTE pero, en mi experiencia, debe usarlos dentro de la cláusula set Y en la cláusula where. Sería bueno si pudieras definir uno o más antes de toda la declaración de actualización y luego solo tener las partes de "consulta principal" en las cláusulas set / where, pero no funciona de esa manera. Y no hay forma de evitar nombres oscuros de tabla / campo en la tabla que está actualizando.
Puede usar CTE para eliminaciones. Puede tomar varios CTE para determinar los valores PK / FK para los registros que desea eliminar de esa tabla. Nuevamente, no puede evitar nombres oscuros de tabla / campo en la tabla que está modificando.
De la misma manera que puede hacer una selección en un inserto, puede usar CTE para insertos. Como siempre, puede estar tratando con nombres oscuros de tabla / campo en la tabla que está modificando.
SQL NO le permite crear el equivalente de un objeto de dominio, envolviendo una tabla, con getters / setters. Para eso, necesitará usar un ORM de algún tipo, junto con un lenguaje de programación más procedimental / OO. He escrito cosas de esta naturaleza en Java / Hibernate.
fuente
Las organizaciones paranoicas de los problemas de rendimiento en la base de datos suelen bloquear la creación de vistas de la base de datos. Este es un problema de cultura organizacional, más que un problema técnico con SQL.
Más allá de eso, las consultas SQL monolíticas grandes se escriben muchas veces, porque el caso de uso es tan específico que muy poco del código SQL puede reutilizarse realmente en otras consultas. Si se necesita una consulta compleja, generalmente es para un caso de uso muy diferente. Copiar el SQL de otra consulta es a menudo un punto de partida, pero debido a las otras subconsultas y uniones en la nueva consulta, termina modificando el SQL copiado lo suficiente como para romper cualquier tipo de abstracción que una "función" en otro idioma ser usado para. Lo que me lleva a la razón más importante por la que SQL es difícil de refactorizar.
SQL solo trata con estructuras de datos concretas, no con un comportamiento abstracto (o una abstracción en cualquier sentido de la palabra). Como SQL se escribe en torno a ideas concretas, no hay nada que abstraer en un módulo reutilizable. Las vistas de la base de datos pueden ayudar con esto, pero no al mismo nivel que una "función" en otro idioma. Una vista de base de datos no es tanto una abstracción como una consulta. Bueno, en realidad, una vista de base de datos es una consulta. Básicamente se usa como una tabla, pero se ejecuta como una subconsulta, así que de nuevo, se trata de algo concreto, no abstracto.
Es con abstracciones que el código se vuelve más fácil de refactorizar, porque una abstracción oculta los detalles de implementación del consumidor de esa abstracción. Straight SQL no proporciona tal separación, aunque las extensiones de procedimiento a SQL como PL / SQL para Oracle o Transact-SQL para SQL Server comienzan a difuminar un poco las líneas.
fuente
Lo que creo que puede faltar en su pregunta / punto de vista es que SQL ejecuta operaciones en conjuntos (utilizando operaciones de conjuntos, etc.).
Cuando opera en ese nivel, naturalmente, cede cierto control sobre el motor. Todavía puede forzar un código de estilo de procedimiento utilizando cursores, pero como muestra la experiencia 99/100 veces, no debería hacerlo.
La refactorización de SQL es posible pero no está utilizando los mismos principios de refactorización de código a los que estamos acostumbrados en el código de nivel de aplicación. En su lugar, optimiza cómo utiliza el motor SQL en sí.
Esto se puede hacer de varias maneras. Si usa Microsoft SQL Server, puede usar SSMS para proporcionarle un plan de ejecución aproximado y puede usarlo para ver qué pasos puede seguir para ajustar su código.
En el caso de dividir el código en módulos más pequeños, como mencionó @ greg-burghardt, SQL es generalmente una pieza de código especialmente diseñada y como resultado. Hace una cosa que necesitas hacer y nada más. Se está adhiriendo a la S en SOLID, solo tiene una razón para cambiar / afectar y es cuando necesita esa consulta para hacer otra cosa. El resto del acrónimo (OLID) no se aplica aquí (AFAIK no hay inyección de dependencias, interfaces o dependencias como tales en SQL) dependiendo del sabor del SQL que esté utilizando, podría extender ciertas consultas envolviéndolos en un procedimiento almacenado / función de tabla o usándolos como subconsultas, entonces, diría que el principio abierto-cerrado todavía se aplicaría, de alguna manera. Pero yo divago.
Creo que necesita cambiar su paradigma en términos de cómo está viendo el código SQL. Debido a su naturaleza establecida, no puede proporcionar muchas de las características que los lenguajes de nivel de aplicación pueden (genéricos, etc.). SQL nunca fue diseñado para ser algo así, es un lenguaje para consultar conjuntos de datos, y cada conjunto es único a su manera.
Dicho esto, hay formas en que puede hacer que su código se vea mejor, si la legibilidad es una alta prioridad dentro de la organización. Almacenar bits de bloques SQL de uso frecuente (conjuntos de datos comunes que usa) en procedimientos almacenados / funciones de valor de tabla y luego consultarlos y almacenarlos en tablas / variables de tabla temporales, seguido de usarlos para unir las piezas en una transacción masiva que de lo contrario escribirías es una opción. En mi humilde opinión, no vale la pena hacer algo así con SQL.
Como lenguaje, está diseñado para que cualquiera pueda leerlo y entenderlo fácilmente, incluso los que no son programadores. Como tal, a menos que esté haciendo algo muy inteligente, no hay necesidad de refactorizar el código SQL en pedazos de bytes más pequeños. Personalmente, he escrito consultas SQL masivas mientras trabajaba en una solución ETL / Reporting de almacén de datos y todo estaba muy claro en términos de lo que estaba sucediendo. Cualquier cosa que pudiera parecer un poco extraña para cualquier otra persona recibiría un breve conjunto de comentarios junto a ella para proporcionar una breve explicación.
Espero que esto ayude.
fuente
Me voy a centrar en las "subconsultas" en su ejemplo.
¿Por qué se usan tan a menudo? Porque usan la forma natural de pensar en una persona: tengo este conjunto de datos y quiero hacer una acción en un subconjunto de ellos y unirlo con un subconjunto de otros datos. 9 de cada 10 veces que veo una subconsulta, se usa mal. Mi broma corriente sobre subconsultas es: las personas que tienen miedo de las uniones usan subconsultas.
Si ve tales subconsultas, a menudo también es un signo de diseño de base de datos no óptimo.
Cuanto más normalizada esté su base de datos, más uniones obtendrá, más se verá su base de datos como una gran hoja de Excel, más subselecciones obtendrá.
La refactorización en SQL a menudo tiene un objetivo diferente: obtener más rendimiento, mejores tiempos de consulta, "evitar escaneos de tabla". Esos incluso pueden hacer que el código sea menos legible, pero son muy valiosos.
Entonces, ¿por qué ves tantas consultas monolíticas no refactorizadas?
(para mí, cuanto más experiencia tengo con SQL, menos grandes son mis consultas, SQL tiene formas para que las personas de todos los niveles de habilidad hagan su trabajo sin importar lo que pase).
fuente
Segregación de deberes
En el espíritu de SQL, la base de datos es un activo compartido que contiene los datos de la compañía, y protegerlos es de vital importancia. Entra en el DBA como guardián del templo.
Se cree que crear una nueva vista en la base de datos tiene un propósito duradero y es compartido por una comunidad de usuarios. En la vista DBA, esto es aceptable solo si la vista está justificada por la estructura de los datos. Cada cambio de una vista se asocia con riesgos para todos sus usuarios actuales, incluso aquellos que no usan la aplicación pero que han descubierto la vista. Finalmente, la creación de nuevos objetos requiere gestionar autorizaciones y, en el caso de la vista, de forma coherente con las autorizaciones de las tablas subyacentes.
Todo esto explica por qué a los DBA no les gusta agregar vistas que son solo para el código de alguna aplicación individual.
Diseño SQL
Si descompone una de sus consultas complejas, es posible que descubra que las subconsultas a menudo necesitarán un parámetro que dependa de otra subconsulta.
Por lo tanto, transformar las subconsultas a la vista no es necesariamente tan simple como se indica. Debe aislar los parámetros variables y diseñar su vista para que los parámetros se puedan agregar como criterios de selección en la vista.
Desafortunadamente, al hacerlo, a veces se impone el acceso a más datos y con menos eficacia que en una consulta personalizada.
Extensiones de propiedad
Podría esperar alguna refactorización, transfiriendo algunas responsabilidades a extensiones de procedimiento de SQL, como PL / SQL o T-SQL. Sin embargo, estos dependen del proveedor y crean una dependencia tecnológica. Además, estas extensiones se ejecutan en el servidor de la base de datos, creando más carga de procesamiento en un recurso que es mucho más difícil de escalar que un servidor de aplicaciones.
¿Pero cuál es el problema al final?
Finalmente, ¿la segregación de tareas y el diseño de SQL con su fuerza y limitaciones son un problema real? Al final, estas bases de datos demostraron manejar de manera exitosa y confiable datos muy críticos, incluso en entornos de misión crítica.
Entonces, para lograr una refactorización exitosa:
Considere una mejor comunicación . Intente comprender las limitaciones de su DBA. Si le demuestra a un DBA que una nueva vista está justificada por las estructuras de datos, que no es una solución alternativa y que no tiene un impacto en la seguridad, él / ella ciertamente aceptará que se cree. Porque, entonces sería un interés compartido.
primero limpie su propia casa : nada lo obliga a generar una gran cantidad de SQL en muchos lugares. Refactorice el código de su aplicación, para aislar los accesos SQL y para crear las clases o funciones para proporcionar subconsultas reutilizables, si se utilizan con frecuencia.
Mejorar la conciencia del equipo : asegúrese de que su aplicación no realice tareas que el motor DBMS podría realizar de manera más eficiente. Como señaló correctamente, el enfoque de procedimiento y el enfoque orientado a datos no son dominados por los diferentes miembros del equipo. Depende de sus antecedentes. Pero para optimizar el sistema como un todo, su equipo necesita entenderlo como un todo. Así que crea conciencia, para estar seguro de que los jugadores menos experimentados no reinventan la rueda y comparten sus pensamientos de DB con miembros más experimentados.
fuente
Re puntos 1 y 3: las vistas no son la única forma. También hay tablas temporales, marts, variables de tabla, columnas agregadas, CTE, funciones, procedimientos almacenados y posiblemente otras construcciones dependiendo del RDBMS.
Los DBA (y estoy hablando como alguien que ha sido DBA y desarrollador) tienden a ver el mundo de una manera bastante binaria, por lo que a menudo están en contra de cosas como las vistas y las funciones debido a la penalidad de rendimiento percibida.
Últimamente, la necesidad de combinaciones complejas se ha reducido con el reconocimiento de que las tablas desnormalizadas a pesar de ser subóptimas desde un punto de vista NF , son altamente productivas.
También existe la tendencia de hacer consultas del lado del cliente con tecnologías como LINQ que plantea en el punto 2.
Si bien estoy de acuerdo en que SQL puede ser un desafío modularizar, se han hecho grandes avances, aunque siempre habrá una dicotomía entre el código del lado del cliente y SQL, aunque 4GL ha borrado las líneas de alguna manera.
Supongo que realmente depende de qué tan lejos estén dispuestos a ceder sus DBA / arquitectos / líderes tecnológicos a este respecto. Si se niegan a permitir cualquier cosa que no sea SQL de vainilla con muchas combinaciones, podrían producirse grandes consultas. Si está atrapado con esto, no se golpee la cabeza contra una pared de ladrillos, escale. En general, hay mejores formas de hacer las cosas con un poco de compromiso, especialmente si puede probar los beneficios.
fuente