2010-06-16 2 views
3

나중에 내 웹 사이트의 사이트 맵을 작성하는 데 사용할 중첩 세트 모델을 사용하고 있습니다. 이것은 내 테이블 구조입니다.깊이 통합을 유지하면서 이름별로 중첩 세트 정렬

create table departments (
    id int identity(0, 1) primary key 
    , lft int 
    , rgt int 
    , name nvarchar(60) 
); 

insert into departments (lft, rgt, name) values (1, 10, 'departments'); 
insert into departments (lft, rgt, name) values (2, 3, 'd'); 
insert into departments (lft, rgt, name) values (4, 9, 'a'); 
insert into departments (lft, rgt, name) values (5, 6, 'b'); 
insert into departments (lft, rgt, name) values (7, 8, 'c'); 

어떻게 깊이와 이름으로 정렬 할 수 있습니까? 할 수 있습니다

select 
    replicate('----', count(parent.name) - 1) + ' ' + node.name 
    , count(parent.name) - 1 as depth 
, node.lft 
from 
    departments node 
    , departments parent 
where 
    node.lft between parent.lft and parent.rgt 
group by 
    node.name, node.lft 
order by 
    depth asc, node.name asc; 

그러나 어떤 이유로 부모와 자녀가 일치하지 않습니다.

department  lft  rgt 
--------------------------- 
departments 0  1 
---- a  1  4 
---- d  1  2 
-------- b 2  5 
-------- c 2  7 

보시다시피 부서 'd'에는 부서 'a의 자녀가 있습니다!

감사합니다.

답변

2

나는 마침내 ANSI SQL 솔루션을 생각해 냈습니다. 기본 요점은 노드 자체의 상위 클래스 중 하나와 같은 수준에서 하위 값 이름을 가진 부모가 있거나 노드와 동일한 수준에 있고 더 낮은 값의 이름을 가진 행의 수를 계산한다는 것입니다. 원하는 경우 들여 쓰기를 추가하려면 약간 조정해야합니다. 또한, 나는 큰 데이터 세트에 대한 성능은 하위 쿼리의 모든 때문일 것입니다 방법을 알고하지 않습니다

SELECT 
    N1.name 
FROM 
    dbo.departments N1 
ORDER BY 
    (
    SELECT 
     COUNT(DISTINCT N2.lft) 
    FROM 
     dbo.departments N2 
    INNER JOIN (
       SELECT 
        N.name, 
        N.lft, 
        N.rgt, 
        (SELECT COUNT(*) FROM dbo.departments WHERE lft < N.lft AND rgt > N.lft) AS depth 
       FROM 
        dbo.departments N) SQ1 ON 
     SQ1.lft <= N2.lft AND SQ1.rgt >= N2.lft 
    INNER JOIN (
       SELECT 
        N3.name, 
        N3.lft, 
        N3.rgt, 
        (SELECT COUNT(*) FROM dbo.departments WHERE lft < N3.lft AND rgt > N3.lft) AS depth 
       FROM 
        dbo.departments N3) SQ2 ON 
     SQ2.lft <= N1.lft AND SQ2.rgt >= N1.lft AND 
     SQ2.depth = SQ1.depth AND 
     SQ2.name > SQ1.name 
    ) 

당신이 그것을 나누기 어떤 상황에 올 경우 알려주세요.

1

이름에 "-"문자가 포함 된 경우 이름이 잘못되었을 수 있지만 다음은 예제와 함께 작동합니다. 그것은 출발점 역할을하지만. 이것은 내가 믿는 SQL Server에 특정한 CTE를 사용합니다. 좀 더 일반적인 ANSI SQL 메소드를 생각해 보면, 나는 그것을 게시 할 것이다.

;WITH Tree_Path AS (
    SELECT 
     lft, 
     rgt, 
     name, 
     CAST(name + '-' AS VARCHAR(MAX)) AS tree_path, 
     1 AS depth 
    FROM 
     dbo.departments 
    WHERE 
     lft = 1 
    UNION ALL 
    SELECT 
     c.lft, 
     c.rgt, 
     c.name, 
     CAST(tp.tree_path + c.name + '-' AS VARCHAR(MAX)), 
     tp.depth + 1 
    FROM 
     Tree_Path tp 
    INNER JOIN dbo.departments AS c ON 
     c.lft > tp.lft AND 
     c.lft < tp.rgt AND 
     NOT EXISTS (SELECT * FROM dbo.departments d WHERE d.lft < c.lft AND d.rgt > c.lft AND d.lft > tp.lft AND d.lft < tp.rgt)) 
SELECT 
    REPLICATE('----', depth - 1) + name, 
    depth - 1, 
    lft 
FROM 
    Tree_Path 
ORDER BY 
    tree_path, 
    name 
+0

보인다. 어쩌면 인라인보기에서 쿼리의 기능을 복제하는 방법은 없습니까? – Mike

+2

나는 그렇게 생각하지 않는다. 불행히도 이것은 모든 자손으로부터 이름을 가져와야하기 때문에 기본적으로 재귀가 필요합니다. 아이러니 컬은 중첩 된 모델을 사용하는 가장 큰 이유 중 하나입니다. 재귀를 없애기 위해서입니다. 물론, 연결된 목록 모델은 재귀를 피하는 데 도움이되지 않습니다. –

+0

BTW, 당신은 아마 이미 이것을 알았을 것입니다. 그러나 단순한 "ORDER BY LFT"는 당신을 거의 거기에 데려옵니다 - 아이들은 부모 밑에서 멋지게 앉아 있지만, 깊이 내에서는 알파벳 순으로 정렬되어 있지 않습니다. –

1

질문에 불일치가 있습니다. 쿼리 반환 : - : - 당신이에 깊이를 원하는 분명하지 않다

department  lft  rgt 

어쨌든, 그 쿼리가 부서 수준에 대한 올바른 결과를 반환 node.name, depthnode.lft 아직 결과 테이블이 표시되어 있습니다 이 경우에 의미합니다. d은 모두 최상위 부서입니다. 당신이 하위 부서의 수, 중첩 된 세트가 제대로 유지 유지하려면

다음 쿼리는 간단하다 : WITH 절은 SQL 서버> 2005처럼

SELECT 
    D1.name, 
    (D1.rgt - D1.lft - 1)/2 AS SubordinateDepartments 
FROM 
    departments AS D1 
ORDER BY 
    SubordinateDepartments DESC, 
    D1.name 
+0

나는 그가 어린애를 원하고 있다고 생각하지 않는다. 그는 모든 노드의 목록을 찾고 있지만 자식 구조가 부모 뒤에 나열되지만 모든 주어진 수준에서 노드는 이름순으로 알파벳순으로 정렬됩니다. –

+0

질문에 OP의 설명이 필요합니다. 이상적으로는 원하는 결과 테이블. –