当前位置: 首页 > 图文教程 > 数据库 > MSSQL > SELECT 赋值与ORDER BY冲突的问题

MSSQL
MySQL中SQL的单字节注入与宽字节注入
解决MySQL 5数据库连接超时问题
通过事务日志解决SQL Server常见四大故障
将SQL 2000日志迁移到SQL Server 2008
在应用环境中如何构造最优的数据库模式
数据库安全管理的三个经验分享
SQL Server应用程序的高级Sql注入
Oracle数据库与SQL Server数据库镜像对比
讲解SQL Server安装sp4补丁报错的解决方法
SQL Server 2005数据库批量更新解决办法
编写和优化SQL Server的存储过程
从数据行入手保护SQL Server数据安全
SQL Server数据体系和应用程序逻辑详解
网友分享:Oracle数据库开发技术经验浅谈
把你的数据库置于版本控制之下
SQL Server 2005实现数据库缓存依赖
SQL Server数据库管理常用SQL和T-SQL语句
初学者必读:提高SQL执行效率的几点建议
初探MS SQL CE+Codesmith
优化SQLServer数据库服务器内存配置的策略

MSSQL 中的 SELECT 赋值与ORDER BY冲突的问题


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-10-17   浏览: 140 ::
收藏到网摘: n/a

  问题描述:
  使用 SELECT 语句,轮询表中的数据,并且处理变量数据时,如果有ORDER BY语句,则得不到想要的结果,但去掉ORDER BY,结果正常。
  具体的问题表现参考下面的问题重现代码
 
  问题重现代码
  -- 测试数据
DECLARE @T TABLE(id int,value nvarchar(16))
INSERT INTO @T SELECT
1,   N'好人' UNION ALL SELECT
2,   N'坏人' UNION ALL SELECT
3,   N'吃饭' UNION ALL SELECT
4,   N'垃圾'
 
  -- 赋值处理
DECLARE @str nvarchar(4000)
SET @str = N'我不是一个好人,也不是垃圾'
SELECT @str = REPLACE(@str, value, N'<u>' + value + N'</u>')
FROM @T
WHERE CHARINDEX(value, @str) > 0
--ORDER BY CHARINDEX(value, @str) DESC
SELECT @str
 
/* -- 结果(当赋值处理语句注释掉ORDER BY 时)
我不是一个<u>好人</u>,也不是<u>垃圾</u>
-- */
 
/* -- 结果(当赋值处理语句加上ORDER BY 时)
我不是一个<u>好人</u>,也不是垃圾
-- */
 
  问题分析:
  两个处理语句的结果不同,通过查看它们的执行计划应该可以看出原因所在,为此,通过
SET SHOWPLAN_ALL ON
  输出了两种执行语句的执行计划(仅StmtText部分,有兴趣的读者在自己的电脑上测试的时候,可以去了解其他部分的信息)

StmtText
Step
DECLARE @str nvarchar(4000) SET @str = N'我不是一个好人,也不是垃圾'
 
SELECT @str = REPLACE(@str, value, N'<u>' + value + N'</u>') FROM @T   WHERE CHARINDEX(value, @str) > 0
4
 
|--
Compute Scalar(DEFINE:([Expr1002]=replace([@str], @T.[value], '<u>'+@T.[value]+'</u>')))
3
 
 
|--
Filter(WHERE:(charindex(@T.[value], [@str], NULL)>0))
2
 
 
 
|--
Table Scan(OBJECT:(@T))
1
 
 
 
 
 
 
 
DECLARE @str nvarchar(4000) SET @str = N'我不是一个好人,也不是垃圾'
 
SELECT @str = REPLACE(@str, value, N'<u>' + value + N'</u>') FROM @T   WHERE CHARINDEX(value, @str) > 0   ORDER BY CHARINDEX(value, @str) DESC
5
 
|--
Sort(ORDER BY:([Expr1003] DESC))
4
 
 
|--
Compute Scalar(DEFINE:([Expr1002]=replace([@str], @T.[value], '<u>'+@T.[value]+'</u>'), [Expr1003]=charindex(@T.[value], [@str], NULL)))
3
 
 
 
|--
Filter(WHERE:(charindex(@T.[value], [@str], NULL)>0))
2
 
 
 
 
|--
Table Scan(OBJECT:(@T))
1

  从上面的列表可以看出,两种处理的最大差异,在于赋值前,是否有ORDER BY 子句,从一般的理解上,可能会认为是否排序并不重要,但换个角度来看问题,就比较容易理解为什么有ORDER BY子句后得不到我们想要的结果了:
当有ORDER BY子句时,对于SELECT @str = 这种赋值处理,SQL Server认为赋值处理肯定只会保留最后一条记录的处理结果,而ORDER BY子句确定了数据顺序,也就知道最后一条记录是那个,因此只会处理ORDER BY的最后一条记录。(读者可以自行去测试一下,调整ORDER BY顺序,看看结果是否与我的推论相符)
当没有ORDER BY子句时,因为无法确定数据顺序,所以SQL Server必须扫描满足条件的每条数据来得到结果,这样每扫描一条记录都会处理一次,所以结果是我们所预知的
 
  问题解决方法:
  修改处理语句,使查询优化器使用与我们需要结果一致的执行方法,可以解决这个问题。
对于示例中的处理语句,可以调整如下:
DECLARE @str nvarchar(4000)
SET @str = N'我不是一个好人,也不是垃圾'
SELECT @str = REPLACE(@str, value, N'<u>' + value + N'</u>')
FROM(
    SELECT TOP 100 PERCENT
        value
    FROM @T
    WHERE CHARINDEX(value, @str) > 0
    ORDER BY CHARINDEX(value, @str) DESC
)A
SELECT @str
 
  补充:
  此问题的结论只是笔者对于查询分析的一个推论,并无相应的官方文档可以证明,所以欢迎大家发表自己的看法