SQL Server 2019入门学习教程,从入门到精通,SQL Server 2019 事务和锁 — 语法知识点及使用方法详解(13)

SQL Server 2019 事务和锁 --- 语法知识点及使用方法详解


一、事务管理

1. 事务的原理

事务(Transaction) 是数据库操作的逻辑单元,具有 ACID 特性

  • 原子性(Atomicity):事务内所有操作要么全部成功,要么全部失败回滚。
  • 一致性(Consistency):事务执行前后,数据库从一个一致状态转移到另一个一致状态。
  • 隔离性(Isolation):并发事务互不干扰。
  • 持久性(Durability):事务提交后,结果永久保存。

SQL Server 使用 事务日志(Transaction Log) 记录所有修改,用于崩溃恢复和回滚。


二、事务管理的常用语句

1. 基本语法结构

sql 复制代码
BEGIN TRANSACTION [transaction_name | @tran_name_variable]
    -- SQL 语句组
COMMIT TRANSACTION [transaction_name | @tran_name_variable]
-- 或
ROLLBACK TRANSACTION [transaction_name | @tran_name_variable]

2. 保存点(Savepoint)

sql 复制代码
SAVE TRANSACTION savepoint_name
ROLLBACK TRANSACTION savepoint_name

案例1:基本事务操作 --- 银行转账

sql 复制代码
-- 创建测试表
CREATE TABLE dbo.Accounts (
    AccountID INT PRIMARY KEY,
    AccountName NVARCHAR(50),
    Balance DECIMAL(18,2)
);
GO

INSERT INTO dbo.Accounts VALUES 
(1, '张三', 1000.00),
(2, '李四', 500.00);
GO

-- 开始事务:从张三账户转300元到李四账户
BEGIN TRY
    BEGIN TRANSACTION TransferMoney;

    -- 1. 减少张三余额
    UPDATE dbo.Accounts 
    SET Balance = Balance - 300.00 
    WHERE AccountID = 1;

    -- 模拟异常:除零错误
    -- SELECT 1/0; -- 取消注释可测试回滚

    -- 2. 增加李四余额
    UPDATE dbo.Accounts 
    SET Balance = Balance + 300.00 
    WHERE AccountID = 2;

    -- 提交事务
    COMMIT TRANSACTION TransferMoney;
    PRINT '转账成功!';
END TRY
BEGIN CATCH
    -- 发生错误则回滚
    IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION TransferMoney;
    
    PRINT '转账失败,已回滚!错误信息:' + ERROR_MESSAGE();
END CATCH
GO

-- 查询结果
SELECT * FROM dbo.Accounts;
GO

注释

  • BEGIN TRANSACTION 标记事务开始。
  • COMMIT 提交事务,使修改永久生效。
  • ROLLBACK 回滚事务,撤销所有修改。
  • TRY...CATCH 捕获异常确保事务完整性。
  • @@TRANCOUNT 返回当前连接中活动事务数,用于判断是否需回滚。

案例2:使用保存点(部分回滚)

sql 复制代码
BEGIN TRANSACTION OrderProcess;

-- 步骤1:插入订单头
INSERT INTO dbo.Orders (OrderID, CustomerID, OrderDate) 
VALUES (1001, 201, GETDATE());
PRINT '订单头插入成功';

-- 设置保存点
SAVE TRANSACTION AfterHeader;

-- 步骤2:插入订单明细(可能失败)
BEGIN TRY
    INSERT INTO dbo.OrderDetails (OrderID, ProductID, Quantity, Price)
    VALUES (1001, 501, 2, 99.99);

    -- 模拟错误:插入不存在的产品ID(外键约束失败)
    INSERT INTO dbo.OrderDetails (OrderID, ProductID, Quantity, Price)
    VALUES (1001, 999, 1, 199.99); -- 假设 ProductID=999 不存在

    COMMIT TRANSACTION OrderProcess;
    PRINT '订单完整提交';
END TRY
BEGIN CATCH
    -- 仅回滚到保存点,保留订单头
    ROLLBACK TRANSACTION AfterHeader;
    PRINT '订单明细失败,回滚到保存点。订单头保留。错误:' + ERROR_MESSAGE();

    -- 可选择提交剩余部分或整体回滚
    COMMIT TRANSACTION OrderProcess; -- 提交订单头
    -- 或 ROLLBACK TRANSACTION OrderProcess; -- 完全回滚
