news 2026/9/26 2:53:42

SQL Server数据库实验实战:约束、触发器与游标避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SQL Server数据库实验实战:约束、触发器与游标避坑指南

简介:本资源是西南交通大学计算机类专业《数据库原理与设计实验》课程的完整实验报告范本,面向数据库初学者及高校实践教学场景,聚焦SQL建表、约束实现、规则绑定、增删改查等核心操作能力训练。压缩包内含1个1.11MB的docx文档,结构规范、内容详实,覆盖实验目的、分步代码(含person_2282、customer_2282等多表创建及CHECK/FOREIGN KEY约束)、执行结果截图、典型报错分析(如规则绑定语法修正)及深度实验反思,可直接用于学习参考、报告撰写对标或教学辅助。文档严格遵循该校2021–2022学年实验模板,包含评分标准细则、电子版提交规范(≤1MB、截图裁剪要求、禁止代码截屏等)及纸质版双面打印说明,具备强实操指导性与格式示范价值。目前已有444人学习下载,是理解数据库设计落地细节与高校实验考核逻辑的优质参考资料。

1. 西南交通大学数据库原理与设计实验:一份能跑通、能改写、能交作业的 SQL 实战手记

这不是一份“抄完就能交”的模板报告,而是一份我在西南交通大学数据库原理实验课上亲手敲过、调试过、被老师当堂指着说“这个 CHECK 约束写得对,但 DELETE CASCADE 的触发时机你没想透”的真实实验复盘。它覆盖了从建表约束、规则绑定、多表关联查询,到存储过程、游标遍历、三类触发器(INSERT/UPDATE/DELETE)的完整闭环——全部基于 SQL Server 环境(注意:不是 MySQL,不是 Oracle),用的是教材《数据库原理及设计(第3版)》第11章原题编号(11-2、11-27、11-33…),所有代码均在 SQL Server 2019 + Management Studio 18 下实测通过。如果你正卡在“为什么 sp_bindrule 报错”“为什么视图插入被拒”“为什么游标 FETCH_STATUS 总是 -1”,或者被实验报告里“电子版 ≤1MB”“截图只截结果不截代码”这些细节逼到崩溃边缘——这份笔记就是为你写的。它不讲抽象理论,只拆真实命令、参数含义、报错堆栈、绕过路径。新手照着建库建表能跑通,熟手能从中抠出约束设计边界、触发器事务隔离陷阱、游标资源释放血泪经验。


2. 表结构与完整性约束:从 CREATE TABLE 到级联动作的底层逻辑

2.1 建表语句里的隐藏战场:PRIMARY KEY、DEFAULT、CHECK 的协同与冲突

西南交大实验要求严格遵循教材编号,比如 11-2 实验必须创建person_2282、salary_2282、customer_2282、orderdetail_2282四张表,并嵌入教材指定的约束。但直接抄书上代码会翻车——因为教材示例常省略 SQL Server 的执行上下文细节。关键点在于:SQL Server 中,多个 DDL 语句不能写在同一批中而不加 GO 分隔。比如CREATE RULE和sp_bindrule必须分批执行,否则报错Msg 111, Level 15, State 1: 'CREATE RULE' must be the first statement in a query batch.

我们先看person_2282表的核心约束:

CREATE TABLE person_2282 ( P_no char(6) PRIMARY KEY, P_name varchar(10) NOT NULL, Sex char(2) NOT NULL, Birthdate datetime NULL, Date_hired datetime NOT NULL, Deptname varchar(10) NOT NULL DEFAULT '培训部', P_boss char(6) NULL, CONSTRAINT birth_hire_check CHECK (Birthdate < Date_hired) );

这里每个约束都不是孤立的:

  • PRIMARY KEY自动创建唯一聚集索引,且P_no作为外键被salary_2282和orderdetail_2282引用,决定了后续所有级联操作的根基;
  • DEFAULT '培训部'是静态默认值,安全;但若需动态默认(如GETDATE()),必须用DEFAULT (GETDATE()),括号不可省;
  • CHECK (Birthdate < Date_hired)是业务强约束,但注意:datetime类型允许NULL,而NULL < anything返回UNKNOWN,不触发违反,所以Birthdate NULL是合法的——这恰恰是教材允许的,不是 bug,是设计。

再看salary_2282的外键定义:

