Showing posts with label queries. Show all posts
Showing posts with label queries. Show all posts

Friday, March 23, 2012

forceseek performance and cost...

Hi,

I have just run the 2 queries from the BOL to test the forceseek option:

USE AdventureWorks;

GO

SELECT*

FROM Sales.SalesOrderHeader AS h

INNERJOIN Sales.SalesOrderDetail AS d

ON h.SalesOrderID = d.SalesOrderID

WHERE h.TotalDue > 100

AND(d.OrderQty > 5 OR d.LineTotal < 1000.00);

GO

SELECT*

FROM Sales.SalesOrderHeader AS h

INNERJOIN Sales.SalesOrderDetail AS d WITH(FORCESEEK)

ON h.SalesOrderID = d.SalesOrderID

WHERE h.TotalDue > 100

AND(d.OrderQty > 5 OR d.LineTotal < 1000.00);

GO

When I take a look at the execution plan, the first query has a cost of 20% and the forceseek take 80% of the total cost.

Also the number of logical read jump from 1238 to 66194!!!

So the forceseek option has a bad impact on the query.

The BOL says if there is a lot of IO, using the forceseek can provide better performance... but in this case its not so clear...

can you explain more in detail when its better to use the forceseek?

Thanks.

The cost increases,but if you look, the index change of clustered index scan to clustered index seek and if you compare the estimated I/O cust and estimated CPU Cost you can see the differences.The number reduce....

|||

Actually this is expected behavior. We have approx 30k rows of Order Header with approx 120k with order lines. What you tell SQL Server in the latter query, is to ignore the fact that we are retrieving all the rows, and still use a lookup instead of a scan. So what SQL Server does (on your demand) is to take each and every order one by one and lookup the corresponding order lines. If you looked carefully at the execution plan, you'ld see that the merge join was replaced with a inner loop join. The latter is more efficient if you lookup only a few values, but when you lookup all the values a merge join is way much faster.

As I understand the FORCESEEK its a way of forcing seeking in the index the few times that SQL Server believes it has to read more or less the whole table, while it actually is going to retrieve just a few rows.

Anybody is free to correct me if I'm wrong on this one.


Edit: That said, when I run a trace it actually seems that the last query is the faster, I would guess that is since all the data already are in memory.

|||

Hints are there to be used when the optimizer produces a less than optimal plan and, despite over two decades of effort, query optimizers still can't get the 100% best plan 100% of the time.

So what is FORCESEEK intended to deal with? The optimizer is always working with imperfect data about the values in the table. And when you start doing a join using multiple predicates that include operators such as "<" and ">" its ability to accurately estimate the number of rows that will be touched declines. Particularly true if you don't have up to date statistics on some of the columns, or if you encounter one of the rare instances where the histograms don't capture enough information to do accurate estimates. Historically (dating back to the System R research project) if you don't have statistics that indicate otherwise then an operator such as ">" is considered to be true for 1/3 of the data. Thus ORing two such conditions would lead to an assumption that 2/3 of the table was going to be retrieved which clearly would lead to a scan over a seek. This is a grand oversimplification of how things work in this day and age, but the basics help you understand why the optimizer might choose the wrong plan. It might think that 2/3 of the rows must be touched while you know that based on the actual data it is more like .01%. The bottom line being, there are cases where the optimizer will think that a scan offers the best performance but where human knowledge of the data makes it clear that a seek is the better alternative.

Using the optimizer's own statistics to see if FORCESEEK is better or not is a mistake. The reason the optimizer didn't choose the seek strategy on its own is that it estimated it would take a lot more I/O and rejected that strategy. So when you use FORCESEEK and look at the estimates what you see are the (incorrect) estimates that the optimizer saw when it considered this strategy on its own. You just can't use that data, you need to look at the actual performance of the query.

Hal

sql

forceseek performance and cost...

Hi,

I have just run the 2 queries from the BOL to test the forceseek option:

USE AdventureWorks;

GO

SELECT*

FROM Sales.SalesOrderHeader AS h

INNERJOIN Sales.SalesOrderDetail AS d

ON h.SalesOrderID = d.SalesOrderID

WHERE h.TotalDue > 100

AND(d.OrderQty > 5 OR d.LineTotal < 1000.00);

GO

SELECT*

FROM Sales.SalesOrderHeader AS h

INNERJOIN Sales.SalesOrderDetail AS d WITH(FORCESEEK)

ON h.SalesOrderID = d.SalesOrderID

WHERE h.TotalDue > 100

AND(d.OrderQty > 5 OR d.LineTotal < 1000.00);

GO

When I take a look at the execution plan, the first query has a cost of 20% and the forceseek take 80% of the total cost.

Also the number of logical read jump from 1238 to 66194!!!

So the forceseek option has a bad impact on the query.

The BOL says if there is a lot of IO, using the forceseek can provide better performance... but in this case its not so clear...

can you explain more in detail when its better to use the forceseek?

Thanks.

The cost increases,but if you look, the index change of clustered index scan to clustered index seek and if you compare the estimated I/O cust and estimated CPU Cost you can see the differences.The number reduce....

|||

Actually this is expected behavior. We have approx 30k rows of Order Header with approx 120k with order lines. What you tell SQL Server in the latter query, is to ignore the fact that we are retrieving all the rows, and still use a lookup instead of a scan. So what SQL Server does (on your demand) is to take each and every order one by one and lookup the corresponding order lines. If you looked carefully at the execution plan, you'ld see that the merge join was replaced with a inner loop join. The latter is more efficient if you lookup only a few values, but when you lookup all the values a merge join is way much faster.

As I understand the FORCESEEK its a way of forcing seeking in the index the few times that SQL Server believes it has to read more or less the whole table, while it actually is going to retrieve just a few rows.

