sql >> Databasteknik >  >> RDS >> PostgreSQL

Lagra flerdimensionell array i databasen:relationell eller multidimensionell?

Om det är allt du behöver kan du använda en GILLA-sökning

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

Med ett index som börjar med CELL detta är en räckviddskontroll, som är snabb.

Om din data inte ser ut så kan du skapa en path kolumn som ser ut som en katalogsökväg och innehåller alla noder "på väg/sökväg" från roten till elementet.

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

För att hämta alla avkomlingar till "AE" (inklusive sig själv) skulle din fråga vara

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

eller (MySQL-specifik sammanlänkning)

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

Resultat:

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

Demo

Prestanda

Jag har skapat 100 000 rader med falska data på MariaDB med sekvensplugin med följande skript:

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.

Tester

Räkna alla element under roten:

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

Hämta underträdselement under en specifik nod:

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

Resultat:

| 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

Detta fungerar även för PostgreSQL. Endast syntaxen för strängsammansättningen måste ändras:

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

Demo: sqlfiddle - rextester

Hur fungerar sökningen

Om du tittar på testexemplet ser du att alla sökvägar i resultatet börjar med '1/4/11/14/614/4284/'. Det är sökvägen till underträdets rot med CELL='3B0' . Om path kolumnen är indexerad, kommer motorn att hitta dem alla effektivt, eftersom indexet sorteras efter path . Det är som att du skulle vilja hitta alla ord som börjar med 'pol' i en ordbok med 100K ord. Du skulle inte behöva läsa hela ordboken.



  1. Kontrollera om det aktuella datumet ligger mellan två datum Oracle SQL

  2. Hur tar man reda på fragmenterade index och defragmenterar dem i PostgreSQL?

  3. Vilka är dina favoritböcker av Ruby on Rails och varför?

  4. Hur kan du namnge datauppsättningens tabeller som du returnerar i en lagrad proc?