SQL Server中存储过程的创建与调用指南

SQL Server中存储过程的创建与调用指南

本文还有配套的精品资源,点击获取

简介:SQL Server存储过程作为数据库中的可重用函数,有助于执行特定任务并提升性能、代码复用和安全性。本文将介绍如何创建和执行存储过程,并展示了通过Java和JSP与之交互的过程。详细步骤包括使用SQL语句创建存储过程、通过 EXEC 关键字调用存储过程以及在Java应用程序和JSP页面中利用JDBC和JSTL技术调用存储过程。掌握这些技术对于开发高效的数据库应用程序至关重要。

1. SQL Server存储过程概念

1.1 存储过程简介

SQL Server中的存储过程是一组为了完成特定功能的SQL语句集,存储在数据库中并编译后执行。它既可以包含无参数的简单操作,也能处理复杂的业务逻辑和数据操作。与单次执行的SQL语句相比,存储过程能够提供更高的执行效率和模块化设计,便于维护和重用。

1.2 存储过程的优势

存储过程有多个优势,包括: - 性能提升 :因为存储过程编译后在服务器端执行,减少了网络往返次数。 - 安全加固 :可以通过权限控制,限制对敏感数据的操作。 - 业务封装 :将业务逻辑封装在存储过程中,简化应用层的代码。 - 易于维护 :集中管理和调试,便于大型系统的维护。

1.3 应用场景分析

在实际应用中,存储过程常用于以下场景: - 复杂查询 :涉及大量数据和复杂运算的查询。 - 数据验证 :确保数据在写入数据库前符合预定义的规则。 - 批量操作 :执行批量插入、更新或删除操作。 - 数据库触发器 :当特定的数据库事件发生时自动执行。

接下来,我们将详细了解如何创建和调用存储过程,以及在不同开发场景下如何与存储过程互动。

2. 创建存储过程的SQL语法

2.1 基本的存储过程创建语句

2.1.1 定义存储过程的开始和结束

在SQL Server中,存储过程是一组为了完成特定功能的SQL语句集。创建存储过程的语法以关键字 CREATE PROCEDURE 开始,以 GO 或分号结束,以标记命令的结束。语法的基本结构如下:

CREATE PROCEDURE ProcedureName

@Param1 datatype,

@Param2 datatype OUTPUT,

...

AS

BEGIN

-- SQL语句和逻辑

END;

在这个结构中, ProcedureName 是你给存储过程命名的标识符, @Param1 , @Param2 是参数列表, datatype 为参数类型, OUTPUT 用于标记参数将被用来输出数据。 AS 关键字标志着存储过程主体的开始。 BEGIN 和 END 关键字用来界定存储过程的主体。存储过程主体可以包含多条SQL语句,这些语句可以进行数据的插入、更新、删除和查询等操作。

2.1.2 存储过程中的参数定义

参数允许在调用存储过程时传递数据,使得存储过程可以具有一定的灵活性。参数定义应明确指出参数的类型,这有助于SQL Server处理传入的数据。参数可以有以下几种:

IN : 默认参数类型,只可以传入数据。 OUT : 允许存储过程将数据传出给调用者。 INOUT : 既可以从调用者接收数据,也可以将数据传回给调用者。

以下是参数定义的示例:

CREATE PROCEDURE usp_GetProductInfo

@ProductID INT,

@ProductName NVARCHAR(50) OUTPUT,

@Price DECIMAL(10, 2)

AS

BEGIN

-- SQL逻辑,例如获取产品信息

SELECT @ProductName = Name, @Price = ListPrice

FROM Production.Product

WHERE ProductID = @ProductID;

END;

在这个例子中, usp_GetProductInfo 是一个名为存储过程,它接受一个整型的输入参数 @ProductID ,一个字符串类型的输出参数 @ProductName ,和一个十进制类型的输入参数 @Price 。

2.2 高级存储过程特性

2.2.1 带输出参数的存储过程

输出参数允许存储过程将一个或多个结果值传递回调用者。在创建存储过程时,可以使用 OUTPUT 关键字定义输出参数,如下所示:

CREATE PROCEDURE usp_GetCustomerDetails

@CustomerID INT,

@CustomerName NVARCHAR(100) OUTPUT,

@Address NVARCHAR(255) OUTPUT

