2014-04-15 55 views
1

我有一个表task_id(pk),client_id,parent_task_id,title。换句话说,任务由客户拥有,一些任务有子任务。SQL Server,自引用国外复合键

例如,客户端7可以有一个任务“洗车”,带有儿童任务“真空地毯”和“擦拭仪表板”。

我想要一个约束,以便任务及其子项始终由同一个客户端拥有。通过一些实验,为此,我创建了一个自引用外键(client_id,parent_task_id)引用(client_id,task_id)。起初,我收到一个错误(在引用的表中没有与外键中的引用列表匹配的主键或候选键)。因此,我为列task_id,client_id添加了唯一键。现在它似乎工作。

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

+0

您是否可以选择重新设计表格,还是您有现有的应用程序/报告,您必须保留当前的结构? – BateTech

+0

是的,我仍然在架构模式 – user3536970

回答

1

一个 '父' 记录将不需要[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 

UPDATE (如何查询盛大孩子的客户端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 - “空真空”)来查找它的最高水平的母体,其保持CLIENT_ID。

如果您希望查看每个父代,父代的父代等直到第一个父代的记录,则可以从最后一个步骤删除WHERE子句。

+0

这会考虑一致性,但对于孙辈等,确定client_id似乎很困难。 – user3536970

+0

@ user3536970 - 这并不难;如果你要超过2个级别(孙,孙,孙等),那么你需要使用递归。看到我更新的答案,一种方法来做到这一点。 – Chains