I've got 34 rows in a database, each row has a column containing xml - the xml is actually in an NVARCHAR(MAX) column not an XML column.
For each row I am selecting values in the xml elements as a single resultset. The performance is pretty poor. I've tried two different queries. The first takes roughly 22 seconds to execute and the second takes 7.
Even at 7 seconds, this is far slower than optimal, I'm hoping for 1-2 seconds at most.
So then I read a rumor online that if you convert the NVARCHAR data to a XML using a temp table or table variable, you will achieve a performance gain, which at least in my case was true... It now executes in under a second. What I'm looking for now is an explanation that can tell my why these 2 approaches actually affect performance.
22 seconds:
SELECT
c.ID,
c.ChannelName,
[Name] = d.c.value('name[1]','varchar(100)'),
[Type] = d.c.value('transportName[1]','varchar(100)'),
[Enabled] = d.c.value('enabled[1]','BIT'),
[Queued] = d.c.value('properties[1]/destinationConnectorProperties[1]/queueEnabled[1]','varchar(100)'),
[RetryInterval] = d.c.value('properties[1]/destinationConnectorProperties[1]/retryIntervalMillis[1]','INT'),
[MaxRetries] = d.c.value('properties[1]/destinationConnectorProperties[1]/retryCount[1]','INT'),
[RotateQueue] = d.c.value('properties[1]/destinationConnectorProperties[1]/rotate[1]','BIT'),
[ThreadCount] = d.c.value('properties[1]/destinationConnectorProperties[1]/threadCount[1]','INT'),
[WaitForPrevious] = d.c.value('waitForPrevious[1]','BIT'),
[Destination] = COALESCE(
d.c.value('properties[1]/channelId[1]','varchar(100)'),
d.c.value('properties[1]/remoteAddress[1]','varchar(100)'),
d.c.value('properties[1]/wsdlUrl[1]','varchar(1024)')),
[DestinationPort] = COALESCE(
d.c.value('properties[1]/remotePort[1]','varchar(100)'),
d.c.value('properties[1]/port[1]','varchar(1024)')),
[Service] = d.c.value('properties[1]/service[1]','varchar(1024)'),
[Operation] = d.c.value('properties[1]/operation[1]','varchar(1024)')
FROM
(
SELECT
[ID],
[ChannelName] = [Name],
[CFG] = Convert(XML, Channel)
FROM
dbo.CHANNEL
) c
CROSS APPLY c.CFG.nodes('/channel/destinationConnectors/connector') d(c)
7 seconds, due to use of text(). I have no idea why text speeds things up.
SELECT
c.ID,
c.ChannelName,
[Name] = d.c.value('(name/text())[1]','varchar(100)'),
[Type] = d.c.value('(transportName/text())[1]','varchar(100)'),
[Enabled] = d.c.value('(enabled/text())[1]','BIT'),
[Queued] = d.c.value('(properties/destinationConnectorProperties/queueEnabled/text())[1]','varchar(100)'),
[RetryInterval] = d.c.value('(properties/destinationConnectorProperties/retryIntervalMillis/text())[1]','INT'),
[MaxRetries] = d.c.value('(properties/destinationConnectorProperties/retryCount/text())[1]','INT'),
[RotateQueue] = d.c.value('(properties/destinationConnectorProperties/rotate/text())[1]','BIT'),
[ThreadCount] = d.c.value('(properties/destinationConnectorProperties/threadCount/text())[1]','INT'),
[WaitForPrevious] = d.c.value('(waitForPrevious/text())[1]','BIT'),
[Destination] = COALESCE(
d.c.value('(properties/channelId/text())[1]','varchar(100)'),
d.c.value('(properties/remoteAddress/text())[1]','varchar(100)'),
d.c.value('(properties/wsdlUrl/text())[1]','varchar(1024)')),
[DestinationPort] = COALESCE(
d.c.value('(properties/remotePort/text())[1]','varchar(100)'),
d.c.value('(properties/port/text())[1]','varchar(1024)')),
[Service] = d.c.value('(properties/service/text())[1]','varchar(1024)'),
[Operation] = d.c.value('(properties/operation/text())[1]','varchar(1024)')
FROM
(
SELECT
[ID],
[ChannelName] = [Name],
[CFG] = Convert(XML, Channel)
FROM
dbo.CHANNEL
) c
CROSS APPLY c.CFG.nodes('/channel/destinationConnectors/connector') d(c)
This query uses the text() approach but puts converts the NVARCHAR column to xml column in a table variable first. Executes in less than a second...
DECLARE @Xml AS TABLE (
[ID] NVARCHAR(36) NOT NULL Primary Key,
[Name] NVARCHAR(100) NOT NULL,
[CFG] XML NOT NULL
);
INSERT INTO @Xml (ID, Name, CFG)
SELECT
c.ID,
c.Name,
Convert(XML, c.Channel)
FROM
[dbo].[CHANNEL] c;
SELECT
c.ID,
c.ChannelName,
[Name] = d.c.value('(name/text())[1]','varchar(100)'),
[Type] = d.c.value('(transportName/text())[1]','varchar(100)'),
[Enabled] = d.c.value('(enabled/text())[1]','BIT'),
[Queued] = d.c.value('(properties/destinationConnectorProperties/queueEnabled/text())[1]','varchar(100)'),
[RetryInterval] = d.c.value('(properties/destinationConnectorProperties/retryIntervalMillis/text())[1]','INT'),
[MaxRetries] = d.c.value('(properties/destinationConnectorProperties/retryCount/text())[1]','INT'),
[RotateQueue] = d.c.value('(properties/destinationConnectorProperties/rotate/text())[1]','BIT'),
[ThreadCount] = d.c.value('(properties/destinationConnectorProperties/threadCount/text())[1]','INT'),
[WaitForPrevious] = d.c.value('(waitForPrevious/text())[1]','BIT'),
[Destination] = COALESCE(
d.c.value('(properties/channelId/text())[1]','varchar(100)'),
d.c.value('(properties/remoteAddress/text())[1]','varchar(100)'),
d.c.value('(properties/wsdlUrl/text())[1]','varchar(1024)')),
[DestinationPort] = COALESCE(
d.c.value('(properties/remotePort/text())[1]','varchar(100)'),
d.c.value('(properties/port/text())[1]','varchar(1024)')),
[Service] = d.c.value('(properties/service/text())[1]','varchar(1024)'),
[Operation] = d.c.value('(properties/operation/text())[1]','varchar(1024)')
FROM
(
SELECT
[ID],
[ChannelName] = [Name],
[CFG]
FROM
@Xml
) c
CROSS APPLY c.CFG.nodes('/channel/destinationConnectors/connector') d(c)