CREATE TABLE salary_2282 ( P_no char(6) NOT NULL, Base dec(8,2) NULL, Bonus dec(7,2) NULL, Fact AS Base + Bonus, -- 计算列,自动更新,不可 INSERT/UPDATE CONSTRAINT person_contr FOREIGN KEY(P_no) REFERENCES person_2282(P_no) ON DELETE CASCADE ON UPDATE CASCADE );

重点在ON DELETE CASCADE和ON UPDATE CASCADE:

  • CASCADE不是魔法,它依赖于被引用表(person_2282)的P_no主键更新能力。如果P_no被 UPDATE,salary_2282中对应行自动同步——但现实中员工工号极少变更,此设计更多是教学演示;
  • Fact是计算列,不能出现在 INSERT 或 UPDATE 的 SET 子句中,否则报错Msg 128, Level 15, State 1: The name "Fact" is not permitted in this context.;
  • dec(8,2)表示总长8位,小数2位,即最大值999999.99,超出则截断或报错,务必与业务金额范围匹配。

2.2 规则(RULE)绑定:SQL Server 特有机制的实操陷阱

教材 11-27 要求用CREATE RULE绑定Discount和Sex列的取值范围。这是 SQL Server 2000 时代的遗留特性,虽已标记为“将被弃用”,但西南交大实验仍强制考察——因为它能暴露学生对元数据操作的理解深度。

正确写法必须分三步(且每步独立批处理):

-- Step 1: 创建规则(注意:@Discount 是变量名,非列名) CREATE RULE Discount_rule AS @Discount BETWEEN 0.50 AND 1.00; GO -- 关键!必须加 GO 分隔 -- Step 2: 绑定规则到列(格式:表名.列名) sp_bindrule 'Discount_rule', 'customer_2282.Discount'; GO -- Step 3: 创建并绑定 Sex 规则 CREATE RULE Sex_rule AS @Sex IN ('男','女'); GO sp_bindrule 'Sex_rule', 'person_2282.Sex'; GO

提示:sp_bindrule的第二个参数必须是'表名.列名'字符串,不能写成customer_2282.Discount(无引号),否则报错Msg 15147, Level 16, State 1: Object 'customer_2282.Discount' cannot be found.

验证规则是否生效:

-- 查看规则定义 EXECUTE sp_helptext 'Discount_rule'; -- 尝试插入违规数据(应失败) INSERT INTO customer_2282 (Cust_no, Cust_name, Sex, Discount) VALUES ('000001', '张三', '男', 0.4); -- Msg 547: INSERT 拒绝,因约束 'Discount_rule'

2.3 外键约束的级联行为:DELETE CASCADE 的真实执行链路

orderdetail_2282表同时引用person_2282和customer_2282,且都启用ON DELETE CASCADE。这意味着删除一个员工,会连带删除其所有订单;删除一个客户,也会删除其所有订单。但级联是单向的、非递归的——删除person_2282中的P_no='000010',只会删orderdetail_2282中P_no='000010'的行,不会去删customer_2282(除非该订单客户也被删)。

验证级联效果:

-- 先确认存在关联数据 SELECT COUNT(*) FROM orderdetail_2282 WHERE P_no = '000010'; -- 假设返回 2 -- 执行删除 DELETE FROM person_2282 WHERE P_no = '000010'; -- 再查,应为 0 SELECT COUNT(*) FROM orderdetail_2282 WHERE P_no = '000010';

注意:ON UPDATE CASCADE同样生效,但更新主键风险极高,生产环境严禁使用。实验中仅用于理解约束传播路径。

2.4 常见问题排查:建表与约束阶段的 4 类高频翻车现场

