2014-04-15 3 views
1

테이블에 task_id (pk), client_id, parent_task_id, title 열이 있습니다. 즉, 작업은 클라이언트가 소유하고 일부 작업은 하위 작업을가집니다.SQL Server, 자체 복합 키 외부 참조

예를 들어, 클라이언트 7에는 하위 작업 인 "vacuum carpet"및 "wipe dashboard"가있는 "car wash"작업이있을 수 있습니다.

작업과 해당 하위 항목이 항상 동일한 클라이언트에 의해 소유되도록 제약 조건을 원합니다.

약간의 실험을 통해이를 수행하기 위해 (client_id, task_id)를 참조하는 자체 참조 외래 키 (client_id, parent_task_id)를 만들었습니다. 처음에는 오류가 발생했습니다 (참조 된 테이블에 외부 키의 참조 열 목록과 일치하는 기본 키 또는 후보 키가 없습니다). 그래서 task_id, client_id 열의 고유 키를 추가했습니다. 이제는 효과가있는 것 같습니다.

이 제약 조건을 적용하기위한 최적의 솔루션인지 아니면 최소한 합리적인 솔루션인지 궁금합니다. 어떤 생각이라도 감사 할 것입니다. 감사합니다!

+0

테이블을 다시 디자인 할 수 있습니까? 아니면 현재 구조를 유지해야하는 기존 응용 프로그램 /보고가 있습니까? – BateTech

+0

예, 여전히 스키마를 레이아웃하고 있습니다. – user3536970

답변

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 

UPDATE (그랜드 아이의 클라이언트 ID를 조회하는 방법) 그것은 CLIENT_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)을 사용했다.

각 부모, 부모의 부모 등을 첫 번째 부모의 레코드까지 보려면 WHERE 절을 마지막 단계에서 제거 할 수 있습니다.

+0

일관성을 유지할 수는 있지만 손자 등의 경우 client_id를 결정하는 것이 어려워 보입니다. – user3536970

+0

@ user3536970 - 어렵지 않습니다. 2 레벨 (손주, 증손자 등) 이상으로 갈 경우 재귀를 사용해야합니다. 이를 수행하는 한 가지 방법에 대한 업데이트 된 답변을 참조하십시오. – Chains