Diskuze: Nekonečný počet podkategorii
V předchozím kvízu, Online test znalostí SQL a databází, jsme si ověřili nabyté zkušenosti z kurzu.
Tvůrce
Zobrazeno 10 zpráv z 10.
//= Settings::TRACKING_CODE_B ?> //= Settings::TRACKING_CODE ?>
V předchozím kvízu, Online test znalostí SQL a databází, jsme si ověřili nabyté zkušenosti z kurzu.
Můžeš to vyřešit tak, že všechny kategorie (i podkategorie) dáš do jedné tabulky a poté akorát vytvoříš tabulku vztahů:
Název nadřazené kategorie | Název podkategorie |
kategorie1 | podkategorie1 |
podkategorie1 | podkategorie2 |
podkategorie2 | podkategorie3 |
Pokud bys to řešil na Oracle, dá se to vyřešit celkem pohodlně pomocí
jedné tabulky a funkce LISTAGG.
Tabulka by obsahovala následující:
nazev_kategorie | id_podkategorie | nazev_podkategorie |
kategorie1 | 1 | podkategorie1 |
kategorie1 | 2 | podkategorie2 |
kategorie1 | 3 | podkategorie3 |
kategorie2 | 1 | podkategorie1 |
kategorie2 | 2 | podkategorie2 |
Sloupec id_podkategorie je tu pouze pro potřeby řazení ve funkci, abys měl na výstupu kategorie správně seřazené.
Funkce by pak vypadala následovně:
SELECT LISTAGG(nazev_podkategorie, '/') WITHIN GROUP (ORDER BY id_podkategorie)
FROM tabulka_kategorii
WHERE nazev_kategorie = 'kategorie1';
Pokud to děláš na MS SQL, tak něco podobného funguje tuším od verze
2017.
V dřívějších verzích se na to musí jít trochu oklikou, ale taky se to
dá udělat.
Na jiných databázích jsem to nikdy neřešil, ale určitě to taky půjde
Hledal jsem po netu a nasel tohle. Vypada to, ze je to to co potrebuji(pokud teda nenarazim na neco co mi tahle moznost zneprijemni praci)
CREATE TABLE Category (
id INT AUTO_INCREMENT NOT NULL,
parent_id INT DEFAULT NULL,
PRIMARY KEY(id)
) ENGINE = InnoDB;
ALTER TABLE Category ADD FOREIGN KEY (parent_id) REFERENCES Category(id);
Vytvori se kategorie bez parent_id, potom podkategorie s parent_id a podle
toho se tahaji podaktegorie atd..
Uvidime.
No tak se to dá udělat, ale myslím že jen v případě, kdy budeš mít
pro všehcny kategorie vždy stejný počet podkategorií. Jak víš kolikrát
na sebe máš joinovat tu tabulku?
Musel by sis někde v proměnné uchovávat počet podkategorií a podle toho
pak generovat dynamické SQL.
Nebo sem zkus napsat ten join, jak jsi to myslel.
sloupce: id, parent_id, name ...........
pak můžeš využít něco takového
WITH category_tree AS
(
SELECT *, 0 AS category_level
FROM dbo.category
UNION ALL
SELECT c.*, category_level + 1 as category_level
FROM category_tree ct
JOIN dbo.category AS c
ON c.Id = ct.parent_id
)
SELECT *
FROM category_tree
Myslím, že tohle nebude fungovat.
V definici category_tree selectuješ z category_tree.
Myslím, že tohle nebude fungovat.
V definici category_tree selectuješ z category_tree.
V tom problém není - jedná se o tzv. Recursive CTE - viz např. https://technet.microsoft.com/…ql.105).aspx
Je to nástroj po práci s hierarchickými daty v MS SQL.
Ale tak, jak je to v příspěvku výše uvedeno to fungovat nebude, protože
jednak jsou tam chyby a především tím nezíská to co je požadováno v
zadání - veškeré podkategorie dané kategorie.
Bylo by to nějak takto:
WITH category_tree AS
(
SELECT *, category_level = 0, id AS root_id
FROM category
WERE parent_id IS NULL -- touto podminkou vyberu nejvyssi uroven, ktera nema parenta
UNION ALL
SELECT c.*, category_level = ct.category_level + 1, ct.root_id
FROM category AS c
INNER JOIN category_tree AS ct ON ct.id = c.parent_id
)
SELECT *
FROM category_tree
WHERE root_id = @id -- @id predstavuje id pozadovane kategorie
K tomu se pouziva neco cemu se rika Traverzovani kolem stromu. Funguje to tak, ze mas polozku, ktera ma v sve ID (neni to cislo zaznamu) a pak dva sloupce od a do. Kdyz vytvoris podpolozku, priradis ji cislo ID o jedno vetsi nez je ID polozky a vsechny ID co jsou pod nim zvednes o jedno. Vcetne zaznamu od a do. Vytvori se tak struktura, ktera se pouziva u diskusnich for se zanorenymi komentari. Jednou se mi podarilo ji skutecne rozebehnout, ale blbe se v tom listuje. A k pochopeni je to taky vorech vorechovic. Pekne a spravneji nez ja to u sebe popisuje Jakub Vrana. Ale nechces li si zivot komplikovat moc, jdi prostsi cestou. Treba vetsim poctem tabulek, kde bude mit kazda uroven svou. A pocitam, ze dale nez do 10 zanoreni se nedostanes. Budes mit pak 10 tabulek, kde v kazde vyssi bude odkaz na rodice. Jestli nebudes mit milion podkategorii, pak by to slo dat i do jedne tabulky, kde budes mit sloupec kategorie a sloupec urovne zanoreni a sloupec rodice.
Ahoj, ja by som to riesil kombinaciou uz spomenutych rieseni. Nie je to uplne programatorsky spravne, ale podla mna jednoduche riesenie tvojho problemu. Nemam moc rad zlozite sql
1. vytvor si tabulku, ako pises, kam sa budu ukladat vsetky zaznamy
CREATE TABLE Category (
id INT AUTO_INCREMENT NOT NULL,
name VARCHAR(50),
parent_id VARCHAR(60),
PRIMARY KEY(id)
) ENGINE = InnoDB;
velkost VARCHAR potom bude zavisiet podla toho, kolko bude zaznamov v tabulke, a kolko budes pocet podkategorii (bude to treba hlidat, aby nedoslo k preteceniu).
Pri pridavani musis vediet parent_id, a preto by som si ukladal do parent_id rovno celu strukturu. Vyhnes sa tak zlozitym repeatom v sql. Ako to myslim:
id | name | parent_id |
1 | domena.cz | NULL |
2 | kat-1 | 1 |
3 | podkat-1 | 1-2 |
4 | kat2 | 1 |
5 | podkat-2 | 1-4 |
6 | podpodkat-2 | 1-4-5 |
Pri vytvarani novej (pod)kategorie vies parent_id kde kam zaradis novu kategoriu(domena.cz, alebo nejaka podkategoria). Problem je, ze poznas len parent_id a nevies celu strukturu az k domena.cz. Preto si tu strukturu uloz, sice to zaberie viac miesta, ale na rychlost to az taky vplyv mat nebude.
povedzme ze chces pridat novu podkategoriu, ktorej parent_id = 6 ( bude patrit do podpodkat-2).
$parent_id_str = execMyQuery('Select parent_id from category where id = ' . $parent_id);
$parent_id_str .= "-" .$parent_id;
execMyQuery("INSERT INTO category (id, name, parent_id) VALUES ('','$newCatName','$parent_id_str')");
najprv ziskas parent_id_str z tabulky, to zretazis s id nadkategorie a to potom ulozis ako novy zaznam. execMyQuery je nejaka implicitna funkcia ktora vykona sql query, a vrati ti hodnotu.
ked si budes chciet vytiahnut strukturu:
$result = execMyQuery('Select parent_id from category where id = ' . $category_id);
$result = explode($result, '-');
$condition = "id = '". implode($result, "' OR id ='") . "'";
$categoryTree = execMyQuery("SELECT id, name FROM category WHERE $condition ORDER BY id");
a teraz budes mat v categoryTree pekne v poli v poradi domena.cz , kat-2,
podkat-2, podpodkat-2.
Na rychlost to nema vplyv lebo sa vzdy odkazujes cez primarne kluce, ktore su
indexovane a nepouzivas fulltext vyhladavanie. ako oddelovac mozes pouzit
cokolvek, pomolcku, podtrznik, pismenko, zvislitko, ...
je treba osetrit to retazenie pre kategoriu priamo pod domena.cz, a mnoho dalsich drobnosti
Berte to ako myslienku. Je to pisane z hlavy, tak to berte s rezervou.
Zobrazeno 10 zpráv z 10.