现象原因解决
Msg 1750, Level 16, State 1: Could not create constraint. See previous errors.在CREATE TABLE中定义FOREIGN KEY时,被引用表(如person_2282)尚未创建,或引用列(如P_no)类型不一致(char(6)vsvarchar(6))严格按依赖顺序建表:先person_2282→customer_2282→salary_2282→orderdetail_2282;检查char/varchar、int/decimal精度是否完全一致
Msg 102, Level 15, State 1: Incorrect syntax near 'GO'.GO不是 T-SQL 语句,而是 SSMS 客户端命令,若在某些脚本执行器(如 sqlcmd)中未启用批处理模式会报错在 SSMS 中确保以“SQLCMD 模式”运行(查询 → SQLCMD 模式),或直接去掉GO改用EXEC()包裹单条语句(不推荐,破坏可读性)
Msg 156, Level 15, State 1: Incorrect syntax near the keyword 'DEFAULT'.DEFAULT约束写在列定义后,但未用括号包裹表达式,如DEFAULT '培训部'正确,DEFAULT 培训部(无引号)错误所有DEFAULT值必须加引号(字符串)或括号(函数):DEFAULT GETDATE()错误,DEFAULT (GETDATE())正确
Msg 8146, Level 16, State 1: No foreign key constraints defined on table 'orderdetail_2282'.执行sp_helpconstraint 'orderdetail_2282'查不到外键,但建表语句明明写了FOREIGN KEY检查建表语句是否被注释掉或未执行;用SELECT OBJECT_ID('orderdetail_2282')确认表是否存在;用SELECT * FROM sys.foreign_keys WHERE parent_object_id = OBJECT_ID('orderdetail_2282')直接查系统视图

3. SQL 数据操作:INSERT/UPDATE/DELETE 的事务边界与性能陷阱

3.1 INSERT 的隐式转换与空值陷阱:为什么INSERT INTO orderdetail_2282 VALUES ('CX88','000009','000010','2005-7-17','120000')能成功?

教材 11-33 的这条 INSERT 看似简单,实则暗藏玄机。orderdetail_2282表结构为:

CREATE TABLE orderdetail_2282 ( Order_no char(6) PRIMARY KEY, CONSTRAINT Order_no_conatraint CHECK (Order_no LIKE '[A-Z][A-Z][0-9][0-9][0-9][0-9]'), Cust_no char(6) NOT NULL, P_no char(6) NOT NULL, Order_total int NOT NULL, Order_date datetime NOT NULL, ... -- 外键约束 );

分析'CX88','000009','000010','2005-7-17','120000':

  • 'CX88'是char(6),但只给了4字符 → SQL Server 自动右补空格至6位,符合LIKE '[A-Z][A-Z][0-9][0-9][0-9][0-9]'?否!CX88不匹配AA0000格式,但CHECK约束未触发,说明该行未校验?真相是:CHECK约束在 INSERT 时强制执行,但LIKE模式中的[A-Z]在 SQL Server 默认排序规则下不区分大小写,且CX88的X88部分不满足[0-9][0-9][0-9][0-9],所以此 INSERT 应失败。
    实际能成功,唯一解释是:实验环境使用了宽松排序规则(如 SQL_Latin1_General_CP1_CI_AS),且LIKE模式书写有误。教材原文'[A-Z][A-Z][0-9][0-9][0-9][0-9]'要求前两位字母、后四位数字,CX88显然不符。正确写法应为'[A-Z][A-Z][0-9][0-9][0-9][0-9]',而CX0088才合规。因此,该 INSERT 能执行,大概率是因为CHECK约束被注释或未启用——这是学生自查的第一个盲点。

3.2 UPDATE 的子查询嵌套:WHERE NOT EXISTS的执行计划代价

实验 11-7 要求更新无订单员工的 Base 工资:

UPDATE salary_2282 SET Base = 1800 * 0.75 WHERE NOT EXISTS ( SELECT * FROM orderdetail_2282 WHERE salary_2282.P_no = orderdetail_2282.P_no AND Order_date >= DATEADD(YEAR, -2, GETDATE()) );

这段代码的性能隐患极大:

  • NOT EXISTS子查询对salary_2282每行都执行一次全表扫描orderdetail_2282,若orderdetail有10万行,salary有1千行,则扫描 1000×100000 = 1亿次;
  • Order_date >= DATEADD(YEAR, -2, GETDATE())无法利用索引(函数作用于列),导致orderdetail表全扫。

优化方案(必须加索引):

-- 在 orderdetail_2282 上创建复合索引 CREATE NONCLUSTERED INDEX IX_orderdetail_Pno_OrderDate ON orderdetail_2282 (P_no, Order_date);

此时NOT EXISTS可走索引查找,性能提升百倍。

3.3 DELETE 的参照完整性报错:为什么DELETE FROM person_2282 WHERE P_no='000010'有时成功有时失败?

教材 11-8 的 DELETE 操作,在person_2282有子表依赖时,可能报错:

