문제

인접성 목록 모델을 사용하여 계층 정보를 저장하는 테이블이 있습니다. (자체 참조 키 사용 - 아래 예제.이 테이블이 보일 수 있습니다. 친숙한):

category_id name                 parent
----------- -------------------- -----------
1           ELECTRONICS          NULL
2           TELEVISIONS          1
3           TUBE                 2
4           LCD                  2
5           PLASMA               2
6           PORTABLE ELECTRONICS 1
7           MP3 PLAYERS          6
8           FLASH                7
9           CD PLAYERS           6
10          2 WAY RADIOS         6

위의 데이터를 이와 같은 것으로 "평평하게"하는 가장 좋은 방법은 무엇입니까?

category_id lvl1        lvl2        lvl3        lvl4
----------- ----------- ----------- ----------- -----------
1           1           NULL        NULL        NULL
2           1           2           NULL        NULL
6           1           6           NULL        NULL
3           1           2           3           NULL
4           1           2           4           NULL
5           1           2           5           NULL
7           1           6           7           NULL
9           1           6           9           NULL
10          1           6           10          NULL
8           1           6           7           8

각 행은 행이있는 경우를 제외하고 계층 구조를 통한 하나의 "경로"입니다. 각 노드 (각각뿐만 아니라 잎 노드). Category_id 열은 현재 노드를 나타내고 "LVL"열은 조상입니다. 현재 노드의 값은 가장 먼 LVL 열에 있어야합니다. LVL1 열의 값은 항상 루트 노드를 나타내고 LVL2의 값은 항상 LVL1의 직접 후손 등을 나타냅니다.

가능하다면이 출력을 생성하는 방법은 SQL에 있으며 N-Tier 계층에 대해 작동합니다.

도움이 되었습니까?

해결책

간단한 인접거 목록에서 멀티 레벨 쿼리를 수행하려면 항상 자기 적을 조절하는 것과 관련이 있습니다. 오른쪽 정렬 테이블을 쉽게 만들 수 있습니다.

SELECT category.category_id,
    ancestor4.category_id AS lvl4,
    ancestor3.category_id AS lvl3,
    ancestor2.category_id AS lvl2,
    ancestor1.category_id AS lvl1
FROM categories AS category
    LEFT JOIN categories AS ancestor1 ON ancestor1.category_id=category.category_id
    LEFT JOIN categories AS ancestor2 ON ancestor2.category_id=ancestor1.parent
    LEFT JOIN categories AS ancestor3 ON ancestor3.category_id=ancestor2.parent
    LEFT JOIN categories AS ancestor4 ON ancestor4.category_id=ancestor3.parent;

예제처럼 왼쪽에 정렬하는 것은 조금 더 까다 롭습니다. 이것은 마음에옵니다 :

SELECT category.category_id,
    ancestor1.category_id AS lvl1,
    ancestor2.category_id AS lvl2,
    ancestor3.category_id AS lvl3,
    ancestor4.category_id AS lvl4
FROM categories AS category
    LEFT JOIN categories AS ancestor1 ON ancestor1.parent IS NULL
    LEFT JOIN categories AS ancestor2 ON ancestor1.category_id<>category.category_id AND ancestor2.parent=ancestor1.category_id
    LEFT JOIN categories AS ancestor3 ON ancestor2.category_id<>category.category_id AND ancestor3.parent=ancestor2.category_id
    LEFT JOIN categories AS ancestor4 ON ancestor3.category_id<>category.category_id AND ancestor4.parent=ancestor3.category_id
WHERE
    ancestor1.category_id=category.category_id OR
    ancestor2.category_id=category.category_id OR
    ancestor3.category_id=category.category_id OR
    ancestor4.category_id=category.category_id;

N-Tier 계층 구조에 대해 작동합니다.

죄송합니다. 인접성 목록 모델에서는 임의의 심한 쿼리가 불가능합니다. 이런 종류의 쿼리를 많이하고 있다면 스키마를 하나로 변경해야합니다. 계층 적 정보 저장의 다른 모델: 완전한 인접 관계 (모든 조상-공인 관계 저장), 구체화 된 경로 또는 중첩 된 세트.

카테고리가 많이 움직이지 않으면 (일반적으로 예와 같은 상점의 경우) 중첩 된 세트를 향한 경향이 있습니다.

