Between AJAX, Web services and just plain XML there is a plethora of information going to and fro. Yes, XML really makes it easy to move data from here to there and from there to here. While this is really great it totally ignores the fact that eventually the data needs to be stored somewhere, most likely in a relational database. This leaves but a single question, how to get the information from the XML document into the relational database?

In a perfect world this type of procedure would be transparent; unfortunately this isn't a perfect world. If it were I'd look like Brad Pitt and have a Bill Gates type checkbook, alas. In order to make it as close to perfect as we can here are our choices:

1. Plastic surgery
2. Unleash my killer robot army

Oops, wrong list. Let's try that again.

1. A plain vanilla looping approach using a single SQL statement per insert
2. A plain vanilla looping approach building a number of delimited SQL statements, which can be executed at once.
3. A bizarre mad scientist approach using XSL to create SQL

Care to venture a guess as to which approach I'm going to be using?

Yeah, number 3 is definitely the way to go. So let's take a look at the XML that we'll be dealing with which is shown in Listing A. Nothing flashy, just what is necessary for a proof of concept.

Listing A - Input XML document


<?xml version="1.0" ?>
<!-- Edited with the Butterfly XML Editor (http://www.butterflyxml.org) -->
<root>
<row>
<state_id>PA</state_id>
<state_name>Pennsylvania</state_name>
</row>
<row>
<state_id>NJ</state_id>
<state_name>New Jersey</state_name>
</row>
</root>

Next let's take a look at the table that we'll be creating the inserts for, shown in Table A.

Table A - Target table


state_id

VARCHAR2(2)

state_name

VARCHAR2(50)

Now with that information there are two possible courses of action that we can take. The first is to create an XSL style sheet that simulates the first approach, "A plain vanilla looping approach using a single SQL statement per insert". This approach has the advantages of speed and being pretty generic, after all XSL is pretty much a world wide standard everywhere except for possibly Redmond.

The XSL necessary for this task, shown in Listing B, looks similar to most other XSL with a couple of differences which I'll point out. First there is the xsl:output element, specifically the media-type attribute. In this style sheet the media-type is set to text/sql instead of the default text/xml or the common text/html. The other big differences are the inclusion of in.xsl and sqlApostrophe.xsl, which are used to determine which elements are text or date as opposed to numeric and replace single apostrophes with double apostrophes to prevent issues with the SQL. In addition, there is also the shear number of xsl:text and xsl:value-of elements used to create the output.

Listing B - XSL style sheet


<?xml version="1.0"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="table"/>
<xsl:param name="textColumns"/>
<xsl:param name="dateColumns"/>
<xsl:includehref="in.xsl"/>
<xsl:includehref="sqlApostrophe.xsl"/>
<!--
Stylesheet: sample.xsl
Creation Date: October 25, 2006
Programmer: Edmond Woychowsky
Purpose: The purpose of this XSL style sheet is to generate multiple SQL insert statements.


Template: match="/"
Creation Date: October 25, 2006
Programmer: Edmond Woychowsky
Purpose: The purpose of this template is to create the outer sql element and invoke the template for the individual INSERT statements.

Update Date:Programmer: Description:
-->
<xsl:template match="/">
<xsl:element name="sql">
<xsl:apply-templates select="//row"/>
</xsl:element>
</xsl:template>
<!--
Template: match="row"
Programmer: Edmond Woychowsky
Purpose: The purpose of this template is to control the creation of the INSERT statements.

Update Date:Programmer: Description:
-->
<xsl:template match="row">
<xsl:element name="statement">
<xsl:value-of select="concat('INSERT INTO ',$table,' (')"/>
<xsl:apply-templates select="*" mode="name"/>
<xsl:text>) VALUES (</xsl:text>
<xsl:apply-templates select="*" mode="value"/>
<xsl:text>)</xsl:text>
</xsl:element>
</xsl:template>
<!--
Template: match="*" mode="name"
Programmer: Edmond Woychowsky
Purpose: The purpose of this template is to list the column names.

