Bus*_*icK 20 sql t-sql sql-server views foreign-keys
我有一个SQL-Server 2008数据库和一个使用外键约束来强制引用完整性的模式.按预期工作.现在,用户创建原始表的视图以仅处理数据的子集.我的问题是过滤某些表中的某些数据集而不是其他表中的某些数据集会违反外键约束.
想象一下两个表"一"和"两个"."one"只包含值为1,2,3的id列."两个"引用"一个".现在,您可以在两个表上创建视图.表"one"的视图不会过滤任何内容,而表"one"的视图会删除除第一行之外的所有行.你最终会在第二个视图中找到无处可见的条目.
有什么方法可以避免这种情况吗?你能在视图之间有外键约束吗?
回应一些评论的一些澄清:
我知道即使通过视图插入,底层约束也将确保数据的完整性.我的问题在于使用视图的语句.这些语句是在考虑原始表的情况下编写的,并假设某些连接不会失败.使用表时,此假设始终有效 - 但视图可能会破坏它.
首先创建视图时加入/检查所有约束是因为大量的引用表而导致的.因此,我希望避免这种情况.
Rob*_*ley 14
我喜欢你的问题.它对查询优化器很熟悉,以及如果它们没有任何用途,或者如果它可以简化一些知道在连接的另一端最多有一个命中的东西,它如何看到一些连接是多余的.
因此,最大的问题是你是否可以针对索引视图的CIX制作FK.答案是否定的.
create table dbo.testtable (id int identity(1,1) primary key, val int not null);
go
create view dbo.testview with schemabinding as
select id, val
from dbo.testtable
where val >= 50
;
go
insert dbo.testtable
select 20 union all
select 30 union all
select 40 union all
select 50 union all
select 60 union all
select 70
go
create unique clustered index ixV on dbo.testview(id);
go
create table dbo.secondtable (id int references dbo.testview(id));
go
Run Code Online (Sandbox Code Playgroud)
所有这些都有效,除了最后一个语句,其错误包括:
Msg 1768, Level 16, State 0, Line 1
Foreign key 'FK__secondtable__id__6A325CF7' references object 'dbo.testview' which is not a user table.
Run Code Online (Sandbox Code Playgroud)
因此,外键必须引用用户表.
但是......下一个问题是关于是否可以引用在SQL 2008中过滤的唯一索引,以实现类似视图的FK.
答案仍然是否定的.
create unique index ixUV on dbo.testtable(val) where val >= 50;
go
Run Code Online (Sandbox Code Playgroud)
这成功了.
但是现在如果我尝试创建一个引用该val列的表
create table dbo.thirdtable (id int identity(1,1) primary key, val int not null check (val >= 50) references dbo.testtable(val));
Run Code Online (Sandbox Code Playgroud)
(我希望与过滤索引中的过滤器匹配的检查约束可能有助于系统理解FK应该保持)
但我得到一个错误说:
There are no primary or candidate keys in the referenced table 'dbo.testtable' that matching the referencing column list in the foreign key 'FK__thirdtable__val__0EA330E9'.
Run Code Online (Sandbox Code Playgroud)
如果我删除过滤的索引并创建一个未过滤的唯一非聚集索引,那么我可以毫无问题地创建dbo.thirdtable.
所以我担心答案似乎仍然是否定的.
Dam*_*vic 10
我花了一些时间来弄清楚这里的误解 - 不确定我是否仍然完全明白,但现在就是这样.我将使用一个靠近你的例子,但有一些数据 - 我更容易用这些术语思考.
所以前两个表; A =部门B =员工
CREATE TABLE Department
(
DepartmentID int PRIMARY KEY
,DepartmentName varchar(20)
,DepartmentColor varchar(10)
)
GO
CREATE TABLE Employee
(
EmployeeID int PRIMARY KEY
,EmployeeName varchar(20)
,DepartmentID int FOREIGN KEY REFERENCES Department ( DepartmentID )
)
GO
Run Code Online (Sandbox Code Playgroud)
现在我要把一些数据丢进去
INSERT INTO Department
( DepartmentID, DepartmentName, DepartmentColor )
SELECT 1, 'Accounting', 'RED' UNION
SELECT 2, 'Engineering', 'BLUE' UNION
SELECT 3, 'Sales', 'YELLOW' UNION
SELECT 4, 'Marketing', 'GREEN' ;
INSERT INTO Employee
( EmployeeID, EmployeeName, DepartmentID )
SELECT 1, 'Lyne', 1 UNION
SELECT 2, 'Damir', 2 UNION
SELECT 3, 'Sandy', 2 UNION
SELECT 4, 'Steve', 3 UNION
SELECT 5, 'Brian', 3 UNION
SELECT 6, 'Susan', 3 UNION
SELECT 7, 'Joe', 4 ;
Run Code Online (Sandbox Code Playgroud)
所以,现在我将在第一个表上创建一个视图来过滤一些部门.
CREATE VIEW dbo.BlueDepartments
AS
SELECT * FROM dbo.Department
WHERE DepartmentColor = 'BLUE'
GO
Run Code Online (Sandbox Code Playgroud)
这回来了
DepartmentID DepartmentName DepartmentColor
------------ -------------------- ---------------
2 Engineering BLUE
Run Code Online (Sandbox Code Playgroud)
根据您的示例,我将为第二个表添加一个不过滤任何内容的视图.
CREATE VIEW dbo.AllEmployees
AS
SELECT * FROM dbo.Employee
GO
Run Code Online (Sandbox Code Playgroud)
这回来了
EmployeeID EmployeeName DepartmentID
----------- -------------------- ------------
1 Lyne 1
2 Damir 2
3 Sandy 2
4 Steve 3
5 Brian 3
6 Susan 3
7 Joe 4
Run Code Online (Sandbox Code Playgroud)
在我看来,你认为员工5号,部门ID = 3指向无处?
"你最终会在第二个视图中找到无处可查的条目."
好吧,它指向使用外键指定的Department表DepartmentID = 3.即使您尝试在视图上加入视图也没有任何损坏:
SELECT e.EmployeeID
,e.EmployeeName
,d.DepartmentID
,d.DepartmentName
,d.DepartmentColor
FROM dbo.AllEmployees AS e
JOIN dbo.BlueDepartments AS d ON d.DepartmentID = e.DepartmentID
ORDER BY e.EmployeeID
Run Code Online (Sandbox Code Playgroud)
返回
EmployeeID EmployeeName DepartmentID DepartmentName DepartmentColor
----------- -------------------- ------------ -------------------- ---------------
2 Damir 2 Engineering BLUE
3 Sandy 2 Engineering BLUE
Run Code Online (Sandbox Code Playgroud)
因此,这里没有任何内容被破坏,连接只是没有找到匹配的记录.DepartmentID <> 2这实际上就像我连接表然后在第一个视图中包含过滤器一样:
SELECT e.EmployeeID
,e.EmployeeName
,d.DepartmentID
,d.DepartmentName
,d.DepartmentColor
FROM dbo.Employee AS e
JOIN dbo.Department AS d ON d.DepartmentID = e.DepartmentID
WHERE d.DepartmentColor = 'BLUE'
ORDER BY e.EmployeeID
Run Code Online (Sandbox Code Playgroud)
再次返回:
EmployeeID EmployeeName DepartmentID DepartmentName DepartmentColor
----------- -------------------- ------------ -------------------- ---------------
2 Damir 2 Engineering BLUE
3 Sandy 2 Engineering BLUE
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,连接都不会失败,它们只是按预期执行.
现在我将尝试通过视图打破引用完整性(没有DepartmentID = 127)
INSERT INTO dbo.AllEmployees
( EmployeeID, EmployeeName, DepartmentID )
VALUES( 10, 'Bob', 127 )
Run Code Online (Sandbox Code Playgroud)
这导致:
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__Employee__Depart__0519C6AF". The conflict occurred in database "Tinker_2", table "dbo.Department", column 'DepartmentID'.
Run Code Online (Sandbox Code Playgroud)
如果我尝试通过视图删除部门
DELETE FROM dbo.BlueDepartments
WHERE DepartmentID = 2
Run Code Online (Sandbox Code Playgroud)
结果如下:
Msg 547, Level 16, State 0, Line 1
The DELETE statement conflicted with the REFERENCE constraint "FK__Employee__Depart__0519C6AF". The conflict occurred in database "Tinker_2", table "dbo.Employee", column 'DepartmentID'.
Run Code Online (Sandbox Code Playgroud)
因此对基础表的约束仍然适用.
希望这会有所帮助,但也许我误解了你的问题.
彼得已经对此有所了解,但最好的解决方案是:
也就是说,
CREATE VIEW v1 AS SELECT * FROM table1 WHERE blah
CREATE VIEW v2 AS SELECT * FROM table2 WHERE EXISTS
(SELECT NULL FROM v1 WHERE v1.id = table2.FKtoTable1)
Run Code Online (Sandbox Code Playgroud)
当然,用于将一个表上的视图的过滤器传播到下级表上的视图的语法糖将会很方便,但是,它不是SQL标准的一部分.也就是说,这个解决方案仍然足够好 - 高效,直接,可维护,并保证消费代码的所需状态.