sql - SQL 更新 WHERE IN 子查询已停止工作
问题描述
我有这张桌子。(还有其他字段,但 Id 和 Lang 在这里很重要。)
CREATE TABLE Request
(
Id int IDENTITY(1,1) NOT NULL,
Lang nvarchar(20) NULL
CONSTRAINT PK_MyTable PRIMARY KEY CLUSTERED (Id ASC)
) ON [PRIMARY]
我有一个用户定义的表类型。
CREATE TYPE dbo.IntList AS TABLE
(
Item int NOT NULL
)
最后,我有一个存储过程,它为我的表类型接收一个变量,以更新所有指定 Id 的 Lang 字段。
CREATE PROCEDURE BulkUpdLang
@ids as IntList READONLY,
@lang as nvarchar(20)
AS
BEGIN
SET NOCOUNT ON;
UPDATE Request
SET Lang = @lang
WHERE Id IN (SELECT Item FROM @ids);
END
这工作正常,由于某种原因,已经停止。它现在因错误而爆炸:
子查询返回超过 1 个值。当子查询跟随 =、!=、<、<=、>、>= 或当子查询用作表达式时,这是不允许的
假设 MyTable 有 10 个 ID 为 1-10 的项目,这是我尝试过的:
declare @ids IntList;
insert into @ids
select 3 union all
select 4 union all
select 5;
-- This is how I test the sproc
-- EXEC BulkUpdLang @ids, 'en-us';
-- This is just a select and works just fine.
--Select * from Request
-- Where Id in (Select Item From @ids);
-- This WAS working, but now throws the subquery error.
Update Request
Set Lang = 'en-us'
Where Id in (Select Item From @ids);
-- This works as expected. Unfortunately, the Ids need to be a variable.
--Update Request
--Set Lang = 'en-us'
--Where Id in (3, 4, 5);
-- This also throws the subquery error.
--Merge Request as Req
--Using @ids as Ids -- "Using (Select Item From @ids) as Ids" changes nothing
--on Req.Id = Ids.Item
--when matched then
--update set
-- Req.Lang = 'en-us';
-- This also throws the subquery error.
--Update Request
--Set Lang = 'en-us'
--From (Select Item From @ids) Ids
--Where Id = Ids.Item;
让我发疯的是,这是有效的。我倾向于相信某些 SQL 服务器设置已经改变了它处理子查询的方式。有没有人有任何想法?我快疯了!
此表上还有一个触发器:
ALTER TRIGGER [dbo].[Request_Status_Audit]
ON [dbo].[Request]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE @oldStatus nvarchar(15) = (SELECT [Status] FROM Deleted);
DECLARE @newStatus nvarchar(15) = (SELECT [Status] FROM Inserted);
IF (@oldStatus <> @newStatus) BEGIN
INSERT INTO dbo.Audit_RequestStatus (RequestId, StatusDate, StatusValue)
SELECT Inserted.Id, GETDATE(), Inserted.[Status]
FROM Inserted;
END
-- data retention
DELETE FROM dbo.Audit_RequestStatus
WHERE StatusDate < DATEADD(YEAR, -6, GETDATE());
END
解决方案
您的触发器不处理多行插入和更新(也不处理 0 行),正如Brent Ozar(我在大多数触发器中发现的静默错误)和其他人所记录的那样。
CREATE OR ALTER TRIGGER [dbo].[Request_Status_Audit]
ON [dbo].[Request]
AFTER INSERT, UPDATE
AS
SET NOCOUNT ON;
IF NOT UPDATE([Status]) OR NOT EXISTS
(
SELECT Id, [Status]
FROM inserted
EXCEPT
SELECT Id, [Status]
FROM deleted
)
RETURN ;
INSERT INTO dbo.Audit_RequestStatus (RequestId, StatusDate, StatusValue)
SELECT i.Id, GETDATE(), i.[Status]
FROM (
SELECT Id, [Status]
FROM inserted
EXCEPT
SELECT Id, [Status]
FROM deleted
) i;
-- data retention
DELETE FROM dbo.Audit_RequestStatus
WHERE StatusDate < DATEADD(YEAR, -6, GETDATE());
GO
笔记:
- 比较必要的列
inserted
并deleted
查看是否有更改,还要检查UPDATE
函数(请注意,UPDATE
它只告诉您查询中是否存在该列,而不是数据是否实际更改),如果可以,请尽早退出。 - 在您
INSERT
再次检查更改的列 - 我强烈建议您将其转移
DELETE
到代理工作中。
推荐阅读
- javascript - 如何确保只有在 async/await 数据库插入成功时才执行某些 JavaScript 代码?
- python-3.x - 为什么 wpide_save_file&filename=/ 不起作用?
- sql-server - SQL Server 2017:“VERIFY_CLONEDB”不是公认的选项
- java - 关于生日悖论的 Java 数组问题 - 帖子中的详细信息。我一直无法弄清楚我的代码出了什么问题,需要帮助
- python - python中的Hungman游戏代码显示不同的输出
- autoit - 带有 autoit 的 MsgBox
- string - 编写一个函数读取 number(s, i),它接受一个字符串 s 和一个整数 I,并返回一个由以下组成的对
- java - 在 Jackson 序列化期间更改值,例如用于 fastjson 的 ValueFileter
- javascript - Discord bot purge 命令 - 代码错误:TypeError:message.reply 不是函数
- javascript - nuxt 2.14 generate 不更新 asyncData