XML to SQL Converter
Get a feed into a table you can query. Records are found without an XPath, attributes become columns alongside elements, and the types come from reading whole columns.
To convert XML to SQL, paste your document above and choose a dialect. The repeating record element is detected automatically, attributes and child elements both become columns, nesting flattens with dot notation, and you get a CREATE TABLE with inferred types followed by batched INSERT statements.
Want to filter records before you load? Open the app
The question you cannot ask an XML file
A feed arrives daily and somebody asks how many records have a missing price, or which supplier appears most often, or whether the count matches yesterday. Those are one-line SQL questions and genuinely awkward against XML.
The formal answer is XQuery, which almost nobody has installed and fewer people can write from memory. The practical answer is to load the feed into a table for ten minutes and then throw the table away.
The other case is a real integration: the feed becomes a staging table on every run, and a script generated here is the prototype for what the pipeline will eventually do.
Finding the records without asking you for a path
The hard part of reading XML as a table is deciding what a row is. A document has one root, and somewhere inside it is a repeating element that represents a record. Most tools make you supply an XPath expression for it.
That is a reasonable design and a bad first experience: you have to open the file, understand its structure, and write an expression before you see anything. Here the repeating element is found by looking for the deepest element name that occurs many times as a sibling, which is the record element in essentially every real feed.
Element children become columns, attributes become columns too, and nesting inside a record flattens with dot notation. What the detection picked is stated under the result, so if it guessed wrong on an unusual document you can see that immediately rather than wondering why the row count looks odd.
Worked example, in Postgres
The product feed:
<?xml version="1.0" encoding="UTF-8"?>
<products>
<product id="SKU-101" category="Audio">
<product_name>Wireless Mouse</product_name>
<unit_price>23.07</unit_price>
<in_stock>161</in_stock>
</product>
<product id="SKU-108" category="Accessories">
<product_name>27in Monitor</product_name>
<unit_price>140.33</unit_price>
<in_stock>0</in_stock>
</product>
</products>
And the script:
CREATE TABLE "products" (
"id" TEXT,
"category" TEXT,
"product_name" TEXT,
"unit_price" DOUBLE PRECISION,
"in_stock" NUMERIC
);
INSERT INTO "products" ("id", "category", "product_name", "unit_price", "in_stock") VALUES
('SKU-101', 'Audio', 'Wireless Mouse', 23.07, 161),
('SKU-108', 'Accessories', '27in Monitor', 140.33, 0);
unit_price is a floating type because it has fractions, in_stock is exact because it does not, and the two attributes are ordinary columns. Everything an XML document knows is text, so the types here were inferred from the values rather than read from a schema, which is why reading the CREATE TABLE before running it is worthwhile.
Attributes are data too
XML has two places to put a value and different specifications choose differently. <product id="SKU-101"> and <product><id>SKU-101</id></product> carry the same fact, and plenty of documents use both at once.
So attributes become columns alongside element children rather than being ignored. A converter that reads only child elements loses the identifier on the majority of real feeds, because the identifier is very often an attribute.
Where an attribute and a child element share a name, both survive as separate columns rather than one overwriting the other. It is rare, and when it happens losing one silently would be much worse than an extra column.
Types from values, and the empty element question
XML carries no types. Every value in the document is text, so every type in the CREATE TABLE is an inference from the data in front of it. A column becomes numeric only when every value in it round trips exactly, which keeps a product code with a padded zero as text.
An empty element, <note/> or <note></note>, becomes NULL rather than an empty string. In a feed those almost always mean absence, and a nullable column is the right home for that. An element that is genuinely present with an empty string in it is indistinguishable in XML, which is a limitation of the format rather than of the conversion.
A missing element in one record where others have it also becomes NULL, because the columns are the union of every field across every record. Nothing is dropped for being absent from the first one.
Dialects, and one detail that matters here
- Identifier quoting differs across all four, and it matters more with XML than most sources: element names from a feed frequently collide with reserved words,
order,groupandvalueamong them. - Dotted column names from flattened nesting must be quoted or the database reads them as table qualifiers.
- MySQL gets DECIMAL(20,0) for exact numbers rather than a bare NUMERIC, which there silently refuses an eleven-digit value.
- SQL Server clamps the batch to its thousand-row ceiling automatically, and says that it did.
- No VARCHAR sized from a sample. A feed's field widths vary daily and a fitted width will truncate within the week.
Frequently Asked Questions
Do I need an XPath or a schema?
Neither. The record element is found from the document's structure and named under the result. If you have an XSD it will tell you more than this can, but you do not need it to get a table.
Are attributes loaded as columns?
Yes, alongside child elements. Many specifications put the identifier in an attribute, so a converter that reads only child elements gives you a table where the rows cannot be told apart.
How are types decided when XML has none?
From the values. A column becomes numeric only when every value in it round trips exactly, which keeps a padded product code as text. Because every type is an inference, the CREATE TABLE is the part worth reading before you run the script.
What does an empty element become?
NULL. In a feed an empty element almost always means absence, and that is what a nullable column is for. XML cannot distinguish an empty element from one holding an empty string, which is a limitation of the format.
One of my elements is called order. Will that break?
No, because identifiers are quoted with the character your chosen dialect uses. Element names from feeds collide with reserved words constantly, which is why the dialect choice is load-bearing here rather than cosmetic.
What if some records are missing a field?
The columns are the union of every field across every record, so nothing is dropped for being absent from the first one. Records lacking a field get NULL in that column.
Make the feed queryable
Records found for you, attributes kept, real column types, four dialects.
Back to the converter