Back To Top

XSLT: SQL DDL erzeugen


SQL DDL aus XML Schema erzeugen

Comelio-Blog XSLT SQL DDL aus XML SchemaDie Erzeugung von SQL aus XML-Dateien lässt sich in vielerlei Hinsicht verwenden. Man kann entweder XML Schema benutzen, um in element- oder attributorientierter Form DB-Modellierungsinformationen zu speichern, oder man setzt auf speziell vereinfachte XML-Dokumente, um in element- oder attributorientierter Form einfache Werte zu speichern, aus denen dann CSV-Dateien oder auch wieder SQL-Dateien erzeugt werden.

In diesem Artikel diskutieren wir grundsätzliche Möglichkeiten, mit XSLT aus XML und XML Schema SQL-Quelltext zu erzeugen.

Zwei grundsätzliche Möglichkeiten bieten sich, um XML Schema für die Speicherung von Datenbankinformationen zu verwenden, die den Schemakatalog betreffen. Beiden ist gemein, dass die element-Elemente der obersten Ebene die einzelnen Tabellen speichern.

Entweder erstellt man die Spalten der beschriebenen Tabelle als Attribute dieses Elements oder als weitere Kindelemente. Durch die XML-Schema-Syntax ergibt sich keine Veränderung hinsichtlich der Datentypangabe. Auch was die Verarbeitung anbetrifft, ist dies relativ simpel zu erkennen, ob man in einer attributorientierten oder elementorientierten Form steckt, sodass man auch umfangreiche Transformationsdokumente schreiben kann, die für beide Formen verwendbar sind. Mögliche Vorteile der einen oder anderen Form können vielleicht hinsichtlich der Lesbarkeit oder einer grafischen Aufbereitung in einem Editor gesehen werden.

Als grundsätzliche, hier jedoch den Rahmen sprengende Erweiterungsoptionen, stehen noch einige Aspekte für eine Diskussion bereit. Man kann XML Schema auch für die Speicherung von anderen Schema-Objekten einsetzen, die sich nicht notwendigerweise auf Tabellen beschränken müssen. Der einfachste Bereich wären hier noch Sichten, weil man nur wenige zusätzliche Informationen ergänzen muss, um die Tabellen- oder Spaltennamen sowie die Abfragestrukturen aufzunehmen, auf denen diese Sicht beruht. Bei Rollen oder Benutzern erfordert dies entweder eine völlige Umformulierung der Syntax von XML Schema oder evtl. auch eine Abkehr von dieser Technik und Entwicklung einer eigenen Syntax.

Folgendes XML-Schema-Dokument speichert nun für die Tabelle Rechnung des Datenmodells, das die RuhrFon GmbH benutzt, die Spaltennamen mit ihren Datentypen. Einige Datentypen sind sehr einfach und werden direkt im type-Attribut angegeben. Andere Datentypen hingegen stellen sich als simpleType-Elemente innerhalb der element-Elemente dar. Auf diesen Umstand muss jeweils reagiert werden, wobei allerdings auch nur diese beiden Fälle zu berücksichtigen sind. Möchte man eigene Datentypen verwenden, empfiehlt sich der Einsatz eines anderen Namensraums. So hat man die Gelegenheit, mit einem einfachen Test bei der Verarbeitung des Wertes in type zu erkennen, ob es ein XML-Schema-Datentyp oder ein eigener Datentyp ist, der außen als globales Element auf der obersten Ebene vorliegen muss.

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="821_01.xslt"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="RECHNUNG">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="R_NR" type="xs:decimal"/>
        <xs:element name="R_DATUM" type="xs:dateTime"/>
        <xs:element name="R_SUMME" type="xs:decimal"/>
        <xs:element name="R_KNR" type="xs:decimal"/>
        <xs:element name="R_TYP">
          <xs:simpleType>
            <xs:restriction base="xs:string">
              <xs:maxLength value="2"/>
            </xs:restriction>
          </xs:simpleType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Tabellenbeschreibung in XML Schema

Bei der Lektüre der Grundvorlage werden Sie viele SQL-Klauseln leicht im Text erkennen. Alle anderen Ausgabestrukturen zielen darauf ab, gut lesbaren Quelltext zu erzeugen, und beschäftigen sich daher mit Einrückungen und Absätzen. Dies erkennen Sie an den Zeilenumbrüchen und Leerzeichen, die erzeugt werden.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xsl:include href="textausrichtung.xslt"/>
  <xsl:output method="text" version="1.0"
       encoding="ISO-8859-1"/>
  <!-- Startvorlage -->
  <xsl:template match="//xs:element[parent::xs:schema]">
    <xsl:text>DROP TABLE "</xsl:text>
    <xsl:value-of select="@name"/>
    <xsl:text>";
