Strom kategorií pomocí CTE (SQL)

DBBrowser SQLite

CTE jsem si na oblíbil a používám je především pro předvýběr, abych databázi ulehčil při následném spojování (JOIN). Jejich rekurzivní použití je možná ještě silnější zbraní. Použil jsem je pro zpracování stromu kategorií, bez traverzování, nebo bez ltree z PgSQL.

Byl jsem postaven před úkol přemístit data do WooCommerce. Kategorie ve WooCommerce byly vytvořeny. V exportní tabulce (xls) byla kategorie produktu uvedena ve formátu „kategorie > podkategorie > podpodkategorie“. Kategorie ve WooCommerce jsou definovány ve stejné struktuře, jako v původním řešení.

Teoreticky postačí vzít poslední část za oddělovačem, jímž je znak „>“, najít jaký má kategorie id a číslo předat do importního programu, jenž načítá xls soubor, doplňuje hodnoty, a přes REST API odesílá položky do instalace WooCommerce. Prakticky může být stejně pojmenovaná podkategorie v několika nadřazených kategoriích a tudíž je praktičtější porovnávat „celou cestu“.

Pro dočasné uložení kategorií jsem použil SQLite, s jedním pohledem, v němž jsou nagenerovány celé cesty. Pohled není materializovaný, pakliže z databáze o řádově stovkách záznamů budou získávána data pouze při prvotním importu, nevadí mi výkonostní penalizace.

Pro údaje kategorií postačuje definice tabulky pouze s 3 atributy – id, parent, title:

CREATE TABLE categories(
    "id" INT,
    "parent" INT,
    "title" TEXT
);

Kategorie jsou získány přes REST API voláním wcapi.get("products/categories?page=" + str(i)).json() (viz dokumentace). Sránkování jsem řešil cyklem, REST API vrací pouze 10 položek.

Po triviálním naplnění do databáze jsem vytvořil pohled, v němž je použita CTE rekurze:

CREATE VIEW "paths"("id", "title") AS WITH RECURSIVE "tpath"("id", "title", "path") AS (
    SELECT
        "id",
        "title",
        "title"
    FROM
        "categories"
    WHERE
        "parent" = 0
    UNION SELECT
        "categories"."id",
        "categories"."title",
        "tpath"."path" || ' > ' || "categories"."title"
    FROM
        "categories" JOIN "tpath"
    WHERE
        "categories"."parent" = "tpath"."id"
    )
    SELECT "id", "path" AS "title" FROM "tpath";

Pak už stačí získat id jednoduchým SELECTem (SELECT "id" FROM "paths" WHERE "title"='cesta_z_XLS').

Závěrem

  • Řešení je trochu antipattern, neřešil jsem třeba indexaci
  • Určitě nebylo nutné tvořit pohled, jen je pohodlné se podívat zda je výstup korektní

Na okraj

Můstky, nejen ty, píši v jazyce Python; dokumentace k patřičné části REST API je na webu WooCommerce.

Popsaná cesta není jedinou a zajisté ne nejlepší. Je to však jednoduché a dostatečné.