自引用表,好还是坏?[关闭]


37

代表应用程序中的地理位置,基础数据模型的设计提出了两个明确的选择(或可能还有更多选择)。

一个表,带有一个自引用的parent_id列UK-伦敦(伦敦父母ID =英国ID)

或两个表,使用外键一对多关系。

我更喜欢一个自引用表,因为它可以轻松扩展到所需的子区域。

通常,人们会偏离自引用表,还是可以?

Answers:


40

自引用表没有错。

它是用于深度(无限)嵌套层次结构的通用数据库设计模式。


@NimChimpsky-像递归的概念一样,这个想法对某些人来说很难。
Oded 2012年

2
(至少)Oracle甚至有一个特殊的SQL结构,即“ START WITH-CONNECT BY”子句,用于处理自引用表。
user281377

1
@ user281377-SQL Server引入了该hierarchyid类型。
奥德

usign休眠,因此将有其自己的特殊调味料
NimChimpsky

4
@NimChimpsky-也可以考虑将“嵌套集模型”作为该“ parent_id”列的替代方法-它提供了相同的功能,但性能更高,查询更容易提取层次结构。en.wikipedia.org/wiki/Nested_set_model Joe Celko的丛书“ SQL for Smarties”中有一些关于嵌套集的优秀SQL示例。
基思·帕尔默(Keith Palmer)

7

死灵法师。
正确的答案是:它取决于哪个数据库引擎和哪个管理工具。

让我们举个例子:
我们有一个报表表,
一个报表可以有一个父项(菜单,如类别),
而该父项本身可以有一个父项(例如,利润中心),
等等。

标准递归关系的最简单示例,与任何自引用实体/层次结构一样。

生成的SQL-Server表为:

IF  EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'dbo.FK_T_FMS_Reports_T_FMS_Reports') AND parent_object_id = OBJECT_ID(N'dbo.T_FMS_Reports'))
ALTER TABLE dbo.T_FMS_Reports DROP CONSTRAINT FK_T_FMS_Reports_T_FMS_Reports
GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.T_FMS_Reports') AND type in (N'U'))
DROP TABLE dbo.T_FMS_Reports 
GO



CREATE TABLE dbo.T_FMS_Reports 
( 
     RE_UID uniqueidentifier NOT NULL 
    ,RE_RE_UID uniqueidentifier NULL 
    ,RE_Text nvarchar(255) NULL 
    ,RE_Link nvarchar(400) NULL 
    ,RE_Sort int NOT NULL 
    ,RE_Status int NOT NULL 
    ,PRIMARY KEY CLUSTERED ( RE_UID ) 
); 

GO

ALTER TABLE dbo.T_FMS_Reports  WITH CHECK ADD  CONSTRAINT FK_T_FMS_Reports_T_FMS_Reports FOREIGN KEY(RE_RE_UID) 
REFERENCES dbo.T_FMS_Reports (RE_UID) 
-- ON DELETE CASCADE -- here, MS-SQL has a problem 
GO

ALTER TABLE dbo.T_FMS_Reports CHECK CONSTRAINT FK_T_FMS_Reports_T_FMS_Reports 
GO

但是,你得到一个问题:
当你需要删除其所有submenupoints一个menupoint,您CAN NOT集中删除级联,因为微软的SQL服务器不支持递归级联删除(在另一方面,PostgreSQL里面[但前提是图不是循环的],而MySQL根本不喜欢这种表结构,因为它不支持递归CTE。

因此,您有点用它来破坏删除完整性/功能,因此必须在您自己的代码或存储过程中(如果RDBMS支持存储过程)实施此类功能。

毫无疑问,这将炸毁任何类型的全自动动态数据导入/导出,因为您既不能简单地根据(非自引用)外键关系对所有表运行delete语句,也不能简单地进行选择*并以任意顺序为每一行创建一个插入。

例如,当您使用SSMS创建INSERT脚本时,SSMS将不会获取外键,因此确实会创建插入语句,该语句将插入具有依赖项的条目,然后再插入依赖项的父级,这会因错误而失败,因为外键就位。

但是,在具有适当工具的适当数据库管理系统(如PostgreSQL)上,这应该不是问题。仅仅因为您为RDBMS(我在看您,Microsoft; Oracle =?)和/或其工具带支付了高昂的费用,并不意味着它已被正确编程。而且,OpenSource(例如MySQL)也无法使您免受如此美妙的细节的影响。

俗话说,魔鬼在细节上。

现在,并不是说您无法解决此类问题,但是如果您的系统非常复杂(例如200多个表),我真的不建议您这样做。
另外,在通常的商业环境中(如Dilbert所描绘的那样),您将不会有那么多时间。

闭包表是一种更好的方法,尽管难度更大。
那会带来额外的好处,那就是它也适用于MySQL。
一旦实现了闭包功能,几乎就可以使它在其他地方工作。


3
+1使我注意到封闭表(至少是术语,已经知道了这个概念)。对于其他可能感兴趣的人,这是一篇不错的文章。coderwall.com/p/lixing/closure-tables-for-browsing-trees-in-sql
Outfast Source'Sep

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.