</xsl:text>
    <xsl:text>CREATE TABLE "</xsl:text>
    <xsl:value-of select="@name"/>
    <xsl:text>" (
</xsl:text>
    <xsl:variable name="aktuellesElement" select="@name"/>
    <xsl:for-each select="//xs:element
    [ancestor::xs:element/@name=$aktuellesElement]">
      <xsl:call-template name="Einzug">
        <xsl:with-param name="grenze" select="5"/>
      </xsl:call-template>
      <xsl:text>"</xsl:text>
      <xsl:value-of select="@name"/>
      <xsl:text>"</xsl:text>
      <xsl:apply-templates select="@type | 
       xs:simpleType/xs:restriction/@base"/>
      <xsl:apply-templates 
       select="descendant::xs:maxLength/@value"/>
      <xsl:choose>
        <xsl:when test="position()=last()">
          <xs:text>
);
</xs:text>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text>,
</xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </xsl:template>

Grundvorlage

Die Datentyperkennung ist wichtig, um nach dem erzeugten Spaltennamen den passenden, in der Datenbank erkannten Datentyp anzugeben. Es besteht die Möglichkeit, entweder einen XML-Schema-Datentyp, einen eigenen auf dem XML-Schema-Datentyp basierenden Datentyp oder auch einfach nur einen Datentypbezeichner zu verwenden. Die ersten beiden Möglichkeiten eröffnen die Aussicht, mit Hilfe der Ableitungen weitere Einschränkungen vorzunehmen und auf einer abstrakten, allgemeinen Ebene bei der Datentypangabe zu bleiben. Die letzte Möglichkeit verhindert ein solches allgemeines Arbeiten und beschränkt sich automatisch auf den Einsatz einer einzigen Datenbank. Allerdings eröffnet sich durch diese Beschränkung der Vorteil, auch seltene Datentypen, die sich mit XML Schema nur schwer oder auch gar nicht angeben lassen, einfach als Wert im Quelltext zu verwenden.

  <!-- Datentyperkennung -->
  <xsl:template match="@type | 
       xs:simpleType/xs:restriction/@base">
    <xsl:call-template name="Datentyp">
      <xsl:with-param name="xsWert" select="."/>
    </xsl:call-template>
  </xsl:template>

Datentyperkennung

Die Länge ermittelt sich aus dem maxLength-Element und seinem value-Attribut. Der Wert wird für SQL einfach in runde Klammern gesetzt und in den Ausgabestrom geschrieben.

  <!-- Längenbeschränkung -->
  <xsl:template match="xs:maxLength/@value">
    <xsl:text>(</xsl:text>
    <xsl:value-of select="."/>
    <xsl:text>)</xsl:text>
  </xsl:template>

Längenbeschränkung

Für die Zuordnung der XML-Schema-Datentypen zu den möglichen DB-Datentypen verwenden wir eine Vorlage, die ganz einfach in einer längeren Fallunterscheidung die vorhandenen Datentypen zuordnet. Dabei ist natürlich in unserem einfachen Beispiel alles auf die spezielle Oracle-Datenbank der RuhrFon GmbH zugeschnitten. Möchte man sich die Freiheit genehmigen, für andere Datenbanken zu arbeiten, muss man die oben beschriebenen Techniken benutzen. Dies waren Verwendung von Fremdattributen und/oder Angabe von Namen der Datenbankdatentypen.

  <!-- Vorlage Datentypzuordnung -->
  <xsl:template name="Datentyp">
    <xsl:param name="xsWert"/>
    <xsl:text> </xsl:text>
    <xsl:choose>
      <xsl:when test="$xsWert = 'xs:decimal'">
        <xsl:text>NUMBER</xsl:text>
      </xsl:when>
      <xsl:when test="$xsWert = 'xs:string'">
        <xsl:text>VARCHAR2</xsl:text>
      </xsl:when>
      <xsl:when test="$xsWert = 'xs:dateTime'">
        <xsl:text>DATE</xsl:text>
      </xsl:when>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

Datentypzuordnung

Man erhält als Ergebnis eine SQL-Datei bzw. einen SQL-Quelltext im Oracle-SQL-Stil, der eine vorhandene Tabelle RECHNUNG löscht und dann eine neue Tabelle RECHNUNG anlegt.

DROP TABLE "RECHNUNG";
CREATE TABLE "RECHNUNG" (
      "R_NR" NUMBER,
      "R_DATUM" DATE,
      "R_SUMME" NUMBER,
      "R_KNR" NUMBER,
      "R_TYP" VARCHAR2(2)
);

Ausgabe von SQL