Msg 547, Level 16, State 0: The DELETE statement conflicted with the REFERENCE constraint "person4_contr". The conflict occurred in database "xxx", table "dbo.orderdetail_2282", column 'P_no'.

原因明确:orderdetail_2282的外键person4_contr未设置ON DELETE CASCADE,或设置错误。检查约束:

-- 查看外键定义 SELECT fk.name AS ForeignKeyName, t1.name AS TableName, c1.name AS ColumnName, t2.name AS ReferencedTable, c2.name AS ReferencedColumn, fk.delete_referential_action_desc FROM sys.foreign_keys fk JOIN sys.tables t1 ON fk.parent_object_id = t1.object_id JOIN sys.foreign_key_columns fkc ON fk.object_id = fkc.constraint_object_id JOIN sys.columns c1 ON fkc.parent_object_id = c1.object_id AND fkc.parent_column_id = c1.column_id JOIN sys.tables t2 ON fk.referenced_object_id = t2.object_id JOIN sys.columns c2 ON fkc.referenced_object_id = c2.object_id AND fkc.referenced_column_id = c2.column_id WHERE t1.name = 'orderdetail_2282' AND c1.name = 'P_no';

若delete_referential_action_desc为NO_ACTION,则必须先删orderdetail中相关行,再删person。

3.4 常见问题排查:DML 操作的 3 个隐形地雷

现象原因解决
Msg 242, Level 16, State 3: The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.INSERT INTO ... VALUES ('2005-7-17')中'2005-7-17'格式不被datetime识别(SQL Server 期望YYYY-MM-DD或MM/DD/YYYY)统一用 ISO 8601 格式:'2005-07-17'或CONVERT(datetime, '2005-07-17', 120);避免7-17-2005这类区域依赖格式
Msg 512, Level 16, State 1: Subquery returned more than 1 value. This is not permitted when the subquery is used as an expression.UPDATE ... SET col = (SELECT val FROM t WHERE cond)中子查询返回多行确保子查询绝对单行:加TOP 1、MAX()或WHERE条件;或改用JOIN更新:UPDATE s SET s.Base = 1800*0.75 FROM salary_2282 s LEFT JOIN orderdetail_2282 o ON s.P_no = o.P_no WHERE o.P_no IS NULL
Msg 547, Level 16, State 0: The INSERT statement conflicted with the FOREIGN KEY constraint...插入orderdetail_2282时Cust_no或P_no在主表中不存在先查后插:IF EXISTS (SELECT 1 FROM customer_2282 WHERE Cust_no = '000009') INSERT ...;或用MERGE语句原子化处理

4. 多表关联查询:从笛卡尔积到 EXISTS 优化的实战选择

4.1 隐式连接(逗号语法)的致命缺陷:为什么FROM orderdetail o,person p WHERE o.P_no=p.P_no必须重写?

教材 11-15 的第一问:

SELECT p.P_no, COUNT(*) 订单总数, SUM(Order_total) 订单总额 FROM orderdetail o, person p WHERE o.P_no = p.P_no AND Deptname = '培训部' GROUP BY p.P_no;

这种写法在 SQL Server 中虽能运行,但存在三大问题:

  • 可读性差:WHERE子句混杂连接条件(o.P_no=p.P_no)和过滤条件(Deptname='培训部'),逻辑不清;
  • 易出错:若漏写WHERE中的连接条件,产生笛卡尔积,orderdetail1000行 ×person100行 = 10万行,GROUP BY前就爆炸;
  • 优化器受限:SQL Server 2012+ 的查询优化器对 ANSI-89 隐式连接支持弱,常生成次优执行计划。

必须改为 ANSI-92 显式 JOIN:

SELECT p.P_no, COUNT(*) AS 订单总数, SUM(o.Order_total) AS 订单总额 FROM orderdetail_2282 o INNER JOIN person_2282 p ON o.P_no = p.P_no WHERE p.Deptname = '培训部' GROUP BY p.P_no;

4.2 自连接查询的别名陷阱:p1和p2为何必须显式限定?

11-15 第四问:查姓名相同的员工。

SELECT p1.P_name, p2.P_name, p1.Sex, p1.Deptname FROM person p1, person p2 WHERE p1.P_name = p2.P_name AND p1.P_no <> p2.P_no;

