2008-11-19 42 views
26

Encontré que los procedimientos almacenados de SQL son muy interesantes y útiles. He escrito los procedimientos almacenados, pero quiero escribir SP bien confeccionados, de buen rendimiento y concisos para cualquier tipo de requerimiento y también me encantaría aprender sobre cualquier trucos o buenas prácticas para los procedimientos almacenados. ¿Cómo paso del nivel principiante al avanzado en la escritura de procedimientos almacenados?¿Cuáles son las mejores prácticas para escribir un procedimiento almacenado SQL

Actualización: Se encontró en los comentarios que mi pregunta debería ser más específica. Todo el mundo tiene algunos trucos en sus mangas y esperaba esos trucos y prácticas para los SP que usan en su código que los diferencia de los demás y lo más importante, mejorar la productividad al escribir y trabajar con procedimientos almacenados.

+2

Eso es similar a decir: "He escrito historias, pero quiero escribir bien elaboradas, best-sellers y novelas para todo tipo de lectores y también me encantaría aprender sobre cualquier trucos o buenas prácticas para escribir historias". Es posible que desee ser más específico en lo que está buscando ... –

+0

De acuerdo, las preguntas más específicas serían buenas. – ahockley

Respuesta

17

El único truco que siempre trato de usar es: siempre incluya un ejemplo de uso en un comentario cerca de la parte superior. Esto también es útil para probar tu SP. Me gusta incluir los ejemplos más comunes, entonces ni siquiera necesita SQL Prompt o un archivo .sql por separado con su invocación favorita, ya que está almacenado allí mismo en el servidor (esto es especialmente útil si tiene procs almacenados que miran sp_who salida para bloques o lo que sea y toma un montón de parámetros).

Algo así como:

/* 
    Usage: 
    EXEC usp_ThisProc @Param1 = 1, @Param2 = 2 
*/ 

Luego de probar o ejecutar el SP, sólo tiene que resaltar que la sección en la secuencia de comandos y ejecutar.

1

Esta no es una pregunta que se pueda responder directamente sin más información, pero se aplican unas pocas reglas generales.

Los procedimientos almacenados son simplemente consultas T-SQL que se almacenan. Por lo tanto, familiarizarse con T-SQL y las diversas funciones y sintaxis es realmente lo que necesita hacer. Y aún más desde el punto de vista del rendimiento, deberá asegurarse de que sus consultas y las estructuras de datos subyacentes coincidan de forma que permitan un buen rendimiento. IE, asegúrese de que los índices, las relaciones, las restricciones, etc. se implementen donde sea necesario.

La comprensión de cómo utilizar las herramientas de ajuste del rendimiento, understaning cómo los planes de ejecución de la obra, y cosas de esa naturaleza son la forma de llegar a ese "siguiente nivel"

11

Esta es una pregunta muy general, pero aquí hay un par de consejos:

  • Nombre sus procedimientos almacenados consistentemente. Muchos usan un prefijo para identificar que es un procedimiento almacenado, pero no usan 'sp_' como el prefijo designado para el DataDae maestro (en SQL Server de todos modos)
  • Configure NOCOUNT, ya que esto reduce el número de posibles valores de retorno
  • Las consultas basadas en conjuntos suelen tener un mejor rendimiento que los cursores. This question entra en esto con mucho más detalle.
  • Si desea DECLARAR variables para su procedimiento almacenado, utilice buenas convenciones de nomenclatura tal como lo haría/debería en cualquier otro tipo de programación.
  • Llame a los SP que utilicen su nombre completo para eliminar cualquier confusión acerca de a qué SP se debe llamar, y para ayudar a aumentar el rendimiento de SQL Server; esto hace que sea más fácil encontrar el SP en cuestión.

Hay mucho más, por supuesto.Aquí hay un enlace con más: SQL Server Stored Procedures Optimization Tips

3

En SQL Server siempre pongo una declaración que soltará el procedimiento si existe, así que puedo presionar fácilmente volver a crear el procedimiento mientras lo estoy desarrollando. Algo así como:

 
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'usp') AND type in (N'P', N'PC')) 
DROP PROCEDURE usp 
+2

puede simplificar esto con: SI OBJECT_ID (N'dbo.usp ') no es NULL PROCEDIMIENTO DROP dbo.usp GO CREAR PROCEDIMIENTO dbo.usp ... – Tom

+0

no es que lo es para ALTER. .. – dotjoe

+3

sí alter puede hacer lo mismo pero si lo escribe como alter e irá a ponerlo en un servidor por primera vez, fallará. Este proceso funcionará ya sea que el proceso exista o no. – HLGEM

0

materia básica:

tener una política de control de errores, y capturar los errores en todas las sentencias SQL.
Decidir sobre una política para usar el control de código fuente para los procedimientos almacenados.
Incluye un encabezado comentado con el usuario, la fecha/hora y el propósito de la sp.
Devuelve explícitamente 0 (éxito) para una ejecución exitosa, algo más.
Para procedimientos no triviales, incluya un caso de prueba (o casos) y una descripción del resultado esperado.
Adquiera el hábito de realizar pruebas de rendimiento. Para casos de texto, registre el tiempo de ejecución al menos.
Comprender transacciones explícitas y usarlas.
Casi nunca llama a los SP de los SP. La reutilización es un juego de pelota diferente con SQL.

+0

¿por qué no llamar a los SP de los SP? Lo hago todo el tiempo por necesidad; si no lo hago, viola el principio DRY ...? –

+0

DRY se aplica al código de procedimiento, no al código declarativo. Si se trata de lógica de procedimiento dentro de la sp, te sugiero que la muevas a otro nivel, donde ciertamente tiene sentido. – dkretz

+1

Otro fuerte desincentivo. Los TRANs de ROLLBACK multinivel están * tan * atornillados. – dkretz

1

Esto depende en gran medida de lo que esté haciendo en los procesos almacenados. Sin embargo, es una buena idea usar transacciones si está realizando varias inserciones/actualizaciones o eliminaciones en un proceso. De esta forma, si una parte falla, las otras partes se retrotraen dejando su base de datos en un estado consistente.

Las dos cosas más importantes a tener en cuenta al escribir en una base de datos (y por lo tanto al utilizar un proceso almacenado que realiza una acción distinta de la seleccionada) son la integridad y el rendimiento de los datos. Sin integridad de datos, solo tiene una base de datos que contiene basura y es inútil. Sin performacne, no tendrá usuarios (si son clientes externos) ni usuarios insatisfechos (si tienen el mandato de usar su producto, generalmente usuarios internos que no tienen otra opción para ir a otra parte). Ninguno de los dos es bueno para tu carrera. Por lo tanto, al escribir un proceso almacenado, asegúrese de asegurarse primero de que los datos se ingresen correctamente en la base de datos y de que fallen si hay un problema en una parte de la acción.

Si es necesario, escriba las comprobaciones en el proceso para asegurarse de que el resultado final sea el correcto. Soy especialista en ETL y siempre escribo mis procesos para que los datos se limpien y normalicen antes de intentar importarlos en mis tablas. Si está haciendo cosas desde la interfaz de usuario, esto podría no ser tan importante en el proceso, aunque haría que el usuario inteface hacer comprobaciones incluso antes de ejecutar el proceso para asegurarse de que los datos son buenos para la inserción (cosas como verificar para asegurarse un campo de fecha contiene una fecha real, que todos los campos obligatorios tienen valores, etc.)

Si está escribiendo procs para poner grandes cantidades de datos en tablas, es mejor tener una forma de probar esos resultados antes de que se finalicen . Se sorprenderá de la basura que obtendrá de los clientes y proveedores para la importación de datos. Escribimos todos nuestros procesos de importación con una bandera de prueba. De esta forma, puede devolver los datos seleccionados en lugar de realizar la acción, para que pueda ver de antemano, exactamente lo que estaría afectando.

No soy partidario de SQL dinámico y prefiero no usarlo en procesos almacenados. Si está atascado con SQl dinámico en procesos existentes, coloque un indicador de depuración que le permita imprimir el SQL en lugar de ejecutarlo. Luego ponga los comentarios en los casos más típicos que necesitará ejecutar. Descubrirá que puede mantener el proceso mucho mejor si lo hace.

No haga las cosas con un cursor, simplemente porque desea reutilizar otro proceso almacenado que solo funciona en un registro a la vez. Reutilización de código que causa problemas de rendimiento si es algo malo.

