必须声明 table 变量“@TempTable”

Must declare the table variable "@TempTable"

我想添加一个脚本post-部署来插入数据。但是当我在下面执行这个脚本时,我收到了这条消息错误:

Must declare the table variable "@TempTable".

这个脚本的目的是强制添加作为主键的 Id,我知道我们可以使用:

SET IDENTITY_INSERT [dbo].[TableName] ON;
GO

合并后

SET IDENTITY_INSERT [dbo].[TableName] OFF;
GO

但它对我不起作用我无法部署我的 DacPac

DECLARE @vehicleType TABLE(
    [VehicleTypeId] BIGINT, 
    [Name] NVARCHAR(200) NOT NULL
);



INSERT INTO @VehicleType ([VehicleTypeId], [Name]) 
VALUES(1,'Automobile'),(2,'HeavyVehicle'),(3,'Motorcycle')

SET IDENTITY_INSERT [dbo].[VehicleType] ON;
GO

MERGE INTO [dbo].[VehicleType]
    USING  @VehicleType as vhlt
        ON ([dbo].[VehicleType].[VehicleTypeId] = vhtl.[VehicleTypeId] and [dbo].[VehicleType].[Name] = vhlt.[Name])
        WHEN NOT MATCHED THEN 
            INSERT VALUES ([VehicleTypeId], [Name]);

SET IDENTITY_INSERT [dbo].[VehicleType] OFF;
GO

我需要指出的第一件事是像这样的查找 table 应该 而不是 有一个自动编号 (IDENTITY)柱子。自动编号值是 合成的,仅当您不关心值是什么时才应使用,只关心它是唯一的。当用户可以将值添加到查找集时,这很有效。

对于非用户可服务的查找 table,就像您的 VehicleType,您 关心 VehicleTypeId 值是什么自动编号对你不利。如果您可以从列定义中删除 IDENTITY 属性,您绝对应该这样做。如果不能,您将无法使用 IDENTITY_INSERT。请记住不要在以后的查找 table 设计中使用 IDENTITY


有一种方法可以在没有 table 变量的情况下执行此操作:使用带有 MERGE 语句的 CTE。

CTE 将包含您使用联合静态 SELECT 语句所需的所有值(使用 UNION ALL 会跳过重复检查,因为您知道不会有任何重复项)。 MERGE 语句使用 CTE 作为其来源 table.

MERGE语句中的ON子句应该只比较主键。如果更改了非键列中的值,则会导致插入新行;新行将违反主键。相反,将 Name 列更新为 CTE 中的值。为此,我在下面添加了一个 WHEN MATCHED 子句。

我建议在 MERGE 语句中始终使用 dstsrc 作为别名。 MERGE 语句可以涉及很多不同的部分。如果您对源和 target/destination table 使用相同的别名,那么您不必担心的复杂性就会降低一点。我已经看到这实际上可以帮助开发人员学习语法和这项技术。

我喜欢将 CTE 命名为 src,这样我就不必为它取别名了。

-- Only IF you cannot remove the IDENTITY property from VehicleTypeId:
SET IDENTITY_INSERT [dbo].[VehicleType] ON;
GO

-- Stage the data and merge it into the table in one shot:
WITH [src] ([VehicleTypeId], [Name])
AS
(
    SELECT           1, 'Automobile'
    UNION ALL SELECT 2, 'HeavyVehicle'
    UNION ALL SELECT 3, 'Motorcycle'
)
MERGE INTO [dbo].[VehicleType] AS [dst]
USING [src]
    ON [dst].[VehicleTypeId] = src.[VehicleTypeId]
WHEN NOT MATCHED BY TARGET THEN
    INSERT ([VehicleTypeId], [Name])
    VALUES ([src].[VehicleTypeId], [src].[Name])
WHEN MATCHED AND ([dst].[Name] <> [src].[Name]) THEN
    UPDATE
    SET [Name] = [src].[Name]
;
GO

-- Again, only IF you cannot remove the IDENTITY property from VehicleTypeId:
SET IDENTITY_INSERT [dbo].[VehicleType] OFF;
GO

当我编写这样的脚本并打算从 SSMS 运行 时,我包含了一个 OUTPUT 子句,这样我就可以看到发生了什么并验证它是否正确。

...
OUTPUT $action AS [*Action],
    COALESCE([inserted].[VehicleTypeId], [deleted].[VehicleTypeId]) AS [=VehicleTypeId],
    [deleted].[Name] AS [-Name],
    [inserted].[Name] AS [+Name]
    -- Repeat deleted and inserted AS -/+ for remaining non-key columns.
;

关于我在别名中使用的前缀的注释:

  • * 用于元数据列(真的,它永远只是 *Action)。
  • = 用于主键列。它们不会改变,因此键中每列只有一个。
  • - 表示旧值(例如 -Name)。
  • + 表示新值(例如 +Name)。