sql >> Databasteknik >  >> RDS >> PostgreSQL

Oracle till PostgreSQL — Markörer och lträd

I vår senaste artikel om markörer i PostgreSQL pratade vi om ommonable xpressions (CTE). Idag fortsätter vi att upptäcka nya alternativ till markörer genom att använda en mindre känd funktion i PostgreSQL.

Vi använder data som vi importerade i föregående artikel (länkad ovan). Jag väntar ett ögonblick tills du följer proceduren där.

Förstod det? Okej.

Uppgifterna är ett taxonomidiagram över den naturliga världen. Som en påminnelse från den grundläggande gymnasiebiologin är dessa data organiserade av Carl Linnéa i kungarike, filum, klass, ordning, familj, släkte och arter. Naturligtvis har vetenskapen gått framåt något så lite under de senaste 250 åren, så det taxonomiska diagrammet är 21 nivåer djupt. Vi hittar hierarkiträdet i en tabell som (föga förvånande) kallas itis.hierarchy .

Ämnet för den här artikeln är hur man använder ltrees i PostgreSQL. Specifikt hur man använder dem för att korsa en komplex postuppsättning mycket effektivt. I den meningen kan vi betrakta dem som ett annat surrogat för markörer.

Uppgifterna är inte kurerade (tyvärr för oss) i ett ltree-format, så vi kommer att omvandla det lite för artikelns skull.

Först måste du installera ltree i databasen som du använder för att följa den här artikeln. Naturligtvis måste du vara en superanvändare för att installera tillägg.

CREATE EXTENSION IF NOT EXISTS ltree;

Nu kommer vi att använda det här tillägget för att ge några mycket effektiva sökningar. Vi kommer att behöva omvandla data till en uppslagstabell. För att utföra denna transformation kommer vi att använda CTE-tekniken som vi täckte i den förra artikeln. Längs vägen ska vi lägga till de latinska namnen och de engelska namnen till taxonomiträdet. Detta hjälper oss att slå upp objekt efter nummer, latinska namn eller engelska namn.

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

Låt oss stanna upp ett ögonblick och lukta på blommorna i den här frågan. Till att börja med skapade vi det utan att fylla i några data. Detta ger oss en chans att ta hand om eventuella syntaktiska problem innan vi genererar mycket värdelös data. Vi använder den iterativa karaktären hos det vanliga tabelluttrycket för att sätta ihop en ganska djup struktur här, och vi skulle enkelt kunna utöka den till att täcka fler språk genom att lägga till data till folkspråkstabellen. Den materialiserade vyn har också några intressanta prestandaegenskaper. Det kommer att trunkeras och bygga om tabellen när en REFRESH MATERIALIZED VIEW kallas.

Vad vi ska göra härnäst är att uppdatera vår världsbild. Mest för att det är hälsosamt att göra det då och då. Men i det här fallet, vad den faktiskt gör är att fylla den materialiserade vyn med data från itis schema.

REFRESH MATERIALIZED VIEW itis.world_view;

Detta kommer att ta några minuter att skapa 600K+ raderna från data.

De första raderna kommer att se ut så här:

┌────────────┬─────────┬───────────────────────────────────────────────────────────────────────────────┐
│ 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                                                                │
└────────────┴─────────┴───────────────────────────────────────────────────────────────────────────────┘

I en taxonomi skulle grafen se ut ungefär så här:

Naturligtvis skulle det faktiskt vara 21 nivåer djupt och 600 000+ rekord totalt.

Nu kommer vi till det roliga! ltrees ger ett sätt att göra några mycket komplexa frågor i en hierarki. Hjälpen för det finns i PostgreSQL-dokumentationen, så vi kommer inte gå in på det särskilt djupt här. För en (mycket snabb) förståelse kallas varje segment av ett lträd en etikett. Så detta lträd kingdom.phylum.class.order.family.genus.species har 7 etiketter.

Frågor mot ett ltree använder en speciell notation som är som reguljära uttryck i begränsad form.

Här är ett enkelt exempel:Animalia.*.Homo_sapiens

Så en fråga för att hitta mänskligheten i världen skulle se ut så här:

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

Vilket resulterar i det förväntade:

┌────────┬────────────┬────────────────────────────────────────────────┬─────────────────────────────────────────────┐
│  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                               │
└────────┴────────────┴────────────────────────────────────────────────┴─────────────────────────────────────────────┘

Naturligtvis skulle PostgreSQL aldrig lämna det här. Det finns en omfattande uppsättning operatorer, index, transformationer och exempel.

Ta en titt på det stora utbudet av funktioner som den här tekniken låser upp.

Föreställ dig nu att den här tekniken tillämpas på andra komplexa datatyper som artikelnummer, fordonsidentifikationsnummer, styckstrukturer eller något annat klassificeringssystem. Det är inte nödvändigt att exponera denna struktur för slutanvändaren på grund av den oöverkomliga inlärningskurvan att använda den direkt. Men det är fullt möjligt att bygga en "lookup"-skärm baserad på en struktur som denna som är mycket kraftfull och döljer komplexiteten i implementeringen.

För vår nästa artikel i serien kommer vi att utforska användningen av plug in-språk. I samband med att hitta alternativ till markörer i PostgreSQL kommer vi att använda ett valfritt språk för att modellera data på det mest lämpliga sättet för våra behov. Vi ses nästa gång!


  1. SQL-fråga för att hitta den n:e högsta lönen från en lönetabell

  2. Hur undkommer man bokstavligt procenttecken när alternativet NO_BACKSLASH_ESCAPES är aktiverat?

  3. Hitta alla icke-numeriska värden i en kolumn i Oracle

  4. hur man deklarerar global variabel i SQL Server ..?