Silent performance killer with promoted column



Half open question, half dire warning ;-p

[I'm kinda hoping Mr. Rys might perhaps spot this one?]

I got stung yesterday by a curious performance issue on 2005 (I haven't
tried it on 2008) - basically, it involves a promoted, persisted and indexed
field from an xml column. When the SET statements are all correct, it works
great. But get any wrong, and it just grinds, doing a table-scan and
re-evaluating the expression instead of using the index.

I can broadly understand the "why" of this, but it seems curious that most
inappropriate SET statements (with xml) raise an error (which I consider a
/good/ thing), where-as this one silently degrades to a crawl.

Easy to avoid if you know about it (just make sure your SET stataments are
appropriate) - but very hard to spot if you don't know what to look for...

Any thoughts? Full script to reproduce is below.

Marc

/*
First part of script creates table and data
Second part of script is performance tests

Typical output:
ANSI_NULLS ON, QUOTED_IDENTIFIER ON
Table 'FOO'. Scan count 1, logical reads 6 [snip]
ANSI_NULLS ON, QUOTED_IDENTIFIER OFF
Table 'FOO'. Scan count 1, logical reads 3527 [snip]
ANSI_NULLS OFF, QUOTED_IDENTIFIER ON
Table 'FOO'. Scan count 1, logical reads 3527 [snip]
ANSI_NULLS OFF, QUOTED_IDENTIFIER OFF
Table 'FOO'. Scan count 1, logical reads 3527 [snip]
ANSI_NULLS ON, QUOTED_IDENTIFIER ON
Table 'FOO'. Scan count 1, logical reads 6 [snip]
*/

-- ******* PART 1 *******
SET STATISTICS IO OFF
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
SET NOCOUNT ON

IF OBJECT_ID('FOO') IS NOT NULL
DROP TABLE FOO
IF OBJECT_ID('Bar') IS NOT NULL
DROP FUNCTION Bar
GO
CREATE FUNCTION Bar (@xml xml) RETURNS varchar(50) WITH SCHEMABINDING
BEGIN
RETURN @xml.value('(/*/@name)[1]', 'varchar(50)')
END
GO
CREATE TABLE FOO(
[ID] int NOT NULL IDENTITY(1,1),
[Content] xml NOT NULL,
CONSTRAINT [PK_FOO] PRIMARY KEY CLUSTERED ([ID] ASC))

CREATE PRIMARY XML INDEX FOO_Content
ON FOO ([Content])

ALTER TABLE FOO ADD [Name] AS [dbo].Bar([Content]) PERSISTED

CREATE NONCLUSTERED INDEX FOO_Name ON FOO ([Name] ASC)

DECLARE @tmp TABLE ([ID] int IDENTITY(1,1) NOT NULL, [Name] varchar(50) NOT
NULL)
INSERT @tmp ([Name]) VALUES ('Fred')
INSERT @tmp ([Name]) VALUES ('Barney')
INSERT @tmp ([Name]) VALUES ('Wilma')
INSERT @tmp ([Name]) VALUES ('Betty')

INSERT @tmp ([Name])
SELECT t1.[Name] FROM @tmp t1 CROSS JOIN @tmp t2 CROSS JOIN @tmp t3
INSERT @tmp ([Name])
SELECT t1.[Name] FROM @tmp t1 CROSS JOIN @tmp t2 CROSS JOIN @tmp t3

INSERT FOO ([Content])
SELECT '<xml name="' + [Name] + CONVERT(varchar(10), [ID]) + '"/>' FROM @tmp

SELECT COUNT(1) AS [Rows] FROM FOO
GO
-- ******* PART 2 *******
SET NOCOUNT ON SET STATISTICS IO ON
SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON
PRINT 'ANSI_NULLS ON, QUOTED_IDENTIFIER ON'
DECLARE @Name varchar(50) SET @Name = 'Betty60000'
SELECT * FROM FOO WHERE [Name] = @Name
GO
SET NOCOUNT ON SET STATISTICS IO ON
SET ANSI_NULLS ON SET QUOTED_IDENTIFIER OFF
PRINT 'ANSI_NULLS ON, QUOTED_IDENTIFIER OFF'
DECLARE @Name varchar(50) SET @Name = 'Betty60000'
SELECT * FROM FOO WHERE [Name] = @Name
GO
SET NOCOUNT ON SET STATISTICS IO ON
SET ANSI_NULLS OFF SET QUOTED_IDENTIFIER ON
PRINT 'ANSI_NULLS OFF, QUOTED_IDENTIFIER ON'
DECLARE @Name varchar(50) SET @Name = 'Betty60000'
SELECT * FROM FOO WHERE [Name] = @Name
GO
SET NOCOUNT ON SET STATISTICS IO ON
SET ANSI_NULLS OFF SET QUOTED_IDENTIFIER OFF
PRINT 'ANSI_NULLS OFF, QUOTED_IDENTIFIER OFF'
DECLARE @Name varchar(50) SET @Name = 'Betty60000'
SELECT * FROM FOO WHERE [Name] = @Name
GO
-- repeat for good measure
SET NOCOUNT ON SET STATISTICS IO ON
SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON
PRINT 'ANSI_NULLS ON, QUOTED_IDENTIFIER ON'
DECLARE @Name varchar(50) SET @Name = 'Betty60000'
SELECT * FROM FOO WHERE [Name] = @Name
GO




.



Relevant Pages

  • RE: temporary table "disapears"
    ... So the original problem is most likely that the create table #tmp and the ... these statements work fine if you just use foo (which isn't ... Toute utilisation de ce ...
    (perl.dbi.users)
  • Re: trigger MVP/MS?: COLUMNS_UPDATES() get before/after values dyn
    ... hopefully MS will chime in and tell us some little odd-ball trick that no ... > you call sp_executesql you essentially create a new execution context. ... The data would be available because the #tmp is created in the ... >> SET NOCOUNT ON ...
    (microsoft.public.sqlserver.programming)
  • Re: any way to avoid warnings about unused outputs in XST?
    ... ENTITY foo IS ... ARCHITECTURE model OF foo IS ... ENTITY tmp IS ...
    (comp.lang.vhdl)
  • Re: use OpenXML in SP - ?
    ... can read 1, 2, 3 in the ADO recordset. ... May I ask why using SET NOCOUNT ON made a difference? ... >Columnist, SQL Server Professional ... >No need to keep #tmp once the operation is complete. ...
    (microsoft.public.sqlserver.programming)
  • Re: Problem with 32 bits executable on HP11 and SHLIB_PATH
    ... extern void foo(); ... > mv libmylib.sl tmp ... > HOWEVER, when I compile everthing with +DD64, it works without any ...
    (comp.sys.hp.hpux)