PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

PostgreSQL vers XML avec 3 tables

Vous disposez de trois niveaux de tableaux imbriqués.

Exemple de données :

CREATE TABLE a(
  a_id integer primary key,
  name text
);

CREATE TABLE b(
  b_id integer primary key,
  a_id integer references a(a_id),
  val text
);

CREATE TABLE c(
  c_id serial primary key,
  b_id integer references b(b_id),
  blah text
);

INSERT INTO a(a_id, name) VALUES (1, 'fred'),(2, 'bert');

INSERT INTO b(b_id, a_id, val) VALUES 
(11, 1, 'x'), (12, 1, 'y'), (21, 2, 'a'), (22, 2, 'b');

INSERT INTO c(b_id, blah) VALUES
(11, 'whatever'), (11, 'gah'), (12, 'borkbork'), (22, 'fuzz');

Méthode 1 :effectuez une jointure à gauche, gérez XML dans le client

La façon la plus simple de gérer cela est de faire une jointure gauche sur les trois tables, ordonnées de la plus externe à la plus interne. Ensuite, vous parcourez le jeu de résultats, en fermant un élément et en en ouvrant un autre chaque fois que le sujet à ce niveau change.

select *
from a left join b on (a.a_id = b.a_id)
       left join c on (b.b_id = c.b_id)
order by a.a_id, b.b_id, c.c_id;

puis bouclez sur les lignes renvoyées, et pour chaque ligne, pseudocode :

cur_row = get_new_row()

if (cur_row[b_id] != prev_row[b_id]) {
   emit_close_tableb();
}
if (cur_row[a_id] != prev_row[a_id]) {
   emit_close_tablea();
   emit_open_tablea(cur_row);
}
if (cur_row[b_id] != prev_row[b_id]) {
   emit_open_tableb(cur_row);
}
emit_tablec(cur_row);

prev_row = cur_row;

Pour écrire le XML, vous utiliseriez quelque chose comme XMLWriter . Pour lire les données de la requête, vous pouvez utiliser quelque chose comme PDO ou tout autre pilote que vous préférez. Si l'ensemble de données est volumineux, envisagez d'utiliser un curseur pour lire les données.

Cela fonctionne bien, mais cela transfère beaucoup beaucoup de données excédentaires, puisque vous transférez n copies des données de la table externe pour chaque n lignes de la table interne qui lui sont associées.

Pour réduire l'excès de données échangées, vous pouvez sélectionner uniquement les ID des tables externes

select a.a_id, b.b_id, c.*
from a left join b on (a.a_id = b.a_id)
       left join c on (b.b_id = c.b_id)
order by a.a_id, b.b_id, c.c_id;

... puis lorsque vous passez à une nouvelle tablea / tableb, SELECT le reste de ses rangées ensuite. Vous utiliserez probablement une deuxième connexion pour ce faire afin de ne pas perturber le jeu de résultats et l'état du curseur sur la connexion principale à partir de laquelle vous lisez les lignes.

Méthode 2 : Tout faire dans PostgreSQL

Pour les ensembles de données plus petits ou pour les niveaux internes d'ensembles de données plus volumineux, vous pouvez utiliser le support XML de PostgreSQL pour construire les documents XML, par exemple :

WITH xmlinput AS (
  SELECT a, b, c
  FROM a
  LEFT JOIN b ON (a.a_id = b.a_id)
  LEFT JOIN c on (b.b_id = c.b_id)
  ORDER BY a.a_id, b.b_id, c.c_id
)
SELECT
  XMLELEMENT(name items,
    xmlagg(
      XMLELEMENT(name a,
        XMLFOREST((a).a_id AS a_id, (a)."name" AS name),
        b_xml
      )
    ORDER BY (a).a_id)
  ) AS output
FROM
(
  SELECT
    a,
    xmlagg(
      XMLELEMENT(name b,
        XMLFOREST((b).b_id AS b_id, (b).val AS val),
        c_xml
      )
    ORDER BY (b).b_id)
    AS b_xml
  FROM
  (
    SELECT
      a, b,
      xmlagg(
        XMLELEMENT(name c,
          XMLFOREST((c).c_id AS c_id, (c).blah AS blah)
        )
      ORDER BY (c).c_id)
      AS c_xml
    FROM xmlinput
    GROUP BY a, b
  ) c_as_xml
  GROUP BY a
) b_as_xml;

... mais vraiment, il faut être une sorte de masochiste pour écrire du code comme ça. Bien que cela puisse s'avérer assez rapide.

Pour comprendre la requête vous devrez lire la documentation XML PostgreSQL . La syntaxe farfelue a été imaginée par le comité SQL/XML, ne nous blâmez pas.

Notez également que row-variables sont largement utilisés dans le code ci-dessus pour le garder organisé. a , b et c sont transmis sous forme de lignes entières aux couches externes de la requête. Cela évite d'avoir à jouer avec les alias lorsque les noms entrent en collision. La syntaxe (a).a_id , etc, signifie "le a_id champ de la variable de ligne a ". Voir le manuel PostgreSQL pour plus de détails.

Ce qui précède utilise une meilleure structure XML (voir les commentaires ci-dessous). Si vous souhaitez émettre des attributs et non des éléments, vous pouvez modifier le XMLFOREST appels à XMLATTRIBUTES appels.

Sortie :

<items><a><a_id>1</a_id><name>fred</name><b><b_id>11</b_id><val>x</val><c><c_id>1</c_id><blah>whatever</blah></c><c><c_id>2</c_id><blah>gah</blah></c></b><b><b_id>12</b_id><val>y</val><c><c_id>3</c_id><blah>borkbork</blah></c></b></a><a><a_id>2</a_id><name>bert</name><b><b_id>21</b_id><val>a</val><c/></b><b><b_id>22</b_id><val>b</val><c><c_id>4</c_id><blah>fuzz</blah></c></b></a></items>

ou, joliment imprimé :

<?xml version="1.0" encoding="utf-16"?>
<items>
    <a>
        <a_id>1</a_id>
        <name>fred</name>
        <b>
            <b_id>11</b_id>
            <val>x</val>
            <c>
                <c_id>1</c_id>
                <blah>whatever</blah>
            </c>
            <c>
                <c_id>2</c_id>
                <blah>gah</blah>
            </c>
        </b>
        <b>
            <b_id>12</b_id>
            <val>y</val>
            <c>
                <c_id>3</c_id>
                <blah>borkbork</blah>
            </c>
        </b>
    </a>
    <a>
        <a_id>2</a_id>
        <name>bert</name>
        <b>
            <b_id>21</b_id>
            <val>a</val>
            <c />
        </b>
        <b>
            <b_id>22</b_id>
            <val>b</val>
            <c>
                <c_id>4</c_id>
                <blah>fuzz</blah>
            </c>
        </b>
    </a>
</items>

Veuillez émettre un meilleur XML

D'un autre côté, l'utilisation d'attributs comme celui-ci dans XML semble tentante, mais cela devient rapidement difficile et moche à utiliser. Veuillez simplement utiliser des éléments XML normaux :

  <Table 1>
    <Nr>1</Nr>
    <Name>blah</Name>
     <Table 2>
       <Nr>1</Nr>
       <Table 3>
          <Col1>42</Col1>
          <Col2>...</Col2>
          <Col3>...</Col3>
          <Col4>...</Col4>
          ...
       </Table 3>
     </Table 2>
   </Table 1>