Crear procedimientos almacenados (motor de base de datos)

Puede crear procedimientos almacenados mediante la instrucción CREATE PROCEDURE de Transact-SQL.

Antes de crearlos, tenga en cuenta lo siguiente:

  • Las instrucciones CREATE PROCEDURE no se pueden combinar con otras instrucciones SQL en el mismo lote.

  • Para crear procedimientos, debe disponer del permiso CREATE PROCEDURE en la base de datos y del permiso ALTER en el esquema donde se crea el procedimiento. En procedimientos almacenados CLR, debe ser propietario del ensamblado al que se hace referencia en <method_specifier> o disponer del permiso REFERENCES en dicho ensamblado.

  • Los procedimientos almacenados son objetos de ámbito de esquema y sus nombres deben ajustarse a las reglas para los identificadores.

  • Sólo puede crear un procedimiento almacenado en la base de datos actual.

Cuando cree un procedimiento almacenado, deberá especificar lo siguiente:

  • Todos los parámetros de entrada y de salida del lote o del procedimiento que realiza la llamada.

  • Las instrucciones de programación que realicen operaciones en la base de datos, incluidas las llamadas a otros procedimientos.

  • El valor de estado devuelto al lote o al procedimiento que realiza la llamada, a fin de indicar que la operación se ha realizado correctamente o que se ha producido un error (y el motivo del mismo).

  • Las instrucciones de control de errores necesarias para detectar y administrar posibles errores.

    Las funciones de control de errores tales como ERROR_LINE y ERROR_PROCEDURE se pueden especificar en el procedimiento almacenado. Para obtener más información, vea Usar TRY...CATCH en Transact-SQL.

Asignar nombre a los procedimientos almacenados

Se recomienda que no cree procedimientos almacenados con el prefijo sp_. SQL Server utiliza el prefijo sp_ para indicar procedimientos almacenados del sistema. El nombre que elija puede entrar en conflicto con algún procedimiento futuro del sistema. Si la aplicación utiliza referencias de nombre completo que no son de esquema y el nombre de su procedimiento entra en conflicto con un procedimiento del sistema, la aplicación generará un error ya que el nombre se enlaza con el procedimiento del sistema, no con el suyo.

Un procedimiento almacenado definido por el usuario, con el mismo nombre que un procedimiento almacenado del sistema y que no esté calificado o se encuentre en el esquema dbo no se ejecutará nunca; siempre se ejecutará el procedimiento almacenado del sistema. En el ejemplo siguiente se muestra este comportamiento.

USE AdventureWorks2008R2;
GO
CREATE PROCEDURE dbo.sp_who
AS
    SELECT FirstName, LastName FROM Person.Person;
GO
EXEC sp_who;
EXEC dbo.sp_who;
GO
DROP PROCEDURE dbo.sp_who;
GO

Si se utiliza un calificador de esquema explícito también se consigue una ligera mejora del rendimiento. La resolución de nombres es un poco más rápida si Motor de base de datos no tiene que buscar en varios esquemas para encontrar el procedimiento. Para obtener más información, vea Ejecutar procedimientos almacenados (motor de base de datos).

Procedimientos almacenados temporales

De forma similar a las tablas temporales, los procedimientos almacenados temporales (tanto privados como globales) se pueden crear agregando los prefijos # y ## delante del nombre del procedimiento. # denota un procedimiento almacenado temporal local; ## denota un procedimiento almacenado temporal global. Estos procedimientos dejan de existir cuando se cierra SQL Server.

Los procedimientos almacenados temporales son útiles para conectarse a versiones anteriores de SQL Server que no permiten volver a utilizar los planes de ejecución para instrucciones o lotes de Transact-SQL. Las aplicaciones que se conecten a SQL Server 2000 y versiones posteriores deben utilizar el procedimiento almacenado del sistema sp_executesql, en vez de procedimientos almacenados temporales. Sólo puede ejecutar un procedimiento temporal local la conexión que lo creó; el procedimiento se elimina automáticamente cuando se cierra la conexión.

Cualquier conexión puede ejecutar un procedimiento almacenado temporal global. Éste existe hasta que se cierra la conexión que el usuario utilizó para crearlo, y hasta que se completan todas las versiones del procedimiento que se estuvieran ejecutando mediante otras conexiones. Una vez cerrada la conexión que se utilizó para crear el procedimiento almacenado temporal, éste ya no se puede volver a ejecutar. Sólo podrán finalizar las conexiones que hayan empezado a ejecutar el procedimiento almacenado.

