短路表

Short-circuiting tables

我正在升级一个数据库的多个相同副本,这些副本可能已经部分升级,并且出于某种原因,布尔值存储在 nvarchar(5).

所以在下面,(存在于 INSERT > SELECT 块中),我需要检查列 ShowCol 是否存在,如果存在则用 0 填充不,或者如果是,则用评估字符串 bool 的结果填充它:

CASE
    WHEN COL_LENGTH('dbo.TableName', 'ShowCol') IS NULL THEN 0
    ELSE IIF(LOWER(ShowCol) = 'false', 0, 1)
END

...但我收到错误“无效的列名称 'ShowCol'”。我好像不能短路这个,你能帮忙吗?

值得注意的是,该列(如果确实存在)包含“false”、“False”和“FALSE”的混合,所以这就是 LOWER() 的重点。 (True 列偶尔也需要处理尾随空格,这就是为什么我只处理 False 而其他一切都是真的。)

我怀疑是因为 LOWER() 中的换行导致服务器始终对表达式求值。

你不能短路一个列的存在(它与LOWER()无关;如果你删除它,什么都不会改变)。

您需要动态 SQL,例如:

DECLARE @sql nvarchar(max) = N'UPDATE trg SET
  trg.col1 = src.col1,
  trg.col2 = src.col2';

IF COL_LENGTH('dbo.TableName', 'ShowCol') > 0
BEGIN
  SET @sql += N', trg.ShowCol = IIF(LOWER(src.ShowCol) = ''false'', 0, 1)';
END

SET @sql += N' ...
  FROM dbo.TableName AS trg
  INNER JOIN dbo.Origin AS src
  ON ...';

EXEC sys.sp_executesql @sql; -- ,N'params', @params;

当你选择数据时,你可以通过引入常量来代替列来稍微欺骗解析器,利用SQL服务器的需求甚至在与语法所建议的范围不同的范围内查找列引用。我在 Make SQL Server DMV Queries Backward Compatible 中谈到了这个。我不知道有什么直接的方法可以在没有动态 SQL 的情况下使用写入,因为解析器会在那里进行更严格的检查,所以更难被愚弄。

假设您有这些表:

CREATE TABLE dbo.SourceTable(a int, b int, c int);
INSERT dbo.SourceTable(a,b,c) VALUES(1,2,3);

CREATE TABLE dbo.DestinationWithAllColumns(a int, b int, c int);
INSERT dbo.DestinationWithAllColumns(a,b,c) VALUES(1,2,3);

CREATE TABLE dbo.DestinationWithoutAllColumns(a int, b int);
INSERT dbo.DestinationWithoutAllColumns(a,b) VALUES(1,2);

您可以针对其中任何一个编写 SELECT,生成名为 c:

的 int 输出列
;WITH optional_columns AS
(
  SELECT c = CONVERT(int, NULL)
)
SELECT trg.a, trg.b, trg.c
FROM optional_columns
CROSS APPLY 
  (SELECT a,b,c FROM dbo.DestinationWithAllColumns) AS trg
INNER JOIN dbo.SourceTable AS src ON src.a = trg.a;

输出:

a b c
1 2 3
;WITH optional_columns AS
(
  SELECT c = CONVERT(int, NULL)
)
SELECT trg.a, trg.b, trg.c 
FROM optional_columns
CROSS APPLY 
  (SELECT a,b,c FROM dbo.DestinationWithoutAllColumns) AS trg
INNER JOIN dbo.SourceTable AS src ON src.a = trg.a;

输出:

a b c
1 2 null

到目前为止,还不错。但是一旦您尝试更新:

;WITH optional_columns AS
(
  SELECT c = CONVERT(int, NULL)
)
UPDATE trg SET trg.b = src.b, trg.c = src.c
FROM optional_columns
CROSS APPLY 
  (SELECT a,b,c FROM dbo.DestinationWithoutAllColumns) AS trg
INNER JOIN dbo.SourceTable AS src ON src.a = trg.a;

Msg 4421, Level 16, State 1
Derived table 'trg' is not updatable because a column of the derived table is derived or constant.