跳至主要内容

SQL 存储过程:自动化并优化查询

了解 SQL 存储过程基础,以及如何在不同数据库(包括 MySQL 与 SQL Server)中实现。
· 9分钟 阅读

使用 AI 探索

SQL 存储过程是一组保存在数据库中的 SQL 语句。它们可按需执行以完成数据操作与校验任务,减少为常见操作反复编写 SQL 代码的需要。存储过程通过提升效率与可复用性,有助于数据库管理。此外,它们还能增强数据库的安全性与可维护性。本文将讨论如何创建与执行 SQL 存储过程、常见使用场景以及最佳实践。

在开始之前,我强烈推荐学习 DataCamp 的 Introduction to SQL 和 Learn SQL 课程,掌握使用 SQL 提取与分析数据的基础知识。此外,可下载的 SQL Basics Cheat Sheet 也很实用,因为它涵盖了最常用的 SQL 函数。

什么是 SQL 中的存储过程?

SQL 中的存储过程是保存在数据库内的一组 SQL 语句。其目的是在数据库上执行一系列操作,例如查询、插入、更新或删除数据。

与作为单独命令执行的常规 SQL 查询不同,存储过程将一组 SQL 语句封装起来,便于复用代码,而无需反复编写 SQL 命令。

SQL 存储过程的优势包括:

  • 代码可复用: 一旦创建了存储过程,便可按需多次调用,消除 SQL 代码的冗余。
  • 性能提升: 存储过程通常执行更快,因为它们在数据库服务器上预编译并存储,降低了网络延迟与编译时间。
  • 安全性: 通过授予用户执行存储过程的权限而非直接访问表,存储过程可提升数据安全性并控制对敏感数据的访问。

基本语法与结构

创建存储过程的语法会因数据库系统(如 MySQL、SQL Server、Oracle)略有差异。以下以 SQL Server 语法为例:

-- Create a stored procedure named ProcedureName
CREATE PROCEDURE ProcedureName 
    @Parameter1 INT, 
    @Parameter2 VARCHAR(50)  
AS
BEGIN
    -- SQL statements go here
    SELECT * FROM TableName WHERE Column1 = @Parameter1 AND Column2 = @Parameter2;
END;

在上述语法中:

  • CREATE PROCEDURE: 用于定义新的存储过程的命令。

  • ProcedureName: 存储过程的名称,应在数据库内保持唯一。

  • @Parameter1、@Parameter2: 参数是可选的;它们允许过程接收输入数据。每个参数以 @ 符号和数据类型(如 INT、VARCHAR(50))定义。

  • AS BEGIN...END: BEGIN 与 END 之间的 SQL 语句构成过程主体,执行主要逻辑。本例中,过程根据特定条件从表中检索记录。

输入与输出参数

输入与输出参数允许您向存储过程传入值并从中取回值。

例如,若 @Parameter1 被定义为输入参数,则在调用过程时可为其赋予任意值,从而影响 SQL 逻辑或输出。下例中,@UserID 参数会检索与所提供 UserID 对应的特定数据。

-- Create a procedure to retrieve data for a specific user by UserID
CREATE PROCEDURE GetUserData 
 -- Input parameter: ID of the user to retrieve
    @UserID INT 
AS
BEGIN
    -- Select all columns from Users where UserID matches the input parameter
    SELECT * FROM Users WHERE UserID = @UserID;
END;

通过 OUTPUT 关键字定义的输出参数,允许存储过程将一个值返回给调用环境。例如,若过程计算折扣,可使用输出参数将结果回传给调用程序。

-- Create a procedure to calculate the discounted price
CREATE PROCEDURE CalculateDiscount 
    @Price DECIMAL(10, 2), 
    @DiscountRate DECIMAL(5, 2),  
    @FinalPrice DECIMAL(10, 2) OUTPUT -- Output: final price after discount
AS
BEGIN
    -- Calculate final price by applying the discount rate to the original price
    SET @FinalPrice = @Price * (1 - @DiscountRate);
END;

调用该过程的方式如下:

-- Declare a variable to store the final price after discount
DECLARE @FinalPrice DECIMAL(10, 2);
-- Execute the CalculateDiscount procedure with a price of 100 and a 10% discount 
-- Store the output in the @FinalPrice variable
EXEC CalculateDiscount @Price = 100, @DiscountRate = 0.1, @FinalPrice = @FinalPrice OUTPUT;
-- Select and display the final discounted price
SELECT @FinalPrice AS FinalPrice;

MySQL 中的存储过程

正如前文所述,执行 SQL 存储过程的方式会随数据库系统与所用工具而异。

在 MySQL 中创建存储过程

在 MySQL 中创建存储过程,需要定义过程名称、参数以及构成其主体的 SQL 语句。下面的示例创建了名为 GetEmployeeDetails 的过程,接收 EmployeeID 作为输入参数,并检索该员工的详细信息。

