Mahesh
Mahesh

Reputation: 61

Extracting data from nested XML PostgreSQL

I have an XML that needs to be inserted into the table. The XML looks something like

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <pats>
      <pat>
         <id>1</id>
         <pat_maps>
            <pat_map>
               <pgid>100</pgid>
               <pgname>test</pgname>
            </pat_map>
            <pat_map>
               <pgid>101</pgid>
               <pgname>test1</pgname>
            </pat_map>
         </pat_maps>
      </pat>
      <pat>
         <id>2</id>
         <pat_maps>
            <pat_map>
               <pgid>102</pgid>
               <pgname>test2</pgname>
            </pat_map>
         </pat_maps>
      </pat>
      <pat>
         <id>3</id>
         <pat_maps>
            <pat_map>
               <pgid>104</pgid>
               <pgname>test6</pgname>
            </pat_map>
            <pat_map>
               <pgid>105</pgid>
               <pgname>test7</pgname>
            </pat_map>
         </pat_maps>
      </pat>
   </pats>
</root> 

I want to insert data in the following way

ID  pgid    pgname
1   100     test
1   101     test1
2   102     test2
3   104     test6
3   105     test7

Tried below but it is applying cross join

with x(t) as (select '<?xml version="1.0" encoding="UTF-8"?>
<root>
   <pats>
..............
..........
      </pat>
   </pats>
</root>'::xml AS t
)
, base_id as (SELECT 
    unnest(xpath('/root/pats/pat/id/text()', t)) AS id
from x
), nested_rec as ( select 
    unnest(xpath('pgid/text()', cat_assn_list)) AS pgid,
    unnest(xpath('pgname/text()', cat_assn_list)) AS pgname
from (select unnest(xpath('/root/pats/pat/pat_maps/pat_map', t)) cat_assn_list from x) q
) 
select base_id.*,nested_rec.* from base_id,nested_rec;

******* output *********
ID  PGID    PGNAME  
"1" "100"   "test"
"1" "101"   "test1"
"1" "102"   "test2"
"1" "104"   "test6"
"1" "105"   "test7"
"2" "100"   "test"
"2" "101"   "test1"
"2" "102"   "test2"
"2" "104"   "test6"
"2" "105"   "test7"
"3" "100"   "test"
"3" "101"   "test1"
"3" "102"   "test2"
"3" "104"   "test6"
"3" "105"   "test7"

I haven't found a way, how to nest the XML ID wise and prepare the resultset?. Is there any other way to convert xml data to table without using XPath function in PostgreSQL? Thanks in advance.

Upvotes: 1

Views: 1106

Answers (1)

user330315
user330315

Reputation:

The following does what you want and is a bit shorter:

select (xpath('/pat/id/text()', d.pat))[1]::text::int as id,
       (xpath('/pat_map/pgid/text()', m.map))[1]::text::int as pgid,
       (xpath('/pat_map/pgname/text()', m.map))[1]::text as pgname
from x 
  cross join unnest(xpath('/root/pats/pat', x.t)) as d(pat)
  cross join unnest(xpath('/pat/pat_maps/pat_map', d.pat)) as m(map)
;

Online example

With a more modern Postgres version, you could use xmltable():

select d.*
from x
  cross join xmltable ('/root/pats/pat/pat_maps/pat_map' 
                       passing t 
                       columns 
                        id integer path '../../id',
                        pgid integer path 'pgid', 
                        pgname text path 'pgname') as d

Upvotes: 4

Related Questions