트리 데이터를 저장하는 빠른 관계 방법 (예 : 기사에 대한 스레드 댓글)
-
21-08-2019 - |
문제
기사에 대한 의견을 저장하는 CMS가 있습니다. 이 의견은 나사산과 스레드가 아닌 것일 수 있습니다. 기술적으로는 회신 열이 나사산되지 않았을 때 비워 두는 것과 동일합니다. 내 응용 프로그램은 sqllite, mysql 및 pgsql에서 작동하므로 상당히 표준 SQL이 필요합니다.
현재 의견 테이블이 있습니다
comment_id
article_id
user_id
comment
timestamp
thread (this is the reply column)
내 질문은 데이터베이스에서 스레드 주석을 가장 잘 표현하는 방법을 알아내는 것입니다. 아마도 내용없이 트리 세트를 지원하는 별도의 테이블과 텍스트를 보유 할 간단한 테이블? 아마도 이미 그 방식에 따라? 아마도 다른 방법?
주석이 스레드되지 않은 경우 타임 스탬프로 쉽게 주문할 수 있습니다.
그들이 실로 된 경우 나는 이것처럼 정렬합니다
ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))
주문에서 볼 수 있듯이 주석 쿼리는 인덱스를 기능 기반 색인으로 사용하지 않습니다. 빠른 댓글 페이지를 밝히도록 도와주세요.
해결책
나는 정말 좋아한다 드 루팔 이 문제를 해결합니다. 각 주석에 스레드 ID를 할당합니다. 이 ID는 첫 번째 주석을 위해 1에서 시작합니다. 이 주석에 답장이 추가되면 ID 1.1
그것에 할당됩니다. 의견에 대한 답변 1.1
스레드 ID가 주어집니다 1.1.1
. 의견의 형제 1.1
스레드 ID가 주어집니다 1.2
. 당신은 아이디어를 얻습니다. 주석이 추가되면 하나의 쿼리로 이러한 스레드 ID를 쉽게 수행 할 수 있습니다.
스레드가 렌더링되면 스레드에 속하는 모든 주석이 스레드 ID로 정렬 된 단일 쿼리로 가져옵니다. 이것은 당신에게 오름차순 순서로 스레드를 제공합니다. 또한 스레드 ID를 사용하여 각 주석의 중첩 레벨을 찾아서 그에 따라 들여 줄 수 있습니다.
1
1.1
1.1.1
1.2
1.2.1
정리해야 할 몇 가지 문제가 있습니다.
- 스레드 ID의 한 구성 요소가 2 자리로 증가하면 스레드 ID로 정렬하면 예상 순서가 생성되지 않습니다. 쉬운 솔루션은 스레드 ID의 모든 구성 요소가 0에 의해 동일한 너비를 갖도록 패딩하는 것입니다.
- 하강 스레드 ID로 정렬해도 예상 하강 순서가 생성되지 않습니다.
Drupal은 Vancode라는 번호 시스템을 사용하여보다 복잡한 방식으로 첫 번째 문제를 해결합니다. 두 번째 문제의 경우, 순서를 내려 놓을 때 BackSlash (ASCII 코드가 숫자보다 높은)를 스레드 ID에 추가하여 해결됩니다. 이 구현에 대한 자세한 내용은 주석 모듈 (COMMENT_GET_THREAD 함수 전에 큰 주석을 참조하십시오).
다른 팁
답이 조금 늦었다는 것을 알고 있지만 나무 데이터의 경우 폐쇄 테이블을 사용하십시오.http://www.slideshare.net/billkarwin/models-for-hierarchical-data
4 가지 방법을 설명합니다.
- 조정 목록 (간단한 부모 외국 키)
- 경로 열거 (허용 답변에 언급 된 드 루팔 전략)
- 중첩 된 세트
- 클로저 테이블 (조상/후손 사실을 별도의 관계에 저장 [표], 가능한 거리 열이있는)
마지막 옵션은 나머지와 비교하여 쉬운 크루드 작업의 장점이 있습니다. 비용은 최악의 경우 수 트리 노드에서 O (n^2) 크기 인 공간이지만 실제로는 그렇게 나쁘지 않을 것입니다.
불행히도, 순수한 SQL 방법은 매우 느립니다.
그만큼 NESTED SETS
에 의해 제안 @Marc W
매우 우아하지만 나무 가지가 범위에 닿으면 트리 전체를 업데이트해야 할 수도 있습니다.
내 블로그에서 빠르게 수행하는 방법에 대한이 기사를 참조하십시오. MySQL
:
- MySQL의 계층 적 쿼리 - 에뮬레이션
Oracle
'에스CONNECT BY
함수를 만들어야합니다.
CREATE FUNCTION hierarchy_connect_by_parent_eq_prior_id(value INT) RETURNS INT
NOT DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE _id INT;
DECLARE _parent INT;
DECLARE _next INT;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET @id = NULL;
SET _parent = @id;
SET _id = -1;
IF @id IS NULL THEN
RETURN NULL;
END IF;
LOOP
SELECT MIN(id)
INTO @id
FROM t_hierarchy
WHERE parent = _parent
AND id > _id;
IF @id IS NOT NULL OR _parent = @start_with THEN
SET @level = @level + 1;
RETURN @id;
END IF;
SET @level := @level - 1;
SELECT id, parent
INTO _id, _parent
FROM t_hierarchy
WHERE id = _parent;
END LOOP;
END
다음과 같은 쿼리에 사용하십시오.
SELECT hi.*
FROM (
SELECT hierarchy_connect_by_parent_eq_prior_id(id) AS id, @level AS level
FROM (
SELECT @start_with := 0,
@id := @start_with,
@level := 0
) vars, t_hierarchy
WHERE @id IS NOT NULL
) ho
JOIN t_hierarchy hi
ON hi.id = ho.id
이것은 물론입니다 MySQL
구체적이지만 정말 빠릅니다.
당신이 이것을 휴대용 베트 웬이되기를 원한다면 PostgreSQL
그리고 MySQL
, 당신이 사용할 수있는 PostgreSQL
의 기여 CONNECT BY
쿼리를 두 시스템에 대해 동일한 이름으로 저장된 절차로 랩합니다.
나는 실제로 이것을 직접했다! 관계형 데이터베이스에서 계층 적 데이터를 나타내는 중첩 세트 모델을 사용했습니다.
MySQL에서 계층 적 데이터 관리 나에게 순수한 금이었다. 중첩 세트는 해당 기사에 설명 된 두 번째 모델입니다.
인접성과 중첩 세트 모델 중에서 선택할 수 있습니다. 기사 MySQL에서 계층 적 데이터 관리 좋은 소개를합니다.
이론적 논의는 Celko 's를 참조하십시오 나무와 계층.
데이터베이스가 윈도우 기능을 지원하는 경우 스레드 목록을 구현하기가 쉽습니다. 대상 데이터베이스 테이블의 재귀 참조 만 있으면됩니다.
create Tablename (
RecordID integer not null default 0 auto_increment,
ParentID integer default null references RecordID,
...
)
그런 다음 재귀 공통 테이블 표현식을 사용하여 나사산보기를 표시 할 수 있습니다. 예를 사용할 수 있습니다 여기.
실제로 읽기와 쓰기 사이의 균형이어야합니다.
모든 인서트에 많은 행을 업데이트해도 괜찮다면 중첩 된 세트 (또는 이와 동등한)는 쉽고 빠른 읽기를 제공합니다.
그 외에도 부모의 간단한 FK는 초강력 삽입물을 제공하지만 검색의 악몽 일 수 있습니다.
나는 중첩 된 세트와 함께 갈 것이라고 생각하지만 예상되는 데이터 볼륨 및 사용 패턴에주의를 기울여야합니다 (모든 인덱스 열에있는 두 개의 색인화 된 열 (왼쪽 및 오른쪽 정보)의 여러 행을 업데이트하면 문제가 될 수 있습니다. 어느 시점에서).