[MSSQL]表变量和临时表的区别

1、表变量数据库

  表变量在SQL Server 2000中首次被引入。表变量的具体定义包括列定义,列名,数据类型和约束。而在表变量中可使用的约束包括主键约束,惟一约束,NULL约束和CHECK约束(外键约束不能在表变量中使用)。定义表变量的语句是和正常使用Create Table定义表语句的子集。只是表变量经过DECLARE @local_variable语句进行定义。网络

  表变量的特征:ide

  • 表变量拥有特定做用域(在当前批处理语句中,但不在任何当前批处理语句调用的存储过程和函数中),表变量在批处理结束后自动被清除
  • 表变量较临时表产生更少的存储过程重编译。
  • 针对表变量的事务仅仅在更新数据时生效,因此锁和日志产生的数量会更少。
  • 因为表变量的做用域如此之小,并且不属于数据库的持久部分,因此事务回滚不会影响表变量。
  •   表变量能够在其做用域内像正常的表同样使用。更确切的说,表变量能够被当成正常的表或者表表达式同样在SELECT,DELETE,UPDATE,INSERT语句中使用,可是表变量不能在相似"SELECT select_list INTO table_variable"这样的语句中使用。而在SQL Server2000中,表变量也不能用于INSERT INTO table_variable EXEC stored_procedure这样的语句中。函数

      表变量不能作以下事情:post

  • 虽然表变量是一个变量,可是其不能赋值给另外一个变量。
  • check约束,默认值和计算列不能引用自定义函数。
  • 不能为约束命名。
  • 不能Truncate表变量。
  • 不能向标识列中插入显式值(也就是说表变量不支持SET IDENTITY_INSERT ON)
  •   下面来玩玩表变量吧。性能

      定义一个表变量,插入一条数据,而后查询:测试

    复制代码
      DECLARE @tb1 Table
      (
       Id int,
       Name varchar(20),
       Age int
      )
    
      INSERT INTO @tb1 VALUES(1,'刘备',22)
    
      SELECT * FROM @tb1
    复制代码

      输出结果以下:this

      

      再来试试一些不符合要求的状况,例如添加表变量后,添加约束,并对约束命名:spa

      ALTER TABLE @tb1
        ADD CONSTRAINT CN_AccountAge
        CHECK 
        (Account_Age > 18);    -- 插入年龄必须大于18

      SQL Server提示错误以下:3d

      

      SQL Server不支持定义表变量时对Constraint命名,也不支持定义表变量后,对其建Constraint。

      更多的不容许,请查看上面的要求。

    2、临时表

      在深刻临时表以前,咱们要了解一下会话(Session),一个会话仅仅是一个客户端到数据引擎的链接。在SQL Server Management Studio中,每个查询窗口都会和数据库引擎创建链接。一个应用程序能够和数据库创建一个或多个链接,除此以外,应用程序还可能创建链接后一直不释放知道应用程序结束,也可能使用完释放链接须要时创建链接。

      临时表和Create Table语句建立的表有着相同的物理工程,但临时表与正常的表不一样之处有:

      一、临时表的名称不能超过116个字符,这是因为数据库引擎为了辨别不一样会话创建不一样的临时表,因此会自动在临时表的名字后附加一串。

      二、局部临时表(以"#"开头命名的)做用域仅仅在当前的链接内,从在存储过程当中创建局部临时表的角度来看,局部临时表会在下列状况下被Drop:
        a、显示调用Drop Table语句
        b、当局部临时表在存储过程内被建立时,存储过程结束也就意味着局部临时表被Drop。
        c、当前会话结束,在会话内建立的全部局部临时表都会被Drop。

      三、全局临时表(以"##"开头命名的)在全部的会话内可见,因此在建立全局临时表以前首先检查其是否存在,不然若是已经存在,你将会获得重复建立对象的错误。
        a、全局临时表会在建立其的会话结束后被Drop,Drop后其余会话将不能对全局临时表进行引用。
        b、引用是在语句级别进行,如:
          1.新建查询窗口,运行语句:

      CREATE TABLE ##temp(RowID int)
      INSERT INTO ##temp VALUES(3)
     
     
     

    --建立局部临时表
    create table #tmpStudent(Tid int,Name varchar(50),Age int)
    insert into #tmpStudent values('xiaowang',25)

    select * from #tmpStudent

    --建立局部临时表 另外一种写法
    select * into #tmpStudent from student
    select * from #tmpStudent

     

    --另外一种建立方法:

    create table tempdb.MyTempTable(Tid int) --有对应权限才能够这么写

     

    --删除

    drop table #tmpStudent

          2.再次新建一个查询窗口,每5秒引用一次全局临时表

      While 1=1 
      BEGIN
      SELECT * FROM ##temp
      WAITFOR delay '00:00:05'
      END

          3.回到第一个窗口,关闭窗口。
          4.下一次第二个窗口引用时,将产生错误。

     

      四、不能对临时表进行分区。

      五、不能对临时表加外键约束。

      六、临时表内列的数据类型不能定义成没有在TempDb中没有定义自定义数据类型(自定义数据类型是数据库级别的对象,而临时表属于TempDb)。因为TempDb在每次SQL Server重启后会被自动建立,因此你必须使用startup stored procedure来为TempDb建立自定义数据类型。你也能够经过修改Model数据库来达到这一目标。

      七、XML列不能定义成XML集合的形式,除非这个集合已经在TempDb中定义。

      临时表既能够经过Create Table语句建立,也能够经过"SELECT <select_list> INTO #table"语句建立。你还能够针对临时表用"INSERT INTO #table EXEC stored_procedure"这样的语句。
      临时表能够拥有命名的约束和索引。可是,当两个用户在同一时间调用同一存储过程时,将会产生”There is already an object named ‘<objectname>’ in the database”这样的错误。因此最好的作法是不用为创建的对象进行命名,而使用系统分配的在TempDb中惟一的。

    3、误区

      误区1.表变量仅仅在内存中。

      误区2.临时表仅仅存储在物理介质中。

      这两种观点都是错误的,只有内存足够,表变量和临时表都会在内存中建立和处理。他们也一样能够在任什么时候间被存入磁盘。

      注意表变量的名字是系统分配的,表变量的第一个字符”@”并非一个字母,因此它并非一个有效的变量名。系统会在TempDb中为表变量建立一个系统分配的名称,因此任何在sysobjects或sys.tables查找表变量的方法都会失败。

      正确的方法应该是我前面例子中的方法,我看到不少人使用以下查询查表变量:

      select * from sysobjects where name like'#tempTables%'

      上述代码看上去貌似很好用,但会产生多用户的问题。你创建两个链接,在第一个链接中建立临时表,在第二个窗口中运行上面的语句能看到第一个链接建立的临时表,若是你在第二个链接中尝试操做这个临时表,那么可能会产生错误,由于这个临时表不属于你的会话。

      误区3.表变量不能拥有索引。

        这个误区也一样错误。虽然一旦你建立一个表变量以后,就不能对其进行DDL语句了,这包括Create Index语句。然而你能够在表变量定义的时候为其建立索引)好比以下语句。

      declare @MyTableVariable table (RowID intPRIMARY KEY CLUSTERED) 

        这个语句将会建立一个拥有汇集索引的表变量。因为主键有了对应的汇集索引,因此一个系统命名的索引将会被建立在RowID列上。

        下面的例子演示你能够在一个表变量的列上建立惟一约束以及如何创建复合索引。

       declare @temp TABLE (
         RowID int NOT NULL,
         ColA int NOT NULL,
         ColB char(1)UNIQUE,
         PRIMARY KEY CLUSTERED(RowID, ColA))

      1) SQL 并不能为表变量创建统计信息,就像其能为临时表创建统计信息同样。这意味着对于表变量,执行引擎认为其只有1行,这也意味着针对表变量的执行计划并非最优。虽然估计的执行计划对于表变量和临时表都为1,可是实际的执行计划对于临时表会根据每次存储过程的重编译而改变。若是临时表不存在,在生成执行计划的时候会产生错误。

      2) 一旦创建表变量后就没法对其进行DDL语句操做。所以若是须要为表创建索引或者加一列,你须要临时表。

      3) 表变量不能使用select …into语句,而临时表能够。

      4) 在SQL Server 2008中,你能够将表变量做为参数传入存储过程。可是临时表不行。在SQL Server 2000和2005中表变量也不行。

      5) 做用域:表变量仅仅在当前的批处理中有效,而且对任何在其中嵌套的存储过程等不可见。局部临时表只在当前会话中有效,这也包括嵌套的存储过程。但对父存储过程不可见。全局临时表能够在任何会话中可见,可是会随着建立其的会话终止而DROP,其它会话这时就不能再引用全局临时表。

      6) 排序规则:表变量使用当前数据库的排序规则,临时表使用TempDb的排序规则。若是它们不兼容,你还须要在查询或者表定义中进行指定。

      7) 你若是但愿在动态SQL中使用表变量,你必须在动态SQL中定义表变量。而临时表能够提早定义,在动态SQL中进行引用。

    4、如何选择

      微软推荐使用表变量,若是表中的行数很是小,则使用表变量。不少”网络专家”会告诉你100是一个分界线,由于这是统计信息建立查询计划效率高低的开始。可是我仍是但愿告诉你针对你的特定需求对临时表和表变量进行测试。不少人在自定义函数中使用表变量,若是你须要在表变量中使用主键和惟一索引,你会发现包含数千行的表变量也依然性能卓越。但若是你须要将表变量和其它表进行join,你会发现因为不精准的执行计划,性能每每会很是差。

      为了证实这点,请看本文的附件。附件中代码建立了表变量和临时表.并装入了AdventureWorks数据库的Sales.SalesOrderDetail表。为了获得足够的测试数据,我将这个表中的数据插入了10遍。而后以ModifiedDate 列做为条件将临时表和表变量与原始的Sales.SalesOrderDetail表进行了Join操做,从统计信息来看IO差异显著。从时间来看表变量作join花了50多秒,而临时表仅仅花了8秒。

      若是你须要在表创建后对表进行DLL操做,那么选择临时表吧。

      临时表和表变量有不少相似的地方。因此有时候并无具体的细则规定如何选择哪个。对任何特定的状况,你都须要考虑其各自优缺点并作一些性能测试。下面的表格会让你比较其优略有了更详细的参考。

    5、总结

    特性 表变量 临时表
    做用域 当前批处理 当前会话,嵌套存储过程,全局:全部会话
    使用场景 自定义函数,存储过程,批处理 自定义函数,存储过程,批处理
    建立方式 DECLARE statement only.只能经过DECLEARE语句建立

    CREATE TABLE 语句

    SELECT INTO 语句.

    表名长度 最多128字节 最多116字节
    列类型

    可使用自定义数据类型

    可使用XML集合

    自定义数据类型和XML集合必须在TempDb内定义
    Collation 字符串排序规则继承自当前数据库 字符串排序规则继承自TempDb数据库
    索引 索引必须在表定义时创建 索引能够在表建立后创建
    约束 PRIMARY KEY, UNIQUE, NULL, CHECK约束可使用,但必须在表创建时声明 PRIMARY KEY, UNIQUE, NULL, CHECK. 约束可使用,能够在任什么时候后添加,但不能有外键约束
    表创建后使用DDL (索引,列) 不容许 容许.
    数据插入方式 INSERT 语句 (SQL 2000: 不能使用INSERT/EXEC).

    INSERT 语句, 包括 INSERT/EXEC.

    SELECT INTO 语句.

    Insert explicit values into identity columns (SET IDENTITY_INSERT). 不支持SET IDENTITY_INSERT语句 支持SET IDENTITY_INSERT语句
    Truncate table 不容许 容许
    析构方式 批处理结束后自动析构 显式调用 DROP TABLE 语句.
    当前会话结束自动析构 (全局临时表: 还包括当其它会话语句不在引用表.)
    事务 只会在更新表的时候有事务,持续时间比临时表短 正常的事务长度,比表变量长
    存储过程重编译 会致使重编译
    回滚 不会被回滚影响 会被回滚影响
    统计数据 不建立统计数据,因此全部的估计行数都为1,因此生成执行计划会不精准 建立统计数据,经过实际的行数生成执行计划。
    做为参数传入存储过程 仅仅在SQL Server2008, 而且必须预约义 user-defined table type. 不容许
    显式命名对象 (索引, 约束). 不容许 容许,可是要注意多用户的问题
    动态SQL 必须在动态SQL中定义表变量 能够在调用动态SQL以前定义临时表
    相关文章
    相关标签/搜索