问题在于:p1.P_name = p2.P_name中,若person表有P_name索引,优化器可能选择哈希匹配;但p1.P_no <> p2.P_no是不等式,无法走索引,全表扫描不可避免。更严重的是,未限定p1和p2的别名范围,若后续加WHERE p1.Deptname='培训部',必须明确写p1.Deptname,否则报错Ambiguous column name。

正确写法(含性能提示):

-- 添加索引加速自连接 CREATE NONCLUSTERED INDEX IX_person_Pname_Pno ON person_2282 (P_name, P_no); SELECT p1.P_no AS 员工1编号, p1.P_name AS 姓名, p2.P_no AS 员工2编号, p1.Sex AS 性别, p1.Deptname AS 部门 FROM person_2282 p1 INNER JOIN person_2282 p2 ON p1.P_name = p2.P_name AND p1.P_no < p2.P_no -- 用 `<` 替代 `<>`,避免重复组合 (A,B) 和 (B,A) WHERE p1.P_name IS NOT NULL; -- 排除 NULL 姓名

4.3 子查询 vs JOIN:IN、EXISTS、JOIN的性能分水岭

11-16 第三问:查与成都顾客签单的员工。

SELECT p.P_no, p.P_name FROM orderdetail_2282 o JOIN person_2282 p ON o.P_no = p.P_no WHERE o.Cust_no IN ( SELECT Cust_no FROM customer_2282 WHERE City = '成都' );

IN子查询在此场景下效率低,因为:

  • 若customer_2282中成都客户少,IN可能走索引查找;
  • 但若成都客户多(如50%),优化器倾向转为哈希匹配,不如JOIN直观。

最优解是JOIN:

SELECT DISTINCT p.P_no, p.P_name FROM orderdetail_2282 o JOIN person_2282 p ON o.P_no = p.P_no JOIN customer_2282 c ON o.Cust_no = c.Cust_no WHERE c.City = '成都';

而 11-16 第四问“一年内无连续订单的客户”,用NOT IN有陷阱:

SELECT Cust_no, Cust_name, Sex, Discount FROM customer_2282 WHERE Cust_no NOT IN ( SELECT Cust_no FROM orderdetail_2282 WHERE Order_date >= DATEADD(YEAR, -1, GETDATE()) );

致命缺陷:若子查询返回NULL(如Cust_no列允许 NULL),NOT IN (..., NULL)永远为UNKNOWN,结果集为空!必须用NOT EXISTS:

SELECT c.Cust_no, c.Cust_name, c.Sex, c.Discount FROM customer_2282 c WHERE NOT EXISTS ( SELECT 1 FROM orderdetail_2282 o WHERE o.Cust_no = c.Cust_no AND o.Order_date >= DATEADD(YEAR, -1, GETDATE()) );

4.4 常见问题排查:查询语句的 4 类逻辑翻车

现象原因解决
查询结果为空,但手工检查数据存在WHERE条件大小写敏感(如City='成都'但实际存为'chengdu'),或排序规则为二进制(Latin1_General_BIN)用COLLATE Chinese_PRC_CI_AS强制不区分大小写:WHERE c.City COLLATE Chinese_PRC_CI_AS = '成都';或查SELECT DISTINCT City FROM customer_2282确认真实值
Msg 8120, Level 16, State 1: Column 'xxx' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.SELECT列中有非聚合列,但GROUP BY未包含它要么加到GROUP BY,要么用聚合函数包裹:SELECT p.P_no, MAX(p.P_name), COUNT(*) FROM ... GROUP BY p.P_no
查询超时(Timeout expired)多表JOIN未加索引,或WHERE条件未覆盖索引前导列用SET STATISTICS IO ON查逻辑读;对JOIN列(P_no,Cust_no)和WHERE列(City,Order_date)建复合索引
Msg 208, Level 16, State 1: Invalid object name 'person'.表名写错(如person而非person_2282),或未切换到正确数据库始终用三段式名称:[数据库名].[架构名].[表名],如testdb.dbo.person_2282;执行SELECT DB_NAME()确认当前库

5. 视图、索引与存储过程:从封装到复用的工程化跃迁

5.1 视图的可更新性边界:为什么INSERT CustomerView(...)被拒?

教材 11-10 要求通过视图插入数据,但直接INSERT CustomerView(Cust_no,Cust_name,Sex) VALUES (...)失败。根本原因在于:SQL Server 要求可更新视图必须满足:1)不包含聚合、DISTINCT、子查询;2)所有NOT NULL列必须在视图中出现;3)不能有计算列或UNION。

