Programing

MySQL의 트리 구조 테이블을 단일 쿼리로 깊이에 관계없이 쿼리 할 수 ​​있습니까?

lottogame 2020. 12. 13. 08:31
반응형

MySQL의 트리 구조 테이블을 단일 쿼리로 깊이에 관계없이 쿼리 할 수 ​​있습니까?


대답은 '아니요'라고 생각하고 있지만 누구나 트리 구조를 SQL (MySQL)의 깊이로 크롤링하는 방법에 대한 통찰력이 있었지만 단일 쿼리로

보다 구체적으로, 트리 구조화 된 테이블 (id, data, data, parent_id)과 테이블의 한 행이 주어지면 모든 자손 (자녀 / 손자 / 기타) 또는 모든 조상 (부모 / 조부모) 을 가져올 수 있습니다. / etc) 단일 쿼리를 사용하여 얼마나 아래로 또는 위로 갈 것인지 알지 못합니까?

아니면 어떤 종류의 재귀를 사용하고 있습니까? 새로운 결과가 없을 때까지 더 깊게 쿼리해야합니까?

특히 저는 Ruby와 Rails를 사용하고 있지만 그다지 관련성이 없다고 생각합니다.


예, 가능합니다. 여기에 가장 잘 설명 된대로 수정 된 선주문 트리 순회라고합니다.

Smarties를위한 SQL의 Joe Celko의 트리 및 계층

작동 예제 (PHP)가 여기에 제공됩니다.

http://www.sitepoint.com/article/hierarchical-data-database/2/


다음은 몇 가지 리소스입니다.

기본적으로 저장 프로 시저 또는 쿼리에서 일종의 커서를 수행하거나 인접 테이블을 작성해야합니다. 나는 db 외부의 재귀를 피할 것입니다. 트리의 깊이에 따라 정말 느려질 수 있습니다.


Daniel Beardsley의 대답은 당신이 묻는 주요 질문이 '모든 자녀는 무엇입니까'와 '모든 부모님은 무엇입니까'일 때 그다지 나쁜 해결책은 아닙니다.

Alex Weinstein에 대한 응답으로이 방법은 실제로 Celko 기술보다 상위 이동의 노드 업데이트가 적습니다. Celko의 기술에서 맨 왼쪽의 레벨 2 노드가 맨 오른쪽의 레벨 1 노드 아래로 이동하면 노드의 자식뿐만 아니라 트리의 거의 모든 노드를 업데이트해야합니다.

그러나 내가 말하고 싶은 것은 Daniel이 경로를 다시 저장하여 잘못된 길을 뿌리는 것입니다.

쿼리가 될 수 있도록 저장합니다.

SELECT FROM table WHERE ancestors LIKE "1,2,6%"

이것은 mysql이 'ancestors'열의 인덱스를 사용할 수 있음을 의미하며, 선행 %로 수행 할 수 없습니다.


나는 전에이 문제를 보았고 하나의 이상한 생각을 가지고있었습니다. 직계 조상 ID의 연결 문자열 인 각 레코드에 필드를 루트까지 다시 저장할 수 있습니다 .

