PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Oracle vers PostgreSQL — Curseurs et ltrees

Dans notre dernier article sur les curseurs dans PostgreSQL, nous avons parlé des xpressions ommonables (CTE). Aujourd'hui, nous continuons à découvrir de nouvelles alternatives aux curseurs en utilisant une fonctionnalité moins connue de PostgreSQL.

Nous utiliserons les données que nous avons importées dans l'article précédent (lien ci-dessus). Je vais attendre un moment que vous suiviez la procédure là-bas.

C'est compris? D'accord.

Les données sont un tableau de taxonomie du monde naturel. Pour rappel de la biologie de base du lycée, ces données sont organisées par Carl Linnaeus en Royaume, Phylum, Classe, Ordre, Famille, Genre et Espèce. Bien sûr, la science a progressé très légèrement au cours des 250 dernières années, de sorte que le tableau taxonomique comporte 21 niveaux de profondeur. On retrouve l'arborescence hiérarchique dans une table qui s'appelle (sans surprise) itis.hierarchy .

Le sujet de cet article est de savoir comment utiliser ltrees dans PostgreSQL. Plus précisément, comment les utiliser pour parcourir très efficacement un jeu d'enregistrements complexe. En ce sens, nous pouvons les considérer comme un autre substitut des curseurs.

Les données ne sont pas conservées (malheureusement pour nous) dans un format ltree, nous allons donc les transformer un peu pour le bien de l'article.

Tout d'abord, vous devrez installer ltree dans la base de données que vous utilisez pour suivre cet article. Bien sûr, vous devez être un super utilisateur pour installer des extensions.

CREATE EXTENSION IF NOT EXISTS ltree;

Nous allons maintenant utiliser cette extension pour fournir des recherches très efficaces. Nous allons devoir transformer les données en une table de recherche. Pour effectuer cette transformation, nous allons utiliser la technique CTE que nous avons abordée dans le dernier article. En cours de route, nous allons ajouter les noms latins et les noms anglais à l'arbre taxonomique. Cela nous aidera à rechercher des éléments par numéro, noms latins ou noms anglais.

-- We need a little helper function to strip out illegal label names.
CREATE OR REPLACE FUNCTION strip_label(thelabel text)
RETURNS TEXT
AS $$
    -- make sure all the characters in the label are legal
    SELECT SELECT 
        regexp_replace(
            regexp_replace(
                regexp_replace(
                    regexp_replace(
                        -- strip anything not alnum (yes, this could be way more accurate)
                        thelabel, '[^[:alnum:]]', '_','g'),
                    -- consolidate underscores
                    '_+', '_', 'g'), 
                -- strip leading/trailing underscores
                '^_*', '', 'g'), 
        '_*$', '', 'g'); 
$$
LANGUAGE sql;

CREATE MATERIALIZED VIEW itis.world_view AS
WITH RECURSIVE world AS (
    -- Start with the basic kingdoms
    SELECT h1.tsn, h1.parent_tsn, h1.tsn::text numeric_taxonomy,
        -- There is no guarantee that there will be a textual name
        COALESCE(l1.completename,h1.tsn::text,'')::text latin_taxonomy, 
        -- and again no guarantee of a common english name
        COALESCE(v1.vernacular_name, lower(l1.completename),h1.tsn::text,'unk')::text english_taxonomy
    FROM itis.hierarchy h1
    LEFT JOIN itis.longnames l1
        ON h1.tsn = l1.tsn
    LEFT JOIN itis.vernaculars v1
        ON (h1.tsn, 'English') = (v1.tsn, v1.language)
    WHERE h1.parent_tsn = 0
    UNION ALL
    SELECT h1.tsn, h1.parent_tsn, w1.numeric_taxonomy || '.' || h1.tsn, 
        w1.latin_taxonomy || '.' || COALESCE(strip_label(l1.completename), h1.tsn::text,'unk'), 
        w1.english_taxonomy || '.' || strip_label(COALESCE(v1.vernacular_name, lower(l1.completename), h1.tsn::text, 'unk'))
    FROM itis.hierarchy h1
    JOIN world w1
    ON h1.parent_tsn = w1.tsn
    LEFT JOIN itis.longnames l1
        ON h1.tsn = l1.tsn
    LEFT JOIN -- just change this to "itis.vernaculars v1" to allow mulitples and all languages.  (Millions of records.)
        (SELECT tsn, min(vernacular_name) vernacular_name FROM itis.vernaculars WHERE language = 'English' GROUP BY tsn) v1
        ON (h1.tsn) = (v1.tsn)
    )
SELECT w2.tsn, w2.parent_tsn, w2.numeric_taxonomy::ltree, w2.latin_taxonomy::ltree latin_taxonomy, w2.english_taxonomy::ltree english_taxonomy
FROM world w2
ORDER BY w2.numeric_taxonomy
WITH NO DATA;

Arrêtons-nous un instant et sentons les fleurs dans cette requête. Pour commencer, nous l'avons créé sans remplir de données. Cela nous donne une chance de nous occuper de tous les problèmes de syntaxe avant de générer beaucoup de données inutiles. Nous utilisons la nature itérative de l'expression de table commune pour mettre en place une structure assez profonde ici, et nous pourrions facilement l'étendre pour couvrir plus de langues en ajoutant des données à la table des vernaculaires. La vue matérialisée présente également des caractéristiques de performances intéressantes. Il tronquera et reconstruira la table chaque fois qu'un REFRESH MATERIALIZED VIEW est appelé.

Ce que nous allons faire ensuite, c'est rafraîchir notre vision du monde. Surtout parce qu'il est sain de le faire de temps en temps. Mais dans ce cas, ce qu'il fait réellement est de remplir la vue matérialisée avec les données de itis schéma.