AS

BEGIN

SELECT @CustomerName = Name, @Address = Address

FROM Sales.Customer

WHERE CustomerID = @CustomerID;

END;

在本例中, usp_GetCustomerDetails 存储过程接收一个客户ID作为输入,然后从 Customer 表中检索相应的客户名称和地址,并将它们作为输出参数返回。

2.2.2 存储过程中的变量和数据类型

存储过程中的变量是可以在存储过程内部使用的命名存储位置,用于保存数据值。使用 DECLARE 语句声明变量,并指定其数据类型,比如:

DECLARE @Counter INT;

SET @Counter = 0;

在声明变量之后,可以在存储过程内部对其进行赋值和操作。变量通常用于循环和条件语句,以控制逻辑流程。

2.2.3 条件语句和循环语句的使用

条件语句如 IF...ELSE ,循环语句如 WHILE ,在存储过程中可以实现复杂的逻辑控制。下面是一个条件语句的示例:

IF @Counter > 10

BEGIN

-- 执行某些操作

END;

ELSE

BEGIN

-- 执行其他操作

END;

循环语句可以用来重复执行一组操作,直到满足特定的条件。下面是 WHILE 循环的示例:

WHILE @Counter < 5

BEGIN

-- 执行循环体内的代码

SET @Counter = @Counter + 1;

END;

这些基本的流程控制语句为存储过程提供了强大的逻辑处理能力,使得开发者可以编写出高度动态和适应性很强的数据库代码。

在接下来的章节中,我们将探讨如何使用 EXEC 关键字调用存储过程,以及在Java和JSP中通过JDBC和JSTL技术调用存储过程,进而使得存储过程能够在企业应用中发挥其应有的作用。

3. 使用 EXEC 关键字调用存储过程

3.1 EXEC 语句的基本使用方法

3.1.1 直接调用存储过程

在SQL Server中, EXEC 或 EXECUTE 关键字用于执行存储过程。当调用一个存储过程时,SQL Server解析并执行存储过程内的SQL语句。例如,我们有一个名为 usp_GetSalesData 的存储过程,它的功能是获取销售数据。

EXEC usp_GetSalesData;

这个调用非常直接,不需要额外的参数。如果存储过程不需要任何参数,你可以像上面这样执行它。但是,大多数存储过程需要参数来执行特定的任务。

3.1.2 带参数的存储过程调用

当存储过程需要参数时,你需要在 EXEC 语句中指定这些参数。假设有一个存储过程 usp_GetEmployeeDetails 需要员工ID作为输入参数,并返回该员工的详细信息。

EXEC usp_GetEmployeeDetails @EmployeeID = 123;

在这个例子中, @EmployeeID 是参数名, 123 是传递给该参数的具体值。如果存储过程有多个参数,你可以在 EXEC 语句中继续添加它们。

EXEC usp_GetEmployeeDetails @EmployeeID = 123, @ShowAddress = 'Yes';

3.2 处理存储过程输出

3.2.1 接收输出参数的值

有时候,存储过程不仅执行任务,还会返回一些值,可能是结果集、输出参数或者是返回代码。输出参数可以在执行存储过程后接收值。

DECLARE @ReturnCode INT;

EXEC @ReturnCode = usp_SetEmployeeStatus @EmployeeID = 123, @NewStatus = 'Active';

SELECT @ReturnCode AS ReturnCode;

在这个例子中,我们声明了一个变量 @ReturnCode 来接收输出值。存储过程 usp_SetEmployeeStatus 执行后,任何指定的输出参数值都会被赋值给 @ReturnCode 变量。

3.2.2 处理返回的结果集

当存储过程返回结果集时,可以将其用于进一步的数据处理或报告目的。结果集可以在应用程序中使用,也可以直接在SQL查询中进行处理。

EXEC usp_GetProductsByCategory @CategoryID = 5;

GO

SELECT * FROM #TempTable;

在这个例子中,我们假设 usp_GetProductsByCategory 存储过程返回一个临时表 #TempTable 的结果集,然后我们可以使用 SELECT 语句对结果集进行查询。

为了完整地展示如何在应用程序中处理存储过程的调用和结果集,请考虑以下代码示例:

// Java 代码示例,假设使用JDBC连接SQL Server

