# Xmltable() mit invalidem XML

**URL:** https://www.pg-forum.de/t/xmltable-mit-invalidem-xml/12895
**Category:** SQL
**Created:** [9. November 2023 um 08:49 UTC](https://www.pg-forum.de/t/xmltable-mit-invalidem-xml/12895 "2023-11-09T08:49:41Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![castorp](https://www.pg-forum.de/letter_avatar_proxy/v4/letter/c/e99b99/32.png) [@castorp](https://www.pg-forum.de/u/castorp)
#### Post date: [9. November 2023 um 08:49 UTC](https://www.pg-forum.de/t/xmltable-mit-invalidem-xml/12895/1 "2023-11-09T08:49:41Z")

</div>

Wir haben hier eine Tabelle mit einer `text` Spalte in der manchmal XML drin steht und manchmal nicht. Vereinfacht ausgedrückt, sowas:

```
create table data 
(
  id int, content text
);

insert into data (id, content)
values
(1, '<info><name>peter</name><location>somewhere</location></info>'),
(2, '$bla=blub');

```

(nicht unser Design, gekaufte Anwendung)

Ich möchte die Daten aus dem XML auslesen, und dachte das Folgende müsste doch eigentlich funktionieren:

```
select d.id,
       x.*
from data d
  left join xmltable ('/info'
                      passing cast (d.content as xml)
                      columns name text path 'name', 
                              location text path 'location'
                     ) as x on d.content like '<info%'

```

Leider sieht es so aus, als ob die JOIN Bedingung die nicht-XML Datensätze nicht _vor_ dem Aufruf von XMLTABLE rausfiltert, sondern danach - und das hat mich etwas verwirrt bzw. überrascht. Ich hätte erwartet, dass die JOIN Bedingung **vor** dem Aufruf der XMLTABLE Funktion ausgeführt wird. Auch ein `ON d.content like '<%'` funktioniert nicht.

Mir ist klar, wie ich das umgehen kann (mit einem CASE), aber ist meine Annahme, dass eine join Bedingung Datensätze _vor_ der Verarbeitung ausfiltert, falsch?

Für mich sieht das eher nach einem Bug aus wenn ich ehrlich bin.

---

<div class="post-metadata">

### Author: ![pogomips](https://www.pg-forum.de/letter_avatar_proxy/v4/letter/p/bbe5ce/32.png) [@pogomips](https://www.pg-forum.de/u/pogomips)
#### Post date: [9. November 2023 um 18:02 UTC](https://www.pg-forum.de/t/xmltable-mit-invalidem-xml/12895/2 "2023-11-09T18:02:42Z")

</div>

Kommt drauf an, wie das ausgeführt wird (im Feld, nicht im Beispiel)  
Explain sagt:

`QUERY PLAN
Nested Loop Left Join (cost=0.00..2883.38 rows=1270 width=68)
  Join Filter: (d.content ~~'<info%'::text)
  -> Seq Scan on data d (cost=0.00..22.70 rows=1270 width=36)
  -> Table Function Scan on "xmltable" x (cost=0.00..1.00 rows=100 width=64)
`  
Der Typ Text erlaubt ja keine Garantien, das es valides XML ist.

Warum nicht sowas:  
select \* from data where content::xml is document

Und mit der Menge dann weiterarbeiten?

Ich könnte mir vorstellen, dass man vielleicht mit der gezeigten Konstellation große Datenmengen überspringen will, aber vielleicht verstehe ich den Sinn davon auch nicht. XMLTable ist vermutlich auf validen Content angewiesen. like \<info% garantiert das aber als Filter auch nicht.

---

<div class="post-metadata">

### Author: ![castorp](https://www.pg-forum.de/letter_avatar_proxy/v4/letter/c/e99b99/32.png) [@castorp](https://www.pg-forum.de/u/castorp)
#### Post date: [9. November 2023 um 18:56 UTC](https://www.pg-forum.de/t/xmltable-mit-invalidem-xml/12895/3 "2023-11-09T18:56:46Z")

</div>

> Der Typ Text erlaubt ja keine Garantien, das es valides XML ist.

Deswegen ja die JOIN Bedingung die dafür sorgt, dass nur valides XML selektiert wird (in diesem Fall reicht die LIKE Bedingung tatsächlich aus)

> [@pogomips](#):
>
> Warum nicht sowas:  
> select \* from data where content::xml is document
> 
> Und mit der Menge dann weiterarbeiten?

Ich will ja alle Datensätze haben, nur für die, die XML enthalten will ich auch die Zusatzinfos.

Aber ich werde mir halt mit einem CASE innerhalb des CAST behelfen, ich hatte gehofft, es mit der JOIN Bedingung etwas effizienter machen zu können.

---

<div class="post-metadata">

### Author: ![laurenz](https://www.pg-forum.de/letter_avatar_proxy/v4/letter/l/bc79bd/32.png) [@laurenz](https://www.pg-forum.de/u/laurenz)
#### Post date: [10. November 2023 um 10:29 UTC](https://www.pg-forum.de/t/xmltable-mit-invalidem-xml/12895/4 "2023-11-10T10:29:06Z")

</div>

Das ist kein Bug; PostgreSQL rechnet die rechte Tabelle aus, bevor es die Join-Bedingung anwendet. Eliminiere die bösen Zeilen einfach früher:

```auto
SELECT d.id,
       x.*
FROM (SELECT id,
             CASE WHEN content LIKE '<info%'
                  THEN content
             END AS content
      FROM data) AS d
   CROSS JOIN LATERAL xmltable(
                         '/info'
                         PASSING cast (d.content AS xml)
                         COLUMNS name text path 'name', 
                                 location text path 'location'
                      );

```

---

<div class="post-metadata">

### Author: ![castorp](https://www.pg-forum.de/letter_avatar_proxy/v4/letter/c/e99b99/32.png) [@castorp](https://www.pg-forum.de/u/castorp)
#### Post date: [10. November 2023 um 10:34 UTC](https://www.pg-forum.de/t/xmltable-mit-invalidem-xml/12895/5 "2023-11-10T10:34:28Z")

</div>

> [@laurenz](#):
>
> Das ist kein Bug; PostgreSQL rechnet die rechte Tabelle aus, bevor es die Join-Bedingung anwendet.

Effizienter wäre es aber, die XMLTABLE Funktion gar nicht erst aufzurufen, wenn die JOIN Bedingung ein `false` liefert. Ich habe das CASE direkt in den CAST Operator aufgenommen.

---

<div class="post-metadata">

### Author: ![system](https://www.pg-forum.de/uploads/default/original/1X/dc97a981dd26692d5fb52e8b8fc7c090d7b9559a.png) [@system](https://www.pg-forum.de/u/system)
#### Post date: [9. Januar 2024 um 10:35 UTC](https://www.pg-forum.de/t/xmltable-mit-invalidem-xml/12895/6 "2024-01-09T10:35:15Z")

</div>

Dieses Thema wurde automatisch 60 Tage nach der letzten Antwort geschlossen. Es sind keine neuen Antworten mehr erlaubt.
