由于语法和回滚事务,我的最终更新语句应该出错,预期的错误应该类似于:
消息 207,级别 16,状态 1,第 45 行无效的列名称“FakeTest1”
我很好奇当允许执行引用不存在的列的子查询时,为什么 SQL Server 更新列中的每一行。
我正在创建一个包含两列的临时表,FakeID
并且FakeVarchar
.
SELECT [FakeID], [FakeVarchar]
INTO [#T]
FROM [CTE];
Run Code Online (Sandbox Code Playgroud)
当我的UPDATE
语句使用此WHERE
子句在 中指定列 [ID] 时SELECT SUBQUERY
,它会更新整个表,而不是在解析命令时出错。
WHERE [FakeTableDestination].[ID] IN
(
SELECT [ID]
FROM [#T]
);
Run Code Online (Sandbox Code Playgroud)
应该在子查询中以使其正确运行的列将是FakeID
. 此语句显然有效,并且按预期更新。
WHERE [FakeTableDestination].[ID] IN
(
SELECT [FakeID]
FROM [#T]
);
Run Code Online (Sandbox Code Playgroud)
当我用任何其他列名替换 FakeID / ID 时,它会按预期失败。
WHERE [FakeTableDestination].[ID] IN
(
SELECT [NotActuallyAColumnInThisTableEither]
FROM [#T]
);
Run Code Online (Sandbox Code Playgroud)
IF OBJECT_ID('dbo.FakeTableSource', 'U') IS NOT NULL
BEGIN
DROP TABLE [dbo].[FakeTableSource];
END;
CREATE TABLE [dbo].[FakeTableSource]
(
[FakeID] INT PRIMARY KEY, [FakeVarchar] VARCHAR(10)
);
GO
IF OBJECT_ID('tempdb.dbo.#T', 'U') IS NOT NULL
BEGIN
DROP TABLE [#T];
END;
IF OBJECT_ID('dbo.FakeTableDestination', 'U') IS NOT NULL
BEGIN
DROP TABLE [dbo].[FakeTableDestination];
END;
CREATE TABLE [dbo].[FakeTableDestination]
(
[ID] INT PRIMARY KEY, [FakeVarchar] VARCHAR(10)
);
GO
INSERT INTO [dbo].[FakeTableSource]( [FakeID], [FakeVarchar] )
VALUES( 1, 'One' ), ( 2, 'Two' ), ( 3, 'Three' ), ( 4, 'Four' );
INSERT INTO [dbo].[FakeTableDestination]( [ID], [FakeVarchar] )
VALUES( 1, 'One' ), ( 2, 'Two' ), ( 3, 'Five' ), ( 5, 'Four' );
WITH CTE
AS (SELECT [FakeID], [FakeVarchar]
FROM [dbo].[FakeTableSource])
SELECT [FakeID], [FakeVarchar]
INTO [#T]
FROM [CTE];
UPDATE [dbo].[FakeTableDestination]
SET [FakeVarchar] = [src].[FakeVarchar]
FROM [dbo].[FakeTableSource] AS [SRC]
WHERE [FakeTableDestination].[ID] IN
(
SELECT [ID]
FROM [#T]
);
SELECT *
FROM [dbo].[FakeTableSource];
SELECT *
FROM [dbo].[FakeTableDestination];
SELECT *
FROM [#t];
Run Code Online (Sandbox Code Playgroud)
IF OBJECT_ID('dbo.FakeTableSource', 'U') IS NOT NULL
BEGIN
DROP TABLE [dbo].[FakeTableSource];
END;
CREATE TABLE [dbo].[FakeTableSource]
(
[FakeID] INT PRIMARY KEY, [FakeVarchar] VARCHAR(10)
);
GO
IF OBJECT_ID('tempdb.dbo.#T', 'U') IS NOT NULL
BEGIN
DROP TABLE [#T];
END;
IF OBJECT_ID('dbo.FakeTableDestination', 'U') IS NOT NULL
BEGIN
DROP TABLE [dbo].[FakeTableDestination];
END;
CREATE TABLE [dbo].[FakeTableDestination]
(
[ID] INT PRIMARY KEY, [FakeVarchar] VARCHAR(10)
);
GO
INSERT INTO [dbo].[FakeTableSource]( [FakeID], [FakeVarchar] )
VALUES( 1, 'One' ), ( 2, 'Two' ), ( 3, 'Three' ), ( 4, 'Four' );
INSERT INTO [dbo].[FakeTableDestination]( [ID], [FakeVarchar] )
VALUES( 1, 'One' ), ( 2, 'Two' ), ( 3, 'Five' ), ( 5, 'Four' );
WITH CTE
AS (SELECT [FakeID], [FakeVarchar]
FROM [dbo].[FakeTableSource])
SELECT [FakeID], [FakeVarchar]
INTO [#T]
FROM [CTE];
UPDATE [dbo].[FakeTableDestination]
SET [FakeVarchar] = [src].[FakeVarchar]
FROM [dbo].[FakeTableSource] AS [SRC]
WHERE [FakeTableDestination].[ID] IN
(
SELECT [FakeID]
FROM [#T]
);
SELECT *
FROM [dbo].[FakeTableSource];
SELECT *
FROM [dbo].[FakeTableDestination];
SELECT *
FROM [#t];
Run Code Online (Sandbox Code Playgroud)
该WHERE
条款似乎是允许这种UPDATE
情况发生的原因。例如,我会将列名FakeTableDestination
从更改FakeID
为Test
。然后我可以引用该Test
列,#T
即使该列的别名仍为FakeID
. 这会导致整个列在更新语句期间更新。
说明该行为的 SQL 代码
IF OBJECT_ID('dbo.FakeTableSource', 'U') IS NOT NULL
BEGIN
DROP TABLE [dbo].[FakeTableSource];
END;
CREATE TABLE [dbo].[FakeTableSource]
(
[FakeID] INT PRIMARY KEY, [FakeVarchar] VARCHAR(10)
);
GO
IF OBJECT_ID('tempdb.dbo.#T', 'U') IS NOT NULL
BEGIN
DROP TABLE [#T];
END;
IF OBJECT_ID('dbo.FakeTableDestination', 'U') IS NOT NULL
BEGIN
DROP TABLE [dbo].[FakeTableDestination];
END;
CREATE TABLE [dbo].[FakeTableDestination]
(
[Test] INT PRIMARY KEY, [FakeVarchar] VARCHAR(10)
);
GO
INSERT INTO [dbo].[FakeTableSource]( [FakeID], [FakeVarchar] )
VALUES( 1, 'One' ), ( 2, 'Two' ), ( 3, 'Three' ), ( 4, 'Four' );
INSERT INTO [dbo].[FakeTableDestination]( [Test], [FakeVarchar] )
VALUES( 1, 'One' ), ( 2, 'Two' ), ( 3, 'Five' ), ( 5, 'Four' );
WITH CTE
AS (SELECT [FakeID], [FakeVarchar]
FROM [dbo].[FakeTableSource])
SELECT [FakeID], [FakeVarchar]
INTO [#T]
FROM [CTE];
UPDATE [dbo].[FakeTableDestination]
SET [FakeVarchar] = [src].[FakeVarchar]
FROM [dbo].[FakeTableSource] AS [SRC]
WHERE [FakeTableDestination].[Test] IN
(
SELECT [Test]
FROM [#T]
);
SELECT *
FROM [dbo].[FakeTableSource];
SELECT *
FROM [dbo].[FakeTableDestination];
SELECT *
FROM [#t];
Run Code Online (Sandbox Code Playgroud)
如果 ID 是临时表中列的名称并且它是隐藏的怎么办?
SELECT
*
FROM [tempdb].[sys].[syscolumns]
WHERE [name] = 'FakeID';
Run Code Online (Sandbox Code Playgroud)
好的,我看到这里列出了 ID,我看到了其他列,如 xtype、typestat 等。那么如果我尝试使用其他列之一呢?我尝试替换 xtype / typestat 并且它们在语法上也失败了。
也许是因为您使用的是临时表?
我对表变量和物理表也有同样的行为。
表变量代码
IF OBJECT_ID('dbo.FakeTableSource', 'U') IS NOT NULL
BEGIN
DROP TABLE [dbo].[FakeTableSource];
END;
CREATE TABLE [dbo].[FakeTableSource]
([FakeID] INT
PRIMARY KEY,
[FakeVarchar] VARCHAR(10)
);
GO
IF OBJECT_ID('dbo.FakeTableDestination', 'U') IS NOT NULL
BEGIN
DROP TABLE [dbo].[FakeTableDestination];
END;
CREATE TABLE [dbo].[FakeTableDestination]
([ID] INT
PRIMARY KEY,
[FakeVarchar] VARCHAR(10)
);
GO
DECLARE @T TABLE
([FakeID] INT
PRIMARY KEY,
[FakeVarchar] VARCHAR(10)
);
INSERT INTO [dbo].[FakeTableSource]
(
[FakeID],
[FakeVarchar]
)
VALUES
(1,
'One'),
(2,
'Two'),
(3,
'Three'),
(4,
'Four');
INSERT INTO [dbo].[FakeTableDestination]
(
[ID],
[FakeVarchar]
)
VALUES
(1,
'One'),
(2,
'Two'),
(3,
'Five'),
(5,
'Four');
WITH CTE
AS (SELECT
[FakeID],
[FakeVarchar]
FROM [dbo].[FakeTableSource])
INSERT INTO @T
(
[FakeID],
[FakeVarchar]
)
SELECT
[FakeID],
[FakeVarchar]
FROM [CTE];
UPDATE [dbo].[FakeTableDestination]
SET
[FakeVarchar] = [src].[FakeVarchar]
FROM [dbo].[FakeTableSource] AS [SRC]
WHERE
[FakeTableDestination].[ID] IN
(
SELECT
[ID]
FROM @T
);
Run Code Online (Sandbox Code Playgroud)
物理表代码片段
SELECT
[FakeID],
[FakeVarchar]
INTO dbo.T
FROM [CTE];
UPDATE [dbo].[FakeTableDestination]
SET
[FakeVarchar] = [src].[FakeVarchar]
FROM [dbo].[FakeTableSource] AS [SRC]
WHERE
[FakeTableDestination].[ID] IN
(
SELECT
[ID]
FROM T
);
Run Code Online (Sandbox Code Playgroud)
如果将 更改为
UPDATE
会NOT IN
怎样?
它不会更新该列中的任何行。
你在什么版本的 SQL Server 上试过这个?SQL Server 2012 SP1、SP2、2016 SP 2。
查询计划是什么样的?