PAYATHON 2026

SQL Server 中如何用变量合并多个查询?

支付小周

结论

需要删除变量声明与使用之间的 GO。GO 会结束当前批次,而局部变量无法跨批次使用。

示例中还有一个更直接的问题:@copy_loc 和 @new_loc 被声明为 varchar(7),但 'OLD LOCATION'、'COPY LOCATION' 和 'NEW LOCATION' 都超过了 7 个字符。赋值时字符串会被截断,导致 location = @copy_loc 匹配不到源数据。

建议这样处理:

  • 变量的数据类型和长度应与 location 列一致;
  • 变量声明、删除和插入需要放在同一个批次中,不要在中间使用 GO;
  • 用 IN 同时处理 'waive'、'payment' 和 'refund',不必重复三遍查询。

推荐写法

假设 location 列可以容纳 50 个字符:

SET XACT_ABORT ON;

BEGIN TRANSACTION;

DECLARE @copy_loc varchar(50) = 'OLD LOCATION';
DECLARE @new_loc  varchar(50) = 'NEW LOCATION';

DELETE FROM circ_slip_field
WHERE location = @new_loc
  AND circ_slip IN ('waive', 'payment', 'refund');

INSERT INTO circ_slip_field
(
    circ_slip,
    location,
    section,
    ord,
    circ_slip_field_type,
    label,
    field_column,
    append_to_prev,
    justify,
    max_field_len,
    min_field_len,
    data_mask
)
SELECT
    circ_slip,
    @new_loc,
    section,
    ord,
    circ_slip_field_type,
    label,
    field_column,
    append_to_prev,
    justify,
    max_field_len,
    min_field_len,
    data_mask
FROM circ_slip_field
WHERE location = @copy_loc
  AND circ_slip IN ('waive', 'payment', 'refund');

COMMIT TRANSACTION;

这段代码会先删除目标 location 下三种 circ_slip 的旧记录,再从源 location 复制对应记录。SELECT 保留了原有的 circ_slip 列,因此三种类型会按各自的原值插入,不会混在一起。

为什么 varchar(7) 会导致查询无效

下面的赋值无法保存完整字符串:

DECLARE @copy_loc varchar(7);
SET @copy_loc = 'OLD LOCATION';

SELECT @copy_loc;

变量最多只能容纳 7 个字符,所以实际得到的是:

OLD LOC

查询条件也就变成了:

WHERE location = 'OLD LOC'

如果表中保存的是完整的 'OLD LOCATION',查询便找不到源记录,INSERT ... SELECT 最终会插入 0 行。

变量类型最好与表中的 location 列完全一致。如果该列使用 nvarchar,变量也应使用 nvarchar,字符串常量则要加上 N 前缀:

DECLARE @copy_loc nvarchar(50) = N'OLD LOCATION';
DECLARE @new_loc  nvarchar(50) = N'NEW LOCATION';

GO 为什么会影响变量

GO 不是 SQL Server 的 T-SQL 语句,而是 SSMS、sqlcmd 等客户端识别的批次分隔符。客户端执行到 GO 时会结束当前批次,其中声明的局部变量也随之失效。

因此,下面两个批次无法共享变量:

DECLARE @new_loc varchar(50) = 'NEW LOCATION';
GO

SELECT @new_loc;

执行后一批次时,通常会出现类似”必须声明标量变量”的错误。要让后续多条语句使用同一组变量,就不能在它们之间加入 GO:

DECLARE @copy_loc varchar(50) = 'OLD LOCATION';
DECLARE @new_loc  varchar(50) = 'NEW LOCATION';

DELETE ...
INSERT ...

如果脚本中有 GO,却没有出现变量未声明错误,需要检查是否只选中了部分脚本执行,或者各批次是否重新声明了变量。通过应用程序驱动或 ORM 执行 SQL 时,GO 通常不会被识别。此时应由程序拆分批次,或者直接删除 GO。

执行前的检查

正式删除数据前,可以先确认源记录是否存在:

DECLARE @copy_loc varchar(50) = 'OLD LOCATION';

SELECT *
FROM circ_slip_field
WHERE location = @copy_loc
  AND circ_slip IN ('waive', 'payment', 'refund');

也可以分别统计每种类型的记录数:

SELECT circ_slip, COUNT(*) AS row_count
FROM circ_slip_field
WHERE location = 'OLD LOCATION'
  AND circ_slip IN ('waive', 'payment', 'refund')
GROUP BY circ_slip;

如果想先检查执行结果再决定是否提交,可以暂时把 COMMIT TRANSACTION 改成 ROLLBACK TRANSACTION。还要确认源 location 与目标 location 不相同,否则前面的 DELETE 会连同稍后需要复制的源记录一起删除。

备注:内容仅供参考。