Si está utilizando sentencias case o declaraciones if, asegúrese de haber hecho las pruebas que afectarán a todas las ramas posibles. El que no prueba es el que fallará.

35

Aquí están mis pautas de manejo de errores de procedimiento almacenado.

  • Llame a cada procedimiento almacenado utilizando su nombre completo para mejorar el rendimiento: ese es el nombre del servidor, nombre de la base de datos, nombre del esquema (propietario) y nombre del procedimiento.
  • En la secuencia de comandos que crea cada procedimiento almacenado, especifique explícitamente qué roles se les permite ejecutar el procedimiento, por ejemplo, público o lo que sea.
  • Use sysmessage, sp_addmessage y placeholders en lugar de mensajes de error codificados.
  • Al usar sp_addmessage y sysmessages, utilice siempre el número de mensaje de error de 50001 o superior.
  • Con RAISERROR, siempre proporcione un nivel de gravedad < = 10 para los mensajes de advertencia.
  • Con RAISERROR, siempre proporcione un nivel de gravedad entre 11 y 16 para los mensajes de error.
  • Recuerde que el uso de RAISERROR no siempre cancela ningún lote en progreso, ni siquiera en el contexto del activador.
  • Guarde @@error en una variable local antes de usarlo o interrogarlo.
  • Guarde @@ rowcount en una variable local antes de usarlo o interrogarlo.
  • Para un procedimiento almacenado, use el valor de retorno para indicar solamente éxito/falla, no otra información adicional.
  • El valor de retorno para un procedimiento almacenado debe establecerse en 0 para indicar el éxito, distinto de cero para indicar la falla.
  • Establecer ANSI_WARNINGS ON - esto detecta valores nulos en cualquier asignación agregada, y cualquier asignación que exceda la longitud máxima de un carácter o columna binaria.
  • Configure NOCOUNT ON, por muchas razones.
  • Piense cuidadosamente si desea XACT_ABORT ON or OFF. De cualquier forma que vayas, sé consistente.
  • Salga en el primer error - esto implementa el modelo KISS.
  • Al ejecutar un procedimiento almacenado, compruebe siempre @@ error y el valor de retorno. Por ejemplo:

    EXEC @err = AnyStoredProc @value 
    SET @save_error = @@error 
    -- NULLIF says that if @err is 0, this is the same as null 
    -- COALESCE returns the first non-null value in its arguments 
    SELECT @err = COALESCE(NULLIF(@err, 0), @save_error) 
    IF @err <> 0 BEGIN 
        -- Because stored proc may have started a tran it didn't commit 
        ROLLBACK TRANSACTION 
        RETURN @err 
    END 
    
  • Cuando se ejecuta un procedimiento almacenado local que resulta en un error, realice una operación de deshacer porque es posible que el procedimiento ha iniciado una transacción que no tenía confirmación o retrotracción.
  • No asuma que solo porque no ha iniciado una transacción, no hay ninguna transacción activa: la persona que llama puede haber iniciado una.
  • Idealmente, evite realizar una reversión en una transacción iniciada por la persona que llama, así que marque @@ trancount.
  • Pero en un desencadenador, siempre retrotrae, ya que no sabe si la persona que llama inició una transacción activa (porque @@ trancount siempre es> = 1).
  • tienda siempre y comprobar @@ error después de las declaraciones siguientes:

    INSERT, DELETE, UPDATE 
    SELECT INTO 
    Invocation of stored procedures 
    invocation of dynamic SQL 
    COMMIT TRANSACTION 
    DECLARE and OPEN CURSOR 
    FETCH from cursor 
    WRITETEXT and UPDATETEXT 
    
  • Si DECLARE CURSOR falla en un cursor de proceso global (por defecto), emita una sentencia a desasignar el cursor.
  • Tenga cuidado con un error en una UDF. Cuando se produce un error en una UDF, la ejecución de la función se cancela inmediatamente y la consulta que invoca el error UDF - pero @@ es 0. Es posible que desee ejecutar con SET XACT_ABORT ON en estas circunstancias.
  • Si desea utilizar SQL dinámico, intente tener solo un SELECCIONAR en cada lote porque @@ error solo mantiene el estado del último comando ejecutado. Los errores más probables de un lote de SQL dinámico son los errores de sintaxis, y SET XACT_ABORT ON no se encarga de ellos.
