我有一个表 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”的正确值?
该IsIdentity
属性指示该列是否被视为具有标识属性。
当您测试视图的列时,您要求引擎确定标识属性是否将传输到视图中的该列。问题不在于基础列是否具有该属性(这是您使用的框架似乎所期望的)。
\n该行为没有很好的记录(或者在所有情况下确实一致),但至少在SELECT - INTO 子句 (Transact-SQL)中引用了它:
\n\n\n当将现有标识列选择到新表中时,新列将继承 IDENTITY 属性,除非满足以下条件之一:
\n\n
\n- SELECT 语句包含一个联接。
\n- 使用 UNION 连接多个 SELECT 语句。
\n- 标识列在选择列表中多次列出。
\n- 标识列是表达式的一部分。
\n- 标识列来自远程数据源。
\n
您不是在新表中选择标识列,但原理是相同的。视图是派生的表值表达式,因此服务器遵循其(奇怪的)规则来继承标识属性。
\n需要明确的是:每个列或表达式都必须具有完全指定的类型。身份属性是该类型的一方面。根据查询构造(在视图中或其他地方),标识属性可能会保留或丢失。上面提到的文档说明了一些情况,但并不详尽。
\n我对SqlBuilder一无所知,但它对 \xe2\x80\x94it\ 的明显期望IsIdentity
并非不合理,只是没有反映 SQL Server 的工作方式(并且一直如此)。
一个非常简单的示例,其中标识属性不会传输,因为该列被列出多次:
\nCREATE 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表标识 | 查看身份 |
---|---|
1 | 0 |
第二个查询在语义上与第一个查询不同,因为它可以在 dbo.atbl_UserSetting 中为每个 ID 返回多行。即,显然,当外部查询仅涉及一个表时,SQL Server 确定假定身份属性是安全的(第二个查询仅在 WHERE 子句中使用,这意味着不可能向结果“添加行”)。