try (Connection conn = DriverManager.getConnection(dbURL, dbUser, dbPass);

CallableStatement stmt = conn.prepareCall("{CALL usp_GetEmployeeDetails(?)}")) {

// 设置参数

stmt.setInt(1, 123);

// 执行存储过程

ResultSet rs = stmt.executeQuery();

// 处理结果集

while (rs.next()) {

int id = rs.getInt("EmployeeID");

String name = rs.getString("Name");

// 更多字段处理

}

}

在上述Java代码中,我们首先通过JDBC建立与数据库的连接。然后,使用 CallableStatement 对象准备调用存储过程 usp_GetEmployeeDetails ,并传递所需的参数。执行查询后,我们处理结果集 ResultSet ,获取所需的数据。

在这一章节中,我们了解了如何使用 EXEC 关键字来调用存储过程,并处理来自存储过程的输出参数和结果集。这为后续章节中介绍存储过程在不同编程环境中的应用打下了基础。接下来的章节中,我们将探索在Java应用和JSP中如何通过不同的方式调用和利用存储过程的强大功能。

4. Java应用中通过JDBC调用存储过程

4.1 JDBC调用存储过程的基本步骤

4.1.1 加载和注册JDBC驱动

在Java中使用JDBC调用存储过程之前,首先需要确保已经加载并注册了相应的JDBC驱动。这一步是与数据库建立连接的前提条件。通常,可以通过以下代码来实现:

Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");

上述代码使用 Class.forName 方法动态加载了SQL Server JDBC驱动。这个方法会尝试加载指定的驱动类,如果加载成功,则返回该类的一个 Class 对象。驱动类名应与实际使用的数据库类型和版本相匹配。加载驱动后,驱动程序的 static 初始化块会被执行,该初始化块中通常包含了驱动程序的注册代码。

4.1.2 建立数据库连接

一旦驱动被加载和注册,接下来就是创建一个数据库连接对象。对于SQL Server,可以使用以下代码:

String url = "jdbc:sqlserver://localhost:1433;databaseName=YourDatabase;user=YourUsername;password=YourPassword";

Connection conn = DriverManager.getConnection(url);

DriverManager.getConnection 方法接受一个数据库连接字符串(URL),它包括协议(jdbc:sqlserver)、服务器地址、端口号、数据库名以及用户名和密码。URL的格式对于不同的数据库系统可能有所不同,需要根据实际的数据库类型和配置进行调整。

4.2 JDBC调用存储过程的高级技巧

4.2.1 使用 CallableStatement 接口

为了调用存储过程,可以使用 CallableStatement 接口,它是 PreparedStatement 的一个子接口,专门用于处理存储过程调用。以下是使用 CallableStatement 调用存储过程的示例代码:

String procedureName = "{call YourStoredProcedure()}";

CallableStatement cs = conn.prepareCall(procedureName);

在该代码块中, prepareCall 方法接受一个表示存储过程调用的字符串。格式为 {call 存储过程名称(参数)} 。这里使用了一个占位符 ? ,它表示存储过程中可能需要的参数。

4.2.2 处理存储过程的输入输出参数

存储过程可能需要输入参数,也可能返回输出参数和结果集。处理这些参数可以通过设置参数值和获取结果来完成。以下示例代码演示了如何处理输入输出参数:

// 设置输入参数

cs.setString(1, "inputValue");

// 设置输出参数

cs.registerOutParameter(2, Types.INTEGER);

// 执行存储过程

cs.execute();

// 获取输出参数的值

int outputValue = cs.getInt(2);

在上述代码中, setString 用于设置输入参数的值。通过指定参数的索引(从1开始)来设置。类似地, registerOutParameter 方法用于注册输出参数的类型和数据类型。使用 execute 方法来执行存储过程,最后通过 getInt 方法来获取输出参数的值。对于不同的数据类型,可以使用不同的方法,例如 getDouble 、 getString 等。

4.2.3 处理存储过程的异常情况

在调用存储过程时,可能会遇到各种异常情况。为了确保程序的健壮性,需要捕获并处理这些异常。以下是如何处理 SQLException 的示例:

try {

// 上述代码部分

} catch (SQLException e) {

// 处理SQL异常

e.printStackTrace();

// 根据错误代码进行自定义错误处理

}