+0

Buena lista larga. Gracias por la guía. – Shiham

+0

Con SQL 2012 en lugar de verificar el error @@ para decidir si es necesaria una reversión, puede usar XACT_STATE() http://technet.microsoft.com/en-us/library/ms189797.aspx – nojetlag

10
  1. Siempre usa SET NOCOUNT EN
  2. Si va a realizar dos o más inserciones/actualizaciones/eliminaciones, por favor utilice una transacción.
  3. Nunca nombre sus procs 'sp_'. SQL Server buscará primero en la base de datos maestra, no la encontrará, luego buscará en su base de datos. Si nombra sus procesos de manera diferente, SQL Server buscará primero en su base de datos.

malo:

SET NOCOUNT ON 
BEGIN TRAN 
    INSERT... 
    UPDATE... 
COMMIT 

mejor, pero se ve desordenado y un gran dolor de código:

SET NOCOUNT ON 
BEGIN TRAN 
    INSERT... 
    IF @ErrorVar <> 0 
    BEGIN 
     RAISERROR(N'Message', 16, 1) 
     GOTO QuitWithRollback 
    END 

    UPDATE... 
    IF @ErrorVar <> 0 
    BEGIN 
     RAISERROR(N'Message', 16, 1) 
     GOTO QuitWithRollback 
    END 

    EXECUTE @ReturnCode = some_proc @some_param = 123 
    IF (@@ERROR <> 0 OR @ReturnCode <> 0) 
     GOTO QuitWithRollback 
COMMIT 
GOTO EndSave    
QuitWithRollback: 
    IF (@@TRANCOUNT > 0) 
     ROLLBACK TRANSACTION 
EndSave: 

bueno:

SET NOCOUNT ON 
SET XACT_ABORT ON 
BEGIN TRY 
    BEGIN TRAN 
    INSERT... 
    UPDATE... 
    COMMIT 
END TRY 
BEGIN CATCH 
    IF (XACT_STATE()) <> 0 
     ROLLBACK 
END CATCH 

Mejor:

SET NOCOUNT ON 
SET XACT_ABORT ON 
BEGIN TRAN 
    INSERT... 
    UPDATE... 
COMMIT 

Entonces, ¿dónde está el manejo de errores en la "mejor" solución? No necesitas ninguno. Consulte SET XACT_ABORT ON, que significa realizar una reversión automática si hay algún error. El código es más limpio y fácil de leer, más fácil de escribir y menos problemático. Menos errores porque no hay posibilidad de perder una condición de error ya que SQL Server ahora hace esto por usted.

+0

Estimado Simon Hughes will Por favor, dé un ejemplo con una declaración de inserción de demostración de principio a fin de escribir un procedimiento – sidhewsar

1

Con SQL Server 2008 use la construcción TRY ... CATCH, que puede usar en sus procedimientos almacenados de T-SQL para proporcionar un mecanismo más elegante para el manejo de excepciones que el disponible en versiones anteriores de SQL Server marcando @@ ERROR (y a menudo el uso de instrucciones GOTO) después de cada instrucción de SQL.

  BEGIN TRY 
      one_or_more_sql_statements 
     END TRY 
     BEGIN CATCH 
      one_or_more_sql_statements 
     END CATCH 

Cuando en un bloque CATCH, se puede utilizar las siguientes funciones de error para capturar información acerca del error que invocó al bloque CATCH,

  ERROR_NUMBER() 
     ERROR_MESSAGE() 
     ERROR_SEVERITY() 
     ERROR_STATE() 
     ERROR_LINE() 
     ERROR_PROCEDURE() 

A diferencia @@ error, que es repuesto por cada declaración que se ejecuta, la información de error recuperada por las funciones de error permanece constante en cualquier lugar dentro del alcance del bloque CATCH de una instrucción TRY ... CATCH. Estas funciones podrían permitir modularizar el manejo de errores en un solo procedimiento para que no tenga que repetir el código de manejo de errores en cada bloque CATCH.

Cuestiones relacionadas