Anybody is free to correct me if I'm wrong on this one.


Edit: That said, when I run a trace it actually seems that the last query is the faster, I would guess that is since all the data already are in memory.

|||

Hints are there to be used when the optimizer produces a less than optimal plan and, despite over two decades of effort, query optimizers still can't get the 100% best plan 100% of the time.

So what is FORCESEEK intended to deal with? The optimizer is always working with imperfect data about the values in the table. And when you start doing a join using multiple predicates that include operators such as "<" and ">" its ability to accurately estimate the number of rows that will be touched declines. Particularly true if you don't have up to date statistics on some of the columns, or if you encounter one of the rare instances where the histograms don't capture enough information to do accurate estimates. Historically (dating back to the System R research project) if you don't have statistics that indicate otherwise then an operator such as ">" is considered to be true for 1/3 of the data. Thus ORing two such conditions would lead to an assumption that 2/3 of the table was going to be retrieved which clearly would lead to a scan over a seek. This is a grand oversimplification of how things work in this day and age, but the basics help you understand why the optimizer might choose the wrong plan. It might think that 2/3 of the rows must be touched while you know that based on the actual data it is more like .01%. The bottom line being, there are cases where the optimizer will think that a scan offers the best performance but where human knowledge of the data makes it clear that a seek is the better alternative.

Using the optimizer's own statistics to see if FORCESEEK is better or not is a mistake. The reason the optimizer didn't choose the seek strategy on its own is that it estimated it would take a lot more I/O and rejected that strategy. So when you use FORCESEEK and look at the estimates what you see are the (incorrect) estimates that the optimizer saw when it considered this strategy on its own. You just can't use that data, you need to look at the actual performance of the query.

Hal

Wednesday, March 7, 2012

For xml auto question:

Hi,