异常处理机制不仅可以记录错误信息,还可以帮助开发者对特定的SQL错误进行详细的诊断和处理。例如,可以利用异常对象中的错误码来判断是哪种类型的SQL错误,并据此采取不同的处理措施。

至此,我们已经完成了JDBC调用存储过程的基本步骤和高级技巧的介绍,为Java应用与数据库交互提供了技术基础。接下来,我们将探讨在JSP页面中如何通过JSTL和EL调用存储过程。

5. JSP中通过JSTL和EL调用存储过程

5.1 JSP和存储过程交互的必要配置

在JSP中通过JSTL和EL调用存储过程,首先需要进行一些必要的配置,以确保应用程序能够成功地与数据库进行交互。

5.1.1 配置数据源

在使用JSP进行数据库操作之前,需要配置数据源。这通常涉及到在服务器上(如Tomcat)的 context.xml 文件中定义一个数据源。例如,在Tomcat中配置一个MySQL数据源可以如下操作:

auth="Container"

type="javax.sql.DataSource"

maxActive="100"

maxIdle="30"

maxWait="10000"

username="your_username"

password="your_password"

driverClassName="com.mysql.jdbc.Driver"

url="jdbc:mysql://localhost:3306/your_database"/>

5.1.2 JSTL标签库的使用

为了在JSP页面中使用JSTL标签,首先需要在JSP页面的顶部引入JSTL核心库和格式库。可以通过以下代码实现:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

引入JSTL库后,你就可以使用 等标签来实现逻辑控制和数据迭代。

5.2 实现存储过程调用的JSP代码

在配置好数据源和引入JSTL库之后,就可以在JSP页面中编写代码来调用存储过程了。

5.2.1 在JSP中使用 等标签

一旦配置了数据源并且JSTL标签可用,你可以通过JDBC的 CallableStatement 接口来调用存储过程,并使用JSTL标签来展示结果。以下是一个示例:

<%@ page import="java.sql.*" %>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%

Connection conn = null;

CallableStatement cs = null;

try {

// 加载JDBC驱动

Class.forName("com.mysql.jdbc.Driver");

// 建立数据库连接

conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/your_database", "your_username", "your_password");

// 调用存储过程

cs = conn.prepareCall("{call your_stored_procedure(?, ?)}");

cs.setString(1, "inputValue");

cs.registerOutParameter(2, Types.VARCHAR);

cs.execute();

String result = cs.getString(2);

out.println("

Result from stored procedure: " + result + "

");

// 使用遍历结果集

${item}

} catch (Exception e) {

e.printStackTrace();

} finally {

// 关闭资源

try { if (cs != null) cs.close(); } catch (SQLException e) { e.printStackTrace(); }

try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); }

}

%>

5.2.2 利用EL表达式访问存储过程结果

通过EL表达式,可以在JSP页面中简化对数据的访问,从而无需在JSP页面中编写大量的Java代码。以下是一个示例,假设我们有一个名为 yourStoredProcedureResult 的属性存储了存储过程的结果:

Stored Procedure Result: ${yourStoredProcedureResult}

此外,如果存储过程返回一个结果集,可以使用 标签来遍历结果集,而每个结果项可以通过EL表达式来访问:

${row.columnName}

请注意,EL表达式访问的对象需要是在页面作用域中的对象,通常是通过 pageContext 对象设置的属性。

在展示完存储过程的结果之后,需要确保正确地关闭数据库资源,避免潜在的内存泄漏或资源占用问题。以上代码段展示了如何安全地处理数据库连接以及 CallableStatement 。

6. 提高代码可维护性和性能的方法

在IT行业中,随着应用规模的增大,代码的可维护性和性能优化成为了开发过程中的重要考量因素。对于使用SQL Server存储过程的开发者来说,如何在保证业务逻辑实现的同时,提高代码的可维护性和性能,是一个值得深入探讨的话题。本章将从代码重构和性能优化两个方面入手,详细讨论实现这一目标的具体方法。

6.1 代码重构技巧

在软件开发领域,重构是一个持续的过程,目的是改进代码的内部结构,同时不改变其外部行为。在存储过程中应用重构技巧,可以帮助我们保持代码的整洁性和可理解性,从而降低维护成本。

6.1.1 重用存储过程的策略

为了提高代码的重用性,我们可以采取以下策略:

创建通用存储过程模块 :设计一些通用的存储过程模块,比如数据验证、分页查询等,可以在多个存储过程中重复使用。

使用存储过程模板 :创建模板化的存储过程,预留接口供其他存储过程调用,这样可以减少重复的代码编写。

模块化设计 :将大型存储过程拆分成多个小的子存储过程,每个子存储过程完成一个特定功能。这样不仅便于维护,还方便了团队协作。

代码示例:

CREATE PROCEDURE [dbo].[uspGetData]

@Param1 INT,

@Param2 VARCHAR(255)

AS

BEGIN

-- 调用通用模块

EXEC [dbo].[uspCommonValidation] @Param1 = @Param1;

-- 调用子存储过程

EXEC [dbo].[uspGetSubData] @Param1 = @Param1, @Param2 = @Param2;

END;

在上述代码中, uspCommonValidation 和 uspGetSubData 是其他存储过程中可以重用的模块。

6.1.2 管理存储过程版本的建议

随着业务的发展,存储过程也需要不断地更新和维护。因此,管理好存储过程的版本就显得尤为重要:

使用版本控制 :对存储过程进行版本控制,比如使用数据库对象的版本号或者文档控制系统。

脚本化存储过程变更 :每次对存储过程进行修改时,都应该记录详细的变更日志,并保存相应的脚本。

定期回顾和审查 :定期回顾存储过程的使用情况和性能指标,必要时进行重构和优化。

6.2 性能优化

性能优化是提高软件效率的重要手段。对于存储过程来说,可以从多个方面进行性能优化,比如分析执行计划,或者使用索引。

6.2.1 分析和优化存储过程的执行计划

SQL Server提供了Query Analyzer工具,可以用来分析存储过程的执行计划。通过这个工具,我们可以找到查询慢的瓶颈并进行优化:

查看执行计划 :使用Query Analyzer查看执行计划,找出执行时间较长的操作。

优化索引 :根据执行计划中的信息,对表进行合理的索引设计。

查询优化 :重构查询语句,减少不必要的全表扫描,使用合适的连接类型等。

代码示例:

-- 查询语句示例

SELECT *

FROM Customers c

INNER JOIN Orders o ON c.CustomerID = o.CustomerID

WHERE c.City = 'Seattle';

在实际应用中,我们会结合执行计划的结果来优化上述查询。

6.2.2 利用索引提高存储过程性能

索引对于提高查询性能至关重要。我们需要根据查询模式创建合适的索引:

理解数据访问模式 :分析应用程序如何访问数据,以此来确定应该建立哪些索引。

创建复合索引 :如果存储过程中的查询常常涉及到多个列,可以考虑创建复合索引。

定期维护索引 :随着数据的增加和变更,索引的性能也会下降,需要定期进行索引的重建和重组。

-- 创建复合索引的示例

CREATE INDEX idx_customer_city ON Customers(City, CustomerID);

在上面的示例中, idx_customer_city 是一个复合索引,用于加速针对 City 和 CustomerID 列的查询。

通过上述各章节的详细讨论,我们提供了在SQL Server存储过程中实现代码重构和性能优化的方法。这样的实践不仅能够提升存储过程的可维护性,还能显著提高它们的性能。在实际操作中,开发者需要根据具体业务和数据库环境,灵活地应用这些策略。

7. 数据库交互的简化

随着应用程序的复杂性增加,与数据库的交互变得越来越繁琐。为了提高开发效率和降低维护难度,我们应着手简化数据库交互。一个有效的方法是使用存储过程来封装复杂的业务逻辑,另一个有效的方法是利用对象关系映射(ORM)框架。

7.1 简化数据库交互的策略

在现代应用开发中,我们面对的主要挑战之一是管理日益增长的代码量和逻辑复杂性。我们通过以下策略来简化数据库交互。

7.1.1 使用ORM框架减少代码量

ORM(对象关系映射)框架如Hibernate或Entity Framework,可以自动处理数据持久化,从而减少手动编写SQL语句的数量。利用ORM,可以将数据库表映射为应用中的类和对象,通过操作这些对象来间接与数据库交互。这种方式不仅减少了代码量,还提高了代码的可读性和可维护性。

// 示例代码:使用Hibernate框架进行对象持久化

Session session = sessionFactory.openSession();