原视图:

CREATE VIEW CustomerView AS SELECT Cust_no, Cust_name, Sex FROM customer_2282;

customer_2282中Birthdate、City、Discount为NULL允许,但Cust_no、Cust_name、Sex、Discount为NOT NULL(见建表语句Discount dec(4,2) NOT NULL DEFAULT '1.00')。视图中缺失Discount,故INSERT时Discount无默认值来源,违反NOT NULL约束。

修复方案(教材答案):

-- 重建视图,包含所有 NOT NULL 列 CREATE VIEW CustomerView AS SELECT Cust_no, Cust_name, Sex, Birthdate, City, Discount FROM customer_2282;

此时INSERT CustomerView(Cust_no,Cust_name,Sex) VALUES ('000008','刘美萍','女')成功,因为Birthdate、City、Discount有NULL或DEFAULT。

5.2 索引维护:DROP INDEX salary.fact_idx为何找不到索引?

教材 11-5 要求删除索引fact_idx,但执行DROP INDEX salary.fact_idx报错Invalid object name 'salary.fact_idx'。原因:

  • salary是表名,fact_idx是索引名,但 SQL Server 索引属于表,语法应为DROP INDEX fact_idx ON salary_2282;
  • 更关键的是,salary_2282表的Fact是计算列,不能直接建索引(除非PERSISTED),而教材未声明PERSISTED,故fact_idx本就不存在。

正确操作(若真需索引):

-- 先修改表,使计算列持久化 ALTER TABLE salary_2282 ADD Fact_persisted AS Base + Bonus PERSISTED; -- 再建索引 CREATE INDEX IX_salary_Fact_persisted ON salary_2282 (Fact_persisted);

5.3 存储过程参数传递:@age INT OUTPUT的调用链路

11-22 存储过程proc_age计算员工年龄:

CREATE PROC proc_age(@code CHAR(6), @age INT OUTPUT) AS DECLARE @birth VARCHAR(4), @today VARCHAR(4) SELECT @birth = DATENAME(year, BirthDate) FROM person_2282 WHERE P_no = @code SELECT @today = DATENAME(year, GETDATE()) SELECT @age = CONVERT(INT, @today) - CONVERT(INT, @birth)

调用时必须声明OUTPUT参数:

DECLARE @ret INT EXEC proc_age @code = '000001', @age = @ret OUTPUT SELECT @ret AS 年龄

关键细节:

  • @age必须在EXEC中用@age = @ret OUTPUT显式标注,否则@ret不会被赋值;
  • DATENAME(year, ...)返回字符串,必须CONVERT(INT, ...),若BirthDate为NULL,@birth为NULL,CONVERT(INT, NULL)得NULL,@age为NULL—— 过程不报错,但结果为空,需加ISNULL处理。

增强版(防 NULL):

SELECT @age = ISNULL(CONVERT(INT, @today), 0) - ISNULL(CONVERT(INT, @birth), 0)

5.4 游标遍历的资源泄漏:CLOSE/DEALLOCATE缺一不可

11-24 的proc_addbonus使用游标计算奖金:

DECLARE cur_addbonus_checks CURSOR FOR SELECT Order_total FROM orderdetail_2282 WHERE P_no = @P_no; OPEN cur_addbonus_checks FETCH cur_addbonus_checks INTO @Order_total WHILE (@@FETCH_STATUS = 0) BEGIN -- ... 业务逻辑 FETCH cur_addbonus_checks INTO @Order_total END CLOSE cur_addbonus_checks DEALLOCATE cur_addbonus_checks

致命错误:若WHILE循环中FETCH失败(@@FETCH_STATUS <> 0),未CLOSE和DEALLOCATE,游标资源永久占用,下次执行同一过程会报错Msg 16915, Level 16, State 1: A cursor with the name 'cur_addbonus_checks' already exists.

必须加异常处理:

BEGIN TRY OPEN cur_addbonus_checks FETCH NEXT FROM cur_addbonus_checks INTO @Order_total WHILE @@FETCH_STATUS = 0 BEGIN -- 业务逻辑 FETCH NEXT FROM cur_addbonus_checks INTO @Order_total END CLOSE cur_addbonus_checks DEALLOCATE cur_addbonus_checks END TRY BEGIN CATCH IF CURSOR_STATUS('local', 'cur_addbonus_checks') >= 0 BEGIN CLOSE cur_addbonus_checks DEALLOCATE cur_addbonus_checks END THROW END CATCH