Si se crea directamente en la base de datos tempdb un procedimiento almacenado sin el prefijo # o ##, el procedimiento en cuestión se eliminará automáticamente cuando se cierre SQL Server, ya que tempdb se vuelve a crear cada vez que se inicia SQL Server. Los procedimientos creados directamente en tempdb existen incluso después de haber finalizado la conexión en que se crearon.

Nota

Utilizar excesivamente los procedimientos almacenados temporales puede causar conflictos en las tablas del sistema de tempdb y afectar de forma negativa al rendimiento. Se recomienda que utilice en su lugar sp_executesql. El procedimiento sp_executesql no almacena datos en las tablas del sistema y, por tanto, evita el problema.

Los procedimientos almacenados CLR no se pueden crear como procedimientos almacenados temporales.

Ejemplos

A. Utilizar un procedimiento sencillo con una instrucción SELECT compleja

El siguiente procedimiento almacenado devuelve todos los empleados (nombre y apellidos), sus puestos y los nombres de sus departamentos a partir de una vista. Este procedimiento almacenado no utiliza ningún parámetro.

USE AdventureWorks2008R2;
GO
IF OBJECT_ID ( 'HumanResources.uspGetAllEmployees', 'P' ) IS NOT NULL 
    DROP PROCEDURE HumanResources.uspGetAllEmployees;
GO
CREATE PROCEDURE HumanResources.uspGetAllEmployees
AS
    SET NOCOUNT ON;
    SELECT LastName, FirstName, Department
    FROM HumanResources.vEmployeeDepartmentHistory;
GO

El procedimiento almacenado uspGetEmployees se puede ejecutar de estas formas:

    EXECUTE HumanResources.uspGetAllEmployees;
    GO
    -- Or
    EXEC HumanResources.uspGetAllEmployees;
    GO
    -- Or, if this procedure is the first statement within a batch:
    HumanResources.uspGetAllEmployees;

B. Utilizar un procedimiento simple con parámetros

El siguiente procedimiento almacenado sólo devuelve el empleado especificado (nombre y apellidos), su puesto y el nombre de su departamento a partir de una vista. Este procedimiento almacenado acepta coincidencias exactas de los parámetros pasados.

    USE AdventureWorks2008R2;
    GO
    IF OBJECT_ID ( 'HumanResources.uspGetEmployees', 'P' ) IS NOT NULL 
        DROP PROCEDURE HumanResources.uspGetEmployees;
    GO
    CREATE PROCEDURE HumanResources.uspGetEmployees 
        @LastName nvarchar(50), 
        @FirstName nvarchar(50) 
    AS 
    
        SET NOCOUNT ON;
        SELECT FirstName, LastName,Department
        FROM HumanResources.vEmployeeDepartmentHistory
        WHERE FirstName = @FirstName AND LastName = @LastName;
    GO

El procedimiento almacenado uspGetEmployees se puede ejecutar de estas formas:


    EXECUTE HumanResources.uspGetEmployees N'Ackerman', N'Pilar';
    -- Or
    EXEC HumanResources.uspGetEmployees @LastName = N'Ackerman', @FirstName = N'Pilar';
    GO
    -- Or
    EXECUTE HumanResources.uspGetEmployees @FirstName = N'Pilar', @LastName = N'Ackerman';
    GO
    -- Or, if this procedure is the first statement within a batch:
    HumanResources.uspGetEmployees N'Ackerman', N'Pilar';

C. Utilizar un procedimiento simple con parámetros comodín

El siguiente procedimiento almacenado sólo devuelve los empleados especificados (nombre y apellidos), sus puestos y los nombres de sus departamentos a partir de una vista. Este patrón de procedimiento almacenado coincide con los parámetros pasados o, si éstos no se proporcionan, utiliza los valores predeterminados (apellidos que comienzan por la letra D).

    USE AdventureWorks2008R2;
    GO
    IF OBJECT_ID ( 'HumanResources.uspGetEmployees2', 'P' ) IS NOT NULL 
        DROP PROCEDURE HumanResources.uspGetEmployees2;
    GO
    CREATE PROCEDURE HumanResources.uspGetEmployees2 
        @LastName nvarchar(50) = N'D%', 
        @FirstName nvarchar(50) = N'%'
    AS 
        SET NOCOUNT ON;
        SELECT FirstName, LastName, Department
        FROM HumanResources.vEmployeeDepartmentHistory
        WHERE FirstName LIKE @FirstName AND LastName LIKE @LastName;
    GO

El procedimiento almacenado uspGetEmployees2 se puede ejecutar en muchas combinaciones. Aquí se muestran sólo algunas combinaciones:

    EXECUTE HumanResources.uspGetEmployees2;
    -- Or
    EXECUTE HumanResources.uspGetEmployees2 N'Wi%';
    -- Or
    EXECUTE HumanResources.uspGetEmployees2 @FirstName = N'%';
    -- Or
    EXECUTE HumanResources.uspGetEmployees2 N'[CK]ars[OE]n';
    -- Or
    EXECUTE HumanResources.uspGetEmployees2 N'Hesse', N'Stefen';
    -- Or
    EXECUTE HumanResources.uspGetEmployees2 N'H%', N'S%';

D. Utilizar parámetros OUTPUT

El siguiente ejemplo crea el procedimiento almacenado uspGetList, que devuelve una lista de productos con precios que no superan un importe especificado. El ejemplo muestra la utilización de varias instrucciones SELECT y varios parámetros OUTPUT. Los parámetros OUTPUT permiten a un procedimiento externo, un proceso por lotes o más de una instrucción Transact-SQL tener acceso a un conjunto de valores durante la ejecución del procedimiento.


    USE AdventureWorks2008R2;
    GO
    IF OBJECT_ID ( 'Production.uspGetList', 'P' ) IS NOT NULL 
        DROP PROCEDURE Production.uspGetList;
    GO
    CREATE PROCEDURE Production.uspGetList @Product varchar(40) 
        , @MaxPrice money 
        , @ComparePrice money OUTPUT
        , @ListPrice money OUT
    AS
        SET NOCOUNT ON;
        SELECT p.[Name] AS Product, p.ListPrice AS 'List Price'
        FROM Production.Product AS p
        JOIN Production.ProductSubcategory AS s 
          ON p.ProductSubcategoryID = s.ProductSubcategoryID
        WHERE s.[Name] LIKE @Product AND p.ListPrice < @MaxPrice;
    -- Populate the output variable @ListPprice.
    SET @ListPrice = (SELECT MAX(p.ListPrice)
            FROM Production.Product AS p
            JOIN  Production.ProductSubcategory AS s 
              ON p.ProductSubcategoryID = s.ProductSubcategoryID
            WHERE s.[Name] LIKE @Product AND p.ListPrice < @MaxPrice);
    -- Populate the output variable @compareprice.
    SET @ComparePrice = @MaxPrice;
    GO

Ejecute uspGetList para obtener una lista de los productos Adventure Works (Bikes) que cuestan menos de 700 $. Los parámetros OUTPUT @Cost y @ComparePrices se utilizan con el lenguaje de control de flujo para devolver un mensaje en la ventana Messages.

Nota

La variable OUTPUT debe definirse durante la creación del procedimiento, así como durante la utilización de la variable. El nombre del parámetro y de la variable no tienen por qué coincidir; sin embargo, el tipo de datos y la posición de los parámetros deben coincidir (a menos que se utilice @Listprice= variable).

    DECLARE @ComparePrice money, @Cost money 
    EXECUTE Production.uspGetList '%Bikes%', 700, 
        @ComparePrice OUT, 
        @Cost OUTPUT
    IF @Cost <= @ComparePrice 
    BEGIN
        PRINT 'These products can be purchased for less than 
        $'+RTRIM(CAST(@ComparePrice AS varchar(20)))+'.'
    END
    ELSE
        PRINT 'The prices for all products in this category exceed 
        $'+ RTRIM(CAST(@ComparePrice AS varchar(20)))+'.'

Este es el conjunto de resultados parciales:

Product                                            List Price
-------------------------------------------------- ------------------
Road-750 Black, 58                                 539.99
Mountain-500 Silver, 40                            564.99
Mountain-500 Silver, 42                            564.99
...
Road-750 Black, 48                                 539.99
Road-750 Black, 52                                 539.99

(14 row(s) affected)

These items can be purchased for less than $700.00.