SQL Server里简单参数化的痛苦

本文涉及的产品
云数据库 RDS SQL Server,独享型 2核4GB
简介:

一般来说,如果你处理所谓的安全执行计划(Safe Execution Plan),SQL Server自动参数化你的SQL语句:不管提供的参数值,查询总必须通向一样的执行计划。如果你的执行计划里有书签查找,这就是不可能的例子。因为临界点定义了是否进行书签查找还是全表/聚集索引扫描。

自动参数化并不那么酷!

如果SQL Server能自动参数化你的SQL语句,你还是要考虑下SQL Server引入的自动参数化SQL语句的一些副作用。我们来看一个具体的例子。下列查询创建一个表,执行一个会被SQL Server自动参数化的简单SQL语句。

复制代码
 1 -- Create a simple table
 2 CREATE TABLE Orders
 3 (
 4     Col1 INT IDENTITY(1, 1) PRIMARY KEY NOT NULL,
 5     Price DECIMAL(18, 2)
 6 )
 7 GO
 8 
 9 -- This query gets auto parametrized, because it is a simple query with a safe (consistent) plan
10 SELECT * FROM Orders
11 WHERE Price = 5.70
12 GO
13 
14 -- Analyze the Plan Cache
15 SELECT
16     st.text, 
17     qs.execution_count, 
18     cp.cacheobjtype,
19     cp.objtype,
20     cp.*,
21     qs.*, 
22     p.* 
23 FROM sys.dm_exec_cached_plans cp
24 CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) p
25 CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st
26 LEFT JOIN sys.dm_exec_query_stats qs ON qs.plan_handle = cp.plan_handle
27 WHERE st.text LIKE '%Orders%'
28 GO
复制代码

然后当你查看计划缓存时,你会看到SQL Server能为你自动参数化SQL语句:

(@1 numeric(3,2))SELECT * FROM [Orders] WHERE [Price]=@1

但什么是选择的作为参数的数据类型?最小可能的那个!在这里是NUMERIC(3,2)!如果现在你执行下列2个查询:

复制代码
1 -- Execute a slightly different query
2 SELECT * FROM Orders
3 WHERE Price = 8.70
4 GO
5 
6 -- Execute a slightly different query
7 SELECT * FROM Orders
8 WHERE Price = 124.50
9 GO
复制代码

SQL Server能重用为第1个使用8.7值SQL语句的参数化SQL语句的执行计划。但用124.50值的第2个SQL语句呢?对于这个SQL语句缓存的计划不能被重用,因为124.50值不符合NUMERIC(3,2)。在这个情况下,SQL Server用NUMERIC(5,2)数据类型生成你SQL语句的新参数化版本。你刚用你的SQL语句的额外的参数化版本污染了你的计划缓存!当你执行下列语句会变得更糟:

-- Execute a slightly different query
SELECT * FROM Orders
WHERE Price = 1204.50
GO

这个会再次给你新的用NUMERIC(6,2)数据类型的新参数化版本——计划缓存里另一个版本!当我展示这个行为的时候,很多人都建议我应该用逆序来执行刚才的SQL语句。我们通过首先清空计划缓存来试下。

复制代码
 1 -- Clear the Plan Cache
 2 DBCC FREEPROCCACHE
 3 GO
 4 
 5 -- Execute a slightly different query
 6 SELECT * FROM Orders
 7 WHERE Price = 1204.50
 8 GO
 9 
10 -- Execute a slightly different query
11 SELECT * FROM Orders
12 WHERE Price = 124.50
13 GO
14 
15 -- Execute a slightly different query
16 SELECT * FROM Orders
17 WHERE Price = 8.70
18 GO
复制代码

然后当你看计划缓存时,没有任何改变:SQL Server还生成了3个不同的参数化SQL语句——每次都用最小可能的数据类型。

你怎么做没有一点关系,即你执行你SQL语句的顺序:在自动参数化期间,SQL Server总会选择最小可能的数据类型。当你依赖SQL Server这个特性时,好好考虑下。