END CATCH
GO

注释

  • SAVE TRANSACTION 创建保存点,允许部分回滚。
  • 适用于"部分操作可接受失败"的场景。
  • 最终仍需 COMMIT 或完全 ROLLBACK

三、事务的隔离级别

SQL Server 支持 5 种隔离级别,控制事务间可见性:

隔离级别 脏读 不可重复读 幻读 并发性
READ UNCOMMITTED 最高
READ COMMITTED(默认)
REPEATABLE READ
SERIALIZABLE 最低
SNAPSHOT 高(行版本控制)

✅ 表示可能发生,❌ 表示被阻止


设置隔离级别语法:

sql 复制代码
SET TRANSACTION ISOLATION LEVEL { 
    READ UNCOMMITTED 
  | READ COMMITTED 
  | REPEATABLE READ 
  | SNAPSHOT 
  | SERIALIZABLE 
}

案例3:演示不同隔离级别的行为

sql 复制代码
-- 准备测试数据
CREATE TABLE dbo.Inventory (
    ProductID INT PRIMARY KEY,
    ProductName NVARCHAR(50),
    Stock INT
);
GO

INSERT INTO dbo.Inventory VALUES (1, '手机', 100);
GO

-- 会话1:设置为 READ UNCOMMITTED(允许脏读)
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN TRANSACTION;

-- 更新库存但不提交
UPDATE dbo.Inventory SET Stock = Stock - 10 WHERE ProductID = 1;
PRINT '会话1:库存已减10,未提交。当前库存:' + CAST((SELECT Stock FROM dbo.Inventory WHERE ProductID=1) AS VARCHAR);

-- 此时切换到会话2执行查询(见下方)
-- 等待5秒模拟并发
WAITFOR DELAY '00:00:05';

-- 回滚事务(模拟取消操作)
ROLLBACK TRANSACTION;
PRINT '会话1:事务已回滚,库存恢复。';
GO

-- ===== 在另一个查询窗口执行(会话2) =====
-- 会话2:读取未提交数据(脏读)
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT Stock AS '脏读到的库存' FROM dbo.Inventory WHERE ProductID = 1;
-- 结果可能为 90(即使会话1未提交且最终回滚)
GO

-- 会话2:使用默认隔离级别(READ COMMITTED)则阻塞直到会话1提交或回滚
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SELECT Stock AS '正常读取' FROM dbo.Inventory WHERE ProductID = 1;
-- 此查询会等待会话1结束
GO

注释

  • READ UNCOMMITTED 允许读取未提交数据(脏读),性能高但数据可能不一致。
  • READ COMMITTED(默认)确保只读已提交数据,避免脏读。
  • 实际开发中应根据业务需求选择隔离级别。

案例4:使用 SNAPSHOT 隔离级别(行版本控制)

sql 复制代码
-- 启用数据库快照隔离(需先设置数据库选项)
ALTER DATABASE YourDatabaseName SET ALLOW_SNAPSHOT_ISOLATION ON;
GO

-- 在会话1中:
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRANSACTION;

SELECT Stock AS '快照读取库存' FROM dbo.Inventory WHERE ProductID = 1;
-- 假设此时读到100

-- 在会话2中(同时执行):
BEGIN TRANSACTION;
UPDATE dbo.Inventory SET Stock = 80 WHERE ProductID = 1;
COMMIT TRANSACTION;
-- 更新为80

-- 回到会话1,再次读取:
SELECT Stock AS '再次快照读取' FROM dbo.Inventory WHERE ProductID = 1;
-- 仍然读到100!因为基于事务开始时的快照

COMMIT TRANSACTION; -- 提交后下次读取才会看到80
GO

注释

  • SNAPSHOT 隔离级别使用行版本控制,读操作不阻塞写,写操作不阻塞读。
  • 避免了脏读、不可重复读、幻读。
  • 需要 tempdb 存储版本数据,可能增加资源消耗。

四、锁(Locking)

1. 锁的内涵与作用

