sql >> データベース >  >> RDS >> PostgreSQL

多次元配列をデータベースに格納する:リレーショナルまたは多次元?

    それだけが必要な場合は、LIKE検索を使用できます

    SELECT *
    FROM Table1
    WHERE CELL LIKE 'AEE%';
    

    CELLで始まるインデックス付き これは範囲チェックであり、高速です。

    データがそのように見えない場合は、pathを作成できます ディレクトリパスのように見え、ルートから要素への「途中/パス」にあるすべてのノードを含む列。

    | id | CELL | parent_id | path     |
    |====|======|===========|==========|
    |  1 | A    |      NULL | 1/       |
    |  2 | AA   |         1 | 1/2/     |
    |  3 | AAA  |         2 | 1/2/3/   |
    |  4 | AAC  |         2 | 1/2/4/   |
    |  5 | AB   |         1 | 1/5/     |
    |  6 | AE   |         1 | 1/6/     | 
    |  7 | AEA  |         6 | 1/6/7/   |
    |  8 | AEE  |         6 | 1/6/8/   |
    |  9 | AEEB |         8 | 1/6/8/9/ |
    

    'AE'のすべての子孫(それ自体を含む)を取得するには、クエリは次のようになります

    SELECT *
    FROM tree t
    WHERE path LIKE '1/6/%';
    

    または(MySQL固有の連結)

    SELECT t.*
    FROM tree t
    CROSS JOIN tree r -- root
    WHERE r.CELL = 'AE'
      AND t.path LIKE CONCAT(r.path, '%');
    

    結果:

    | id | CELL | parent_id |     path |
    |====|======|===========|==========|
    |  6 | AE   |         1 | 1/6/     |
    |  7 | AEA  |         6 | 1/6/7/   |
    |  8 | AEE  |         6 | 1/6/8/   |
    |  9 | AEEB |         8 | 1/6/8/9/ |
    

    デモ

    パフォーマンス

    MariaDB に10万行の偽のデータを作成しました シーケンスプラグイン を使用 次のスクリプトを使用します:

    drop table if exists tree;
    CREATE TABLE tree (
      `id` int primary key,
      `CELL` varchar(50),
      `parent_id` int,
      `path` varchar(255),
      unique index (`CELL`),
      unique index (`path`)
    );
    
    DROP TRIGGER IF EXISTS `tree_after_insert`;
    DELIMITER //
    CREATE TRIGGER `tree_after_insert` BEFORE INSERT ON `tree` FOR EACH ROW BEGIN
        if new.id = 1 then
            set new.path := '1/';
        else    
            set new.path := concat((
                select path from tree where id = new.parent_id
            ), new.id, '/');
        end if;
    END//
    DELIMITER ;
    
    insert into tree
        select seq as id
            , conv(seq, 10, 36) as CELL
            , case 
                when seq = 1 then null
                else floor(rand(1) * (seq-1)) + 1 
            end as parent_id
            , null as path
        from seq_1_to_100000
    ;
    DROP TRIGGER IF EXISTS `tree_after_insert`;
    -- runtime ~ 4 sec.
    

    テスト

    ルートの下のすべての要素を数えます:

    SELECT count(*)
    FROM tree t
    CROSS JOIN tree r -- root
    WHERE r.CELL = '1'
      AND t.path LIKE CONCAT(r.path, '%');
    -- result: 100000
    -- runtime: ~ 30 ms
    

    特定のノードの下のサブツリー要素を取得します:

    SELECT t.*
    FROM tree t
    CROSS JOIN tree r -- root
    WHERE r.CELL = '3B0'
      AND t.path LIKE CONCAT(r.path, '%');
    -- runtime: ~ 30 ms
    

    結果:

    | id    | CELL | parent_id | path                                |
    |=======|======|===========|=====================================|
    |  4284 | 3B0  |       614 | 1/4/11/14/614/4284/                 |
    |  6560 | 528  |      4284 | 1/4/11/14/614/4284/6560/            |
    |  8054 | 67Q  |      6560 | 1/4/11/14/614/4284/6560/8054/       |
    | 14358 | B2U  |      6560 | 1/4/11/14/614/4284/6560/14358/      |
    | 51911 | 141Z |      4284 | 1/4/11/14/614/4284/51911/           |
    | 55695 | 16Z3 |      4284 | 1/4/11/14/614/4284/55695/           |
    | 80172 | 1PV0 |      8054 | 1/4/11/14/614/4284/6560/8054/80172/ |
    | 87101 | 1V7H |     51911 | 1/4/11/14/614/4284/51911/87101/     |
    

    PostgreSQL

    これはPostgreSQLでも機能します。文字列連結構文のみを変更する必要があります:

    SELECT t.*
    FROM tree t
    CROSS JOIN tree r -- root
    WHERE r.CELL = 'AE'
      AND t.path LIKE r.path || '%';
    

    デモ: sqlfiddle - rextester

    検索はどのように機能しますか

    テスト例を見ると、結果のすべてのパスが「1/4/11/14/614/4284/」で始まっていることがわかります。これは、CELL='3B0'のサブツリールートのパスです。 。 pathの場合 列にインデックスが付けられている場合、インデックスはpathで並べ替えられるため、エンジンはそれらすべてを効率的に検出します。 。 'pol'で始まるすべての単語を10万語の辞書で検索したいようなものです。辞書全体を読む必要はありません。



    1. 奇妙な文字がWebページに表示されないようにする方法

    2. PostgreSQL:読み取り専用テーブル

    3. MySQLとCASEWHENと値の範囲

    4. SQLでCASEステートメントを作成する方法