VARCHAR如何呢?SQL Server自动参数化包含字符值(例如VARCHAR)的SQL语句时,事情会好点。假设有下列表定义和下列2个查询:

复制代码
 1 -- Create another table to demonstrate this problem
 2 CREATE TABLE Orders3
 3 (
 4     Col1 INT IDENTITY(1, 1) PRIMARY KEY NOT NULL,
 5     Col2 VARCHAR(100)
 6 )
 7 GO
 8 
 9 -- Clears the Plan Cache
10 DBCC FREEPROCCACHE
11 GO
12 
13 -- A VARCHAR/CHAR column is always auto parametrized to a VARCHAR(8000)
14 SELECT * FROM Orders3
15 WHERE Col2 = 'Woody'
16 GO
17 
18 -- A VARCHAR column is always auto parametrized to a VARCHAR(8000)
19 SELECT * FROM Orders3
20 WHERE Col2 = 'Tu'
21 GO
复制代码

在这个情况下,SQL Server用VARCHAR(8000)生成1个自动参数化SQL语句——最大可能的数据类型。从刚才例子里,这是你所期待的行为。有时SQL Server好事坏事同时做……

小结

当你和简单SQL语句打交道时,自动参数化可以非常棒。但如你在这个文章里所见,你要知道SQL Server引入的副作用。另外SQL Server的简单参数化特性还会提供你强制参数化(Forced Parameterization)功能,这个我会在以后的文章里介绍。



本文转自Woodytu博客园博客,原文链接:http://www.cnblogs.com/woodytu/p/4728447.html,如需转载请自行联系原作者

相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情: https://www.aliyun.com/product/rds/sqlserver
相关文章
|
8天前
|
SQL 人工智能 算法
【SQL server】玩转SQL server数据库:第二章 关系数据库
【SQL server】玩转SQL server数据库:第二章 关系数据库
51 10
|
1月前
|
SQL 数据库 数据安全/隐私保护
Sql Server数据库Sa密码如何修改
Sql Server数据库Sa密码如何修改
|
2月前
|
SQL 算法 数据库
【数据库SQL server】关系数据库标准语言SQL之数据查询
【数据库SQL server】关系数据库标准语言SQL之数据查询
95 0
|
2月前
|
SQL 算法 数据库
【数据库SQL server】关系数据库标准语言SQL之视图
【数据库SQL server】关系数据库标准语言SQL之视图
76 0
|
2月前
|
SQL 人工智能 算法
【数据库SQL server】传统运算符与专门运算符
【数据库SQL server】传统运算符与专门运算符
68 0
|
18天前
|
SQL
启动mysq异常The server quit without updating PID file [FAILED]sql/data/***.pi根本解决方案
启动mysq异常The server quit without updating PID file [FAILED]sql/data/***.pi根本解决方案
16 0
|
8天前
|
SQL 算法 数据库
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(二)数据查询
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(二)数据查询
66 6
|
8天前
|
SQL 存储 数据挖掘
数据库数据恢复—RAID5上层Sql Server数据库数据恢复案例
服务器数据恢复环境: 一台安装windows server操作系统的服务器。一组由8块硬盘组建的RAID5,划分LUN供这台服务器使用。 在windows服务器内装有SqlServer数据库。存储空间LUN划分了两个逻辑分区。 服务器故障&初检: 由于未知原因,Sql Server数据库文件丢失,丢失数据涉及到3个库,表的数量有3000左右。数据库文件丢失原因还没有查清楚,也不能确定数据存储位置。 数据库文件丢失后服务器仍处于开机状态,所幸没有大量数据写入。 将raid5中所有磁盘编号后取出,经过硬件工程师检测,没有发现明显的硬件故障。以只读方式将所有磁盘进行扇区级的全盘镜像,镜像完成后将所
数据库数据恢复—RAID5上层Sql Server数据库数据恢复案例
|
12天前
|
SQL 安全 Java
SQL server 2017安装教程
SQL server 2017安装教程
14 1
|
25天前
|
SQL 存储 Python
Microsoft SQL Server 编写汉字转拼音函数
Microsoft SQL Server 编写汉字转拼音函数