I ran the two queries below on SQL server 2005 enterprise 9.00.1399.06 and 9.00.2047.00. The first ("Top" paginated) query ran fine (from my C# code based dataset) on the older version, but returns results like those below (See Result1) from the newer version. I need to get my data back with the xml parent child nesting intact and table handles as they are designated in the main query text. Perhaps there is another way to do a paginated query that will deliver xml nested as shown in Result2. If so I would like to know how to code it.

If you have any suggestions, I would appreciate any help you can give. Thanks, Dave

Query 1 (Paginated using Top):

select niin, item_name, cage, partno, vendorname, ui, price from (select top 10 niin, item_name, cage, partno, vendorname, ui, price from (select top 30 flis_a.niin,flis_a.item_name, flis_cage.cage,flis_cage.partno,flis_cage.vendorname, pricing.ui,pricing.price from dbFlisCurPlusHist..niindata AS flis_a left outer join dbFlisCurPlusHist..mcrldata AS flis_cage on flis_a.niin = flis_cage.niin left outer join dbFlisCurPlusHist..mlcdata AS pricing on flis_a.niin = pricing.niin where flis_a.niin like '0000%' and flis_a.item_name like '%BOLT%' and flis_cage.cage like '%205%' and pricing.sa ='DN' order by flis_a.niin, flis_cage.niin, pricing.niin) as newtbl order by niin desc) as newtbl2 order by niin asc for xml auto

Query 2:

select flis_a.niin,flis_a.item_name, flis_cage.cage,flis_cage.partno,flis_cage.vendorname, pricing.ui,pricing.price from dbFlisCurPlusHist..niindata AS flis_a left outer join dbFlisCurPlusHist..mcrldata AS flis_cage on flis_a.niin = flis_cage.niin left outer join dbFlisCurPlusHist..mlcdata AS pricing on flis_a.niin = pricing.niin where flis_a.niin like '0000%' and flis_a.item_name like '%BOLT%' and flis_cage.cage like '%205%' and pricing.sa ='DN' order by flis_a.niin, flis_cage.niin, pricing.niin asc for xml auto

Result 1: (Results not nested as they need to be. This ran fine on older version from c# dataset but now fails on the newer sql server)

<newtbl2 niin="000041534" item_name="BOLT,MACHINE" cage="80205" partno="AN7-24A" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000000.68" />
<newtbl2 niin="000041535" item_name="BOLT,SHEAR" cage="80205" partno="NAS1303-12" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000000.23" />
<newtbl2 niin="000045155" item_name="BOLT,MACHINE" cage="80205" partno="AN17-40A" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000009.81" />
<newtbl2 niin="000050435" item_name="BOLT,SHEAR" cage="80205" partno="NAS627H14" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000009.03" />
<newtbl2 niin="000050546" item_name="BOLT,CLOSE TOLERANCE" cage="80205" partno="MS27576-5-35" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000012.94" />
<newtbl2 niin="000050549" item_name="BOLT,CLOSE TOLERANCE" cage="80205" partno="MS27576-6-19" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000015.52" />
<newtbl2 niin="000050550" item_name="BOLT,CLOSE TOLERANCE" cage="80205" partno="MS27576-6-23" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000020.73" />
<newtbl2 niin="000056093" item_name="BOLT,CLOSE TOLERANCE" cage="80205" partno="MS27576-4-27" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000011.12" />
<newtbl2 niin="000061454" item_name="BOLT,INTERNAL WRENCHING" cage="80205" partno="NAS1351-4H12" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000000.25" />
<newtbl2 niin="000062269" item_name="BOLT,SQUARE NECK" cage="80205" partno="MS35751-53" vendorname="NATIONAL AEROSPACE STANDARDS" ui="BX" price="000000005.61" />

-

The above response should look like this with child elements nested, etc and the flis_a table handle intact. The response below ran fine on SQL Server 2005 Enterprise version 9.00.1399.06.

<flis_a niin="000041534" item_name="BOLT,MACHINE"><flis_cage cage="80205" partno="AN7-24A" vendorname="NATIONAL AEROSPACE STANDARDS"><pricing ui="EA" price="000000000.68"/></flis_cage></flis_a><flis_a niin="000041535" item_name="BOLT,SHEAR"><flis_cage cage="80205" partno="NAS1303-12" vendorname="NATIONAL AEROSPACE STANDARDS"><pricing ui="EA" price="000000000.23"/></flis_cage></flis_a><flis_a niin="000045155" item_name="BOLT,MACHINE"><flis_cage cage="80205" partno="AN17-40A" vendorname="NATIONAL AEROSPACE STANDARDS"><pricing ui="EA" price="000000009.81"/></flis_cage></flis_a><flis_a niin="000050435" item_name="BOLT,SHEAR"><flis_cage cage="80205" partno="NAS627H14" vendorname="NATIONAL AEROSPACE STANDARDS"><pricing ui="EA" price="000000009.03"/></flis_cage></flis_a><flis_a niin="000050546" item_name="BOLT,CLOSE TOLERANCE"><flis_cag ...

--

Result 2: (Non paginated and works fine on both versions)

<flis_a niin="000011989" item_name="BOLT,SHEAR">
<flis_cage cage="80205" partno="NAS1308-29" vendorname="NATIONAL AEROSPACE STANDARDS">
<pricing ui="EA" price="000000008.65" />
</flis_cage>
</flis_a>
<flis_a niin="000011993" item_name="BOLT,SHEAR">
<flis_cage cage="80205" partno="NAS464P8-79" vendorname="NATIONAL AEROSPACE STANDARDS">
<pricing ui="EA" price="000000030.46" />
</flis_cage>
</flis_a>
<flis_a niin="000014780" item_name="BOLT,INTERNAL WRENCHING">
<flis_cage cage="80205" partno="NAS1352-06LE8" vendorname="NATIONAL AEROSPACE STANDARDS">
<pricing ui="EA" price="000000003.72" />
</flis_cage>
</flis_a>
<flis_a niin="000014807" item_name="BOLT,CLOSE TOLERANCE">
<flis_cage cage="80205" partno="MS27576-5-22" vendorname="NATIONAL AEROSPACE STANDARDS">
<pricing ui="EA" price="000000013.93" />
</flis_cage>
</flis_a>
<flis_a niin="000014847" item_name="BOLT,SHEAR">
<flis_cage cage="80205" partno="MS21250-03020" vendorname="NATIONAL AEROSPACE STANDARDS">
<pricing ui="EA" price="000000001.02" />
</flis_cage>
</flis_a>
<flis_a niin="000014899" item_name="BOLT,MACHINE">
<flis_cage cage="80205" partno="NAS428-3-15" vendorname="NATIONAL AEROSPACE STANDARDS">
<pricing ui="EA" price="000000000.86" />
</flis_cage>
</flis_a>
<flis_a niin="000016674" item_name="BOLT,SHEAR">
<flis_cage cage="80205" partno="NAS464P5LA33" vendorname="NATIONAL AEROSPACE STANDARDS">
<pricing ui="EA" price="000000002.86" />
</flis_cage>
</flis_a>
.
.
.

Subqueries in the from clause are now treated like views (as they should be) and become opaque for auto mode queries.

You may want to run the queries under compat level 80 (sp_dbcmptlevel 'dbname', 80) if you want the SQL Server 2000 behaviour or rewrite your queries using FOR XML PATH.

Also, it would help if you could provide a schema definition and some sample data to repro the behaviour.

Best regards

Michael

For xml auto question:

Hi,

I ran the two queries below on SQL server 2005 enterprise 9.00.1399.06 and 9.00.2047.00. The first ("Top" paginated) query ran fine (from my C# code based dataset) on the older version, but returns results like those below (See Result1) from the newer version. I need to get my data back with the xml parent child nesting intact and table handles as they are designated in the main query text. Perhaps there is another way to do a paginated query that will deliver xml nested as shown in Result2. If so I would like to know how to code it.

If you have any suggestions, I would appreciate any help you can give. Thanks, Dave

Query 1 (Paginated using Top):

select niin, item_name, cage, partno, vendorname, ui, price from (select top 10 niin, item_name, cage, partno, vendorname, ui, price from (select top 30 flis_a.niin,flis_a.item_name, flis_cage.cage,flis_cage.partno,flis_cage.vendorname, pricing.ui,pricing.price from dbFlisCurPlusHist..niindata AS flis_a left outer join dbFlisCurPlusHist..mcrldata AS flis_cage on flis_a.niin = flis_cage.niin left outer join dbFlisCurPlusHist..mlcdata AS pricing on flis_a.niin = pricing.niin where flis_a.niin like '0000%' and flis_a.item_name like '%BOLT%' and flis_cage.cage like '%205%' and pricing.sa ='DN' order by flis_a.niin, flis_cage.niin, pricing.niin) as newtbl order by niin desc) as newtbl2 order by niin asc for xml auto

Query 2:

select flis_a.niin,flis_a.item_name, flis_cage.cage,flis_cage.partno,flis_cage.vendorname, pricing.ui,pricing.price from dbFlisCurPlusHist..niindata AS flis_a left outer join dbFlisCurPlusHist..mcrldata AS flis_cage on flis_a.niin = flis_cage.niin left outer join dbFlisCurPlusHist..mlcdata AS pricing on flis_a.niin = pricing.niin where flis_a.niin like '0000%' and flis_a.item_name like '%BOLT%' and flis_cage.cage like '%205%' and pricing.sa ='DN' order by flis_a.niin, flis_cage.niin, pricing.niin asc for xml auto

Result 1: (Results not nested as they need to be. This ran fine on older version from c# dataset but now fails on the newer sql server)

<newtbl2 niin="000041534" item_name="BOLT,MACHINE" cage="80205" partno="AN7-24A" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000000.68" />
<newtbl2 niin="000041535" item_name="BOLT,SHEAR" cage="80205" partno="NAS1303-12" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000000.23" />
<newtbl2 niin="000045155" item_name="BOLT,MACHINE" cage="80205" partno="AN17-40A" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000009.81" />
<newtbl2 niin="000050435" item_name="BOLT,SHEAR" cage="80205" partno="NAS627H14" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000009.03" />
<newtbl2 niin="000050546" item_name="BOLT,CLOSE TOLERANCE" cage="80205" partno="MS27576-5-35" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000012.94" />
<newtbl2 niin="000050549" item_name="BOLT,CLOSE TOLERANCE" cage="80205" partno="MS27576-6-19" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000015.52" />
<newtbl2 niin="000050550" item_name="BOLT,CLOSE TOLERANCE" cage="80205" partno="MS27576-6-23" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000020.73" />
<newtbl2 niin="000056093" item_name="BOLT,CLOSE TOLERANCE" cage="80205" partno="MS27576-4-27" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000011.12" />
<newtbl2 niin="000061454" item_name="BOLT,INTERNAL WRENCHING" cage="80205" partno="NAS1351-4H12" vendorname="NATIONAL AEROSPACE STANDARDS" ui="EA" price="000000000.25" />
<newtbl2 niin="000062269" item_name="BOLT,SQUARE NECK" cage="80205" partno="MS35751-53" vendorname="NATIONAL AEROSPACE STANDARDS" ui="BX" price="000000005.61" />

-

The above response should look like this with child elements nested, etc and the flis_a table handle intact. The response below ran fine on SQL Server 2005 Enterprise version 9.00.1399.06.

<flis_a niin="000041534" item_name="BOLT,MACHINE"><flis_cage cage="80205" partno="AN7-24A" vendorname="NATIONAL AEROSPACE STANDARDS"><pricing ui="EA" price="000000000.68"/></flis_cage></flis_a><flis_a niin="000041535" item_name="BOLT,SHEAR"><flis_cage cage="80205" partno="NAS1303-12" vendorname="NATIONAL AEROSPACE STANDARDS"><pricing ui="EA" price="000000000.23"/></flis_cage></flis_a><flis_a niin="000045155" item_name="BOLT,MACHINE"><flis_cage cage="80205" partno="AN17-40A" vendorname="NATIONAL AEROSPACE STANDARDS"><pricing ui="EA" price="000000009.81"/></flis_cage></flis_a><flis_a niin="000050435" item_name="BOLT,SHEAR"><flis_cage cage="80205" partno="NAS627H14" vendorname="NATIONAL AEROSPACE STANDARDS"><pricing ui="EA" price="000000009.03"/></flis_cage></flis_a><flis_a niin="000050546" item_name="BOLT,CLOSE TOLERANCE"><flis_cag ...

--

Result 2: (Non paginated and works fine on both versions)

<flis_a niin="000011989" item_name="BOLT,SHEAR">
<flis_cage cage="80205" partno="NAS1308-29" vendorname="NATIONAL AEROSPACE STANDARDS">
<pricing ui="EA" price="000000008.65" />
</flis_cage>
</flis_a>
<flis_a niin="000011993" item_name="BOLT,SHEAR">
<flis_cage cage="80205" partno="NAS464P8-79" vendorname="NATIONAL AEROSPACE STANDARDS">
<pricing ui="EA" price="000000030.46" />
</flis_cage>
</flis_a>
<flis_a niin="000014780" item_name="BOLT,INTERNAL WRENCHING">
<flis_cage cage="80205" partno="NAS1352-06LE8" vendorname="NATIONAL AEROSPACE STANDARDS">
<pricing ui="EA" price="000000003.72" />
</flis_cage>
</flis_a>
<flis_a niin="000014807" item_name="BOLT,CLOSE TOLERANCE">
<flis_cage cage="80205" partno="MS27576-5-22" vendorname="NATIONAL AEROSPACE STANDARDS">
<pricing ui="EA" price="000000013.93" />
</flis_cage>
</flis_a>
<flis_a niin="000014847" item_name="BOLT,SHEAR">
<flis_cage cage="80205" partno="MS21250-03020" vendorname="NATIONAL AEROSPACE STANDARDS">
<pricing ui="EA" price="000000001.02" />
</flis_cage>
</flis_a>
<flis_a niin="000014899" item_name="BOLT,MACHINE">
<flis_cage cage="80205" partno="NAS428-3-15" vendorname="NATIONAL AEROSPACE STANDARDS">
<pricing ui="EA" price="000000000.86" />
</flis_cage>
</flis_a>
<flis_a niin="000016674" item_name="BOLT,SHEAR">
<flis_cage cage="80205" partno="NAS464P5LA33" vendorname="NATIONAL AEROSPACE STANDARDS">
<pricing ui="EA" price="000000002.86" />
</flis_cage>
</flis_a>
.
.
.

Subqueries in the from clause are now treated like views (as they should be) and become opaque for auto mode queries.

You may want to run the queries under compat level 80 (sp_dbcmptlevel 'dbname', 80) if you want the SQL Server 2000 behaviour or rewrite your queries using FOR XML PATH.

Also, it would help if you could provide a schema definition and some sample data to repro the behaviour.

Best regards

Michael

Sunday, February 26, 2012

FOR XML and performance

Does anyone have any suggestions or pointers for performance optimizations
for SELECT queries that take relational data and
return it as XML columns?
For example, I have a Person table. In our system, a person may have
multiple names, addresses, phone numbers, and email
addresses. So, I have a PersonNames table, PersonAddresses table,
PersonPhones table and a PersonEmails table. I'm
returning all of the person information at once, instead of having to make
multiple database calls.
One way to do this is to return multiple resultsets: the first is the person
base information, and each of the associated
collections of informataion would be in the next result sets.
There are some problems with this, though: if I ever want to bring back
multiple people with all of their supporting
information, I have to return them with multiple result sets in the same
order:
Person1
Person1Names
Person1Addresses
.
.
.
Person2
Person2Names
Person2Addresses
.
.
.
etc.
I've dealt with this by defining a view that selects the various information
as XML columns, like this:
SELECT personId,
dateOfBirth,
gender,
(SELECT DISTINCT nameId,
salutation,
firstName,
middleName,
surName,
suffix,
nameTypeId
FROM Names AS [Name]
WHERE personId = People.personId
FOR XML AUTO, TYPE, Root('Names')) AS Names,
(SELECT Address.addressId,
PeopleAddresses.addressTypeId AS addressTypeId,
cityId,
countyId,
stateId,
streetAddr1,
streetAddr2,
cityName,
stateAbbrev,
zipCode,
stateName,
countyName,
areaCode,
timeZone,
useDST
FROM PeopleAddresses INNER JOIN
VAddresses AS [Address] ON PeopleAddresses.addressId =
Address.addressId
WHERE PeopleAddresses.personId = People.personId AND
PeopleAddresses.addressId = Address.AddressId
FOR XML RAW('Address'), TYPE, Root('Addresses')) AS Addresses,
(SELECT emailAddressId,
emailAddress,
emailAddressTypeId
FROM EmailAddresses AS [EmailAddress]
WHERE EmailAddress.personId = People.personId FOR XML AUTO, TYPE,
Root('EmailAddresses')) AS EmailAddresses,
(SELECT PhoneNumbers.phoneTypeId,
PhoneNumber.phoneNumberId,
PhoneNumber.areaCode,
PhoneNumber.phoneNumber,
PhoneNumber.extension,
PhoneNumber.prefix
FROM PeoplePhones PhoneNumbers INNER JOIN
PhoneNumbers PhoneNumber ON PhoneNumbers.phoneNumberId =
PhoneNumber.phoneNumberId
WHERE PhoneNumbers.personId = People.personId
FOR XML RAW('PhoneNumber'), TYPE, Root('PhoneNumbers')) AS PhoneNumbers
FROM dbo.People
This works really well - I can reach into the XML columns of the result set,
and pull back the data. I can also have a
SINGLE piece of code that knows how to read addresses, phone numbers, etc.
for other entities that have these. I just return
them in the queries for these entities as XML columns.
However, the performance is not optimal. I know that XML introduces a
performance penalty, and I'm willing to pay one for
this flexibility - but I was wondering if anyone has any performance
optimization tips for this scenario?
I could obviously change how I'm doing things: store the names, addresses,
etc. as XML, so that I only construct it once,
have special cases for returning this information as multiple result sets,
etc. I don't expect this to be the
best-performing method, but I'd like to eliminate as many performance
bottlenecks as possible.
Any comments are appreciated. Thanks!Hello PMarino,
If you need to read all that data then thats your option. However you are
readd all people in this example normally an app would only select 1 person.
Also you need to make sure the query is optimal so the subqueries are using
good plans
Simon Sabin
SQL Server MVP
http://sqlblogcasts.com/blogs/simons

> Does anyone have any suggestions or pointers for performance
> optimizations for SELECT queries that take relational data and
> return it as XML columns?
> For example, I have a Person table. In our system, a person may have
> multiple names, addresses, phone numbers, and email
> addresses. So, I have a PersonNames table, PersonAddresses table,
> PersonPhones table and a PersonEmails table. I'm
> returning all of the person information at once, instead of having to
> make multiple database calls.
> One way to do this is to return multiple resultsets: the first is the
> person base information, and each of the associated
> collections of informataion would be in the next result sets.
> There are some problems with this, though: if I ever want to bring
> back multiple people with all of their supporting
> information, I have to return them with multiple result sets in the
> same order:
> Person1
> Person1Names
> Person1Addresses
> .
> .
> .
> Person2
> Person2Names
> Person2Addresses
> .
> .
> .
> etc.
> I've dealt with this by defining a view that selects the various
> information as XML columns, like this:
> SELECT personId,
> dateOfBirth,
> gender,
> (SELECT DISTINCT nameId,
> salutation,
> firstName,
> middleName,
> surName,
> suffix,
> nameTypeId
> FROM Names AS [Name]
> WHERE personId = People.personId
> FOR XML AUTO, TYPE, Root('Names')) AS Names,
> (SELECT Address.addressId,
> PeopleAddresses.addressTypeId AS addressTypeId,
> cityId,
> countyId,
> stateId,
> streetAddr1,
> streetAddr2,
> cityName,
> stateAbbrev,
> zipCode,
> stateName,
> countyName,
> areaCode,
> timeZone,
> useDST
> FROM PeopleAddresses INNER JOIN
> VAddresses AS [Address] ON PeopleAddresses.addressId =
> Address.addressId
> WHERE PeopleAddresses.personId = People.personId AND
> PeopleAddresses.addressId = Address.AddressId
> FOR XML RAW('Address'), TYPE, Root('Addresses')) AS Addresses,
> (SELECT emailAddressId,
> emailAddress,
> emailAddressTypeId
> FROM EmailAddresses AS [EmailAddress]
> WHERE EmailAddress.personId = People.personId FOR XML AUTO,
> TYPE,
> Root('EmailAddresses')) AS EmailAddresses,
> (SELECT PhoneNumbers.phoneTypeId,
> PhoneNumber.phoneNumberId,
> PhoneNumber.areaCode,
> PhoneNumber.phoneNumber,
> PhoneNumber.extension,
> PhoneNumber.prefix
> FROM PeoplePhones PhoneNumbers INNER JOIN
> PhoneNumbers PhoneNumber ON PhoneNumbers.phoneNumberId =
> PhoneNumber.phoneNumberId
> WHERE PhoneNumbers.personId = People.personId
> FOR XML RAW('PhoneNumber'), TYPE, Root('PhoneNumbers')) AS
> PhoneNumbers
> FROM dbo.People
> This works really well - I can reach into the XML columns of the
> result set, and pull back the data. I can also have a
> SINGLE piece of code that knows how to read addresses, phone numbers,
> etc. for other entities that have these. I just return
> them in the queries for these entities as XML columns.
> However, the performance is not optimal. I know that XML introduces a
> performance penalty, and I'm willing to pay one for
> this flexibility - but I was wondering if anyone has any performance
> optimization tips for this scenario?
> I could obviously change how I'm doing things: store the names,
> addresses, etc. as XML, so that I only construct it once,
> have special cases for returning this information as multiple result
> sets, etc. I don't expect this to be the
> best-performing method, but I'd like to eliminate as many performance
> bottlenecks as possible.
> Any comments are appreciated. Thanks!
>|||Did you try to analyze why your query is expensive? Can it be because of the
SELECT DISTINCT?
You can find some details of FOR XML implementation that could potentially
help optimize FOR XML query performance at
http://blogs.msdn.com/sqlprogrammab...ges/576095.aspx .
Best regards,
Eugene
"PMarino" <PMarino@.discussions.microsoft.com> wrote in message
news:B9127CC7-2413-4047-9E50-C779032E1586@.microsoft.com...
> Does anyone have any suggestions or pointers for performance optimizations
> for SELECT queries that take relational data and
> return it as XML columns?
> For example, I have a Person table. In our system, a person may have
> multiple names, addresses, phone numbers, and email
> addresses. So, I have a PersonNames table, PersonAddresses table,
> PersonPhones table and a PersonEmails table. I'm
> returning all of the person information at once, instead of having to make
> multiple database calls.
> One way to do this is to return multiple resultsets: the first is the
> person
> base information, and each of the associated
> collections of informataion would be in the next result sets.
> There are some problems with this, though: if I ever want to bring back
> multiple people with all of their supporting
> information, I have to return them with multiple result sets in the same
> order:
> Person1
> Person1Names
> Person1Addresses
> .
> .
> .
> Person2
> Person2Names
> Person2Addresses
> .
> .
> .
> etc.
>
> I've dealt with this by defining a view that selects the various
> information
> as XML columns, like this:
> SELECT personId,
> dateOfBirth,
> gender,
> (SELECT DISTINCT nameId,
> salutation,
> firstName,
> middleName,
> surName,
> suffix,
> nameTypeId
> FROM Names AS [Name]
> WHERE personId = People.personId
> FOR XML AUTO, TYPE, Root('Names')) AS Names,
> (SELECT Address.addressId,
> PeopleAddresses.addressTypeId AS addressTypeId,
> cityId,
> countyId,
> stateId,
> streetAddr1,
> streetAddr2,
> cityName,
> stateAbbrev,
> zipCode,
> stateName,
> countyName,
> areaCode,
> timeZone,
> useDST
> FROM PeopleAddresses INNER JOIN
> VAddresses AS [Address] ON PeopleAddresses.addressId =
> Address.addressId
> WHERE PeopleAddresses.personId = People.personId AND
> PeopleAddresses.addressId = Address.AddressId
> FOR XML RAW('Address'), TYPE, Root('Addresses')) AS Addresses,
> (SELECT emailAddressId,
> emailAddress,
> emailAddressTypeId
> FROM EmailAddresses AS [EmailAddress]
> WHERE EmailAddress.personId = People.personId FOR XML AUTO, TYPE,
> Root('EmailAddresses')) AS EmailAddresses,
> (SELECT PhoneNumbers.phoneTypeId,
> PhoneNumber.phoneNumberId,
> PhoneNumber.areaCode,
> PhoneNumber.phoneNumber,
> PhoneNumber.extension,
> PhoneNumber.prefix
> FROM PeoplePhones PhoneNumbers INNER JOIN
> PhoneNumbers PhoneNumber ON PhoneNumbers.phoneNumberId =
> PhoneNumber.phoneNumberId
> WHERE PhoneNumbers.personId = People.personId
> FOR XML RAW('PhoneNumber'), TYPE, Root('PhoneNumbers')) AS PhoneNumbers
> FROM dbo.People
>
> This works really well - I can reach into the XML columns of the result
> set,
> and pull back the data. I can also have a
> SINGLE piece of code that knows how to read addresses, phone numbers, etc.
> for other entities that have these. I just return
> them in the queries for these entities as XML columns.
> However, the performance is not optimal. I know that XML introduces a
> performance penalty, and I'm willing to pay one for
> this flexibility - but I was wondering if anyone has any performance
> optimization tips for this scenario?
> I could obviously change how I'm doing things: store the names, addresses,
> etc. as XML, so that I only construct it once,
> have special cases for returning this information as multiple result sets,
> etc. I don't expect this to be the
> best-performing method, but I'd like to eliminate as many performance
> bottlenecks as possible.
> Any comments are appreciated. Thanks!
>

FOR XML and performance

Does anyone have any suggestions or pointers for performance optimizations
for SELECT queries that take relational data and
return it as XML columns?
For example, I have a Person table. In our system, a person may have
multiple names, addresses, phone numbers, and email
addresses. So, I have a PersonNames table, PersonAddresses table,
PersonPhones table and a PersonEmails table. I'm
returning all of the person information at once, instead of having to make
multiple database calls.
One way to do this is to return multiple resultsets: the first is the person
base information, and each of the associated
collections of informataion would be in the next result sets.
There are some problems with this, though: if I ever want to bring back
multiple people with all of their supporting
information, I have to return them with multiple result sets in the same
order:
Person1
Person1Names
Person1Addresses
..
..
..
Person2
Person2Names
Person2Addresses
..
..
..
etc.
I've dealt with this by defining a view that selects the various information
as XML columns, like this:
SELECT personId,
dateOfBirth,
gender,
(SELECT DISTINCT nameId,
salutation,
firstName,
middleName,
surName,
suffix,
nameTypeId
FROM Names AS [Name]
WHERE personId = People.personId
FOR XML AUTO, TYPE, Root('Names')) AS Names,
(SELECT Address.addressId,
PeopleAddresses.addressTypeId AS addressTypeId,
cityId,
countyId,
stateId,
streetAddr1,
streetAddr2,
cityName,
stateAbbrev,
zipCode,
stateName,
countyName,
areaCode,
timeZone,
useDST
FROM PeopleAddresses INNER JOIN
VAddresses AS [Address] ON PeopleAddresses.addressId =
Address.addressId
WHERE PeopleAddresses.personId = People.personId AND
PeopleAddresses.addressId = Address.AddressId
FOR XML RAW('Address'), TYPE, Root('Addresses')) AS Addresses,
(SELECT emailAddressId,
emailAddress,
emailAddressTypeId
FROM EmailAddresses AS [EmailAddress]
WHERE EmailAddress.personId = People.personId FOR XML AUTO, TYPE,
Root('EmailAddresses')) AS EmailAddresses,
(SELECT PhoneNumbers.phoneTypeId,
PhoneNumber.phoneNumberId,
PhoneNumber.areaCode,
PhoneNumber.phoneNumber,
PhoneNumber.extension,
PhoneNumber.prefix
FROM PeoplePhones PhoneNumbers INNER JOIN
PhoneNumbers PhoneNumber ON PhoneNumbers.phoneNumberId =
PhoneNumber.phoneNumberId
WHERE PhoneNumbers.personId = People.personId
FOR XML RAW('PhoneNumber'), TYPE, Root('PhoneNumbers')) AS PhoneNumbers
FROM dbo.People
This works really well - I can reach into the XML columns of the result set,
and pull back the data. I can also have a
SINGLE piece of code that knows how to read addresses, phone numbers, etc.
for other entities that have these. I just return
them in the queries for these entities as XML columns.
However, the performance is not optimal. I know that XML introduces a
performance penalty, and I'm willing to pay one for
this flexibility - but I was wondering if anyone has any performance
optimization tips for this scenario?
I could obviously change how I'm doing things: store the names, addresses,
etc. as XML, so that I only construct it once,
have special cases for returning this information as multiple result sets,
etc. I don't expect this to be the
best-performing method, but I'd like to eliminate as many performance
bottlenecks as possible.
Any comments are appreciated. Thanks!
Hello PMarino,
If you need to read all that data then thats your option. However you are
readd all people in this example normally an app would only select 1 person.
Also you need to make sure the query is optimal so the subqueries are using
good plans
Simon Sabin
SQL Server MVP
http://sqlblogcasts.com/blogs/simons

> Does anyone have any suggestions or pointers for performance
> optimizations for SELECT queries that take relational data and
> return it as XML columns?
> For example, I have a Person table. In our system, a person may have
> multiple names, addresses, phone numbers, and email
> addresses. So, I have a PersonNames table, PersonAddresses table,
> PersonPhones table and a PersonEmails table. I'm
> returning all of the person information at once, instead of having to
> make multiple database calls.
> One way to do this is to return multiple resultsets: the first is the
> person base information, and each of the associated
> collections of informataion would be in the next result sets.
> There are some problems with this, though: if I ever want to bring
> back multiple people with all of their supporting
> information, I have to return them with multiple result sets in the
> same order:
> Person1
> Person1Names
> Person1Addresses
> .
> .
> .
> Person2
> Person2Names
> Person2Addresses
> .
> .
> .
> etc.
> I've dealt with this by defining a view that selects the various
> information as XML columns, like this:
> SELECT personId,
> dateOfBirth,
> gender,
> (SELECT DISTINCT nameId,
> salutation,
> firstName,
> middleName,
> surName,
> suffix,
> nameTypeId
> FROM Names AS [Name]
> WHERE personId = People.personId
> FOR XML AUTO, TYPE, Root('Names')) AS Names,
> (SELECT Address.addressId,
> PeopleAddresses.addressTypeId AS addressTypeId,
> cityId,
> countyId,
> stateId,
> streetAddr1,
> streetAddr2,
> cityName,
> stateAbbrev,
> zipCode,
> stateName,
> countyName,
> areaCode,
> timeZone,
> useDST
> FROM PeopleAddresses INNER JOIN
> VAddresses AS [Address] ON PeopleAddresses.addressId =
> Address.addressId
> WHERE PeopleAddresses.personId = People.personId AND
> PeopleAddresses.addressId = Address.AddressId
> FOR XML RAW('Address'), TYPE, Root('Addresses')) AS Addresses,
> (SELECT emailAddressId,
> emailAddress,
> emailAddressTypeId
> FROM EmailAddresses AS [EmailAddress]
> WHERE EmailAddress.personId = People.personId FOR XML AUTO,
> TYPE,
> Root('EmailAddresses')) AS EmailAddresses,
> (SELECT PhoneNumbers.phoneTypeId,
> PhoneNumber.phoneNumberId,
> PhoneNumber.areaCode,
> PhoneNumber.phoneNumber,
> PhoneNumber.extension,
> PhoneNumber.prefix
> FROM PeoplePhones PhoneNumbers INNER JOIN
> PhoneNumbers PhoneNumber ON PhoneNumbers.phoneNumberId =
> PhoneNumber.phoneNumberId
> WHERE PhoneNumbers.personId = People.personId
> FOR XML RAW('PhoneNumber'), TYPE, Root('PhoneNumbers')) AS
> PhoneNumbers
> FROM dbo.People
> This works really well - I can reach into the XML columns of the
> result set, and pull back the data. I can also have a
> SINGLE piece of code that knows how to read addresses, phone numbers,
> etc. for other entities that have these. I just return
> them in the queries for these entities as XML columns.
> However, the performance is not optimal. I know that XML introduces a
> performance penalty, and I'm willing to pay one for
> this flexibility - but I was wondering if anyone has any performance
> optimization tips for this scenario?
> I could obviously change how I'm doing things: store the names,
> addresses, etc. as XML, so that I only construct it once,
> have special cases for returning this information as multiple result
> sets, etc. I don't expect this to be the
> best-performing method, but I'd like to eliminate as many performance
> bottlenecks as possible.
> Any comments are appreciated. Thanks!
>
|||Did you try to analyze why your query is expensive? Can it be because of the
SELECT DISTINCT?
You can find some details of FOR XML implementation that could potentially
help optimize FOR XML query performance at
http://blogs.msdn.com/sqlprogrammability/pages/576095.aspx .
Best regards,
Eugene
"PMarino" <PMarino@.discussions.microsoft.com> wrote in message
news:B9127CC7-2413-4047-9E50-C779032E1586@.microsoft.com...
> Does anyone have any suggestions or pointers for performance optimizations
> for SELECT queries that take relational data and
> return it as XML columns?
> For example, I have a Person table. In our system, a person may have
> multiple names, addresses, phone numbers, and email
> addresses. So, I have a PersonNames table, PersonAddresses table,
> PersonPhones table and a PersonEmails table. I'm
> returning all of the person information at once, instead of having to make
> multiple database calls.
> One way to do this is to return multiple resultsets: the first is the
> person
> base information, and each of the associated
> collections of informataion would be in the next result sets.
> There are some problems with this, though: if I ever want to bring back
> multiple people with all of their supporting
> information, I have to return them with multiple result sets in the same
> order:
> Person1
> Person1Names
> Person1Addresses
> .
> .
> .
> Person2
> Person2Names
> Person2Addresses
> .
> .
> .
> etc.
>
> I've dealt with this by defining a view that selects the various
> information
> as XML columns, like this:
> SELECT personId,
> dateOfBirth,
> gender,
> (SELECT DISTINCT nameId,
> salutation,
> firstName,
> middleName,
> surName,
> suffix,
> nameTypeId
> FROM Names AS [Name]
> WHERE personId = People.personId
> FOR XML AUTO, TYPE, Root('Names')) AS Names,
> (SELECT Address.addressId,
> PeopleAddresses.addressTypeId AS addressTypeId,
> cityId,
> countyId,
> stateId,
> streetAddr1,
> streetAddr2,
> cityName,
> stateAbbrev,
> zipCode,
> stateName,
> countyName,
> areaCode,
> timeZone,
> useDST
> FROM PeopleAddresses INNER JOIN
> VAddresses AS [Address] ON PeopleAddresses.addressId =
> Address.addressId
> WHERE PeopleAddresses.personId = People.personId AND
> PeopleAddresses.addressId = Address.AddressId
> FOR XML RAW('Address'), TYPE, Root('Addresses')) AS Addresses,
> (SELECT emailAddressId,
> emailAddress,
> emailAddressTypeId
> FROM EmailAddresses AS [EmailAddress]
> WHERE EmailAddress.personId = People.personId FOR XML AUTO, TYPE,
> Root('EmailAddresses')) AS EmailAddresses,
> (SELECT PhoneNumbers.phoneTypeId,
> PhoneNumber.phoneNumberId,
> PhoneNumber.areaCode,
> PhoneNumber.phoneNumber,
> PhoneNumber.extension,
> PhoneNumber.prefix
> FROM PeoplePhones PhoneNumbers INNER JOIN
> PhoneNumbers PhoneNumber ON PhoneNumbers.phoneNumberId =
> PhoneNumber.phoneNumberId
> WHERE PhoneNumbers.personId = People.personId
> FOR XML RAW('PhoneNumber'), TYPE, Root('PhoneNumbers')) AS PhoneNumbers
> FROM dbo.People
>
> This works really well - I can reach into the XML columns of the result
> set,
> and pull back the data. I can also have a
> SINGLE piece of code that knows how to read addresses, phone numbers, etc.
> for other entities that have these. I just return
> them in the queries for these entities as XML columns.
> However, the performance is not optimal. I know that XML introduces a
> performance penalty, and I'm willing to pay one for
> this flexibility - but I was wondering if anyone has any performance
> optimization tips for this scenario?
> I could obviously change how I'm doing things: store the names, addresses,
> etc. as XML, so that I only construct it once,
> have special cases for returning this information as multiple result sets,
> etc. I don't expect this to be the
> best-performing method, but I'd like to eliminate as many performance
> bottlenecks as possible.
> Any comments are appreciated. Thanks!
>