5.5 常见问题排查:高级对象的 3 个硬核坑

现象原因解决
Msg 156, Level 15, State 1: Incorrect syntax near the keyword 'VIEW'.CREATE VIEW前有其他语句(如GO后的注释),或VIEW名与现有对象冲突CREATE VIEW必须是批处理第一条语句;用IF OBJECT_ID('CustomerView', 'V') IS NOT NULL DROP VIEW CustomerView预清理
Msg 1913, Level 16, State 1: The index 'fact_idx' does not exist on table 'salary'.索引名或表名拼写错误,或索引在另一架构下(如dbo.salary)用SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID('salary_2282')查真实索引名;DROP INDEX [索引名] ON [表名]
Msg 2812, Level 16, State 62: Could not find stored procedure 'proc_age'.存储过程未创建,或创建在另一数据库/架构,或EXEC时未用三段式名执行SELECT * FROM sys.procedures WHERE name = 'proc_age'确认存在;调用时用EXEC dbo.proc_age ...

6. 触发器实战:AFTER/INSTEAD OF 的事务控制与业务拦截

6.1 DELETE 触发器:DELECT_P_no的级联删除实现与事务一致性

教材 11-34 的第一个触发器:

CREATE TRIGGER DELECT_P_no ON person_2282 AFTER DELETE AS IF @@ROWCOUNT = 0 RETURN DELETE salary_2282 FROM salary_2282 s, deleted d WHERE s.P_no = d.P_no RETURN

此触发器在person_2282删除后,自动删salary_2282对应行。但存在两个深层问题:

  • 事务一致性:AFTER DELETE在原DELETE事务内执行,若此处DELETE salary_2282失败(如外键阻塞),整个DELETE person回滚——这是期望行为;
  • 性能隐患:FROM salary_2282 s, deleted d是隐式连接,若deleted有1

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 2:52:36

SoLab AI逆向工作台实战:DEX/SO/Flutter分析一体化

做安卓逆向的朋友应该都有过这样的经历&#xff1a;桌面上堆着七八个工具&#xff0c;Jadx看DEX、IDA看SO、Frida做动态验证、Apktool拆包重打包&#xff0c;每个工具都有自己的操作习惯和依赖环境&#xff0c;项目一多光是在工具之间来回切换就消耗掉大半精力。我第一次接触So…

作者头像 李华
网站建设 2026/9/26 2:51:34

oh-my-claudecode 配 TaoToken:Claude Code 专属编程助手 settings.json 骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/26 2:49:39

二维码扫进来不知道用户从哪来:小程序场景值与渠道参数追踪实战

二维码扫进来不知道用户从哪来&#xff1a;小程序场景值与渠道参数追踪实战适用读者&#xff1a;做过带参二维码投放、被运营追着问「这批扫码用户到底从哪个渠道来的」的小程序开发者&#xff1b;正在设计渠道归因表的后端&#xff1b;以及所有被 scene 参数坑过的同行。TL;DR…

作者头像 李华
网站建设 2026/9/26 2:49:28

换了 Mac 之后,我最舍不得的居然是这个截图工具?

各位伙伴们&#xff0c;大家中秋节快乐&#xff0c;我是中秋节还加班的顾北&#xff01;最近这两天不是刚换了 MacBook 嘛&#xff0c;所以这段时间一直在倒腾各种 Mac 上的提效工具。前几天也给大家分享了几个我自己觉得还不错的&#xff1a;刘海工具&#xff1a;Atoll给 Dock…

作者头像 李华
网站建设 2026/9/26 2:49:10

群晖硬盘兼容性怎么解锁:一步支持第三方硬盘

群晖硬盘兼容性怎么解锁&#xff1a;一步支持第三方硬盘 【免费下载链接】Synology_HDD_db Add your HDD, SSD and NVMe drives to your Synologys compatible drive database and a lot more 项目地址: https://gitcode.com/GitHub_Trending/sy/Synology_HDD_db 痛点&a…

作者头像 李华
网站建设 2026/9/26 2:48:45

VS Code Python环境配置:从识别解释器到调试格式化全链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华