是 SQL Server 用于实现事务隔离性和一致性的机制,防止多个事务同时修改同一数据导致冲突。

  • 作用:保证数据一致性、实现事务隔离级别。
  • 自动管理:SQL Server 自动加锁和释放锁(提交或回滚时)。
  • 粒度:从细到粗:RID(行ID)→ KEY(索引键)→ PAGE(页)→ EXTENT(区)→ TABLE(表)→ DATABASE(库)

2. 可锁定资源与锁的类型

常见锁类型:
锁类型 缩写 说明
共享锁 S 读操作持有,允许多个事务同时读
排他锁 X 写操作持有,阻止其他事务读写
更新锁 U 防止死锁的中间状态,读取后准备更新
意向锁 IS/IX/UIX 表示下层资源有对应锁(如表上有IX,表示某行有X锁)
架构锁 SCH-M/SCH-S 表结构变更时使用
大容量更新锁 BU BULK INSERT 时使用

3. 死锁(Deadlock)

死锁:两个或多个事务互相等待对方释放锁,形成循环等待。

SQL Server 会自动检测死锁,并选择一个"牺牲者"(Deadlock Victim)回滚其事务,解除死锁。


案例5:手动加锁(LOCK HINTS)与死锁演示

sql 复制代码
-- 准备数据
CREATE TABLE dbo.TableA (ID INT PRIMARY KEY, Value INT);
CREATE TABLE dbo.TableB (ID INT PRIMARY KEY, Value INT);
GO

INSERT INTO dbo.TableA VALUES (1, 100), (2, 200);
INSERT INTO dbo.TableB VALUES (1, 1000), (2, 2000);
GO

-- 会话1:先锁 TableA,再锁 TableB
BEGIN TRANSACTION;
-- 对 TableA 加排他锁
SELECT * FROM dbo.TableA WITH (XLOCK, ROWLOCK) WHERE ID = 1;
PRINT '会话1:已锁定 TableA.ID=1';

-- 等待会话2锁定 TableB
WAITFOR DELAY '00:00:05';

-- 尝试锁定 TableB
UPDATE dbo.TableB SET Value = Value + 1 WHERE ID = 1;
PRINT '会话1:更新 TableB 成功';

COMMIT TRANSACTION;
GO

-- ===== 在另一个窗口执行会话2 =====
-- 会话2:先锁 TableB,再锁 TableA(与会话1顺序相反 → 死锁风险)
BEGIN TRANSACTION;
-- 对 TableB 加排他锁
SELECT * FROM dbo.TableB WITH (XLOCK, ROWLOCK) WHERE ID = 1;
PRINT '会话2:已锁定 TableB.ID=1';

-- 等待会话1锁定 TableA
WAITFOR DELAY '00:00:05';

-- 尝试锁定 TableA → 此时可能被死锁检测器选为牺牲者
UPDATE dbo.TableA SET Value = Value + 1 WHERE ID = 1;
PRINT '会话2:更新 TableA 成功'; -- 可能不会执行到此

COMMIT TRANSACTION;
GO

注释

  • WITH (XLOCK, ROWLOCK) 手动指定加排他锁、行级锁。
  • 死锁发生时,SQL Server 返回错误 1205,回滚牺牲者事务。
  • 避免死锁的最佳实践:所有事务按相同顺序访问资源。

案例6:查看当前锁信息(使用 DMV)

sql 复制代码
-- 查询当前数据库中的锁信息
SELECT 
    request_session_id AS SessionID,
    resource_type AS ResourceType,
    resource_database_id AS DBID,
    DB_NAME(resource_database_id) AS DatabaseName,
    resource_description AS ResourceDesc,
    resource_associated_entity_id AS EntityID,
    request_mode AS LockMode, -- S, X, U, IX, IS etc.
    request_status AS Status, -- GRANT, WAIT
    t.text AS SQLText
FROM sys.dm_tran_locks l
LEFT JOIN sys.dm_exec_requests r ON l.request_session_id = r.session_id
OUTER APPLY sys.dm_exec_sql_text(r.sql_handle) t
WHERE resource_database_id = DB_ID()
ORDER BY request_session_id, resource_type;
GO

注释

  • sys.dm_tran_locks 动态管理视图显示当前锁。
  • request_status = 'WAIT' 表示正在等待锁(可能阻塞)。
  • 结合 sys.dm_exec_sql_text 查看阻塞的SQL语句。

五、综合性案例

综合案例1:高并发库存扣减(避免超卖)