Update Date:Programmer: Description:
-->
<xsl:template match="*" mode="name">
<xsl:if test="position() != 1">
<xsl:text>, </xsl:text>
</xsl:if>
<xsl:value-of select="name(.)"/>
</xsl:template>
<!--
Template: match="*" mode="value"
Programmer: Edmond Woychowsky
Purpose: The purpose of this template is to list the column values.

Update Date:Programmer: Description:
-->
<xsl:template match="*" mode="value">
<xsl:variable name="text">
<xsl:call-template name="in">
<xsl:with-param name="list" select="$textColumns"/>
<xsl:with-param name="value" select="name(.)"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="date">
<xsl:call-template name="in">
<xsl:with-param name="list" select="$dateColumns"/>
<xsl:with-param name="value" select="name(.)"/>
</xsl:call-template>
</xsl:variable>
<xsl:if test="position() != 1">
<xsl:text>, </xsl:text>
</xsl:if>
<xsl:choose>
<xsl:when test="$text = 'true'">
<xsl:text>'</xsl:text>
<xsl:call-template name="sqlApostrophe">
<xsl:with-param name="string" select="."/>
</xsl:call-template>
<xsl:text>'</xsl:text>
</xsl:when>
<xsl:when test="$date = 'true'">
<xsl:value-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

Do you need help with XML? Gain advice from Builder AU forums

Comments

1

Abi - 31/01/07

By the looks of it you are using Oracle. Doesn't Oracle have code something similar to below SQL Server 2000?

CREATE TABLE xStates OF XMLTYPE;
INSERT INTO xStates VALUES(xmltype(XmlDocText)) with help of EXTRACTVALUE?

--Create an internal representation of the XML document.
EXEC sp_xml_preparedocument @TempXmlDocID OUTPUT, @XmlDocText
-- Execute a SELECT statement that uses the OPENXML rowset provider.
INSERT INTO MyTable
SELECT *
FROM OPENXML (@TempXmlDocID, '/root/row',2)
WITH (state_id varchar(10),
state_name varchar(20))

» Report offensive content

2

Sidu - 02/02/07

Or you can simply use Apache Jackrabbit or any other JSR 170 compatible content repository. Much simple. Much more powerful.

» Report offensive content

3

dc - 03/02/07

Or you can use OR mapping. XML -> Object -> DB

Nice idea to use xsl for the insert statements.

» Report offensive content

4

Dan - 03/02/07

You could use a database that has separate disk formats for relational tables and XML and can combine SQL and XQuery. And it's free:

http://www-128.ibm.com/developerworks/downloads/im/udbexp/?S_TACT=105AGX19

» Report offensive content

Leave a comment

You must read and type the 6 chars within 0..9 and A..F

* indicates mandatory fields.

4

Dan - 02/03/07

You could use a database that has separate disk formats for relational tables and XML and can combine SQL and XQuery. ... more

3

dc - 02/03/07

Or you can use OR mapping. XML -> Object -> DB Nice idea to use xsl for the insert statements. ... more

2

Sidu - 02/02/07

Or you can simply use Apache Jackrabbit or any other JSR 170 compatible content repository. Much simple. Much more powerful. ... more

Log in


Sign up | Forgot your password?

  • Staff Microsoft prescribes more REST

    Details have begun to emerge about the next versions of Visual Studio and Windows Server this week -- and the message from Redmond is to REST up Read more »

    -- posted by Staff

  • Chris Duckett .NET looks to REST

    With news that REST will play a big part in the next version of the .NET Framework, it is timely to take a look at ADO.NET. Read more »

    -- posted by Chris Duckett

  • Renai LeMay Spellr.us needs a new dictionary

    One of the only Australian start-ups to present at the recent round of conferences in the US was Sydney-based spellr.us, which has launched a Web-based tool to check and monitor websites for spelling mistakes. Read more »

    -- posted by Renai LeMay

What's on?