PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Stocker un tableau multidimensionnel dans une base de données :relationnel ou multidimensionnel ?

Si c'est tout ce dont vous avez besoin, vous pouvez utiliser une recherche LIKE

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

Avec un index commençant par CELL c'est une vérification de portée, qui est rapide.

Si vos données ne ressemblent pas à cela, vous pouvez créer un path colonne qui ressemble à un chemin de répertoire et contient tous les nœuds "sur le chemin/chemin" de la racine à l'élément.

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

Pour récupérer tous les descendants de 'AE' (y compris lui-même), votre requête serait

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

ou (concaténation spécifique à MySQL)

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

Résultat :

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

Démo

Performances

J'ai créé 100 000 lignes de fausses données sur MariaDB avec le plugin de séquence en utilisant le script suivant :

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.

Tests

Comptez tous les éléments sous la racine :

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

Obtenir des éléments de sous-arborescence sous un nœud spécifique :

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

Résultat :

| 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

Cela fonctionne également pour PostgreSQL. Seule la syntaxe de concaténation de chaînes doit être modifiée :

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

Démo : sqlfiddle - rextester

Comment fonctionne la recherche

Si vous regardez l'exemple de test, vous verrez que tous les chemins dans le résultat commencent par '1/4/11/14/614/4284/'. C'est le chemin de la racine du sous-arbre avec CELL='3B0' . Si le path colonne est indexée, le moteur les trouvera toutes efficacement, car l'index est trié par path . C'est comme si vous vouliez trouver tous les mots commençant par 'pol' dans un dictionnaire de 100 000 mots. Vous n'auriez pas besoin de lire tout le dictionnaire.