PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Armazenar array multidimensional no banco de dados:relacional ou multidimensional?


Se isso é tudo que você precisa, você pode usar uma pesquisa LIKE
SELECT *
FROM Table1
WHERE CELL LIKE 'AEE%';

Com um índice começando com CELL esta é uma verificação de intervalo, que é rápida.

Se seus dados não forem assim, você pode criar um path coluna que se parece com um caminho de diretório e contém todos os nós "no caminho/caminho" da raiz para o elemento.
| 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/ |

Para recuperar todos os descendentes de 'AE' (incluindo ele mesmo) sua consulta seria
SELECT *
FROM tree t
WHERE path LIKE '1/6/%';

ou (concatenação específica do MySQL)
SELECT t.*
FROM tree t
CROSS JOIN tree r -- root
WHERE r.CELL = 'AE'
  AND t.path LIKE CONCAT(r.path, '%');

Resultado:
| 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/ |

Demonstração

Desempenho


Criei 100 mil linhas de dados falsos em MariaDB com o plug-in de sequência usando o seguinte script:
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.

Testes


Conte todos os elementos sob a raiz:
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

Obtenha elementos de subárvore em um nó específico:
SELECT t.*
FROM tree t
CROSS JOIN tree r -- root
WHERE r.CELL = '3B0'
  AND t.path LIKE CONCAT(r.path, '%');
-- runtime: ~ 30 ms

Resultado:
| 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


Isso também funciona para o PostgreSQL. Apenas a sintaxe de concatenação de strings deve ser alterada:
SELECT t.*
FROM tree t
CROSS JOIN tree r -- root
WHERE r.CELL = 'AE'
  AND t.path LIKE r.path || '%';

Demonstração: sqlfiddle - rextester

Como funciona a pesquisa


Se você observar o exemplo de teste, verá que todos os caminhos no resultado começam com '1/4/11/14/614/4284/'. Esse é o caminho da raiz da subárvore com CELL='3B0' . Se o path coluna está indexada, o mecanismo irá encontrá-los todos eficientemente, porque o índice é classificado por path . É como se você quisesse encontrar todas as palavras que começam com 'pol' em um dicionário com 100 mil palavras. Você não precisaria ler o dicionário inteiro.