2011-08-24 34 views
19

Como desarrollador principalmente escribiendo C# He adoptado algunas buenas prácticas al escribir el código C#. Cuando a veces escribo procedimientos almacenados tengo problemas para aplicar esas prácticas al código de procedimiento almacenado.Mejores prácticas de estructuración de procedimientos almacenados

En varias ocasiones he heredado el código de procedimiento almacenado de pesadilla, primero tres o cuatro capas de procedimientos almacenados configurando algunas tablas temporales y sobre todo llamándome entre sí. No se hizo un trabajo real y solo unas pocas líneas de código. Entonces, finalmente, hay una llamada al procedimiento almacenado "final", un gran monstruo de 3000-5000 líneas de código SQL. Ese código generalmente tiene un montón de código que huele a duplicación de código, intrincados flujos de control (también conocidos como spaghetti) y un método que hace demasiadas cosas apiladas una tras otra sin una separación clara donde comienza un pedazo de trabajo y dónde termina (ni siquiera un comentar como un divisor).

También he notado el uso de declaraciones de comentarios comentadas que seleccionan de tablas temporales intermedias. Las selecciones se pueden volver a activar con fines de depuración, pero deben eliminarse antes de cualquier código de llamada esperando un orden específico de los conjuntos de resultados devueltos.

Aparentemente, mis compañeros de equipo también comparten mi falta de buenas prácticas de escritura SQL.

Entonces ... (y aquí viene la verdadera pregunta) ... ¿cuáles son las buenas prácticas para escribir procedimientos almacenados modulares y mantenibles?

Tanto las prácticas caseras como las referencias a libros/blogs son bienvenidas. Métodos y herramientas que ayudan con ciertas tareas.

Permite resumir algunas áreas donde no he encontrado buenas prácticas

  • modularización y la encapsulación (se almacenan los procedimientos de comunicación a través de tablas temporales realmente el camino a seguir?)
    • en C# utilizo asambleas, clases y métodos decorados con modificadores de acceso para lograr esto.
  • Depuración/prueba (mejor que la modificación del objeto de la depuración?)
    • herramientas de depuración?
    • ¿Debug traces?
    • ¿Accesorio de prueba?
  • Destacando código de la lógica de flujo de control// data/utilizando el código de la estructura del código
    • en C# I refactorizar y salir métodos más pequeñas que hace precisamente una tarea lógica cada uno.
  • duplicación Código

Parcialmente me encuentro con SQL Server como DBMS pero DBMS respuestas o respuestas agnósticos señalando características de otros DBMS: es que ayuda en casos anteriores también son bienvenidos.

Para dar algunos antecedentes: La mayoría de los procedimientos almacenados grandes que he encontrado se encuentran en escenarios de informes donde la base es simplemente crear algunos valores de resumen de una tabla grande. Pero a lo largo del camino debe excluir algunos de los valores que se encuentran en alguna tabla de excepciones, agregue algunos de los valores en una tabla de elementos no completada, compare con el año pasado (¿se imagina el código feo que maneja los productos cambiando de departamento? entre años?), etc.

+2

Por estas razones, así como por la imposibilidad de utilizar procedimientos almacenados con SCM, los he evitado para todos los usos más necesarios y obvios (activadores, etc.). Mantenemos la lógica comercial en la capa de negocios (.NET) y utilizamos la base de datos para eso: una base de datos. – gahooa

+0

Idealmente, los procedimientos almacenados no deben llamar a otros procedimientos almacenados. Para eso es la capa BL. Desafortunadamente, ese sentimiento realmente no ayuda si ya tiene una base de datos llena de llamadas SP anidadas. ¿Está buscando consejos sobre cómo hacer que el modelo actual sea más fácil de mantener, o consejos sobre cómo refactorizar su sistema para seguir mejor las mejores prácticas? –

+0

@gahooa Puse tanto como sea posible en .NET también (en parte porque ese es el escenario de mi casa) pero en algunas ocasiones, como cuando heredas una gran base de código o cuando tienes grandes cantidades de datos, estás más o menos atorado con el almacenamiento procedimientos. –

Respuesta

13

Escribo muchos procesos almacenados complejos. Algunas cosas que consideraría mejores prácticas:

No use SQl dinámico en un proceso almacenado a menos que esté haciendo un proceso de búsqueda con muchos parámetros que pueden ser necesarios o no (entonces es actualmente una de las mejores soluciones) Si debe usar SQl dinámico en un proceso, siempre debe tener un parámetro de entrada de depuración y si el parámetro de depuración está establecido, imprima la instrucción SQL creada en lugar de ejecutarla. ¡Esto ahorrará horas de depuración!

Si está realizando más de una consulta de acción en un proceso (insertar/actualizar/eliminar), use los bloques Try Cacth y el procesamiento de transacciones. Agregue un parámetro de prueba a los parámetros de entrada y cuando esté configurado en 1, siempre retrotraiga la transacción completa. Antes de retroceder en el modo de prueba, generalmente tengo una sección que devuelve los valores en las tablas que estoy afectando para asegurar que lo que creo que estoy haciendo en la base de datos es de hecho lo que sí hice. O bien, podría tener controles como se muestra a continuación. Eso es tan simple como poner el siguiente código alrededor de las selecciones actualmente comentadas (y descomentarlas) una vez que tenga el parámetro @test.

If @test =1 
Begin 
Select * from table1 where field1 = @myfirstparameter 
End 

Ahora no tiene que pasar, comentar y descomentar cada vez que realiza la prueba.

@test o @debuig siempre se deben establecer con un valor predeterminado de 0 y se colocan al final de la lista. De esta forma, agregarlos no romperá las llamadas existentes del proceso.

Considere la posibilidad de tener tablas de registro y/o registro de errores para los procesos que realizan inserciones/actualizaciones/eliminaciones. Si registra los pasos y/o errores en las variables de la tabla sobre la marcha, todavía están disponibles después de una restauración para insertarse en la tabla de registro. Saber qué parte de un proceso complejo falló y cuál fue el error puede ser invaluable más adelante.

Siempre que sea posible, no anide los procesos almacenados. Si necesita ejecutar múltiples registros en un bucle, reemplace el proceso almacenado por uno que tenga un parámetro con valores de tabla y configure el proceso para que se ejecute en un modo basado en conjuntos y no en registros individuales. Esto funcionará si el parámetro con valores de tabla tiene un registro o muchos registros.

Si tiene una selección compleja con muchas subconsultas o tablas derivadas, considere usar CTE en su lugar. Refactorice las subconsultas o cursores correlacionados para mejorar el rendimiento del código basado en conjuntos. Siempre piense en términos de conjuntos de datos, no en un solo registro.

No, bajo ninguna circunstancia concebible, vistas nido. El golpe de rendimiento es mucho peor que cualquier pequeña cantidad de tiempo de desarrollo guardado. Y créanme, las vistas anidadas no ahorran tiempo de mantenimiento cuando el cambio debe ser para la vista lo más lejos posible en la cadena de vistas.

Todos los procs almacenados (y otro código de base de datos) deben estar en control de fuente.

