SQL Server,自引用外部复合键

时间:2014-04-15 17:20:00

标签: sql constraints

我有一个包含task_id(pk),client_id,parent_task_id,title的列的表。换句话说,任务由客户拥有,而某些任务具有子任务。

例如,客户端7可能有一项任务"洗车,"有孩子的任务"真空地毯"和"擦除仪表板。"

我想要一个约束,以便任务及其子节点始终由同一客户端拥有。

通过一些实验,为此,我创建了一个自引用外键(client_id,parent_task_id)引用(client_id,task_id)。起初我收到一个错误(引用表中没有与外键中的引用列列表匹配的主键或候选键。)因此,我为列task_id,client_id添加了一个唯一键。现在它似乎有效。

我想知道这是否是强制执行此约束的最佳解决方案(或至少是合理的解决方案)。任何想法将不胜感激。非常感谢!

1 个答案:

答案 0 :(得分:1)

A'父母'记录不需要[parent_task_id]

TASK ID | CLIENT ID | PARENT TASK ID | TITLE
1       | 7         | NULL           | wash the car

(要查找所有父记录,SELECT * FROM TABLE WHERE [parent_task_id] is null

A'孩子'记录需要[parent_task_id],而不是[client_id](因为,正如您所规定的那样,孩子与其父母拥有相同的客户)。

TASK ID | CLIENT ID | PARENT TASK ID | TITLE
2       | NULL      | 1              | vacuum carpent
3       | NULL      | 1              | wipe dashboard

通过这种方式,您的自引用外键是您需要的所有约束。子记录上没有关于[client_id]的约束/规则是必要的,因为子记录上的所有[client_id]值都将被忽略,而有利于父记录上的[client_id]。

例如,如果您想知道子记录的[client_id]是什么:

SELECT
   c.task_id,
   p.client_id,
   c.title
FROM
   table p --parent
   INNER JOIN table c --child
   ON p.task_id = c.parent_task_id

<强> 更新 (如何查询祖母的客户端ID)

--Create and populate your table (using a table var in this sample)
DECLARE @table table (task_id int, client_id int, parent_task_id int, title varchar(50))
INSERT INTO @table VALUES (1,7,NULL,'wash the car')
INSERT INTO @table VALUES (2,NULL,1,'vacuum carpet')
INSERT INTO @table VALUES (3,NULL,1,'wipe dashboard')
INSERT INTO @table VALUES (4,NULL,2,'Step 1: plug-in the vacuum')
INSERT INTO @table VALUES (5,NULL,2,'Step 2: turn-on the vacuum')
INSERT INTO @table VALUES (6,NULL,2,'Step 3: use the vacuum')
INSERT INTO @table VALUES (7,NULL,2,'Step 4: turn-off the vacuum')
INSERT INTO @table VALUES (8,NULL,2,'Step 5: empty the vacuum')
INSERT INTO @table VALUES (9,NULL,2,'Step 6: put-away the vacuum')
INSERT INTO @table VALUES (10,NULL,3,'Step 1: spray cleaner on the rag')
INSERT INTO @table VALUES (11,NULL,3,'Step 2: use the rag')
INSERT INTO @table VALUES (12,NULL,3,'Step 3: put-away the cleaner')
INSERT INTO @table VALUES (13,NULL,3,'Step 4: toss the rag in the laundry bin')

--Determine which grandchild you want the client_id for
DECLARE @task_id int
SET @task_id = 8 -- grandchild's ID to use to find client_id

--Create your CTE (this is the recursive part)
;WITH myList (task_id, client_id, parent_task_id, title)
AS
(
    SELECT a.task_id, a.client_id, a.parent_task_id, a.title
    FROM @table a
    WHERE a.task_id = @task_id
    UNION ALL
    SELECT a.task_id, a.client_id, a.parent_task_id, a.title
    FROM @table a
    INNER JOIN myList m
    ON a.task_id = m.parent_task_id
)

--Query your CTE
SELECT task_id, client_id, title FROM myList WHERE client_id is not null

在这个例子中,我使用了一个granchild的task_id(8 - &#39;清空真空&#39;)来找到它的最高级父级,它拥有client_id。

如果您想查看父母,父母的父母等,直到第一父母的记录,您可以从最后一步删除WHERE子句。