zoukankan      html  css  js  c++  java
  • SQL Server里简单参数化的痛苦

    在今天的文章里,我想谈下对于即席SQL语句(ad-hoc SQL statements),SQL Server使用的简单参数化(Simple Parameterization)的一些特性和副作用。首先,如果你的SQL语句包含这些,简单参数化不会发生:

    • JOIN
    • IN
    • BULK INSERT
    • UNION
    • INTO
    • DISTINCT
    • TOP
    • GROUP BY
    • HAVING
    • COMPUTE
    • Sub Queries

    一般来说,如果你处理所谓的安全执行计划(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)功能,这个我会在以后的文章里介绍。

    感谢关注!

    参考文章:

    https://www.sqlpassion.at/archive/2015/04/27/the-pain-of-simple-parameterization-in-sql-server/

  • 相关阅读:
    dp周训练 状态压缩
    计算几何 点对处理 #345 (Div. 2) C. Watchmen
    Codeforces Round #303 (Div. 2) D. Queue 水题贪心
    TTTTTTTTTTTTT 树的直径 Codeforces Beta Round #14 (Div. 2) D. Two Paths
    TTTTTTTTTTTTT CF Good Bye 2015 C- New Year and Domino(CF611C) 二维前缀
    巨蟒python全栈开发数据库前端8:jQuery框架2
    计时器练习
    onload事件,解决不能在head写代码
    js引入方式的弹框方法2
    pycharm中格式标准化代码
  • 原文地址:https://www.cnblogs.com/woodytu/p/4728447.html
Copyright © 2011-2022 走看看