sql >> Databáze >  >> RDS >> PostgreSQL

Uložit vícerozměrné pole v databázi:relační nebo vícerozměrné?

Pokud je to vše, co potřebujete, můžete použít vyhledávání LIKE

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

S indexem začínajícím CELL toto je kontrola rozsahu, která je rychlá.

Pokud vaše data tak nevypadají, můžete vytvořit path sloupec, který vypadá jako cesta k adresáři a obsahuje všechny uzly "na cestě/cestě" od kořene k prvku.

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

Chcete-li získat všechny potomky 'AE' (včetně samotného), váš dotaz by byl

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

nebo (specifické zřetězení MySQL)

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

Výsledek:

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

Ukázka

Výkon

Vytvořil jsem 100 tisíc řádků falešných dat na MariaDB pomocí sekvenčního pluginu pomocí následujícího skriptu:

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.

Testy

Spočítejte všechny prvky pod kořenem:

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

Získejte prvky podstromu pod konkrétním uzlem:

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

Výsledek:

| 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

To také funguje pro PostgreSQL. Musí se změnit pouze syntaxe zřetězení řetězců:

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

Ukázka: sqlfiddlerextester

Jak funguje vyhledávání

Pokud se podíváte na testovací příklad, uvidíte, že všechny cesty ve výsledku začínají '1/4/11/14/614/4284/'. To je cesta ke kořenu podstromu s CELL='3B0' . Pokud path je indexován, engine je všechny efektivně najde, protože index je řazen podle path . Je to jako byste chtěli najít všechna slova, která začínají na „pol“ ve slovníku se 100 000 slovy. Nepotřebovali byste číst celý slovník.



  1. Dotazování na „běh“ po sobě jdoucích sloupců v Postgresu

  2. Jak pretify výstup pocházející z dotazu SELECT v příkazovém řádku?

  3. Pokoušíte se refaktorovat rekurzivní dotaz v Oracle CTE?

  4. Odebrat duplicitní záznamy ze zobrazení