PostgreSQL에서 ltree를 사용하여 계층 구조 데이터를 처리하는 방법

본고에서 우리는 PostgreSQL의 ltree 모듈을 어떻게 사용하는지 배울 것이다. 이 모듈은 층을 나누는 트리 구조로 데이터를 저장할 수 있다.

무엇이 ltree입니까?


Ltree는 PostgreSQL 모듈입니다.이것은 층수 구조에 저장된 데이터의 라벨을 나타내는 데이터 형식ltree를 실현했다.태그 트리 검색을 위한 광범위한 도구가 제공됩니다.

왜 ltree를 선택했습니까?

  • ltree는 INSERT/UPDATE/DELETE는 매우 빠르고 SELECT 작업은 비교적 빠르다
  • 일반적으로 지점을 다시 계산해야 하는 귀속 CTE 또는 귀속 함수를 사용하는 것보다 빠르다
  • 쿼리 및 탐색 트리를 위한 내장 쿼리 구문 및 연산자
  • 인덱스!!!
  • 초기 데이터


    우선, 데이터베이스에서 확장을 활성화해야 합니다.다음 명령을 사용하여 이 작업을 수행할 수 있습니다.
    
    CREATE EXTENSION ltree;
    테이블을 만들고 데이터를 추가합니다.
    
    CREATE TABLE comments (user_id integer, description text, path ltree);
    INSERT INTO comments (user_id, description, path) VALUES ( 1, md5(random()::text), '0001');
    INSERT INTO comments (user_id, description, path) VALUES ( 2, md5(random()::text), '0001.0001.0001');
    INSERT INTO comments (user_id, description, path) VALUES ( 2, md5(random()::text), '0001.0001.0001.0001');
    INSERT INTO comments (user_id, description, path) VALUES ( 1, md5(random()::text), '0001.0001.0001.0002');
    INSERT INTO comments (user_id, description, path) VALUES ( 5, md5(random()::text), '0001.0001.0001.0003');
    INSERT INTO comments (user_id, description, path) VALUES ( 6, md5(random()::text), '0001.0002');
    INSERT INTO comments (user_id, description, path) VALUES ( 6, md5(random()::text), '0001.0002.0001');
    INSERT INTO comments (user_id, description, path) VALUES ( 6, md5(random()::text), '0001.0003');
    INSERT INTO comments (user_id, description, path) VALUES ( 8, md5(random()::text), '0001.0003.0001');
    INSERT INTO comments (user_id, description, path) VALUES ( 9, md5(random()::text), '0001.0003.0002');
    INSERT INTO comments (user_id, description, path) VALUES ( 11, md5(random()::text), '0001.0003.0002.0001');
    INSERT INTO comments (user_id, description, path) VALUES ( 2, md5(random()::text), '0001.0003.0002.0002');
    INSERT INTO comments (user_id, description, path) VALUES ( 5, md5(random()::text), '0001.0003.0002.0003');
    INSERT INTO comments (user_id, description, path) VALUES ( 7, md5(random()::text), '0001.0003.0002.0002.0001');
    INSERT INTO comments (user_id, description, path) VALUES ( 20, md5(random()::text), '0001.0003.0002.0002.0002');
    INSERT INTO comments (user_id, description, path) VALUES ( 31, md5(random()::text), '0001.0003.0002.0002.0003');
    INSERT INTO comments (user_id, description, path) VALUES ( 22, md5(random()::text), '0001.0003.0002.0002.0004');
    INSERT INTO comments (user_id, description, path) VALUES ( 34, md5(random()::text), '0001.0003.0002.0002.0005');
    INSERT INTO comments (user_id, description, path) VALUES ( 22, md5(random()::text), '0001.0003.0002.0002.0006');
    또한 색인을 추가해야 합니다.
    
    CREATE INDEX path_gist_comments_idx ON comments USING GIST(path);
    CREATE INDEX path_comments_idx ON comments USING btree(path);
    보시다시피,comments표를 만들 때path 필드가 있습니다. 이 필드는 이 테이블의tree 모든 경로를 포함합니다.보시다시피 트리 구분자에 대해서는 4개의 숫자와 점을 사용합니다.
    commenets 테이블에서 path가'0001.0003'의 기록을 찾습니다.
    
    $ SELECT user_id, path FROM comments WHERE path <@ '0001.0003';
     user_id |   path
    ---------+--------------------------
      6 | 0001.0003
      8 | 0001.0003.0001
      9 | 0001.0003.0002
      11 | 0001.0003.0002.0001
      2 | 0001.0003.0002.0002
      5 | 0001.0003.0002.0003
      7 | 0001.0003.0002.0002.0001
      20 | 0001.0003.0002.0002.0002
      31 | 0001.0003.0002.0002.0003
      22 | 0001.0003.0002.0002.0004
      34 | 0001.0003.0002.0002.0005
      22 | 0001.0003.0002.0002.0006
    (12 rows)
    EXPLAIN 명령을 통해 이 SQL을 확인하겠습니다.
    
    $ EXPLAIN ANALYZE SELECT user_id, path FROM comments WHERE path <@ '0001.0003';
                QUERY PLAN
    ----------------------------------------------------------------------------------------------------
     Seq Scan on comments (cost=0.00..1.24 rows=2 width=38) (actual time=0.013..0.017 rows=12 loops=1)
     Filter: (path <@ '0001.0003'::ltree)
     Rows Removed by Filter: 7
     Total runtime: 0.038 ms
    (4 rows)
    테스트를 위해 seq scan을 비활성화합니다.
    
    $ SET enable_seqscan=false;
    SET
    $ EXPLAIN ANALYZE SELECT user_id, path FROM comments WHERE path <@ '0001.0003';
                   QUERY PLAN
    -----------------------------------------------------------------------------------------------------------------------------------
     Index Scan using path_gist_comments_idx on comments (cost=0.00..8.29 rows=2 width=38) (actual time=0.023..0.034 rows=12 loops=1)
     Index Cond: (path <@ '0001.0003'::ltree)
     Total runtime: 0.076 ms
    (3 rows)
    현재 SQL은 느리지만 SQL이 index를 어떻게 사용하는지 볼 수 있습니다.
    첫 번째 SQL 문장은 테이블에 데이터가 많지 않기 때문에 sequence scan을 사용합니다.
    우리는 select'path<@'0001.0003'를 실현 방법으로 바꿀 수 있다.
    
    $ SELECT user_id, path FROM comments WHERE path ~ '0001.0003.*';
    user_id |   path
    ---------+--------------------------
      6 | 0001.0003
      8 | 0001.0003.0001
      9 | 0001.0003.0002
      11 | 0001.0003.0002.0001
      2 | 0001.0003.0002.0002
      5 | 0001.0003.0002.0003
      7 | 0001.0003.0002.0002.0001
      20 | 0001.0003.0002.0002.0002
      31 | 0001.0003.0002.0002.0003
      22 | 0001.0003.0002.0002.0004
      34 | 0001.0003.0002.0002.0005
      22 | 0001.0003.0002.0002.0006
    (12 rows)
    당신은 데이터의 순서를 잊어서는 안 됩니다. 다음과 같은 예입니다.
    
    $ INSERT INTO comments (user_id, description, path) VALUES ( 9, md5(random()::text), '0001.0003.0001.0001');
    $ INSERT INTO comments (user_id, description, path) VALUES ( 9, md5(random()::text), '0001.0003.0001.0002');
    $ INSERT INTO comments (user_id, description, path) VALUES ( 9, md5(random()::text), '0001.0003.0001.0003');
    $ SELECT user_id, path FROM comments WHERE path ~ '0001.0003.*';
    user_id |   path
    ---------+--------------------------
      6 | 0001.0003
      8 | 0001.0003.0001
      9 | 0001.0003.0002
      11 | 0001.0003.0002.0001
      2 | 0001.0003.0002.0002
      5 | 0001.0003.0002.0003
      7 | 0001.0003.0002.0002.0001
      20 | 0001.0003.0002.0002.0002
      31 | 0001.0003.0002.0002.0003
      22 | 0001.0003.0002.0002.0004
      34 | 0001.0003.0002.0002.0005
      22 | 0001.0003.0002.0002.0006
      9 | 0001.0003.0001.0001
      9 | 0001.0003.0001.0002
      9 | 0001.0003.0001.0003
    (15 rows)
    이제 정렬:
    
    $ SELECT user_id, path FROM comments WHERE path ~ '0001.0003.*' ORDER by path;
     user_id |   path
    ---------+--------------------------
      6 | 0001.0003
      8 | 0001.0003.0001
      9 | 0001.0003.0001.0001
      9 | 0001.0003.0001.0002
      9 | 0001.0003.0001.0003
      9 | 0001.0003.0002
      11 | 0001.0003.0002.0001
      2 | 0001.0003.0002.0002
      7 | 0001.0003.0002.0002.0001
      20 | 0001.0003.0002.0002.0002
      31 | 0001.0003.0002.0002.0003
      22 | 0001.0003.0002.0002.0004
      34 | 0001.0003.0002.0002.0005
      22 | 0001.0003.0002.0002.0006
      5 | 0001.0003.0002.0003
    (15 rows)
    lquery의 비별표 탭 끝에 수식자를 몇 개 추가하여 완전히 일치하는 것보다 더 일치하게 할 수 있습니다.
    "@"- 대소문자 일치를 구분하지 않습니다. 예를 들어 a @ 일치 A
    "*"- 접두사가 있는 모든 태그와 일치합니다. 예를 들어 foo*와 일치합니다.
    "%"- 밑줄로 시작하는 단어와 일치
    
    $ SELECT user_id, path FROM comments WHERE path ~ '0001.*{1,2}.0001|0002.*' ORDER by path;
     user_id |   path
    ---------+--------------------------
      2 | 0001.0001.0001
      2 | 0001.0001.0001.0001
      1 | 0001.0001.0001.0002
      5 | 0001.0001.0001.0003
      6 | 0001.0002.0001
      8 | 0001.0003.0001
      9 | 0001.0003.0001.0001
      9 | 0001.0003.0001.0002
      9 | 0001.0003.0001.0003
      9 | 0001.0003.0002
      11 | 0001.0003.0002.0001
      2 | 0001.0003.0002.0002
      7 | 0001.0003.0002.0002.0001
      20 | 0001.0003.0002.0002.0002
      31 | 0001.0003.0002.0002.0003
      22 | 0001.0003.0002.0002.0004
      34 | 0001.0003.0002.0002.0005
      22 | 0001.0003.0002.0002.0006
      5 | 0001.0003.0002.0003
    (19 rows)
    parent'0001.0003'를 위해 모든 직접적인 childrens를 찾으러 왔습니다. 다음을 보십시오.
    
    $ SELECT user_id, path FROM comments WHERE path ~ '0001.0003.*{1}' ORDER by path;
     user_id |  path
    ---------+----------------
      8 | 0001.0003.0001
      9 | 0001.0003.0002
    (2 rows)
    parent'0001.0003'의 모든 childrens를 찾으려면 다음을 보십시오.
    
    $ SELECT user_id, path FROM comments WHERE path ~ '0001.0003.*' ORDER by path;
     user_id |   path
    ---------+--------------------------
      6 | 0001.0003
      8 | 0001.0003.0001
      9 | 0001.0003.0001.0001
      9 | 0001.0003.0001.0002
      9 | 0001.0003.0001.0003
      9 | 0001.0003.0002
      11 | 0001.0003.0002.0001
      2 | 0001.0003.0002.0002
      7 | 0001.0003.0002.0002.0001
      20 | 0001.0003.0002.0002.0002
      31 | 0001.0003.0002.0002.0003
      22 | 0001.0003.0002.0002.0004
      34 | 0001.0003.0002.0002.0005
      22 | 0001.0003.0002.0002.0006
      5 | 0001.0003.0002.0003
    (15 rows)
    children'0001.0003.0002.0002.0002.0005'를 위해 parent를 찾습니다:
    
    $ SELECT user_id, path FROM comments WHERE path = subpath('0001.0003.0002.0002.0005', 0, -1) ORDER by path;
     user_id |  path
    ---------+---------------------
      2 | 0001.0003.0002.0002
    (1 row)
    만약 당신의 경로가 유일한 것이 아니라면, 당신은 여러 개의 기록을 얻을 수 있을 것이다.

    개요


    ltree를 사용하는 물화 경로가 매우 간단하다는 것을 알 수 있다.본문에서 나는 ltree의 모든 가능한 용법을 열거하지 않았다.이것은 전체 텍스트 검색 문제ltxtquery로 간주되지 않습니다.하지만 PostgreSQL 공식 문서 http://www.postgresql.org/docs/current/static/ltree.html 에서 찾을 수 있습니다.
    더 많은 PostgreSQL 핫이슈, 뉴스 동향, 하이라이트 이벤트에 대해 알고 싶으시면 중국 PostgreSQL 공식 사이트를 방문하십시오: www.postgresqlchina.com
    더 많은 PostgreSQL 관련 지식, 기술, 업무 문제를 해결하려면 중국 PostgreSQL 공식 퀴즈 커뮤니티를 방문하십시오: www.pgfans.cn
    더 많은 PostgreSQL 관련 자료, 도구, 플러그인 문제를 다운로드하려면 중국 PostgreSQL 공식 다운로드 사이트를 방문하십시오: www.postgreshub.cn
    PostgreSQL에서 ltree를 사용하여 차원 구조 데이터를 처리하는 것에 관한 이 글은 여기에 소개되었습니다. 더 많은 PostgreSQL 차원 구조 데이터와 관련된 내용은 저희 이전의 글을 검색하거나 아래의 관련 글을 계속 훑어보십시오. 앞으로 많은 응원 부탁드립니다!

    좋은 웹페이지 즐겨찾기