Получить узлы заказа с дочерними элементами в XML XPATH POSTGRES

#sql #xml #postgresql #xpath

Вопрос:

У меня есть некоторый XML в таблице (назовем его xml_table), мне нужно привести родителей с его детьми в идеальный порядок, но этого не происходит. Я упрощу задачу.

Это мой XML:

 <group name="A">
    <category target_id="1001">
        <category flow_id="0" />
        <category flow_id="1" />
        <category flow_id="2" />
    </category>
</group>

<group name="B">
    <category target_id="1002">
        <category flow_id="0" />
    </category>
    <category target_id="1003">
        <category flow_id="0" />
    </category>
    <category target_id="1004">
        <category flow_id="0" />
    </category>
    <category target_id="1005">
        <category flow_id="0" />
    </category>
</group>

<group name="C">
    <category target_id="1006">
        <category flow_id="0" />
        <category flow_id="2" />
        <category flow_id="4" />
    </category>
    <category target_id="1007">
        <category flow_id="1" />
        <category flow_id="6" />
    </category>
    <category target_id="1008">
        <category flow_id="0" />
        <category flow_id="1" />
        <category flow_id="2" />
    </category>
    <category target_id="1009">
        <category flow_id="0" />
        <category flow_id="1" />
        <category flow_id="2" />
        <category flow_id="3" />
    </category>
</group>
 

И это мой SQL:

 SELECT      unnest((xpath('@name', team)::text[])) AS group
            ,unnest((xpath('category/@target_id', team)::text[]::int[]))  AS target_id
            ,unnest((xpath('category/category/@flow_id', team)::text[]::int[]))  AS flow_id
FROM (

        SELECT team::xml
        FROM xml_table;
    )
 

С помощью этого SQL я идеально подхожу для ГРУПП A и B. Когда в группе только 1 идентификатор цели и много идентификаторов потока (например, A)

Группа Target_id Flow_id
A 1001 0
A 1001 1
A 1001 2

Работает Идеально

Когда у каждого target_id есть только один flow_id, не важно, много ли target_id, например, группа B.

Группа Target_id Flow_id
B 1002 0
B 1003 0
B 1004 0
B 1005 0

Works perfect too.

But in the group C doesn’t work, repeat many times the instance target_id — flow_id and even mix it, for example appear target_id with other flow_id than doesn’t belong (here repeat 3 times Target_id-Flow_id.

Group Target_id Flow_id
C 1006 0
C 1006 2
C 1006 4
C 1006 0
C 1006 2
C 1006 4
C 1006 0
C 1006 2
C 1006 4

Смешайте тоже

Группа Target_id Flow_id
C 1006 0
C 1006 2
C 1006 6

Flow_id = 6 является частью target_id = 1007 и отображается в target_id = 1006.

Это я хочу получить, та же структура XML, в которой хранится заказ.

Группа Target_id Flow_id
A 1001 0
A 1001 1
A 1001 2
B 1002 0
B 1003 0
B 1004 0
B 1005 0
C 1006 0
C 1006 2
C 1006 4
C 1007 1
C 1007 6
C 1008 0
C 1008 1
C 1008 2
C 1009 0
C 1009 1
C 1009 2
C 1009 3

Комментарии:

1. Xpath не хватает контекста, я думаю , так и должно быть //category/@target_id , /group/@name и //category/category/@flow_id

Ответ №1:

Выражения XPATH могут быть действительно неприятными при выполнении сложных задач. Мое предложение: Разделите операции на несколько подзапросов или CTE, чтобы упростить задачу. Это может дать вам некоторое вдохновение:

 SELECT DISTINCT
  g.group_id,
  t.target_id,
  unnest(xpath('//group[@name="'||g.group_id||'"]/category/category/@flow_id', team)::text[]) flow_id
FROM 
  xml_table x,
  LATERAL unnest(xpath('//group/@name', x.team)::text[]) g (group_id),
  LATERAL unnest(xpath('//group[@name="'||g.group_id||'"]/category/@target_id', team)::text[]) t (target_id)  
ORDER BY g.group_id,t.target_id,flow_id;
 

ДЕМОНСТРАЦИЯ: db<>fiddle

Комментарии:

1. Спасибо, парень, за твой ответ, извини за поздний ответ. Сначала я нахожу другое решение, не по этапам, а не все вместе. В любом случае, ваш sql-это другой способ, чем я не знаю, спасибо за это. Но в XML есть 279 записей, и этот sql возвращает 450. Я не знаю почему, я посмотрю и проверю позже. В любом случае, это дает мне другую перспективу, спасибо!