이와 같은 레코드가 있다고 상상해보십시오 (들여 쓰기는 계층 구조를 의미하고 숫자는 ID, 조상임을 의미합니다.

  • 1, "1"
    • 2, "2,1"
      • 5, "5,2,1"
      • 6, "6,2,1"
        • 7, "7,6,2,1"
        • 11, "11,6,2,1"
    • 3, "3,1"
      • 8, "8,3,1"
      • 9, "9,3,1"
      • 10, "10,3,1"

그런 다음 id : 6의 하위 항목선택하려면 다음을 수행하십시오.

SELECT FROM table WHERE ancestors LIKE "%6,2,1"

조상 열을 최신 상태로 유지하는 것은 가치가있는 것보다 더 문제가 될 수 있지만 모든 DB에서 실행 가능한 솔루션입니다.


Celko의 기술 (중첩 세트)은 꽤 좋습니다. 나는 또한 "ancestor"와 "descendant"및 "distance"필드가있는 인접 테이블을 사용했습니다 (예 : 직계 자녀 / 부모는 거리가 1, 손자 / 조부모는 거리가 2 등).

이것은 유지되어야하지만 삽입에 대해서는 매우 쉽습니다. 트랜잭션을 사용한 다음 직접 링크 (부모, 자식, 거리 = 1)를 테이블에 넣은 다음 거리를 추가하여 기존 부모 및 자식의 선택을 INSERT IGNORE 성능을 위해 3 개 필드 각각에 대한 인덱스가 필요한 경우 SQL을 가져올 수 있습니다. 이 접근 방식이 추악한 곳은 삭제를위한 것입니다. 기본적으로 영향을받은 모든 항목을 표시 한 다음 다시 빌드해야합니다. 그러나 이것의 장점은 임의의 비순환 그래프를 처리 할 수있는 반면, 중첩 된 집합 모델은 직선 계층 만 수행 할 수 있다는 것입니다 (예 : 루트를 제외한 각 항목에는 부모가 하나만 있음).


SQL은 Turing Complete 언어가 아니므로 이러한 종류의 루핑을 수행 할 수 없습니다. SQL 및 트리 구조로 매우 영리한 작업을 수행 할 수 있지만 임의의 깊이 계층 구조에 대해 "계층 구조에서"특정 ID를 가진 행을 설명하는 방법을 생각할 수 없습니다.

가장 좋은 방법은 @Dan이 제안한 내용을 따르는 것입니다. 이는 다른 유능한 언어로 트리를 통해 작업하는 것입니다. 실제로 루프를 사용하여 범용 언어로 쿼리 문자열을 생성 할 수 있습니다. 여기서 쿼리는 찾고있는 계층의 깊이를 반영하는 복잡한 일련의 조인 (또는 하위 쿼리) 일뿐입니다. 루핑 및 다중 쿼리보다 효율적입니다.


이것은 확실히 할 수 있으며 SQL의 경우 그렇게 복잡하지 않습니다. 이 질문에 답하고 여기에 mysql 절차 코드를 사용하여 작업 예제를 제공했습니다.

MySQL : 특정 노드에서 잎을 찾는 방법

부스 : 만족하면 답변 중 하나를 수락으로 표시해야합니다.


난에 기재된 "에뮬레이터"일상 사용 https://stackoverflow.com/questions/27013093/recursive-query-emulation-in-mysql (의해 제공 https://stackoverflow.com/users/1726419/yossico을 ). 지금까지 매우 좋은 결과를 얻었지만 (성능 측면에서) 검색 / 검색 할 데이터가 많거나 하위 항목이 많지 않습니다.


당신은 거의 확실히 그것을 위해 약간의 재귀를 사용하고 싶을 것입니다. 그리고 그렇게한다면 나무의 일부를 고정 된 깊이로 만드는 것보다 전체 트리를 얻는 것이 사소한 (사실상 더 쉬울 것입니다).

정말 대략적인 의사 코드에서는 다음과 같은 내용을 원할 것입니다.

getChildren(parent){
    children = query(SELECT * FROM table WHERE parent_id = parent.id)
    return children
}

printTree(root){
    print root
    children = getChildren(root)
    for child in children {
        printTree(child)
    }
}

실제로는 이와 같은 작업을 거의 원하지 않을 것입니다. 테이블의 모든 행에 대해 하나의 요청을 만들기 때문에 다소 비효율적이므로 작은 테이블이나 너무 깊게 중첩되지 않은 트리에만 적합합니다. 솔직히 말해서 두 경우 모두 깊이를 제한하고 싶을 것입니다.

그러나 이러한 종류의 데이터 구조의 인기를 감안할 때 특히 작성해야하는 쿼리 수를 줄이기 위해이를 지원하는 몇 가지 MySQL 항목이있을 수 있습니다.

Edit: Having thought about it, it makes very little sense to make all these queries. If you're reading the entire table anyway, then you can just slurp the whole thing into RAM - assuming it's small enough!

참고URL : https://stackoverflow.com/questions/169817/is-it-possible-to-query-a-tree-structure-table-in-mysql-in-a-single-query-to-an

반응형