DELIMITER $
-- Create a procedure to retrieve details for a specific employee by EmployeeID
CREATE PROCEDURE GetEmployeeDetails(IN EmployeeID INT)  
BEGIN  
    -- Select all columns from Employees where EmployeeID matches the input parameter  
    SELECT * FROM Employees WHERE EmployeeID = EmployeeID;  
END$
DELIMITER ;

在 MySQL 中执行存储过程

最常见的执行存储过程方式是使用 SQL 命令。在 MySQL 中,我们使用 CALL 命令来执行存储过程。

CALL ProcedureName();

使用已定义的 GetEmployeeDetails 过程,执行语句如下:

-- Execute the stored procedure to retrieve details for EmployeeID 101
CALL GetEmployeeDetails(101);

SQL Server 中的存储过程

SQL Server 提供了用于创建、执行和管理存储过程的特定语法与命令。这种方式便于构建高效、可复用的 SQL 例程,以最小的重复处理复杂任务。

在 SQL Server 中创建存储过程

在 SQL Server 中创建存储过程,需要定义过程名称、参数以及构成其主体的 SQL 语句。下例创建了名为 GetEmployeeDetails 的过程,接收 @EmployeeID 作为输入参数,并检索该员工的详细信息。

-- Create a procedure to retrieve details for a specific employee by EmployeeID
CREATE PROCEDURE GetEmployeeDetails
    @EmployeeID INT          -- Input parameter: ID of the employee to retrieve
AS
BEGIN
    -- Select all columns from Employees where EmployeeID matches the input parameter
    SELECT * FROM Employees WHERE EmployeeID = @EmployeeID;
END;

在 SQL Server 中执行存储过程

在 SQL Server 中,使用 EXEC 或 EXECUTE 命令调用存储过程。以下示例展示了如何使用特定输入参数执行 GetEmployeeDetails 存储过程。

-- Execute the GetEmployeeDetails procedure with the EmployeeIDset to 102
EXEC GetEmployeeDetails @EmployeeID = 102;

您也可以通过在命令中声明变量来接收输出参数。下面的示例中,@TotalSales 被声明为变量,用于接收 CalculateTotalSales 的输出。

-- Declare a variable to store the total sales amount
DECLARE @TotalSales DECIMAL(10, 2);
-- Execute CalculateTotalSales for SalespersonID 5, store the result in @TotalSales
EXEC CalculateTotalSales @SalespersonID = 5, @TotalSales = @TotalSales OUTPUT;
-- Display the total sales amount
SELECT @TotalSales AS TotalSales;

我建议学习我们的 Introduction to SQL Server 课程,了解 SQL Server 在查询数据方面的不同功能。此外,可以考虑完整的 SQL Server Developer 职业路径,它不仅能帮助您掌握创建、更新与执行存储过程的技能,还将涵盖聚合函数、连接、插入与删除表等诸多内容。

存储过程的常见用途

在需要反复执行复杂任务的场景中,SQL 存储过程非常有用。以下是存储过程在数据管理与业务运营中的实际应用。

数据验证与完整性约束

存储过程可在更新或插入前验证数据。下例中,存储过程在向 Customers 表插入新记录前,检查客户邮箱是否唯一,以确保数据一致性。这将验证逻辑集中在数据库内,减少冗余,并在不同应用间统一执行。

-- Create a procedure to add a new customer, checking for duplicate email
CREATE PROCEDURE AddCustomer
    @CustomerName VARCHAR(50), 
    @CustomerEmail VARCHAR(50)
AS
BEGIN
    -- Check if the email already exists in the Customers table
    IF EXISTS (SELECT 1 FROM Customers WHERE Email = @CustomerEmail)
        -- Throw an error if the email is already in use
        THROW 50000, 'Email already exists.', 1;
    ELSE
        -- Insert new customer details if email is unique
        INSERT INTO Customers (Name, Email) VALUES (@CustomerName, @CustomerEmail);
END;

自动化数据处理与报表

您还可以使用存储过程生成定期报表或处理大型数据集。例如,存储过程可以汇总电商平台的每日销售数据并写入报表表,使团队无需运行复杂查询即可获取销售洞察。

-- Create a procedure to generate a daily sales report
CREATE PROCEDURE GenerateDailySalesReport
AS
BEGIN
    -- Insert today's date and total sales into the SalesReport table
    INSERT INTO SalesReport (ReportDate, TotalSales)
    
    -- Select current date and sum of sales for today from Sales table
    SELECT CAST(GETDATE() AS DATE), SUM(SalesAmount) 
    FROM Sales 
    WHERE SaleDate = CAST(GETDATE() AS DATE);
END;

事务管理

通过使用存储过程,您可以确保多项操作作为单个事务执行。例如,在银行系统中,存储过程可同时处理转账中的借记与贷记操作,确保两者要么同时成功、要么同时失败。

-- Create a procedure to transfer funds between accounts
CREATE PROCEDURE TransferFunds
    @SenderAccount INT,
    @ReceiverAccount INT,
    @Amount DECIMAL(10, 2)
