sql >> Databasteknik >  >> RDS >> PostgreSQL

PostgreSQL till XML med 3 tabeller

Du har tre nivåer av kapslade tabeller.

Exempeldata:

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');

Metod 1:Gör en vänsterkoppling, hantera XML i klienten

Det enklaste sättet att hantera detta är att göra en left join över alla tre borden, ordnade från yttersta till innersta. Sedan itererar du ner resultatuppsättningen, stänger ett element och öppnar ett annat när ämnet på den nivån ändras.

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;

loopa sedan över raderna som returneras och pseudokod för varje rad :

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;

För att skriva XML skulle du använda något som XMLWriter . För att läsa frågedata kan du använda något som PDO eller vilken drivrutin du föredrar. Om datamängden är stor, överväg att använda en markör för att läsa data.

Det här fungerar bra, men det överför mycket av överflödig data, eftersom du överför n kopior av den yttre tabellens data för varje n rader i den inre tabellen som är associerad med den.

För att minska överskottsdata som utbyts kan du endast välja ID för de yttre tabellerna

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;

... sedan när du byter till en ny tablea / tableb, SELECT resten av dess rader då. Du kommer förmodligen att använda en andra anslutning för att göra detta så att du inte stör resultatuppsättningen och markörtillståndet på huvudanslutningen du läser rader från.

Metod 2:Gör allt i PostgreSQL

För mindre datamängder, eller för de inre nivåerna av större datamängder, kan du använda PostgreSQL:s XML-stöd för att konstruera XML-dokumenten, t.ex.:

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;

... men egentligen måste man vara någon sorts masochist för att skriva kod som det. Även om det kan visa sig vara ganska snabbt.

För att förstå frågan måste du läsa PostgreSQL XML-dokumenten . Den knasiga syntaxen skapades av SQL/XML-kommittén, skyll inte på oss.

Observera också att radvariabler används flitigt i ovanstående kod för att hålla den organiserad. a , b och c skickas som hela rader till de yttre lagren av frågan. Detta undviker behovet av att bråka med alias när namn kolliderar. Syntaxen (a).a_id , etc, betyder "a_id fältet för radvariabeln a ". Se PostgreSQL-manualen för detaljer.

Ovanstående använder en bättre XML-struktur (se kommentarer nedan). Om du vill avge attribut inte element, kan du ändra XMLFOREST anrop till XMLATTRIBUTES samtal.

Utdata:

<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>

eller, vackert tryckt:

<?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>

Vänligen skicka ut bättre XML

På en sidonotering verkar det lockande att använda sådana attribut i XML, men det blir snabbt svårt och fult att arbeta med. Använd bara vanliga XML-element:

  <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>



  1. Bästa metoden för att lagra en lista med användar-ID:n

  2. Använder PHP och RegEx för att hämta alla alternativvärden från en webbplatss källkod

  3. Hur Strftime()-funktionen fungerar i SQLite

  4. Hur man väljer kolumn i tabell genom att skapa rad i en annan tabell i MySQL