Las variables de tabla son buenas para conjuntos de datos más pequeños, pero las tablas temporales (reales que comienzan con # o ## no tablas de etapas) pueden ser mejores para el rendimiento en grandes conjuntos de datos. Si usa tablas temporales, colóquelas cuando ya no las necesite. Intenta evitar el uso de tablas temporales globales.

Aprenda a escribir SQL de rendimiento. Por lo general, es tan fácil escribir SQL que funcionará mejor que SQL, que no lo hará una vez que conozca las técnicas. Si escribe procesos almacenados complejos, no hay excusa para no saber qué técnicas funcionan mejor que cuáles otras. Aprenda cómo asegurarse de que su consulta sea sargable. Evite los cursores, las subconsultas correlacionadas, las funciones escalares y otras cosas que se ejecutan row-by-agonizing-row.

+0

Vistas anidadas de RE: Heredé un sistema con vistas anidadas, y al realizar una investigación de rendimiento en SQL Server, no causó ningún problema. Tal vez solía ser un problema, pero en MSSQL 2008 y superior, el planificador de consultas es bastante inteligente y con eficacia simplemente enmarca la vista. –

+0

El servidor en el que tuvimos el problema fue un servidor SQl Server 2008. T puede depender de cómo se escriban las observaciones. El otro problema que teníamos era que las vistas se volvían ridículamente difíciles de mantener y, finalmente, llegamos al límite estricto del sistema y las vistas no se podían ejecutar en absoluto. Respaldo lo que dije, es una mala práctica usar vistas para llamar a otras vistas. – HLGEM

5

La comunicación a través de tablas temporales es a veces un gran olor a código. Tales procedimientos a menudo no pueden ser ejecutados por un usuario sin interferir entre sí (si reutiliza un nombre de tabla temporal para las entradas y salidas de diferentes procedimientos y no se vuelven a crear o si usa el mismo nombre con dos tablas diferentes esquemas). Pueden ser difíciles de solucionar, como cualquier característica, utilícelos cuando sea necesario y no existan mejores alternativas. Usar tablas reales temporalmente también puede ser problemático.

Procs almacenados que pasan datos entre sí en SQL Server (más que parámetros) pueden ser problemáticos. Ahora hay parámetros con valores de tabla y muchas cosas que anteriormente se habrían hecho con procs ahora se pueden hacer con funciones en línea con valores de tabla o (y generalmente preferidas) funciones con valores de tabla de multi-instrucción.

En SQL Server, evite el uso intensivo de funciones escalares y funciones de valores múltiples en conjuntos de filas grandes; no funcionan muy bien, por lo que las técnicas modulares que parecen obvias en C# no se aplican aquí.

Yo recomendaría que mirara Ken Henderson's Guru's Guide to SQL Server Stored Procedures - publicado en 2002, todavía tiene una gran cantidad de información útil sobre el diseño de la aplicación de base de datos.

+0

Tal vez no entiendo bien, pero las tablas temporales se pueden ejecutar con múltiples usuarios sin problemas. ¿Pero tal vez estás hablando de un caso especial? –

+0

@Mark SQLDev Está en lo correcto: las tablas temporales locales son locales de conexión: me parece difícil depurar las tablas temporales (y las variables de tabla). Y, por supuesto, está tomando decisiones sobre qué materializar y cuándo dejarlo en manos del optimizador. Y necesita realizar un seguimiento de la nomenclatura para que no entre en conflicto. –

2

Esta es una buena pregunta. Como desarrollador de C# tengo que incursionar en SQL, parece que SQL por su propia naturaleza se interpone en el camino de las mejores prácticas a las que estoy acostumbrado con C#.

¡Las expresiones de tabla comunes son geniales para aislar consultas en un procedimiento almacenado pero solo puede usarlas una vez! Eso lo lleva a definir vistas, pero luego ha perdido su encapsulación.

Un conjunto de resultados de un procedimiento almacenado es muy difícil de usar en otro, por lo que es posible que tenga la tentación de escribir funciones con valores de tabla. Eso aumenta la carga de mantenimiento de permisos y le obliga a escribir funciones 'dos ​​veces', una como función y otra como un procedimiento que llama a la función. De lo contrario, tiene diferentes interfaces para su DAL dependiendo de si se trata de un procedimiento o no.

Todo esto me ha llevado, con el tiempo, a seguir procedimientos simples CRUD almacenados (que no se llaman entre sí) en la base de datos y pocas consultas aisladas cuando las relaciones son complejas. Más BI-cosas. Todo lo demás está en el BLL.

Físicamente, SQL está aislado en archivos separados por función o por la tabla en la que giran y se gestionan en control de fuente.

Evite SELECCIONAR * y favorecer la especificación de columnas. Eso le ahorra problemas de tiempo de ejecución cuando cambia una tabla y no toca todos los procesos. Sí, hay una recompilación para procs pero PERDERá algunas, especialmente si hay vistas involucradas. Además, SELECT * casi siempre devuelve más columnas de las que realmente necesita y eso es una pérdida de ancho de banda.

+0

Las mejores prácticas de SQL definitivamente no son las mismas a las que está acostumbrado con C#. Intentar utilizar las mejores prácticas de C# en el mundo relacional es una de las causas de que las bases de datos funcionen mal. Las bases de datos están diseñadas para maximizar el rendimiento para no facilitar el mantenimiento. – HLGEM

+0

Y puede usar tablas temporales en lugar de CTE cuando necesite usarlas más de una vez en un proceso. – HLGEM

Cuestiones relacionadas