2009-08-16 3 views
1

카테고리 계층 구조를 포함하기 위해 중첩 세트처럼 작동하는 MySQL 테이블이 있습니다.중첩 세트 검색

CREATE TABLE IF NOT EXISTS `categories` (
    `id` int(11) NOT NULL auto_increment, 
    `name` varchar(200) NOT NULL, 
    `parent_id` int(11) default NULL, 
    `lft` int(11) default NULL, 
    `rgt` int(11) default NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `index_categories_on_parent_id_and_name` (`parent_id`,`name`) 
) 

lftrgt가 노드 (중첩 된 세트는 각 노드의 ID가 부모의 경계 내에 있다는 것입니다 작동하는 방식)의 왼쪽과 오른쪽 경계를 정의하고, parent_id는 부모 노드를 지정합니다 같은 테이블 스키마 보인다 . 고유 색인을 사용하면 동일한 상위가없는 한 같은 이름의 여러 범주가있을 수 있습니다.

계층 구조에 따라 집합에서 특정 노드를 찾는 올바른 방법을 찾아 내려고합니다. 예를 들어 foo/bar/baz를 찾으면 baz라는 노드를 검색하려고합니다.이 노드의 부모는 bar이고 부모는 foo입니다. 분명히 이름만으로 검색 할 수는 없습니다. 동일한 이름을 가진 여러 카테고리가있을 수 있기 때문입니다.

내가 이것을 할 수있는 방법은 최상위 카테고리를 찾은 다음 이전에 발견 된 카테고리의 상위 ID 인 주어진 이름으로 각각의 후속 카테고리를 찾는 것입니다. 그러나 이것은 나에게 매우 효율적이지 않습니다. 중첩 세트를 검색하는 더 좋은 방법이 있습니까?

답변

1

나는 완벽하고 깨끗하고 효율적인 방법이 있다고 생각하지 않습니다. 중첩 된 세트로이를 수행하십시오. 비정규 화 된 열에 노드의 조상 목록을 저장하면 효율적으로 제공되지만 구현할 것을 제안하지는 않습니다.

비록 1 개의 검색어이며 이미 가지고있는 색인을 쉽게 찾을 수있는 괜찮은 방법이 있습니다. 대상 노드의 각 수준에 대해 하나의 조인을 찾고 있습니다.

select c3.*
from categories c1
inner join categories c2 on c2.parent_id = c1.id AND c2.name = 'bar'
inner join categories c3 on c3.parent_id = c2.id AND c2.name = 'baz'
where c1.name = 'foo'

그것은 가장 큰 아니지만, 당신은 비정규 정보의 무리를 저장하는 노력에 가고 싶어하지 않는 한 아마 당신의 최선의 방법입니다 귀하의 예제 foo는 바 - 바즈

를 들어

. 코드에서 SQL을 생성하는 것은 상당히 간단합니다.

0

나는 PHP 프로젝트에서 나에게 넘겨 줬어. 우우, 그냥 나쁜 .. 할 수 있으면, 적어도 2 개의 테이블로 나누어; 카테고리에 1 개 이상, 항목에 1 개 이상 있습니다. 여러 쿼리를해야 할 필요가 있습니다. 두려워합니다.

+0

나는 당신이 그 질문을 아주 잘 이해하고 있다고 생각지 않는다. 카테고리 및 항목에 대해 별도의 표가 있지만 항목에 대해서는 신경 쓰지 않습니다. 나는 단지 주어진 계층을 기반으로 특정 카테고리의 ID를 얻고 싶다. –

1
TopVar = 'foo' 
MidVar = 'bar' 
BotVar = 'baz' 

SELECT D0.* 
FROM categories D0, categories D1, categories D2 
WHERE D0.name = :BotVar 
    AND D0.lft > D1.lft 
    AND D0.rgt < D1.rgt 
    AND D1.name = :MidVar 
    AND D1.lft > D2.lft 
    AND D1.rgt < D2.rgt 
    AND D2.name = :TopVar; 

-A1

+0

중첩 된 세트로 작업 할 때 너무 길어서 waaaaaaay가 너무 오래 걸리는 작은 팁. 노드의 자손을 찾으려면 쿼리의 'lft'열만 참조하십시오. 왼쪽 및 오른쪽 열을 클릭하면 검색어에 두 개의 범위 조건이 포함됩니다 (다소 최적화 된 저항 조건). 왼쪽 만 참조하면 단일 범위 조건이되며 lft의 인덱스로 완전히 덮을 수 있습니다. 노드 A의 자손이 12와 155를 남기고 싶다고합시다. select * from categories c where c.lft between 12 and 155 Michael