简体   繁体   中英

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM