Jag har precis begått en patch av Pavel Stěhule som lägger till XMLTABLE funktionalitet till PostgreSQL 10. XMLTABLE är en mycket användbar funktion som dikteras av SQL/XML-standarden, som låter dig förvandla din XML-data till relationsform så att du kan blanda den med resten av din relationsdata. Denna funktion har många användningsområden; fortsätt läsa för lite detaljer om det.
Förmodligen det mest intressanta användningsfallet för XMLTABLE är att extrahera data från något XML-dokument för att infoga i en relationstabell under ETL-bearbetning i databasen. Men XMLTABLE kan användas direkt på data som lagras i XML-kolumner, så att när data är i relationsform kan du använda vilka standardoperationer du vill, som att lägga till WHERE satser, gör aggregationer, sammanfogar till andra tabeller och så vidare.
Ett enkelt exempel
Som ett exempel, låt oss anta att du administrerar en hotellkedja och att uppgifterna lagras på följande sätt:
CREATE TABLE hoteldata AS SELECT xml
$$<hotels>
<hotel id="mancha">
<name>La Mancha</name>
<rooms>
<room id="201"><capacity>3</capacity><comment>Great view of the Channel</comment></room>
<room id="202"><capacity>5</capacity></room>
</rooms>
<personnel>
<person id="1025">
<name>Ferdinando Quijana</name><salary currency="PTA">45000</salary>
</person>
</personnel>
</hotel>
<hotel id="valpo">
<name>Valparaíso</name>
<rooms>
<room id="201"><capacity>2</capacity><comment>Very noisy</comment></room>
<room id="202"><capacity>2</capacity></room>
</rooms>
<personnel>
<person id="1026"><name>Katharina Wuntz</name><salary currency="EUR">50000</salary></person>
<person id="1027"><name>Diego Velázquez</name><salary currency="CLP">1200000</salary></person>
</personnel>
</hotel>
</hotels>$$ AS hotels;
Med XMLTABLE , kan du förvandla detta till en relationellt formaterad tabell som består av rumsnummer och kapacitet, med anteckningar för varje hotell i din kedja:
SELECT xmltable.*
FROM hoteldata,
XMLTABLE ('/hotels/hotel/rooms/room' PASSING hotels
COLUMNS
id FOR ORDINALITY,
hotel_name text PATH '../../name' NOT NULL,
room_id int PATH '@id' NOT NULL,
capacity int,
comment text PATH 'comment' DEFAULT 'A regular room'
);
id | hotellnamn | rums-id | kapacitet | kommentar |
---|---|---|---|---|
1 | La Mancha | 201 | 3 | Fantastisk utsikt över kanalen |
2 | La Mancha | 202 | 5 | Ett vanligt rum |
3 | Valparaíso | 201 | 2 | Mycket bullrigt |
4 | Valparaíso | 202 | 2 | Ett vanligt rum |
Förklara syntaxen
Låt oss studera frågan ovan. XMLTABLE klausul måste finnas i FROM del av frågan. Vi har även hotelldata i FRÅN , vilket är det som matar in data till XMLTABLE .
Först, PASSERING satsen är där vi anger XML-data som vi vill bearbeta. I det här fallet kommer data från hotellen kolumnen i hotelldata tabell. Vi kallar detta dokumentuttrycket .
Strax före PASSERING klausul ser du ett XPath-uttryck '/hotels/hotel/rooms/room' . Vi kallar detta det radgenererande uttrycket eller bara raduttrycket .
Vi har KOLUMNER klausul nästa, deklarerar några kolumner. För varje kolumn anger vi en datatyp samt en valfri PATH sats, som vi kallar kolumnuttrycket .
XMLTABELL Operationsteorin är att raduttrycket appliceras på dokumentuttrycket, skär dokumentet i bitar för att generera rader; för varje rad som genereras på detta sätt används de olika kolumnuttrycken för att erhålla värdena för varje kolumn.
Kolumnuttrycket är ett XPath-uttryck som erhåller ett värde som börjar från XML för den aktuella raden. Om ingen PATH anges, används själva kolumnnamnet som XPath-uttryck. Observera att i kolumnen hotellnamn vi använde en sökväg med "../ ", vilket betyder att "gå upp" i XML-dokumentet för att hämta värden från "container"-objekten i dokumentet. Vi kan också använda xml PATH '.' i rad, vilket ger oss den fullständiga XML-källkoden för den raden.
En kolumn kan markeras med FÖR ORDINALITET . Kolumnen är då av typen INTEGER , och numreras sekventiellt för varje rad som erhålls från dokumentet. (Om det finns flera inmatningsdokument, till exempel när du har flera rader i en tabell, börjar räknaren från 1 för varje nytt dokument).
Det finns också en DEFAULT klausul. Om XPath för en kolumn inte stämmer överens med ett värde för en viss rad, visas DEFAULT värde används.
Några av dessa kolumner har markerats som NOT NULL . Om det inte finns någon matchning och ingen DEFAULT satsen är specificerad (eller DEFAULT utvärderas också till NULL ), skapas ett fel.
Jag kommer inte att gå in mer i detalj på XPath, som är ett kraftfullt språk, men jag kan erbjuda XPath-artikeln på Wikipedia och det officiella rekommendationsdokumentet från W3C som användbara resurser.
Den fullständiga XMLTABLE-syntaxen
Den dokumenterade syntaxöversikten är:
xmltable
( [XMLNAMESPACES(namespace uri
ASnamespace name
[, ...])]row_expression
PASSING [BY REF]document_expression
[BY REF] COLUMNSname
{type
[PATHcolumn_expression
] [DEFAULTexpr
] [NOT NULL | NULL] | FOR ORDINALITY } [, ...] )
Observera att dokumentuttrycket kan vara en referens till en tabell som du har i FROM-satsen, eller så kan det vara ett komplett XML-dokument som en bokstavlig sträng. BY REF-klausulerna har ingen effekt; de finns för kompatibilitet med standarden och med andra databassystem.
Jag har inte täckt XMLNAMESPACES klausul i detta inlägg; Jag lämnar det för en framtida avbetalning.
Tillämpa SQL ovanpå
Som nämnts, när XMLTABLE har bearbetat datan till relationsform, kan du göra vad du vill med välkända verktyg. Om du till exempel hade ett annat XML-dokument med mer personal på varje hotell,
INSERT INTO hoteldata VALUES (xml $$<hotels> <hotel id="mancha"> <name>La Mancha</name> <personnel> <person id="1028"> <name>Sancho Panza</name><salary currency="PTA">35000</salary> </person> </personnel> </hotel> <hotel id="valpo"> <name>Valparaíso</name> <personnel> <person id="1029"><name>Kurt Werner</name><salary currency="EUR">30000</salary></person> </personnel> </hotel> </hotels>$$);
Det är enkelt att få de totala lönerna för varje valuta du behöver betala på varje hotell,
SELECT hotel, currency, sum(salary) FROM hoteldata, XMLTABLE ('/hotels/hotel/personnel/person' PASSING hotels COLUMNS hotel text PATH '../../name' NOT NULL, salary integer PATH 'salary' NOT NULL, currency text PATH 'salary/@currency' NOT NULL ) GROUP BY hotel, currency;
hotell | valuta | summa |
---|---|---|
Valparaíso | CLP | 1200000 |
Valparaíso | EUR | 80000 |
La Mancha | PTA | 80000 |
Slutsats
I den här artikeln har jag tagit upp den nya funktionen XMLTABLE att visas i PostgreSQL version 10. Jag tror XMLTABLE är en utmärkt funktion för att integrera extern data, och jag hoppas att du också tycker att den är värdefull. Testa det och rapportera eventuella problem, så att vi kan lösa dem innan den slutliga releasen. Om du gillar XMLTABLE , se till att meddela oss och lämna en kommentar!