Transaction transaction = session.beginTransaction();

User user = new User();

user.setName("John Doe");

user.setEmail("john.doe@example.com");

session.save(user);

transaction.commit();

session.close();

在上述示例中,我们创建了一个 User 对象并将其保存到数据库中,而无需编写任何SQL语句。Hibernate框架负责生成必要的SQL并执行数据库操作。

7.1.2 利用存储过程封装业务逻辑

存储过程允许我们定义一系列操作,这些操作封装在数据库服务器上,并通过一个过程名调用。这样做可以将业务逻辑保留在数据库层面,简化应用层代码。

CREATE PROCEDURE InsertUser

@name NVARCHAR(100),

@email NVARCHAR(100)

AS

BEGIN

INSERT INTO Users (Name, Email) VALUES (@name, @email);

END

在这个存储过程示例中,我们封装了一个插入用户的操作。客户端应用可以简单地调用 EXEC InsertUser 'John Doe', 'john.doe@example.com'; 来执行插入操作,无需知道底层实现细节。

7.2 存储过程与应用程序的协同工作

存储过程与应用程序协同工作的关键在于合理分工,保证存储过程专注于数据处理,而应用程序专注于业务逻辑。

7.2.1 分层架构中的存储过程角色

在分层架构中,存储过程通常位于数据访问层(DAL),负责执行所有的数据访问操作。它们作为数据访问点,使得业务层(BLL)可以不必关心数据如何被检索和修改。

flowchart LR

subgraph "分层架构"

direction TB

DAL["数据访问层 (DAL)"] -->|调用| SP["存储过程"]

BLL["业务层 (BLL)"] -->|请求| DAL

UI["用户界面 (UI)"] -->|交互| BLL

end

如上图所示,用户界面(UI)通过业务层(BLL)与数据访问层(DAL)交互。BLL将对数据操作的请求传递给DAL,DAL通过调用存储过程(SP)来完成这些操作。

7.2.2 存储过程与业务层、数据访问层的交互

在分层架构中,存储过程与业务层、数据访问层的交互应该清晰定义。开发人员需要确保存储过程的接口既清晰又灵活,以适应上层业务逻辑的变化。

// 示例代码:业务层调用数据访问层,进而调用存储过程

public class UserService {

private UserDao userDao;

public UserService(UserDao userDao) {

this.userDao = userDao;

}

public void addUser(String name, String email) {

userDao.insertUser(name, email);

}

}

// UserDaoImpl 实现了 UserDao 接口

public class UserDaoImpl implements UserDao {

@Override

public void insertUser(String name, String email) {

try (Connection connection = getConnection();

PreparedStatement statement = connection.prepareStatement("EXEC InsertUser ?, ?")) {

statement.setString(1, name);

statement.setString(2, email);

statement.executeUpdate();

} catch (SQLException e) {

// 异常处理逻辑

e.printStackTrace();

}

}

}

在这个例子中,业务层(UserService)通过数据访问层(UserDao)调用存储过程(InsertUser)。数据访问层负责与数据库交互的细节,使得业务层可以独立于数据存储的实现。

通过将业务逻辑从应用程序代码中分离出来,使用存储过程和ORM框架,我们可以有效地简化数据库交互。这种策略不但减少了代码量,还提高了应用程序的可维护性和扩展性。随着应用的发展,我们可以轻松地管理和优化存储过程,同时保持应用层代码的整洁。

本文还有配套的精品资源,点击获取

简介:SQL Server存储过程作为数据库中的可重用函数,有助于执行特定任务并提升性能、代码复用和安全性。本文将介绍如何创建和执行存储过程,并展示了通过Java和JSP与之交互的过程。详细步骤包括使用SQL语句创建存储过程、通过 EXEC 关键字调用存储过程以及在Java应用程序和JSP页面中利用JDBC和JSTL技术调用存储过程。掌握这些技术对于开发高效的数据库应用程序至关重要。

本文还有配套的精品资源,点击获取

相关推荐

还原 Beats
beat365平台正版

还原 Beats

🗓️ 07-21 👁️ 3027
薛之谦的歌为什么免费知乎(为什么薛之谦的歌免费?)
琼 - 人物详情
365bet盘口

琼 - 人物详情

🗓️ 10-12 👁️ 1502

友情链接