sql >> Base de Datos >  >> RDS >> PostgreSQL

Almacene una matriz multidimensional en la base de datos:¿relacional o multidimensional?

Si eso es todo lo que necesita, puede usar una búsqueda LIKE

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

Con un índice que comienza con CELL esta es una verificación de rango, que es rápida.

Si sus datos no se ven así, puede crear una path columna que parece una ruta de directorio y contiene todos los nodos "en el camino/ruta" desde la raíz hasta el 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 los descendientes de 'AE' (incluido él mismo), su consulta sería

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

o (concatenación específica de 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/ |

Demostración

Rendimiento

Creé 100 000 filas de datos falsos en MariaDB con el complemento de secuencia usando el siguiente 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.

Pruebas

Cuente todos los elementos bajo la raíz:

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

Obtener elementos de subárbol bajo un nodo 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

Esto también funciona para PostgreSQL. Solo se debe cambiar la sintaxis de concatenación de cadenas:

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

Demostración: sqlfiddle - rextester

Cómo funciona la búsqueda

Si observa el ejemplo de prueba, verá que todas las rutas en el resultado comienzan con '1/4/11/14/614/4284/'. Esa es la ruta de la raíz del subárbol con CELL='3B0' . Si la path la columna está indexada, el motor las encontrará todas de manera eficiente, porque el índice está ordenado por path . Es como si quisiera encontrar todas las palabras que comienzan con 'pol' en un diccionario con 100K palabras. No necesitarías leer todo el diccionario.