2017-12-09 15 views
0

나는 가능한 부모/자식 관계가있는 아래에 설명 된 간단한 테이블을 가지고 있습니다. 이것은 실제로 매우 큰 테이블이지만, 이것은 공정한 표현입니다. "손자"관계는 없습니다.간단하지만 비효율적 인 부모/자식 관계 쿼리를 향상시키는 방법

약간의 입력 값으로 필터링되는이 테이블을 약간 다른 테이블로 변환해야합니다.

declare @pc table (myId char(1) not null, parentId char(1) ); 

    insert into @pc (myId, parentId) values ('A', null) 
    insert into @pc (myId, parentId) values ('B', 'A') 
    insert into @pc (myId, parentId) values ('C', 'A') 
    insert into @pc (myId, parentId) values ('D', null) 
    insert into @pc (myId, parentId) values ('E', null) 
    insert into @pc (myId, parentId) values ('F', 'E') 
    insert into @pc (myId, parentId) values ('G', null) 
    insert into @pc (myId, parentId) values ('H', 'G') 
    insert into @pc (myId, parentId) values ('I', 'G') 
    insert into @pc (myId, parentId) values ('J', 'G') 
    insert into @pc (myId, parentId) values ('K', null) 

    -- This is the results I need 
    declare @target table (myId char(1) not null, parentId char(1), hasFamily bit); 
"A"의

감안할 때 입력 한 후이 같은 세 개의 행이 필요합니다 : A. 나는 "가족 그룹"에 속하는 모든 필요, 즉

A NULL 1 
    B A 1 
    C A 1 

가 주어
A NULL 1 
    B A 1 
    C A 1 

는 "D"나는 단지 필요 "B"감안할 때 나도 같은 출력 (A의를 될 일이) B의 가족 그룹의 모든 필요 아무도 D의 가족 없기 때문에 한 줄이 :

D NULL 0 

null을 감안할 때, 나는 설정 전체 테이블의 데이터가 필요하지만 적절한 행이 "가진 가족 '여부로 표시.

여기에 기술적으로 정확 내 시도하지만 전혀 그것을 할 데이터에서 3 개 패스를 복용 효율적이지의 :

declare @testcase char(1) = 'B'; 

    -- The inefficient method 
    INSERT INTO @target(myId,parentId) 
      SELECT pc.myId, pc.parentId 
      FROM @pc pc 
      WHERE(pc.myId = ISNULL(@testcase, pc.myId)) 
       OR (pc.parentId [email protected]); 

    INSERT INTO @target(myId,parentId) 
      SELECT pc.myId, pc.parentId 
      FROM @pc pc 
      WHERE pc.myId IN (SELECT parentId FROM @target) 
       AND pc.myId NOT IN (SELECT myId FROM @target); 

    update t 
     set t.hasFamily = 1 
     from @target t 
     left outer join @target t2 
     on t.myId = t2.parentId 
     where t.parentId is not null or t2.myId is not null; 

이이 문제를 보는 더 나은 방법을 볼 수 있을까요?

답변

1

모든 행에서 사용할 수 있도록 어린이를 계산하는 창 함수는 원하는 쿼리를 단순화합니다. 배포 방법을 선택하는 것이 좋습니다. 예를 들어 지속 된 계산 열 또는 트리거를 사용하도록 선택할 수 있습니다. 규모와 성능이 문제가되는 경우 인덱싱을 고려하고 실행 계획을 검사해야합니다.

아마도 내가 여기 제시 한 작은 모델의 복잡성을 보지 못했을 것입니다. 따라서 제 제안은 너무 단순 할 수 있습니다.

declare @want char(1) = 'A' 

select myId, parentId, hasFamily 
from too_simplistic 
where parent_flat = (select parent_flat from too_simplistic where MyId = @want) 
or MyId = @want 

Results :

데모 SQL Fiddle

create table Table1 (myId char(1) not null, parentId char(1) ); 

insert into Table1 (myId, parentId) values ('A', null); 
insert into Table1 (myId, parentId) values ('B', 'A'); 
insert into Table1 (myId, parentId) values ('C', 'A'); 
insert into Table1 (myId, parentId) values ('D', null); 
insert into Table1 (myId, parentId) values ('E', null); 
insert into Table1 (myId, parentId) values ('F', 'E'); 
insert into Table1 (myId, parentId) values ('G', null); 
insert into Table1 (myId, parentId) values ('H', 'G'); 
insert into Table1 (myId, parentId) values ('I', 'G'); 
insert into Table1 (myId, parentId) values ('J', 'G'); 
insert into Table1 (myId, parentId) values ('K', null); 

create view too_simplistic as 
select 
     myId 
    , parentId 
    , coalesce(parentId, myId) parent_flat 
    , case when count(*) over(partition by coalesce(parentId, myId))-1 = 0 
       then 0 
       else 1 
     end as hasFamily 
    from table1 
; 

쿼리 1에서

| myId | parentId | hasFamily | 
|------|----------|-----------| 
| A | (null) |   1 | 
| B |  A |   1 | 
| C |  A |   1 | 

쿼리 2 :

declare @want char(1) = 'B' 

select myId, parentId, hasFamily 
from too_simplistic 
where parent_flat = (select parent_flat from too_simplistic where MyId = @want) 
or MyId = @want 

Results : 3

| myId | parentId | hasFamily | 
|------|----------|-----------| 
| A | (null) |   1 | 
| B |  A |   1 | 
| C |  A |   1 | 

검색어 :

declare @want char(1) = 'D' 

select myId, parentId, hasFamily 
from too_simplistic 
where parent_flat = (select parent_flat from too_simplistic where MyId = @want) 
or MyId = @want 

Results :

| myId | parentId | hasFamily | 
|------|----------|-----------| 
| D | (null) |   0 | 

쿼리 4 :

select * 
from too_simplistic 

Results :

| myId | parentId | parent_flat | hasFamily | 
|------|----------|-------------|-----------| 
| A | (null) |   A |   1 | 
| B |  A |   A |   1 | 
| C |  A |   A |   1 | 
| D | (null) |   D |   0 | 
| E | (null) |   E |   1 | 
| F |  E |   E |   1 | 
| G | (null) |   G |   1 | 
| H |  G |   G |   1 | 
| I |  G |   G |   1 | 
| J |  G |   G |   1 | 
| K | (null) |   K |   0 | 



select d.* 
    , case when children = 0 then 0 else 1 end as hasFamily 
from (
    select * 
     , count(*) over(partition by coalesce(parentId, myId))-1 children 
    from @pc 
    ) d 
; 
+0

예,이 모든 행에 hasFamily 플래그를 설정하는 데 도움이,하지만 여전히 다른 두 개의 중첩 된 하위 쿼리를 취 또는 또는 (잘못된 쿼리 계획의 원인이되는 것 같은) 집합으로 그룹을 찾습니다. –

+0

그것은 추측입니까 아니면 공부할 실제 계획이 있습니까? 데이터를 한 번만 통과하고 중첩 수준은 하나뿐입니다. 창 함수는 매우 효율적입니다. –

+0

"및"nor "또는"내 제안 된 쿼리에서 사용됩니다; 그래서 나는 당신이 왜 거기에 있다고 말하는지 이해하지 못합니다. –