短路表
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.
我正在升级一个数据库的多个相同副本,这些副本可能已经部分升级,并且出于某种原因,布尔值存储在 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
:
;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.