¿Por qué no es SQL más refactible? [cerrado]


39

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:

  1. Esto ya es común y estoy trabajando con personas sin experiencia.

  2. Los programadores experimentados no escriben SQL complejo porque prefieren resolver problemas de procesamiento de datos con código de procedimiento

  3. Algo más


12
Hay organizaciones que solo le permiten consultar una base de datos a través de vistas y modificarla a través de procedimientos almacenados.
Pieter B

3
SQL se volvió mucho más agradable para mí cuando finalmente acepté que nunca sería tan SECO como mi código de procedimiento normal.
Graham

1
4. SQL es muy antiguo y no se ha actualizado materialmente en décadas. Para cosas súper complejas, muchos equipos optan por procedimientos almacenados. Puede agregar diferentes cláusulas para eso. A veces solo tiene que ejecutar trabajos para organizar los datos en una tabla temporal y luego unirse a eso. Observe cuán diferentes son los lenguajes declarativos y de procedimiento.
Berin Loritsch

8
También una razón es que hay un problema de rendimiento horrible llamado "unión triangular" que puede ocurrir cuando usas vistas (por accidente, por supuesto). Si su consulta se une a la Vista A y la Vista B, pero la Vista A también en su implementación reutiliza la Vista B, comienza a ver ese problema. Entonces, la gente a menudo comienza escribiendo una sola consulta monolítica para poder ver lo que realmente funcionaría mejor en términos de refactorización a las vistas, y luego sus fechas límite, y el monolito pasa a producción. Algo así como el 98% de todos los desarrolladores de software, realmente :) :)
Stephen Byrne

3
"Imagine si otros tipos de programadores tuvieran que enviar una solicitud cada vez que crearan una función" ... umm. ¿No haces revisiones de código?
svidgen

Respuestas:


25

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:

with custs as (
    select acct# as accountNumber, cfname as firstName, clname as lastName,
    from wrdCsts
    where -- various criteria
)
, accounts as (
    select acct# as accountNumber, crBal as currentBalance
    from crzyAcctTbl
)
select firstName, lastName, currentBalance
from custs
inner join accounts on custs.accountNumber = accounts.accountNumber

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:

  • DB / 2
  • PostGreSQL
  • Oráculo
  • MS SQL Server
  • MySQL (última versión; todavía un poco nueva)
  • probablemente otros

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.


44
Hicimos que el Sr. Big CTE sea el hombre que escribe el peor SQL. El problema era que los CTE eran malas elecciones de abstracción y el optimizador no puede deshacer todos los algoritmos descabellados que haya introducido.
Joshua

3
También ORM puede hacer algunas cosas bastante atroces en cuanto al rendimiento, especialmente ... especialmente cuando solo está usando getters y setters para obtener un montón de datos. Hibernate es conocido por usar cientos de consultas individuales en lugar de una gran consulta unida, lo cual es un problema cuando hay sobrecarga en cada consulta.
user3067860

2
@ Joshua Puedes escribir código incorrecto en cualquier idioma. Incluyendo SQL. Pero la refactorización a CTE, realizada correctamente, puede crear diseños ascendentes que son más fáciles de analizar para los humanos. Tiendo a ver eso como un rasgo deseable, independientemente del idioma con el que esté tratando :-)
Meower68

2
Las otras respuestas son geniales, pero esto es lo que estaba buscando personalmente. '¿Por qué no sé acerca de los CTE?' Fue la mayoría de mi problema.
Ebrts

2
@ Meower68 ¿No existe el riesgo de que el uso extensivo de CTE impida que las personas aprendan correctamente y aprendan sobre un buen diseño de base de datos? Apoyo el valor de los CTE pero también hace que sea demasiado fácil trabajar con subconsultas, donde no debería.
Pieter B

36

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.


"SQL solo trata con estructuras de datos concretas, no con un comportamiento abstracto (o una abstracción en cualquier sentido de la palabra)". Esta es una afirmación extraña, ya que desde mi punto de vista, SQL trata completamente con el comportamiento abstracto y no con la programación concreta en ningún sentido de la palabra. Solo considere todos los grados masivos de complejidad que se resumen en la simple palabra "UNIRSE": usted dice que desea un resultado combinado extraído de dos conjuntos de datos diferentes, y deje que el DBMS determine las técnicas concretas involucradas. indexación, manejar la diferencia entre tablas y subconsultas, etc.
Mason Wheeler

55
@MasonWheeler: Creo que estaba pensando en SQL más desde el punto de vista de los datos en los que trabaja, no en la implementación de las características del lenguaje. Las tablas en una base de datos no parecen una abstracción. Son concretos, como en una tabla llamada "phone_numbers" contiene números de teléfono. Un número de teléfono no es un concepto abstracto.
Greg Burghardt

12

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.


6

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?

  • SQL, en muchos sentidos, no es un lenguaje de programación.
  • Mal diseño de la base de datos.
  • La gente no es realmente fluida en SQL.
  • Sin poder sobre la base de datos (por ejemplo, no se le permite usar vistas)
  • Diferentes objetivos con refactorización.

(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).


66
Las "subconsultas" tienen la misma probabilidad de ser una agregación de una base de datos correctamente normalizada que una normalización ad-hoc de una base de datos no normalizada
Caleth

@Caleth eso es muy cierto.
Pieter B

55
Incluso en bases de datos bien normalizadas, a menudo es necesario unirse con subconsultas, en lugar de unirse directamente con tablas. Por ejemplo, si necesita unirse con datos agrupados.
Barmar

1
@Barmar definitivamente, de ahí mi comentario de 9 sobre 10. Las subconsultas tienen su lugar, pero las veo en exceso por personas sin experiencia.
Pieter B

Me gusta su métrica de "número de subconsultas" como una indicación de la normalización de la base de datos (o falta de ella).
Jason

2

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.


+1 Algunos buenos puntos aquí. Dado lo malo que es un SQL, la reticencia de los DBA para permitir vistas a menudo es completamente comprensible. Además, SQL definitivamente puede beneficiarse de la revisión por pares si necesita recursos y / o si se ejecutará con frecuencia.
Robbie Dee

1

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.


1
Nunca he oído hablar de una construcción "mart". ¿Que es eso?
obispo

1
Marts son solo un subconjunto del repositorio (base de datos maestra). Si hay consultas complejas específicas que deben ejecutarse, se puede crear una base de datos especial específicamente para atender esas solicitudes. Un ejemplo muy común es una tienda de informes.
Robbie Dee

1
Confundido por qué esto fue rechazado. No responde directamente a la pregunta, pero da una respuesta implícita bastante clara de "opción 3: hay muchas formas de manejar esto, que son ampliamente utilizadas".
Dewi Morgan

TIL sobre data marts. Tener un +1!
obispo
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.