使用内连接时可更新视图的列属性不正确

jme*_*hus 5 sql-server view

我有一个表 atbl_UserSetting,其中有一列名称为 ID,该列是具有标识和种子/增量的主键。

在此输入图像描述

普通用户可以通过视图访问该表。这是视图:

CREATE OR ALTER   VIEW [dbo].[atbv_UserSetting]
AS
SELECT        ID, User_ID, [Key], Value
FROM            dbo.atbl_UserSetting AS US
WHERE        EXISTS
    (SELECT        TOP (1) 1 AS Expr1
    FROM            dbo.atbl_User AS U
    WHERE        (ID = US.User_ID) AND (Login = SUSER_NAME()))
Run Code Online (Sandbox Code Playgroud)

现在,当我运行此查询时,它将返回 true (1):

SELECT COLUMNPROPERTY(OBJECT_ID('atbv_UserSetting'),'ID','IsIdentity')
Run Code Online (Sandbox Code Playgroud)

INFORMATION_SCHEMA.COLUMNS 相同。

但是,如果我将视图更改为此(加入用户表):

CREATE OR ALTER   VIEW [dbo].[atbv_UserSetting]
AS
SELECT        US.ID, US.User_ID, US.[Key], US.Value
FROM            dbo.atbl_UserSetting AS US
INNER JOIN dbo.atbl_User AS U ON U.ID = US.User_ID
WHERE U.[Login] = SUSER_NAME()
Run Code Online (Sandbox Code Playgroud)

该列的相同属性返回 false (0)。

这反过来会导致 SqlCommandBuilder (.NET System.Data.SqlClient) 生成带有 ID 列的插入命令,并且在 SqlAdapter 上调用 Update 时,它​​将失败,因为 IDENTITY_INSERT 设置为 OFF。当视图使用 WHERE 子句而不是加入另一个表时,columnproperty 会为 IsIdentity 返回 true(1),并且 SqlCommandBuilder 会正确地在插入命令中不包含 ID 列。

所以我的问题是,当其他表加入时,为什么 SQL Server 无法识别视图的列属性“IsIdentity”的正确值?

Pau*_*ite 7

IsIdentity属性指示该列是否被视为具有标识属性。

\n

当您测试视图的列时,您要求引擎确定标识属性是否将传输到视图中的该列。问题在于基础列是否具有该属性(这是您使用的框架似乎所期望的)。

\n

该行为没有很好的记录(或者在所有情况下确实一致),但至少在SELECT - INTO 子句 (Transact-SQL)中引用了它:

\n
\n

当将现有标识列选择到新表中时,新列将继承 IDENTITY 属性,除非满足以下条件之一:

\n
    \n
  • SELECT 语句包含一个联接。
  • \n
  • 使用 UNION 连接多个 SELECT 语句。
  • \n
  • 标识列在选择列表中多次列出。
  • \n
  • 标识列是表达式的一部分。
  • \n
  • 标识列来自远程数据源。
  • \n
\n
\n

您不是在新表中选择标识列,但原理是相同的。视图是派生的表值表达式,因此服务器遵循其(奇怪的)规则来继承标识属性。

\n

需要明确的是:每个列或表达式都必须具有完全指定的类型。身份属性是该类型的一方面。根据查询构造(在视图中或其他地方),标识属性可能会保留或丢失。上面提到的文档说明了一些情况,但并不详尽。

\n

我对SqlBuilder一无所知,但它对 \xe2\x80\x94it\ 的明显期望IsIdentity并非不合理,只是没有反映 SQL Server 的工作方式(并且一直如此)。

\n

一个非常简单的示例,其中标识属性不会传输,因为该列被列出多次:

\n
CREATE TABLE dbo.T1 (i integer IDENTITY NOT NULL);\nGO\nCREATE OR ALTER VIEW dbo.V\nWITH SCHEMABINDING AS\nSELECT T1.i, T1.i AS i2\nFROM dbo.T1 AS T1;\nGO\nSELECT TableIdentity = COLUMNPROPERTY(OBJECT_ID(N\'dbo.T1\', \'U\'), N\'i\', \'IsIdentity\');\nSELECT ViewIdentity = COLUMNPROPERTY(OBJECT_ID(N\'dbo.V\', \'V\'), N\'i\', \'IsIdentity\');\n
Run Code Online (Sandbox Code Playgroud)\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n
表标识查看身份
10
\n
\n

在线数据库<>小提琴演示

\n


Tib*_*szi 1

第二个查询在语义上与第一个查询不同,因为它可以在 dbo.atbl_UserSetting 中为每个 ID 返回多行。即,显然,当外部查询仅涉及一个表时,SQL Server 确定假定身份属性是安全的(第二个查询仅在 WHERE 子句中使用,这意味着不可能向结果“添加行”)。