다른 팁

언급했듯이 SQL은 동적으로 다양한 수의 열을 가진 테이블을 구현할 수있는 깨끗한 방법이 없습니다. 내가 이전에 사용한 유일한 두 가지 솔루션은 다음과 같습니다. 1. 고정 번호 자체 조인, 고정 된 수의 열을 제공합니다 (Bobince에 따라) 2. 단일 열에서 문자열로 결과를 생성합니다.

두 번째는 처음에는 괴상한 소리를냅니다. ID를 문자열로 저장합니까?! 그러나 출력이 XML 또는 무언가로 형식화되면 사람들은 그렇게 많이 신경 쓰지 않는 것 같습니다.

마찬가지로, 이것은 SQL에서 결과에 참여하려면 거의 사용하지 않습니다. 결과를 응용 프로그램에 제공하려면 매우 적합 할 수 있습니다. 그러나 개인적으로, 나는 SQL이 아닌 응용 프로그램에서 아파트를하는 것을 선호합니다.


SQL에 액세스하지 않고 10 인치 화면에 붙어있어 테스트 된 코드를 제공 할 수는 없지만 기본 방법은 재귀를 어떤 식 으로든 활용하는 것입니다.
- 재귀 스칼라 함수가이를 수행 할 수 있습니다
-MS SQL은 Recursive with Statement (보다 효율적)를 사용하여이를 수행 할 수 있습니다.

스칼라 함수 (같은) :

CREATE FUNCTION getGraphWalk(@child_id INT)
RETURNS VARCHAR(4000)
AS
BEGIN

  DECLARE @graph VARCHAR(4000)

  -- This step assumes each child only has one parent
  SELECT
    @graph = dbo.getGraphWalk(parent_id)
  FROM
    mapping_table
  WHERE
    category_id = @child_id
    AND parent_id IS NOT NULL

  IF (@graph  IS NULL)
    SET @graph = CAST(@child_id AS VARCHAR(16))
  ELSE
    SET @graph = @graph + ',' + CAST(@child_id AS VARCHAR(16))

  RETURN @graph

END


SELECT
  category_id                         AS [category_id],
  dbo.getGraphWalk(category_id)       AS [graph_path]
FROM
  mapping_table
ORDER BY
  category_id

나는 한동안 재귀를 사용하지 않았지만, 여기에 SQL이 없어도 구문을 줄 것입니다. :)

재귀

WITH
  result (
    category_id,
    graph_path
  )
AS
(
  SELECT
    category_id,
    CAST(category_id AS VARCHAR(4000))
  FROM
    mapping_table
  WHERE
    parent_id IS NULL

  UNION ALL

  SELECT
    mapping_table.category_id,
    CAST(result.graph_path + ',' + CAST(mapping_table.category_id AS VARCHAR(16)) AS VARCHAR(4000))
  FROM
    result
  INNER JOIN
    mapping_table
      ON result.category_id = mapping_table.parent_id
)

SELECT
  *
FROM
  result
ORDER BY
  category_id


편집 - 두 가지 모두에 대한 출력이 동일합니다.

1   '1'
2   '1,2'
3   '1,2,3'
4   '1,2,4'
5   '1,2,5'
6   '1,6'
7   '1,6,7'
8   '1,6,7,8'
9   '1,6,9'

임의의 깊이의 트리를 가로 지르면 일부 DBM의 특수 기능을 사용하지 않는 한 일반적으로 재귀적인 절차 코드가 포함됩니다.

Oracle에서 Connect By Clause를 사용하면 여기에서와 같이 인접력 목록을 사용하는 경우 트리를 먼저 순서대로 이동할 수 있습니다.

중첩 세트를 사용하는 경우 왼쪽 시퀀스 번호는 노드를 방문하라는 순서를 제공합니다.

실제로 상점 절차 내에서 동적 SQL로 수행 할 수 있습니다. 그런 다음 저장 절차를 할 수있는 일로 제한됩니다. 분명히 결과를 예상 수를 알지 못하는 임시 테이블로 결과를 실행하는 것은 어려운 일이됩니다. 그러나 목표가 웹 페이지 나 다른 UI에 출력하는 것이라면 노력할 가치가있을 수 있습니다 ...

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top