有人可以解释为什么Sql Server抱怨围绕“WITH”子句的语法吗?
感谢您的帮助。
CREATE TABLE TestTable1 (
Id int not null,
Version int not null constraint d_Ver default (0),
[Name] nvarchar(50) not null,
CONSTRAINT pk_TestTable1 PRIMARY KEY (Id, Version)
);
GO
CREATE TRIGGER trg_iu_UniqueActiveName
ON [dbo].[TestTable1]
AFTER INSERT, UPDATE
AS
IF(UPDATE([Name]))
BEGIN
IF(
(
WITH MaxVers AS
(SELECT Id, Max(Version) AS MaxVersion
FROM [dbo].[TestTable1]
GROUP BY Id)
SELECT Count(1)
FROM [dbo].[TestTable1] t
INNER JOIN MaxVers ON t.Id = MaxVers.Id AND t.Version = MaxVers.MaxVersion
WHERE t.[Name] = inserted.[Name]
)
> 0
)
BEGIN
DECLARE @name nvarchar(50)
SELECT @name = [Name] FROM inserted;
RAISERROR('The name "%s" is already in use.', 16, 1, @name);
END
END;
GO
编辑2: 对于任何好奇的人来说,这是CTE版本,其中包含以下所有优秀评论。我想我会切换到子查询方法,以便我可以按照建议使用“EXISTS”。
CREATE TRIGGER trg_iu_UniqueActiveName
ON [dbo].[TestTable1]
AFTER INSERT, UPDATE
AS
IF(UPDATE([Name]))
BEGIN
DECLARE @cnt [int];
WITH MaxVers AS
(SELECT Id, Max(Version) AS MaxVersion
FROM [dbo].[TestTable1]
GROUP BY Id)
SELECT @cnt = COUNT(1)
FROM [dbo].[TestTable1] t
INNER JOIN MaxVers ON t.Id = MaxVers.Id AND t.Version = MaxVers.MaxVersion
INNER JOIN [inserted] i ON t.[Id] = MaxVers.[Id]
WHERE t.[Name] = i.[Name] AND NOT [t].[Id] = [i].[Id] ;
IF( @cnt > 0)
BEGIN
DECLARE @name nvarchar(50)
SELECT @name = [Name] FROM inserted;
RAISERROR('The name "%s" is already in use by an active entity.', 16, 1, @name);
ROLLBACK TRANSACTION;
END
END;
GO
编辑3:这是“存在”版本(注意,我认为错误处理部分中的选择不能与多个插入的记录一起正常工作):
CREATE TRIGGER trg_iu_UniqueActiveName
ON [dbo].[TestTable1]
AFTER INSERT, UPDATE
AS
IF(UPDATE([Name]))
BEGIN
IF(EXISTS (
SELECT t.Id
FROM [dbo].[TestTable1] t
INNER JOIN (
SELECT Id, Max(Version) AS MaxVersion
FROM [dbo].[TestTable1]
GROUP BY Id) maxVer
ON t.[Id] = [maxVer].[Id] AND [t].[Version] = [maxVer].[MaxVersion]
INNER JOIN [inserted] i ON t.[Id] = MaxVer.[Id]
WHERE [t].[Name] = [i].[Name] AND NOT [t].[Id] = [i].[Id]
))
BEGIN
DECLARE @name nvarchar(50)
SELECT @name = [Name] FROM inserted;
RAISERROR('The name "%s" is already in use by an active entity.', 16, 1, @name);
ROLLBACK TRANSACTION;
END
END;
GO
答案 0 :(得分:2)
我唯一能想到的是语句“When a CTE is used in a statement that is part of a batch, the statement before it must be followed by a semicolon.”(Transact SQL Reference)意味着CTE不能在IF语句中使用。
顺便说一句,你还有两个错误:1)inserted
伪表不包含在第一个子查询中,即使你在are子句中引用它也是如此。 2)您的触发器假设正在插入或更新单行。可能会有多个重复的名称,但raiserror只会报告其中一个名称。
编辑并在(select count(*) ...) >
执行时避免exists (select * ....)
存在可以在第一行停止。
编辑2 废话。 SQL Server触发默认为after触发器。因此,当触发器触发时,您检查存在的行已存在于表中:
CREATE TRIGGER trg_iu_UniqueActiveName
ON [dbo].[TestTable1]
AFTER INSERT, UPDATE
AS
IF(UPDATE([Name]))
BEGIN
IF EXISTS
(
SELECT *
FROM [dbo].[TestTable1] t
INNER JOIN inserted i on i.[NAME] = t.[NAME]
INNER JOIN (SELECT Id, Max(Version) AS MaxVersion
FROM [dbo].[TestTable1]
GROUP BY Id) MaxVers ON t.Id = MaxVers.Id AND t.Version = MaxVers.MaxVersion
)
BEGIN
DECLARE @name nvarchar(50)
SELECT @name = [Name] FROM inserted;
RAISERROR('The name "%s" is already in use.', 16, 1, @name);
END
END;
GO
insert into testTable1 (name) values ('Hello')
结果:
Msg 50000, Level 16, State 1, Procedure trg_iu_UniqueActiveName, Line 20
The name "Hello" is already in use.
(1 row(s) affected)
另外,raiserror不会执行回滚,因此该行仍然存在。
答案 1 :(得分:1)
我认为您不能将CTE用于内部查询。
使用此方法解决方法:
DECLARE @cnt int;
WITH MaxVers AS
(SELECT Id, Max(Version) AS MaxVersion
FROM [dbo].[TestTable1]
GROUP BY Id)
SELECT @cnt = Count(1)
FROM [dbo].[TestTable1] t
INNER JOIN MaxVers ON t.Id = MaxVers.Id AND t.Version = MaxVers.MaxVersion
WHERE t.[Name] = inserted.[Name];
IF @cnt > 0
BEGIN
DECLARE @name nvarchar(50)
SELECT @name = [Name] FROM inserted;
RAISERROR('The name "%s" is already in use.', 16, 1, @name);
END
答案 2 :(得分:1)
似乎不喜欢WITH
内的IF
语句。
请尝试使用以下SQL:
SELECT COUNT(1)
FROM TestTable1 t1
WHERE t.Name = (SELECT [Name] FROM inserted)
AND t.Version = (SELECT MAX(Version) FROM TestTable1 t2 WHERE t2.Id = t.Id)
在我看来简单得多。但是,这并不考虑插入表中的多行。将其更改为IN
而不是=
可能会这样做。
正如其他人所指出的那样,有时会从WITH
语句中加入分号,但在这种情况下我无法得到它。