您将如何避免此数据库模式中的数据冲突?

Posted

技术标签:

【中文标题】您将如何避免此数据库模式中的数据冲突?【英文标题】:What would you do to avoid conflicting data in this database schema? 【发布时间】:2010-11-16 22:30:13 【问题描述】:

我正在开发一个以 SQL Server 2008 / LinqToSQL / 定制存储库作为 DAL 的多用户 Internet 数据库驱动网站。我遇到了一个规范化问题,如果正确利用,可能会导致数据库状态不一致,我想知道如何处理这个问题。

问题:几家不同的公司可以访问我的网站。他们应该能够在我的网站上跟踪他们的项目和客户。一些(但不是全部)项目应该可以分配给客户。

这会产生以下数据库架构:

**Companies:**
ID
CompanyName

**Clients:** 
ID 
CompanyID (not nullable)
FirstName
LastName


**Projects:**
ID
CompanyID (not nullable)
ClientID (nullable)
ProjectName

这导致以下关系:

Companies-Clients (1:n)
Companies-Projects (1:n)
Clients-Projects(1:n)

现在,如果用户是恶意的,他可能会插入一个项目,该项目具有他自己的 CompanyID,但其 ClientID 属于另一个用户,从而使数据库处于不一致的状态。

这个问题在我的数据库模式中以类似的方式发生,所以如果可能的话,我想以一种通用的方式解决这个问题。我有以下两个想法:

检查可能导致 DAL 不一致的数据库写入。这将是通用的,但在执行更新和创建查询之前需要一些额外的数据库查询,因此会导致性能下降。

为客户-项目关系创建一个附加表,并确保以这种方式创建的关系是一致的。这也需要一些额外的选择查询,但远远少于第一种情况。另一方面,它不是通用的,因此从长远来看更容易遗漏某些东西,尤其是在向数据库添加更多表/依赖项时。

你会怎么做?我错过了任何更好的解决方案吗?

编辑:您可能想知道为什么 Projects 表有 CompanyID。这是因为我希望用户能够添加有和没有客户的项目。我需要跟踪无客户端项目属于哪个公司(以及哪个网站用户),这就是项目需要 CompanyID 的原因。

【问题讨论】:

【参考方案1】:

我会选择后者,有一个或多个表来定义实体之间允许的关系。

【讨论】:

【参考方案2】:

请注意,您的参考文献没有循环性,因此标题具有误导性。

你所拥有的是数据冲突的可能性,那是不同的。


为什么项目表中有“CompanyID”?所涉及公司的 ID 由您链接到的客户隐含地给出。你不需要它。

删除该列,您的问题就解决了。

另外,客户表中的“名称”列的用途是什么?您可以有一个名称与公司名称不同的客户吗?

或者“客户”是那家公司的人?


编辑:好的,关于没有公司的项目的澄清,我会分开引用,但如果没有阻止多个引用的约束,你不会摆脱你描述的问题正在制作中。

现有表的一个简单约束是项目行的 CompanyID 和 ClientID 字段不能同时为非空。

【讨论】:

这将是一个选项,尽管它确实使列出给定公司的所有项目变得更加复杂和缓慢,因为我必须遵循两个参考路径来获取所有项目。我想我仍然会使用单独的客户 - 项目参考表。我目前正在编写一个抽象的 Linq 实体基类,以确保在遵循不同的引用路径时,任何实体都不能属于不同的公司。【参考方案3】:

如果您想像这样使用表并避免所有新查询,只需在表上放置触发器,当用户尝试插入包含错误数据的行时,触发器会阻止他。 最好的祝福, 约旦

【讨论】:

我认为关键是如果没有某种方法记录数据库中允许或不允许的内容,触发器无法确定有效的数据是否真的有意义。 没错。除了使用触发器之外,我也可能会尝试在我的 DAL 中执行相同的操作,但我需要为每个更新/插入/删除进行一些额外的选择查询才能使其工作,这将导致我上面提到的第一个可能的解决方案。 【参考方案4】:

我的第一个想法是为每个名为“No client”的公司创建一个特殊的客户记录。然后从 Project 表中删除 CompanyId,如果项目没有客户,则使用“无客户”记录而不是“正常”客户记录。如果对此类无客户端的处理很特殊,请在无客户端记录中添加一个标志以明确标识它。 (我不想依赖“No Client”之类的名字——太模糊了。)

那么就没有办法存储不一致的数据,这样问题就会消失。

【讨论】:

我也考虑过这个选项,但很快就放弃了这个想法,因为它创造了另一种数据库可能性,使我的数据库变得不一致或在我的视图中显示垃圾数据。例如,如果我忘记检查客户端是否是 Clients/Delete 操作方法上的“No Client”实体怎么办?像这样的问题是我瞄准通用方法的原因【参考方案5】:

最后我实现了一个完全通用的解决方案,它解决了我的问题,没有太多的运行时开销,也不需要对数据库进行任何更改。我会在这里描述它以防其他人有同样的问题。

首先,该方法之所以有效,是因为其他表通过多个路径引用的唯一表是 Companies 表。由于我的数据库中就是这种情况,因此我只需检查要创建/更新/删除的每个实体的所有 n:1 引用实体是否引用同一公司(或根本没有公司)。

我通过从以下类型之一派生我的所有 Linq 实体来执行此操作:

    SingleReferenceEntityBase - 规范。仅检查(通过反射)是否确实只有一个对 Companies 表的引用(无论是传递的还是不传递的)。如果是这种情况,对公司表的引用不能变得不一致。

    MultiReferenceEntityBase - 用于特殊情况,例如上面的项目表。询问所有直接引用的实体他们引用的公司 ID。如果存在不一致,则引发异常。这会花费我每个 CRUD 操作的一些选择查询,但由于 MultiReferenceEntities 比 SingleReferenceEntities 少得多,因此可以忽略不计。

这两种类型都实现了“CheckReferences”,每当将 linq 实体写入数据库时​​,我都会调用它,方法是部分实现为所有 Linq 实体自动生成的 OnValidate(System.Data.Linq.ChangeAction action) 方法.

【讨论】:

以上是关于您将如何避免此数据库模式中的数据冲突?的主要内容,如果未能解决你的问题,请参考以下文章

我如何与Metal沟通,以避免GPU和CPU之间的数据冲突

将数据从全局加载到共享内存时如何避免银行冲突

您将如何设计数据库以允许用户定义模式

多主复制下处理写冲突-同步与异步冲突检测及避免冲突

如何避免星型模式中的复杂连接?

单例模式