sql 复制代码
-- 场景:电商秒杀,多个用户同时抢购同一商品
-- 要求:库存不能为负,避免超卖

-- 创建商品表
CREATE TABLE dbo.Products (
    ProductID INT PRIMARY KEY,
    ProductName NVARCHAR(100),
    Stock INT CHECK (Stock >= 0) -- 约束防止负库存
);
GO

INSERT INTO dbo.Products VALUES (1, '限量版手机', 10);
GO

-- 存储过程:安全扣减库存
CREATE PROCEDURE dbo.usp_DecreaseStock
    @ProductID INT,
    @Quantity INT,
    @Result INT OUTPUT -- 0=成功, 1=库存不足
AS
BEGIN
    SET NOCOUNT ON;
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

    BEGIN TRY
        BEGIN TRANSACTION;

        -- 使用 UPDLOCK, ROWLOCK 防止并发读取旧值
        -- 使用 HOLDLOCK 保持锁到事务结束(等价于 SERIALIZABLE)
        DECLARE @CurrentStock INT;
        SELECT @CurrentStock = Stock 
        FROM dbo.Products WITH (UPDLOCK, ROWLOCK, HOLDLOCK)
        WHERE ProductID = @ProductID;

        IF @CurrentStock >= @Quantity
        BEGIN
            UPDATE dbo.Products 
            SET Stock = Stock - @Quantity 
            WHERE ProductID = @ProductID;

            SET @Result = 0; -- 成功
            PRINT '扣减成功,剩余库存:' + CAST((@CurrentStock - @Quantity) AS VARCHAR);
        END
        ELSE
        BEGIN
            SET @Result = 1; -- 库存不足
            PRINT '库存不足!当前库存:' + CAST(@CurrentStock AS VARCHAR) + ',需求数量:' + CAST(@Quantity AS VARCHAR);
        END

        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0
            ROLLBACK TRANSACTION;
        
        SET @Result = -1; -- 系统错误
        PRINT '系统错误:' + ERROR_MESSAGE();
    END CATCH
END
GO

-- 测试:模拟并发调用
DECLARE @Result INT;
EXEC dbo.usp_DecreaseStock @ProductID = 1, @Quantity = 3, @Result = @Result OUTPUT;
SELECT @Result AS ResultCode;
GO

-- 可同时在多个窗口运行以上测试,观察库存变化和错误处理

注释

  • WITH (UPDLOCK, ROWLOCK, HOLDLOCK) 确保读取时加更新锁,并保持到事务结束,避免其他事务读取或修改。
  • CHECK 约束作为最后防线。
  • 输出参数 @Result 用于应用程序判断结果。

综合案例2:死锁监控与处理脚本

sql 复制代码
-- 创建死锁事件监控表
CREATE TABLE dbo.DeadlockLog (
    LogID INT IDENTITY(1,1) PRIMARY KEY,
    LogTime DATETIME2 DEFAULT GETDATE(),
    DeadlockGraph XML,
    VictimSessionID INT,
    VictimSQLText NVARCHAR(MAX)
);
GO

-- 创建死锁事件通知(需启用跟踪标志 1222 或使用扩展事件)
-- 此处使用 SQL Server Agent + WMI Event Alert 或扩展事件更佳
-- 简化版:定期查询错误日志(不推荐生产环境)

-- 替代方案:使用扩展事件捕获死锁(推荐)
CREATE EVENT SESSION [DeadlockMonitor] ON SERVER 
ADD EVENT sqlserver.xml_deadlock_report
ADD TARGET package0.event_file(SET filename=N'DeadlockMonitor')
WITH (STARTUP_STATE=ON);
GO

-- 启动会话
ALTER EVENT SESSION [DeadlockMonitor] ON SERVER STATE = START;
GO

-- 查询死锁报告
SELECT 
    XEvent.value('(@timestamp)[1]', 'datetime2') AS DeadlockTime,
    XEvent.query('.') AS DeadlockGraph
FROM 
    (SELECT CAST(event_data AS XML) AS XEvent
     FROM sys.fn_xe_file_target_read_file('DeadlockMonitor*.xel', NULL, NULL, NULL)
    ) AS EventData;
GO

