3

Possible Duplicate:
ORDER BY suddenly conflicting with VARCHAR concatenation in TSQL

recently, I found create clustered index in one table and this clustered index changed the results of a dynamic SQL statement. When the table have clustered index, the statement only returned the last filed result. drop the clustered index from the table or delete the 'ORDER BY fieldnumber', will return the full result (15 fields). The change in behavior was caused by the clustered index in conjunction with the replace calls, as well as the order by, and the declaration of k being 1000 vs max in the sample statement.

Can the Clustered index change dynamic SQL statement behavior and return different results or something else I don't know? Any comments are welcome!

--Create the test table

USE [test] 

GO 

SET ANSI_NULLS ON 

GO 

SET QUOTED_IDENTIFIER ON 

GO 

CREATE TABLE [dbo].[test]( 
[companyid] [int] NOT NULL, 
[fieldName] [nvarchar](50) NOT NULL, 
[fieldnumber] [tinyint] NOT NULL, 
[Tagname] [nvarchar](15) NULL 
) ON [PRIMARY] 

GO 

-- insert test data

INSERT [dbo].[test] ([companyid], [fieldName], [fieldnumber], [Tagname]) VALUES (1, N'Employee Status', 1, N'<CHARACTER_1>') 

INSERT [dbo].[test] ([companyid], [fieldName], [fieldnumber], [Tagname]) VALUES (1, N'Admin Grouping', 2, N'<CHARACTER_2>') 

INSERT [dbo].[test] ([companyid], [fieldName], [fieldnumber], [Tagname]) VALUES (1, N'Department Code', 3, N'<CHARACTER_3>') 

INSERT [dbo].[test] ([companyid], [fieldName], [fieldnumber], [Tagname]) VALUES (1, N'Job Code', 4, N'<CHARACTER_4>') 

INSERT [dbo].[test] ([companyid], [fieldName], [fieldnumber], [Tagname]) VALUES (1, N'FLSA - Exempt', 5, N'<CHARACTER_5>') 

INSERT [dbo].[test] ([companyid], [fieldName], [fieldnumber], [Tagname]) VALUES (1, N'Action Field 06', 6, N'<CHARACTER_6>') 

INSERT [dbo].[test] ([companyid], [fieldName], [fieldnumber], [Tagname]) VALUES (1, N'Action Field 07', 7, N'<CHARACTER_7>') 

INSERT [dbo].[test] ([companyid], [fieldName], [fieldnumber], [Tagname]) VALUES (1, N'Action Field 08', 8, N'<CHARACTER_8>') 

INSERT [dbo].[test] ([companyid], [fieldName], [fieldnumber], [Tagname]) VALUES (1, N'Action Field 09', 9, N'<CHARACTER_9>') 

INSERT [dbo].[test] ([companyid], [fieldName], [fieldnumber], [Tagname]) VALUES (1, N'Action Field 10', 10, N'<CHARACTER_10>') 

INSERT [dbo].[test] ([companyid], [fieldName], [fieldnumber], [Tagname]) VALUES (1, N'ProcessLevel', 11, N'<CHARACTER_11>') 

INSERT [dbo].[test] ([companyid], [fieldName], [fieldnumber], [Tagname]) VALUES (1, N'Department Name', 12, N'<CHARACTER_12>') 

INSERT [dbo].[test] ([companyid], [fieldName], [fieldnumber], [Tagname]) VALUES (1, N'Job Title', 13, N'<CHARACTER_13>') 

INSERT [dbo].[test] ([companyid], [fieldName], [fieldnumber], [Tagname]) VALUES (1, N'Information Field 04', 14, N'<CHARACTER_14>') 

INSERT [dbo].[test] ([companyid], [fieldName], [fieldnumber], [Tagname]) VALUES (1, N'Information Field 05', 15, N'<CHARACTER_15>') 

go 

-- test script

declare @k nvarchar(1000) --–-or max 

set @k = '' 

SELECT @k = @k + REPLACE(REPLACE(TagName,'<',''),'>','') + ',' FROM test WITH (NOLOCK) 
WHERE CompanyID = 1 ORDER BY fieldnumber 


select @k as test_result_without_index 

go 

-- create one clustered index to test the above script

