此Sql Server查询中的CTE语法有什么问题?

此Sql Server查询中的CTE语法有什么问题?,sql,sql-server,tsql,Sql,Sql Server,Tsql,有人能解释为什么SQLServer抱怨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 TRIGGE

有人能解释为什么SQLServer抱怨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:这是存在的版本说明,我认为错误处理部分中的select无法正确处理多条插入的记录:

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

我认为不能将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

我唯一能理解的是,语句Transact-SQL引用意味着不能在IF语句中使用CTE

顺便说一句,您还有两个错误:1插入的伪表未包含在第一个子查询中,即使您在WARE子句中引用了它。2您的触发器假定插入或更新一行。可能存在多个重复名称,但raiserror将只报告其中一个

编辑并避免选择计数*…>存在时,选择*。。。。将要执行的操作可以在第一行停止

编辑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不会执行回滚,因此该行仍然存在。

似乎不喜欢IF中的WITH语句

请尝试以下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而不是an=可能会做到这一点


正如其他人所指出的,有时在WITH语句的from中放置分号是有效的,但在本例中我无法将其设置为。

为什么不将CTE select表达式移动到内部联接中的子select?您应该使用EXISTS而不是Count>0>0。。。。嗯。。。您是否正在将CTE与0进行比较?您的触发器未被写入以正确处理多行更新,这可以通过从插入中选择@name=[name]来证明;id是标识列吗?那你为什么需要按id分组呢?谢谢你指出EricZ的身份。这是一个打字错误。看起来你是对的,不允许在那个位置使用。谢谢