-- 自动记录到表(需配合 SQL Agent 定期执行)
INSERT INTO dbo.DeadlockLog (DeadlockGraph, VictimSessionID, VictimSQLText)
SELECT 
    XEvent.query('.') AS DeadlockGraph,
    XEvent.value('(//deadlock/victim-list/victimProcess/@id)[1]', 'varchar(100)') AS VictimSessionID,
    XEvent.value('(//deadlock/process-list/process/inputbuf)[1]', 'nvarchar(max)') AS VictimSQLText
FROM 
    (SELECT CAST(event_data AS XML) AS XEvent
     FROM sys.fn_xe_file_target_read_file('DeadlockMonitor*.xel', NULL, NULL, NULL)
    ) AS EventData
WHERE XEvent.value('(@name)[1]', 'varchar(100)') = 'xml_deadlock_report';
GO

注释

  • 扩展事件(Extended Events)是监控死锁的推荐方式。
  • xml_deadlock_report 事件包含完整的死锁图。
  • 可分析死锁图找出根本原因(如访问顺序不一致、缺少索引等)。

综合案例3:事务与锁的完整电商订单示例

sql 复制代码
-- 创建订单相关表
CREATE TABLE dbo.Customers (
    CustomerID INT PRIMARY KEY,
    Name NVARCHAR(50),
    CreditLimit DECIMAL(18,2)
);
GO

CREATE TABLE dbo.Orders (
    OrderID INT IDENTITY(1000,1) PRIMARY KEY,
    CustomerID INT,
    OrderDate DATETIME2 DEFAULT GETDATE(),
    TotalAmount DECIMAL(18,2),
    Status NVARCHAR(20) DEFAULT 'Pending'
);
GO

CREATE TABLE dbo.OrderItems (
    OrderItemID INT IDENTITY(1,1) PRIMARY KEY,
    OrderID INT,
    ProductID INT,
    Quantity INT,
    Price DECIMAL(18,2)
);
GO

CREATE TABLE dbo.Products (
    ProductID INT PRIMARY KEY,
    ProductName NVARCHAR(100),
    Price DECIMAL(18,2),
    Stock INT
);
GO

-- 插入测试数据
INSERT INTO dbo.Customers VALUES (1, 'VIP客户', 5000.00);
INSERT INTO dbo.Products VALUES (1, '笔记本电脑', 4500.00, 5);
GO

-- 创建下单存储过程(包含事务、锁、错误处理)
CREATE PROCEDURE dbo.usp_CreateOrder
    @CustomerID INT,
    @ProductID INT,
    @Quantity INT,
    @OrderID INT OUTPUT,
    @ErrorMessage NVARCHAR(4000) OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON; -- 严重错误时自动回滚
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; -- 最高隔离,避免幻读

    DECLARE @TotalPrice DECIMAL(18,2);
    DECLARE @CustomerCredit DECIMAL(18,2);
    DECLARE @ProductPrice DECIMAL(18,2);
    DECLARE @AvailableStock INT;

    BEGIN TRY
        BEGIN TRANSACTION;

        -- 1. 检查客户信用额度(加共享锁)
        SELECT @CustomerCredit = CreditLimit 
        FROM dbo.Customers WITH (HOLDLOCK) 
        WHERE CustomerID = @CustomerID;

        -- 2. 检查产品价格和库存(加更新锁)
        SELECT 
            @ProductPrice = Price, 
            @AvailableStock = Stock 
        FROM dbo.Products WITH (UPDLOCK, HOLDLOCK) 
        WHERE ProductID = @ProductID;

        -- 3. 验证业务规则
        IF @CustomerCredit IS NULL
        BEGIN
            SET @ErrorMessage = '客户不存在';
            THROW 50001, @ErrorMessage, 1;
        END

        IF @ProductPrice IS NULL
        BEGIN
            SET @ErrorMessage = '产品不存在';
            THROW 50002, @ErrorMessage, 1;
        END

        IF @AvailableStock < @Quantity
        BEGIN
            SET @ErrorMessage = '库存不足,当前库存:' + CAST(@AvailableStock AS NVARCHAR);
            THROW 50003, @ErrorMessage, 1;
        END

        SET @TotalPrice = @ProductPrice * @Quantity;
        IF @TotalPrice > @CustomerCredit
        BEGIN
            SET @ErrorMessage = '超出信用额度,订单金额:' + CAST(@TotalPrice AS NVARCHAR) + ',信用额度:' + CAST(@CustomerCredit AS NVARCHAR);
            THROW 50004, @ErrorMessage, 1;
        END

        -- 4. 创建订单
        INSERT INTO dbo.Orders (CustomerID, TotalAmount, Status)
        VALUES (@CustomerID, @TotalPrice, 'Confirmed');
        SET @OrderID = SCOPE_IDENTITY();

        -- 5. 创建订单明细
        INSERT INTO dbo.OrderItems (OrderID, ProductID, Quantity, Price)
        VALUES (@OrderID, @ProductID, @Quantity, @ProductPrice);

        -- 6. 扣减库存
        UPDATE dbo.Products 
        SET Stock = Stock - @Quantity 
        WHERE ProductID = @ProductID;

        -- 7. 扣减客户信用额度(模拟)
        UPDATE dbo.Customers 
        SET CreditLimit = CreditLimit - @TotalPrice 
        WHERE CustomerID = @CustomerID;

        COMMIT TRANSACTION;
        SET @ErrorMessage = NULL; -- 成功
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0
            ROLLBACK TRANSACTION;

        SET @OrderID = NULL;
        SET @ErrorMessage = ERROR_MESSAGE();
        -- 可在此记录错误日志
    END CATCH
