Mar*_*aun 5 database-design sql-server sql-server-2012
我有一个代表特定类型操作的表格。让我们称之为[ACTION]
。
一个操作可以由用户 ( [USER]
) 执行,所以我有关系[USER] 1:N [ACTION]
。
用户可以有孩子 ( [CHILD]
),所以[USER] 1:N [CHILD]
.
问题开始于操作必须由用户执行的事实,但是它可以为用户或其子代之一执行。
如果每个用户总是有一个或多个孩子,我可以简单地做[CHILD] 1:N [ACTION]
而不是[USER] 1:N [ACTION]
,因为在孩子身上我会找到执行操作的用户。
如果我只是 add [CHILD] 0...1:N [ACTION]
,那么 child 成为 action 表中的一个可选字段,可能会添加一个不属于添加到 action 的用户的 child。
这种情况通常称为什么?
如何克服这个问题或者我能走的最好的方法是什么?
我不想在代码中检查这个,仅数据库(SQL Server 2012)解决方案将是完美的。
行动是一种投资。用户将始终接受投资,但他/她可以决定他/她是为他/她还是为他/她的孩子之一投资。在这种情况下,孩子总是未成年。如果孩子足够大,可以自己进行投资,那么它将注册并成为用户。所以这就是为什么孩子不能成为用户的原因。他们需要保持两个不同的实体。
所以我有点需要两个彼此保持稳定的关系。我需要知道谁(用户)采取了行动(投资),我需要知道为谁(用户或他/她的孩子之一)采取了行动(投资)。
我正在寻求一种解决方案,即不能为与用户无关的孩子采取行动(投资)。换句话说,父亲/母亲只能为他/她或他/她的孩子投资,不能为别人的孩子投资。
我看到 2 个选项来实现这一点。第一个是你已经拥有的,稍作调整,以强制执行这部分:
我正在寻求一种解决方案,即不能为与用户无关的孩子采取行动(投资)。换句话说,父亲/母亲只能为他/她或他/她的孩子投资,不能为别人的孩子投资。
调整将是action references (child)
在外键上有一个复合键。可以action (child_id)
为空,但当值不为空时,外键约束确保它引用执行操作的用户的子项。
示例代码:
-- design 1
CREATE TABLE user
( user_id INT NOT NULL PRIMARY KEY
) ;
CREATE TABLE child
( user_id INT NOT NULL,
child_id INT NOT NULL PRIMARY KEY,
FOREIGN KEY (user_id)
REFERENCES user (user_id),
UNIQUE (user_id, child_id) -- this is needed for the FK below
) ;
CREATE TABLE action
( action_id INT NOT NULL PRIMARY KEY,
user_id INT NOT NULL,
child_id INT NULL, -- nullable
FOREIGN KEY (user_id)
REFERENCES user (user_id),
FOREIGN KEY (user_id, child_id)
REFERENCES child (user_id, child_id)
) ;
Run Code Online (Sandbox Code Playgroud)
另一种方法是将child
表重命名为dependent / investor / beneficiary
(选择一个更合适的名称)并不仅在那里存储孩子,还存储用户本身(因此所有投资者/受益人/用户的受抚养人)。这样只有一个,外键 from action
toinvestor
将需要,并且该列不可为空:
-- design 2
CREATE TABLE user
( user_id INT NOT NULL PRIMARY KEY
) ;
CREATE TABLE investor
( user_id INT NOT NULL,
investor_id INT NOT NULL PRIMARY KEY,
FOREIGN KEY (user_id)
REFERENCES user (user_id),
UNIQUE (user_id, child_id)
) ;
CREATE TABLE action
( action_id INT NOT NULL PRIMARY KEY,
user_id INT NOT NULL,
investor_id INT NOT NULL,
FOREIGN KEY (user_id, investor_id)
REFERENCES investor (user_id, investor_id)
) ;
Run Code Online (Sandbox Code Playgroud)
您child
在第一个设计中拥有的表格可以是一个视图:
CREATE VIEW child AS
SELECT user_id,
investor_id AS child_id
FROM investor
WHERE user_id <> investor_id ;
Run Code Online (Sandbox Code Playgroud)
作为一个副作用,具有设计2,我们并不真正需要user_id
的action
表(除非其他,而不是在问题或性能方面的原因提到)。我们可以删除它并摆脱复合外键。该user_id
可与发现加盟investor
:
-- design 2b
CREATE TABLE user
-- unchanged
CREATE TABLE investor
( user_id INT NOT NULL,
investor_id INT NOT NULL PRIMARY KEY,
FOREIGN KEY (user_id)
REFERENCES user (user_id)
) ;
CREATE TABLE action
( action_id INT NOT NULL PRIMARY KEY,
investor_id INT NOT NULL,
FOREIGN KEY (investor_id)
REFERENCES investor (investor_id)
) ;
CREATE VIEW child AS
-- unchanged
Run Code Online (Sandbox Code Playgroud)
另一种更复杂但从以前的设计中汲取优点并捕获所有不同实体(人、用户、孩子、动作)的方法是使用超类型/子类型模式。
这基本上将以下内容添加到设计(实体person
)中:
Aperson
是 auser
或 a child
。
(具有子类型的超类型)
Aperson
可以有任意数量的children
.
Achild
正好有一个父级 ( user
)。
(1:n 关系)
Aperson
可以是任意数量action
(投资)的受益人。
Anaction
仅适用于一位受益人 ( person
)。
(1:n 关系)
代码:
-- design 3
CREATE TABLE person
( person_id INT NOT NULL PRIMARY KEY
) ;
CREATE TABLE user
( user_id INT NOT NULL PRIMARY KEY,
FOREIGN KEY (user_id)
REFERENCES person (person_id),
) ;
CREATE TABLE child
( user_id INT NOT NULL,
child_id INT NOT NULL PRIMARY KEY,
FOREIGN KEY (child_id)
REFERENCES person (person_id),
FOREIGN KEY (user_id)
REFERENCES user (user_id)
) ;
CREATE TABLE action
( action_id INT NOT NULL PRIMARY KEY,
beneficiary_id INT NOT NULL,
FOREIGN KEY (beneficiary_id)
REFERENCES person (person_id)
) ;
Run Code Online (Sandbox Code Playgroud)
我们不需要user_id
在action
表中,因为一个企业的用户action
可以通过查看其它表中找到,它要么是受益人自己(如果beneficiary_id
是在user
表),否则将是用户(从user_id
在相关child
)。