REFRESH MATERIALIZED VIEW itis.world_view;

Cela va prendre quelques minutes pour créer les lignes de plus de 600 000 lignes à partir des données.

Les premières lignes ressembleront à ceci :

┌────────────┬─────────┬───────────────────────────────────────────────────────────────────────────────┐
│ parent_tsn │   tsn   │                               english_taxonomy                                │
├────────────┼─────────┼───────────────────────────────────────────────────────────────────────────────┤
│     768374 │ 1009037 │ animals.bilateria.protostomia.ecdysozoa.arthropods.hexapods.insects.winged_in…│
│            │         │…sects.modern_wing_folding_insects.holometabola.ants.ants.aculeata.apoid_wasps…│
│            │         │….cicadakillers.crabroninae.larrini.gastrosericina.gastrosericus.gastrosericus…│
│            │         │…_xanthophilus                                                                 │
│     768374 │ 1009038 │ animals.bilateria.protostomia.ecdysozoa.arthropods.hexapods.insects.winged_in…│
│            │         │…sects.modern_wing_folding_insects.holometabola.ants.ants.aculeata.apoid_wasps…│
│            │         │….cicadakillers.crabroninae.larrini.gastrosericina.gastrosericus.gastrosericus…│
│            │         │…_zoyphion                                                                     │
│     768374 │ 1009039 │ animals.bilateria.protostomia.ecdysozoa.arthropods.hexapods.insects.winged_in…│
│            │         │…sects.modern_wing_folding_insects.holometabola.ants.ants.aculeata.apoid_wasps…│
│            │         │….cicadakillers.crabroninae.larrini.gastrosericina.gastrosericus.gastrosericus…│
│            │         │…_zyx                                                                          │
│     768216 │  768387 │ animals.bilateria.protostomia.ecdysozoa.arthropods.hexapods.insects.winged_in…│
│            │         │…sects.modern_wing_folding_insects.holometabola.ants.ants.aculeata.apoid_wasps…│
│            │         │….cicadakillers.crabroninae.larrini.gastrosericina.holotachysphex              │
│     768387 │ 1009040 │ animals.bilateria.protostomia.ecdysozoa.arthropods.hexapods.insects.winged_in…│
│            │         │…sects.modern_wing_folding_insects.holometabola.ants.ants.aculeata.apoid_wasps…│
│            │         │….cicadakillers.crabroninae.larrini.gastrosericina.holotachysphex.holotachysph…│
│            │         │…ex_holognathus                                                                │
└────────────┴─────────┴───────────────────────────────────────────────────────────────────────────────┘

Dans une taxonomie, le graphique ressemblerait à ceci :

Bien sûr, il y aurait en fait 21 niveaux de profondeur et plus de 600 000 enregistrements au total.

Passons maintenant à la partie amusante ! Les arbres fournissent un moyen de faire des requêtes très complexes sur une hiérarchie. L'aide pour cela se trouve dans la documentation de PostgreSQL, nous n'allons donc pas l'approfondir ici. Pour une compréhension (très rapide), chaque segment d'un ltree est appelé une étiquette. Donc, cet arbre kingdom.phylum.class.order.family.genus.species a 7 étiquettes.

Les requêtes sur un ltree utilisent une notation spéciale qui ressemble à des expressions régulières sous une forme limitée.

Voici un exemple simple :Animalia.*.Homo_sapiens

Ainsi, une requête pour trouver l'humanité dans le monde ressemblerait à ceci :

SELECT tsn, parent_tsn, latin_taxonomy, english_taxonomy 
FROM itis.world_view WHERE latin_taxonomy ~ 'Animalia.*.Homo_sapiens';

Ce qui donne les résultats attendus :

┌────────┬────────────┬────────────────────────────────────────────────┬─────────────────────────────────────────────┐
│  tsn   │ parent_tsn │                 latin_taxonomy                 │              english_taxonomy               │
├────────┼────────────┼────────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ 180092 │     180091 │ Animalia.Bilateria.Deuterostomia.Chordata.Vert…│ animals.bilateria.deuterostomia.chordates.v…│
│        │            │…ebrata.Gnathostomata.Tetrapoda.Mammalia.Theria…│…ertebrates.gnathostomata.tetrapoda.mammals.…│
│        │            │….Eutheria.Primates.Haplorrhini.Simiiformes.Hom…│…theria.eutheria.primates.haplorrhini.simiif…│
│        │            │…inoidea.Hominidae.Homininae.Homo.Homo_sapiens  │…ormes.hominoidea.Great_Apes.African_apes.ho…│
│        │            │                                                │…minoids.Human                               │
└────────┴────────────┴────────────────────────────────────────────────┴─────────────────────────────────────────────┘

Bien sûr, PostgreSQL n'en resterait jamais là. Il existe un ensemble complet d'opérateurs, d'index, de transformations et d'exemples.

Allez jeter un œil à la vaste gamme de capacités que cette technique débloque.

Imaginez maintenant que cette technique soit appliquée à d'autres types de données complexes tels que les numéros de pièces, les numéros d'identification des véhicules, les structures de nomenclature ou tout autre système de classification. Il n'est pas nécessaire d'exposer cette structure à l'utilisateur final en raison de la courbe d'apprentissage d'une complexité prohibitive pour l'utiliser directement. Mais il est tout à fait possible de construire un écran « lookup » basé sur une structure comme celle-ci qui est très puissante et cache la complexité de la mise en œuvre.

Pour notre prochain article de la série, nous explorerons l'utilisation des langages plug-in. Dans le cadre de la recherche d'alternatives aux curseurs dans PostgreSQL, nous utiliserons un langage de notre choix pour modéliser les données de la manière la plus appropriée à nos besoins. A la prochaine !