END
GO

-- 测试下单
DECLARE @NewOrderID INT, @ErrorMsg NVARCHAR(4000);
EXEC dbo.usp_CreateOrder 
    @CustomerID = 1, 
    @ProductID = 1, 
    @Quantity = 2, 
    @OrderID = @NewOrderID OUTPUT, 
    @ErrorMessage = @ErrorMsg OUTPUT;

SELECT @NewOrderID AS CreatedOrderID, @ErrorMsg AS ErrorMessage;
GO

-- 查询结果
SELECT * FROM dbo.Orders;
SELECT * FROM dbo.OrderItems;
SELECT * FROM dbo.Products;
SELECT * FROM dbo.Customers;
GO

注释

  • 使用 SERIALIZABLE 隔离级别确保整个事务期间数据不被其他事务修改。
  • WITH (UPDLOCK, HOLDLOCK) 防止并发冲突。
  • SET XACT_ABORT ON 确保严重错误自动回滚。
  • 完整的错误处理和业务规则验证。
  • 适用于高一致性要求的核心业务。

六、最佳实践总结

  1. 事务要短小精悍:减少锁持有时间,提高并发。
  2. 按固定顺序访问资源:避免死锁。
  3. 合理选择隔离级别:平衡一致性与并发性。
  4. 使用锁提示谨慎 :仅在必要时手动加锁(如 UPDLOCK, HOLDLOCK)。
  5. 监控死锁和阻塞:使用扩展事件或 DMV。
  6. 错误处理必须包含回滚:确保事务完整性。
  7. 避免在事务中执行用户交互或耗时操作
  8. 测试高并发场景:确保系统稳定。

✅ 以上内容涵盖 SQL Server 2019 事务与锁的核心语法、管理操作及实战案例,可直接用于学习、开发与调优。

相关推荐
之歆2 小时前
iSCSI + GFS2 + cLVM 共享存储完全指南
数据库
一个天蝎座 白勺 程序猿2 小时前
KingbaseES约束机制:数据迁移中的数据完整性保障
开发语言·数据库·kingbase·kingbasees
软件派2 小时前
数据库技术探秘:JDBC、ODBC、CDC与LOAD的深度解析
数据库
XP62262 小时前
MySQL单表存多大的数据量比较合适
数据库·mysql
是店小二呀2 小时前
MySQL 核心操作:表的CRUD、聚合与分组深度实践
数据库·mysql
嵌入式×边缘AI:打怪升级日志3 小时前
第十二章:上位机访问多个传感器(事件驱动的网关设计)
数据库
m***49583 小时前
LangChain-08 Query SQL DB 通过GPT自动查询SQL
数据库·sql·langchain
悠哉悠哉愿意3 小时前
【强化学习学习笔记】马尔科夫决策过程
笔记·学习·交互·强化学习
babe小鑫3 小时前
高职商务数据分析与应用专业学习数据分析的重要性
学习·数据挖掘·数据分析