AS
BEGIN
    BEGIN TRANSACTION;  -- Start a transaction to ensure atomicity
    -- Deduct the specified amount from the sender's account balance
    UPDATE Accounts SET Balance = Balance - @Amount WHERE AccountID = @SenderAccount;
    -- Add the specified amount to the receiver's account balance
    UPDATE Accounts SET Balance = Balance + @Amount WHERE AccountID = @ReceiverAccount;
    -- Check for errors and rollback if any occurred; otherwise, commit the transaction
    IF @@ERROR <> 0
        ROLLBACK TRANSACTION;  -- Undo all changes if an error occurred
    ELSE
        COMMIT TRANSACTION;    -- Confirm changes if no errors
END;

访问控制与数据安全

您也可以使用 SQL 存储过程来控制对敏感信息的数据访问。例如,存储过程可限制直接表访问,仅允许用户调用过程以检索相关字段(如账户余额),而不暴露交易明细。

-- Create a procedure to retrieve account balance, with authorization check
CREATE PROCEDURE GetAccountBalance
    @AccountID INT, 
    @UserID INT 
AS
BEGIN
    -- Check if the account exists and is owned by the specified user
    IF EXISTS (SELECT 1 FROM Accounts WHERE AccountID = @AccountID AND UserID = @UserID)
        -- If authorized, select and return the account balance
        SELECT Balance FROM Accounts WHERE AccountID = @AccountID;
    ELSE
        -- If unauthorized, throw an error
        THROW 50000, 'Unauthorized access.', 1;
END;

数据迁移与 ETL 流程

存储过程也可用于系统间的数据加载、转换与迁移。存储过程可以自动化地从源数据库提取数据、按需转换,并插入目标表,简化用于报表或分析的数据集成。

CREATE PROCEDURE ETLProcess
AS
BEGIN
    -- Extract
    INSERT INTO StagingTable
    SELECT * FROM SourceTable WHERE Condition;
    -- Transform
    UPDATE StagingTable SET ColumnX = TransformationLogic(ColumnX);
    -- Load
    INSERT INTO TargetTable
    SELECT * FROM StagingTable;
END;

存储过程的最佳实践

编写高效且易维护的存储过程,可确保数据库获得最佳性能。以下是为您的 SQL 数据库编写存储过程的建议。

  • 使用一致的命名规范: 为便于识别与理解,采用一致且具描述性的命名格式。同时,避免在 SQL Server 中使用为系统过程保留的 sp_ 前缀,以防潜在冲突与性能问题。

  • 实施错误处理: 使用 TRY...CATCH 块包裹 SQL 语句以捕获并处理错误,维护数据完整性。

  • 为性能优化: 尽量减少游标的使用,因为它们可能较慢且资源占用高。优先采用基于集合的操作,通常更高效。同时,为常用列建立索引,并避免在大型表上进行复杂连接,以降低内存开销并提升效率。

  • 参数化存储过程: 使用参数而非硬编码数值,以便向过程传递动态值,使其更灵活且可复用。

查看我们的 Intermediate SQL 课程,进一步学习使用聚合函数与连接来筛选数据。还可尝试我们的 SQL Server Fundamentals 与 SQL Fundamentals 技能路径,以提升表连接与数据分析能力。

结论

SQL 存储过程可提升代码复用与性能优化,助力数据库管理。存储过程还能通过受控访问与确保数据完整性来增强数据库安全性。作为数据从业者,建议您多练习创建与执行存储过程,以掌握更佳的数据库管理实践。

如果您希望成为熟练的数据分析师,欢迎查看我们的 Associate Data Analyst in SQL 职业路径,学习所需技能。若您想掌握如何用 SQL 构建专业仪表板,也可学习 Reporting in SQL 课程。最后,建议获取 SQL Associate Certification,以展示您使用 SQL 进行数据分析的能力,在众多数据从业者中脱颖而出。

Allan Ouko

作者

Allan Ouko

数据科学技术写作者,具备数据分析、商业智能和数据科学的一线实践经验。我撰写以行业为导向的实用内容,涵盖 SQL、Python、Power BI、Databricks 和数据工程,并以真实的分析工作为基础。我的写作兼顾技术深度与业务影响,帮助专业人士将数据转化为自信的决策。

存储过程常见问答

什么是 SQL 中的存储过程?

存储过程是一组执行特定任务的 SQL 语句,存储于数据库中以便复用。

存储过程与常规 SQL 查询有何不同?

与单条查询不同,存储过程是预编译的,并可包含流程控制语句、参数与错误处理,从而支持更复杂的操作。

存储过程中的输入参数与输出参数有何区别?

输入参数允许用户将数值传入过程;输出参数则将过程中的值返回给调用方。

存储过程是否与数据库相关联(特定于某数据库)?

存储过程依赖具体的 SQL 数据库管理系统(如 SQL Server、MySQL、Oracle),不同平台间语法有所差异。

主题