CREATE CLUSTERED INDEX [ix-test] ON [dbo].[test] 
( 
[CompanyID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 

GO 

-- test script which with nvarchar(1000)to test the behavior change with clustered index

declare @k nvarchar(1000) --–-or max 

set @k = '' 

SELECT @k = @k + REPLACE(REPLACE(TagName,'<',''),'>','') + ',' FROM test WITH (NOLOCK) 
WHERE CompanyID = 1 ORDER BY fieldnumber 


select @k as test_result_with_clustered_index_varchar1000_combine 

go 

-- test script which with nvarchar(max)to test the behavior no change under nvarchar (max) combine with clustered index

declare @k nvarchar(Max) --–-or max 

set @k = '' 

SELECT @k = @k + REPLACE(REPLACE(TagName,'<',''),'>','') + ',' FROM test WITH (NOLOCK) 
WHERE CompanyID = 1 ORDER BY fieldnumber 


select @k as test_result_with_clustered_index_and_varcharMax__combine 

go 

--drop the clustered index

IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[test]') AND name = N'ix-test') 

DROP INDEX [ix-test] ON [dbo].[test] WITH ( ONLINE = OFF ) 

GO 

-- then create nonclustered index

USE [test] 

GO 

CREATE NONCLUSTERED INDEX [ix_test] ON [dbo].[test] 

( 
[companyid] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 

GO 

-- test script which with nvarchar(100)to test the behavior no change under nvarchar (100) combine with NONclustered index

declare @k nvarchar(1000) --–-or max 

set @k = '' 

SELECT @k = @k + REPLACE(REPLACE(TagName,'<',''),'>','') + ',' FROM test WITH (NOLOCK) 
WHERE CompanyID = 1 ORDER BY fieldnumber 


select @k as test_result_with_nonClustered_index_and_varchar1000_combine 

go 
4

1 回答 1

2

此查询从未保证有效。下面展示了创建聚集索引后的查询计划。

在此处输入图像描述

使用 FOR XML(变通方法和正确方法)保证顺序,因为始终在查询正确完成后处理。但是,使用@var concat 查询,SQL Server 可以根据需要自由优化查询。

在这里您可以看到 SELECT 语句的连接是在排序之前完成的,因此可以按任何顺序连接行,返回的不一定是最长的(最终的)。

我更喜欢显示表达式的文本计划,所以这里是:

StmtText
---------------------------------------------------------------------------------------------------------------------------------------------
  |--Sort(ORDER BY:([test].[dbo].[test].[fieldnumber] ASC))
       |--Compute Scalar(DEFINE:([Expr1005]=CONVERT_IMPLICIT(nvarchar(1000),([@k]+[Expr1006])+N',',0)))
            |--Compute Scalar(DEFINE:([Expr1006]=replace(replace([test].[dbo].[test].[Tagname],N'<',N''),N'>',N'')))
                 |--Clustered Index Seek(OBJECT:([test].[dbo].[test].[ix-test]), SEEK:([test].[dbo].[test].[companyid]=(1)) ORDERED FORWARD)

REPLACE x2 和 CONCAT 操作被放在一起,表面上是为了提高性能。
但是,如果您将查询更改为

declare @k nvarchar(1000) --–-or max 
set @k = '' 
SELECT @k = @k + REPLACE(REPLACE(TagName,'<',''),'>','') + ','
FROM (
    select TOP(100) TagName, fieldnumber
    from test WITH (NOLOCK)
    WHERE CompanyID = 1
    order by fieldnumber
    ) X
order by fieldnumber
select @k as test_result_with_clustered_index_varchar1000_combine 

可以看到 SQL Server 在执行 CONCAT 之前被强制在子查询中进行 SORT。 注意: TOP 100 PERCENT当它被优化掉时不起作用,但是 TOP(N) ,其中 N 大于表中的记录数。然而,正确的解决方案是使用 FOR XML。

StmtText
---------------------------------------------------------------------------------------------------------------------------------------------
  |--Compute Scalar(DEFINE:([Expr1005]=CONVERT_IMPLICIT(nvarchar(1000),([@k]+[Expr1006])+N',',0)))
       |--Sort(TOP 100, ORDER BY:([test].[dbo].[test].[fieldnumber] ASC))
            |--Compute Scalar(DEFINE:([Expr1006]=replace(replace([test].[dbo].[test].[Tagname],N'<',N''),N'>',N'')))
                 |--Clustered Index Seek(OBJECT:([test].[dbo].[test].[ix-test]), SEEK:([test].[dbo].[test].[companyid]=(1)) ORDERED FORWARD)
于 2012-10-23T01:29:56.500 回答