更新两个表的联合视图中的值

Update values in a view that is a Union of two tables

提问人:B.I. 提问时间:11/7/2023 最后编辑:PM 77-1B.I. 更新时间:11/7/2023 访问量:68

问:

我在 SQL Server 上托管了两个具有相同架构的简单表(无约束、一个主键、无触发器等)。出于各种原因,我必须将表格分开,但我希望能够将它们合并在一起,以便进行监督。我希望工作人员能够通过此联合视图编辑表格。

GlobalID 是每个表的主键,它在联合视图中也是唯一的。

CREATE VIEW V_View AS 
SELECT
[GlobalID]
,[UserComment]
FROM
TABLE_A

UNION

SELECT
[GlobalID]
,[UserComment]
FROM
TABLE_B

我尝试通过几个应用程序(按时编码和 Microsoft 访问)编辑此视图中的数据,但我收到相同的错误:

视图或函数的更新或插入V_View失败,因为它包含派生字段或常量字段

如果我从视图中删除并集,以便只有一个或另一个表参与,则编辑工作完美,因此我确信是 UNION 命令阻止了编辑。

有什么方法可以使这样的视图可编辑吗?我能否以不同的方式构造它,以便派生/常量错误不会阻止编辑?

SQL Server 视图

评论

1赞 Xedni 11/7/2023
不能对合并视图进行更新。SQL 不知道要更新哪个表。
2赞 Dan Guzman 11/7/2023
查看可更新的视图文档。只有一个表可以作为更新的目标。

答:

2赞 Stu 11/7/2023 #1

如前所述,视图不可更新,因为 SQL Server 不知道哪些行属于哪个表。

您需要的概念称为分区视图。为了允许视图可更新,每个表都需要对其主键进行检查约束,以定义允许的值范围 - 例如:

create table t1 (id Int constraint t1chk check (id < 10)  primary key, other columns);

请在此处查看工作示例 Fiddle

评论

3赞 Charlieface 11/7/2023
实际上,确切的错误是因为它是一个 which 隐含 which impile ,所以有一个隐含的派生表,但你是对的,即使会因为你的原因而失败 dbfiddle.uk/VSoChoANUNIONDISTINCTGROUP BYUNION ALL
1赞 siggemannen 11/7/2023
一个人每天都能学到一些东西!
0赞 B.I. 11/7/2023
谢谢你,斯图。我不知道如何检查视图,因为 PK (GlobalID) 是一个 UUID,所以我没有想到的数学约束 - 有什么建议吗?
0赞 Charlieface 11/7/2023
@B.I.例如,您需要添加一个单独的列,同样需要为 添加一个单独的列。如果检查约束严格不相交,则可以将它们用作视图中的分部列。alter column TABLE_A add Discriminator char(1) not null check (Discriminator = 'A')TABLE_B
0赞 B.I. 11/8/2023
不幸的是,GUID 是我唯一的选择。这些表通常会脱机更新/插入,并在重新建立与 Internet 的连接后同步回数据库。GUID 是保证主键唯一性的唯一方法,然后可以使用主键在脱机时创建相关表中的相关记录并链接到相关记录
2赞 Aardvark 11/7/2023 #2

我不相信尝试更新这样的视图是一个好主意,但 INSTEAD OF 触发器应该有效。像这样:

SET ANSI_NULLS, QUOTED_IDENTIFIER ON;
GO
CREATE TRIGGER V_View_UpD
ON dbo.V_View
INSTEAD OF UPDATE
AS

SET NOCOUNT, XACT_ABORT ON;

UPDATE T
SET UserComment = I.UserComment
FROM dbo.TABLE_A T
    JOIN inserted I
        ON T.GlobalID = I.GlobalID
    JOIN deleted D
        ON T.GlobalID = D.GlobalID
WHERE COALESCE(I.UserComment, '') <> COALESCE(D.UserComment, '');

UPDATE T
SET UserComment = I.UserComment
FROM dbo.TABLE_B T
    JOIN inserted I
        ON T.GlobalID = I.GlobalID
    JOIN deleted D
    ON T.GlobalID = D.GlobalID
WHERE COALESCE(I.UserComment, '') <> COALESCE(D.UserComment, '');
GO