There are two things to know about the XML
datatype that together explain what you are experiencing:
- As noted in @EvanCarroll's answer, the
XML
datatype is optimized. Meaning, rather than repeat element and attribute names (which are typically repeated quite a bit and are a large part of why so many people, sometimes rightfully-so, complain about XML documents being so bulky), a dictionary / lookup list is created to store each unique name once, given a numeric ID, and that ID is used to populate the structure of the document. This is why the XML
datatype is quite often a better way to store XML documents.
- Additionally, the
XML
datatype uses UTF-16 (Little Endian) to store string values (both element and attribute names as well as any actual string content). This datatype does not use compression, so strings are essentially 2 or 4 bytes per character, with most characters being the 2-byte variety.
Looking at the particular test XML document you are using, and the VARCHAR
datatype (1 to 2 bytes per character, most often the 1-byte variety), we can now explain what you are seeing as being a result of:
- Each of your elements (
root
, element1
, etc) are used only once, so the only savings of placing the names into the lookup list is to cut the size in exactly half. But, the XML type uses UTF-16 so the size of each string is twice as much, cancelling out the savings of moving the element names into the lookup list. At this point, if only looking at the document structure (i.e. element names) then there is should effectively be no difference between the XML
type and the VARCHAR
version.
- But, the string content in each element (i.e.
test
) takes up twice the number of bytes: 8 bytes in XML
as opposed to 4 bytes in VARCHAR
. Given that there are 5 instances of "test" per each row, that is 20 extra bytes per row for the XML
type. At 10k rows, that is 200,000 extra bytes of the 600,000 byte difference. The rest is internal overhead of the XML
type and the additional page overhead of the additional number of datapages needed to store the same number of rows due to each row being slightly larger.
To better illustrate this behavior, consider the following two variations of XML data: the first being the exact same XML as in the question, and the second being almost the same, but with all elements being the same name. In the second version, all element names are "element1" so that they are the same length as each element in the original version. This is results in the VARCHAR
data length being the same in both cases. But the element names being the same in the second version allow the internal optimizations to be more noticeable.
-- Original XML (unique element names -- "element1", "element2", ... "elementN"):
DECLARE @xml XML = '<root><element1>test</element1><element2>test</element2>
<element3>test</element3><element4>test</element4><element5>test</element5></root>';
SELECT DATALENGTH(@xml) AS [XmlBytes],
DATALENGTH(CONVERT(VARCHAR(MAX), @xml)) AS [VarcharBytes];
-- More "typical" XML (repeated element names -- all "element1"):
DECLARE @xml2 XML = '<root><element1>test</element1><element1>test</element1>
<element1>test</element1><element1>test</element1><element1>test</element1></root>';
SELECT DATALENGTH(@xml2) AS [XmlBytes],
DATALENGTH(CONVERT(VARCHAR(MAX), @xml2)) AS [VarcharBytes];
Results:
ElementNames XmlBytes VarcharBytes
------------ -------- ------------
Unique 197 138
Non-Unique 109 138