表变量与临时表的优缺点_Gordennizaicunzai的博客-CSDN博客

mikel阅读(745)

来源: 表变量与临时表的优缺点_Gordennizaicunzai的博客-CSDN博客

表变量:

DECLARE @tb  table(id   int   identity(1,1), name   varchar(100))
INSERT @tb

SELECT id, name  FROM mytable   WHERE name like ‘zhang%’

 

临时表:

SELECT name, address
INTO #ta   FROM mytable
WHERE name like ‘zhang%’

(if exists (select * from tempdb.dbo.sysobjects where id = object_id(N’tempdb..#ta’) and type=’U’)
drop table #ta)

 

表变量和临时表的比较:

  • 临时表是利用了硬盘(tempdb数据库) ,表名变量是占用内存,因此小数据量当然是内存中的表变量更快。当大数据量时,就不能用表变量了,太耗内存了。大数据量时适合用临时表。
  • 表变量缺省放在内存,速度快,所以在触发器,存储过程里如果数据量不大,应该用表变量。
  • 临时表缺省使用硬盘,一般来说速度比较慢,那是不是就不用临时表呢?也不是,在数据量比较大的时候,如果使用表变量,会把内存耗尽,然后使用 TEMPDB的空间,这样主要还是使用硬盘空间,但同时把内存基本耗尽,增加了内存调入调出的机会,反而降低速度。这种情况建议先给TEMPDB一次分配合适的空间,然后使用临时表。
  • 临时表相对而言表变量主要是多了I/O时间,但少了对内存资源的占用。数据量较大的时候,由于对内存资源的消耗较少,使用临时表比表变量有更好的性能。
  • 建议:触发器、自定义函数用表变量;存储过程看情况,大部分用表变量;特殊的应用,大数据量的场合用临时表。
  • 表变量有明确的作用域,在定义表变量的函数、存储过程或批处理结束时,会自动清除表变量。
  • 在存储过程中使用表变量与使用临时表相比,减少了存储过程的重新编译量。
  • 涉及表变量的事务只在表变量更新期间存在。这样就减少了表变量对锁定和记录资源的需求。
  • 表变量需要事先知道表结构,普通临时表,只在当前会话中可用与表变量相同into一下就可以了,方便;全局临时表:可在多个会话中使用存在于temp中需显示的drop。(不知道表结构情况下临时表方便一些)
  • 全局临时表的功能是表变量没法达到的。
  • 表变量不必删除,也就不会有命名冲突,临时表特别是全局临时表用的时候必须解决命名冲突。
  • 应避免频繁创建和删除临时表,减少系统表资源的消耗。
  • 在新建临时表时,如果一次性插入数据量很大,那么可以使用select into代替create table,避免log,提高速度;如果数据量不大,为了缓和系统表的资源,建议先create table,然后insert。
  • 如果临时表的数据量较大,需要建立索引,那么应该将创建临时表和建立索引的过程放在单独一个子存储过程中,这样才能保证系统能够很好的使用到该临时表的索引。
  • 如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先truncate table,然后drop table,这样可以避免系统表的较长时间锁定。
  • 慎用大的临时表与其他大表的连接查询和修改,减低系统表负担,因为这种操作会在一条语句中多次使用tempdb的系统表。
————————————————–
问题 1:为什么在已经有了临时表的情况下还要引入表变量?

解答 1:与临时表相比,表变量具有下列优点:

SQL Server 联机丛书“表”(Table) 一文中所述,表变量(如局部变量)具有明确定义的范围,在该范围结束时会自动清除这些表变量。
与临时表相比,表变量导致存储过程的重新编译更少。
涉及表变量的事务仅维持表变量上更新的持续时间。因此,使用表变量时,需要锁定和记录资源的情况更少。因为表变量具有有限的范围并且不是持久性数据库的一部分,所以事务回滚并不影响它们。

问题 2:如果说使用表变量比使用临时表导致存储过程的重新编译更少,这意味着什么?

解答 2:下面的文章讨论了重新编译存储过程的一些原因:

243586  (http://support.microsoft.com/kb/243586/) 存储过程重新编译的疑难解答

“由于某些临时表操作引起的重新编译”一节还列出了为避免一些问题(例如使用临时表导致重新编译)而需要满足的一些要求。这些限制不适用于表变量。

表变量完全独立于创建这些表变量的批,因此,当执行 CREATE 或 ALTER 语句时,不会发生“重新解析”,而在使用临时表时可能会发生“重新解析”。临时表需要此“重新解析”,以便从嵌套存储过程引用该表。表变量完全避免了此问题,因此存储过程可以使用已编译的计划,从而节省了处理存储过程的资源。

问题 3:表变量有哪些缺陷?

解答 3:与临时表相比,它存在下列缺陷:

在表变量上不能创建非聚集索引(为 PRIMARY 或 UNIQUE 约束创建的系统索引除外)。与具有非聚集索引的临时表相比,这可能会影响查询性能。
表变量不像临时表那样可以维护统计信息。在表变量上,不能通过自动创建或使用 CREATE STATISTICS 语句来创建统计信息。因此,在大表上进行复杂查询时,缺少统计信息可能会妨碍优化器确定查询的最佳计划,从而影响该查询的性能。
在初始 DECLARE 语句后不能更改表定义。
表变量不能在 INSERT EXEC 或 SELECT INTO 语句中使用。
表类型声明中的检查约束、默认值以及计算所得的列不能调用用户定义的函数。
如果表变量是在 EXEC 语句或 sp_executeSQL 存储过程外创建的,则不能使用 EXEC 语句或sp_executesql 存储过程来运行引用该表变量的动态 SQL Server 查询。由于表变量只能在它们的本地作用域中引用,因此 EXEC 语句和 sp_executesql 存储过程将在表变量的作用域之外。但是,您可以在 EXEC 语句或 sp_executesql 存储过程内创建表变量并执行所有处理,因为这样表变量本地作用域将位于 EXEC 语句或 sp_executesql 存储过程中。

问题 4:与临时表或永久表相比,表变量的仅存在于内存中的结构保证了更好的性能,是否因为它们是在驻留在物理磁盘上的数据库中维护的?

解答 4:表变量不是仅存在于内存中的结构。由于表变量可能保留的数据较多,内存中容纳不下,因此它必须在磁盘上有一个位置来存储数据。与临时表类似,表变量是在  tempdb 数据库中创建的。如果有足够的内存,则表变量和临时表都在内存(数据缓存)中创建和处理。

问题 5:必须使用表变量来代替临时表吗?

解答 5:答案取决于以下三个因素:

插入到表中的行数。
从中保存查询的重新编译的次数。
查询类型及其对性能的指数和统计信息的依赖性。

在某些情况下,可将一个具有临时表的存储过程拆分为多个较小的存储过程,以便在较小的单元上进行重新编译。

通常情况下,应尽量使用表变量,除非数据量非常大并且需要重复使用表。在这种情况下,可以在临时表上创建索引以提高查询性能。但是,各种方案可能互不相同。Microsoft 建议您做一个测试,来验证表变量对于特定的查询或存储过程是否比临时表更有效。

转自:http://blog.csdn.net/leamonjxl/article/details/6602716
一直以来大家对临时表与表变量的孰优孰劣争论颇多,一些技术群里的朋友甚至认为表变量几乎一无是处,比如无统计信息,不支持事务等等.但事实并非如此.这里我就临时表与表变量做个对比,对于大多数人不理解或是有歧义的地方进行详细说明.
注:这里只讨论一般临时表,对全局临时表不做阐述.
生命周期
临时表:会话中,proc中,或使用显式drop
表变量:batch中
这里用简单的code说明表变量作用域
复制代码
DECLARE @t TABLE(i int) ----定义表变量@t

SELECT *FROM @t        -----访问OK

insert into @t select 1 -----插入数据OK

select * from  @t      -------访问OK
go                     -------结束批处理
select * from @t       -------不在作用域出错
复制代码
注意:虽然说sqlserver在定义表变量完成前不允许你使用定义的变量.但注意下面情况仍然可正常运行!

 


if 'a'='b'
begin
DECLARE @t TABLE(i int)
end
SELECT *FROM @t        -----仍然可以访问!

日志机制
临时表与表变量都会记录在tempdb中记录日志
不同的是临时表的活动日志在事务完成前是不能截断的.
这里应注意的是由于表变量不支持truncate,所以完全清空对象结果集时临时表有明显优势,而表变量只能delete
事务支持
临时表:支持
表变量:不支持
我们通过简单的实例加以说明
复制代码
create table #t (i int)
declare @t table(i int)
BEGIN TRAN ttt
insert into #t select 1
insert into @t select 1
SELECT * FROM #t  ------returns 1 rows
SELECT * FROM @t  ------returns 1 rows
ROLLBACK tran ttt
SELECT * FROM #t    -------no rows
SELECT * FROM @t    -------still 1 rows
drop table #t       ----no use drop @t in session
复制代码

 

锁机制(select)
临时表 会对相关对象加IS(意向共享)锁
表变量 会对相关对象加SCH-S(架构共享)锁(相当于加了nolock hint)
可以看出虽说锁的影响范围不同,但由于作用域都只是会话或是batch中,临时表的IS锁虽说兼容性不如表变量的SCH-S但绝大多数情况基本无影响.
感兴趣的朋友可以用TF1200测试
索引支持
临时表  支持
表变量  条件支持(仅SQL2014)
没错,在sql2014中你可以在创建表的同时创建索引 图1-1
注:在sql2014之前表变量只支持创建一个默认的唯一性约束
code
复制代码
DECLARE @t TABLE 
(
col1 int index inx_1 CLUSTERED, 
col2 int  index index_2 NONCLUSTERED,
       index index_3 NONCLUSTERED(col1,col2)
)

复制代码

图1-1

 

用户自定义函数(UDFs)
临时表 不支持作为UDF的结果集返回
表变量 支持作为UDF的结果集返回
注:当表变量作为UDF的结果集返回时分为TVF(Table-Valued Function),TVP(Table-Valued Parameters)两种类型,只有TVF支持plan cache
如图1-2
Code
复制代码
CREATE FUNCTION TVP_Customers (@cust nvarchar(10))
RETURNS TABLE
AS
 RETURN
 (SELECT RowNum, CustomerID, OrderDate, ShipCountry
 FROM BigOrders
 WHERE CustomerID = @cust);
GO
CREATE FUNCTION TVF_Customers (@cust nvarchar(10))
RETURNS @T TABLE (RowNum int, CustomerID nchar(10), OrderDate date,
 ShipCountry nvarchar(30))
AS
BEGIN
 INSERT INTO @T
  SELECT RowNum, CustomerID, OrderDate, ShipCountry
  FROM BigOrders
  WHERE CustomerID = @cust
  RETURN
END;

DBCC FREEPROCCACHE
GO
SELECT * FROM TVF_Customers('CENTC');
GO
SELECT * FROM TVP_Customers('CENTC');
GO
SELECT * FROM TVF_Customers('SAVEA');
GO
SELECT * FROM TVP_Customers('SAVEA');
GO

select b.text,a.execution_count,a.* from sys.dm_exec_query_stats a
cross apply sys.dm_exec_sql_text(a.sql_handle) b
where b.text like '%_Customers%'
复制代码

图1-2

 

其它方面
表变量不支持select into,alter,truncate,dbcc等
表变量不支持table hint 如(force seek)

 

执行计划预估
我想这里可能是引起使用何种方式争论比较突出的地方,由于表变量没有统计信息,无法添加索引等使得大家对其在执行计划中的性能表现嗤之以鼻,但实际情况呢?我们需要深入分析.
关于临时表的预估这里我就不做介绍了,主要对表变量的预估做详细阐述.
表变量在sql2000引入的一个原因就是为了在一些执行过程中减少重编译.以获得更好的性能.当然带来好处的同时也会带来一定弊端.由于其不涉及重编译,优化器其实并不知道表变量中的具体行数,此时他采取了保守的预估方式:预估行数为1行.如图2-1

Code

复制代码
declare @t table (i int)
select * from @t-----此时0行预估行数为1行
insert into @t select 1
select * from @t-----此时1行,预估行数仍为1行
insert into @t values (2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(14),(15),(16),(17),(18),(19),(20)
select * from @t ----此时19行,预估行数仍为1行

--....无论实际@t中有多少行,由于没有重编译,预估均为1行
复制代码

 

图2-1

所以当我们加上重编译的的操作,此时优化器就知道了表变量的具体行数.如图2-2

Code

 

复制代码
declare @t table (i int)
select * from @t option(recompile)-----此时0行预估行数为1行
insert into @t select 1
select * from @t  option(recompile)-----此时1行,预估行数为1行
insert into @t values (2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(14),(15),(16),(17),(18),(19),(20)
select * from @t  option(recompile)----此时19行,预估行数为19行
--....当加入重编译hint时,优化器就知道的表变量的行数.
复制代码

 

图2-2

 

至此,我们可以看到优化器知道了表变量中的行数.这样在表变量扫描的过程中,尤其针对数据量较大的情形,不会因为预估总是1而引起一些问题.
如果你刚知道这里的预估原理,现有的代码都加上重编译那工作量可想而知了..这里介绍一个新的跟踪标记,Trace Flag 2453.
TF2453可以一定程度上替代重编译Hint,但只是在非简单计划(trivial plans)的情形下
注:TF2453只在sql2012 SP2和SQL2014中的补丁中起作用

表变量谓词预估
由于表变量木有统计信息,在优化器知道整体行数的前提下将会根据谓词的情形
采用不同的规则"猜"来进行预估.
注:这里有些规则笔者未找到微软相应的算法文档,经过自己根据数据推算得出.
看到这里的朋友请为我点个赞J(很长时间推算得出.可能数学忘得差不多了)
注:由于检索对象本身及为变量,谓词为变量,或是常数无影响
常见谓词下预估算法:
a ">", "<" 运算符 按照表变量数据量的30%进行预估
b "like" 运算符 按照表变量数据量的10%进行预估
c "="  运算符 按照表变量数据量的0.75次方预估
实例如图2-3
code
复制代码
declare @i int
set @i=13
DECLARE @T TABLE(I INT);
INSERT INTO @T VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(14),(15),(16),(17),(18),(19),(20)
------表变量中存在个数字
select * from @T where I < 1  option(recompile) ------20*30% 预估数为6
select * from @T where I > @i option(recompile) --------20*30%预估数为6
select * from @T where I like @i  option(recompile) --------20*10% 预估数为2
select * from @T where I like 1  option(recompile)  --------20*10 预估数为2
select * from @T where I = @i  option(recompile) --------POWER(20.00000,0.75) 预估数为9.45742
select * from @T where I = 1  option(recompile)  --------POWER(20.00000,0.75) 预估数为9.45742

insert into @T
select DatabaseLogID from AdventureWorks2008R2.dbo.DatabaseLog------insert new records
select * from @T option(recompile) ------------此时数据为行
select * from @T where I = 1  option(recompile)--------------------POWER(1617.00000,0.75) 预估数为254.99550
复制代码

图2-3

可以看出根据不同的谓词优化器会采用不同的预估方式,虽然它不如统计信息下的密度,直方图等来的精确(尤其是等值预估,在数据量巨大的情形下,其效果可能接近统计信息),但在了解数据的前提下如果适合表变量我们还是可以大胆使用的.

Tempdb竞争
tempdb的竞争本身涵盖的知识面比较大,这里我们只讨论临时表与表变量的孰优孰劣.
通过前面的介绍我们知道临时表是支持事务的,而表变量时不支持的.正因如此很多人放弃了表变量的使用.但任何事情都有两方面,支持就一定好吗?由于临时表对事务的支持,在高并发的情形中可能正因为其事务的支持造成系统表锁,总而影响并发.

我们通过一个简单的实例来说明

日常管理中,我发现很多开发人员在使用临时表时采用select * into #t from …的语法,这样的写法如果数据量稍大,将会造成事务持有系统表锁的时间变长,从而影响并发,吞吐.我们通过一个简单的实例说明.如图3-1

 

Code 我们通过sqlquerystress模拟并发

复制代码
----SSMS测试数据
Use tempdb
create table t
( id int identity,str1 char(8000))----more pages for many records

insert into t select 'a'
go 100

----sqlquerystress
select * into #t
from t----57s

----sqlquerystress
declare @t table
( id int,str1 char(8000))
insert into @t
select * from t-----1s
复制代码

 

图3-1

 

通过图3-1可以看出上述情形中临时表简直不堪重负.临时表与表变量到底该如何应用不是看谁比谁的优点多,应视具体情形而定

结语:借用火影忍者中宇智波. 鼬的一句名言:”任何术都是有缺陷的” 同样,在数据库的世界里没有哪项技术是完美无缺的.根据实际的场景,情形,选择合理的实现方式才是我们的初衷.

转自:http://www.cnblogs.com/shanksgao/p/3988089.html

临时表VS表变量--因地制宜,合理使用 - ShanksGao - 博客园

mikel阅读(1069)

来源: 临时表VS表变量–因地制宜,合理使用 – ShanksGao – 博客园

一直以来大家对临时表与表变量的孰优孰劣争论颇多,一些技术群里的朋友甚至认为表变量几乎一无是处,比如无统计信息,不支持事务等等.但事实并非如此.这里我就临时表与表变量做个对比,对于大多数人不理解或是有歧义的地方进行详细说明.
注:这里只讨论一般临时表,对全局临时表不做阐述.
生命周期
临时表:会话中,proc中,或使用显式drop
表变量:batch中
这里用简单的code说明表变量作用域
复制代码
DECLARE @t TABLE(i int) ----定义表变量@t

SELECT *FROM @t        -----访问OK

insert into @t select 1 -----插入数据OK

select * from  @t      -------访问OK
go                     -------结束批处理
select * from @t       -------不在作用域出错
复制代码
注意:虽然说sqlserver在定义表变量完成前不允许你使用定义的变量.但注意下面情况仍然可正常运行!

 


if 'a'='b'
begin
DECLARE @t TABLE(i int)
end
SELECT *FROM @t        -----仍然可以访问!

日志机制
临时表与表变量都会记录在tempdb中记录日志
不同的是临时表的活动日志在事务完成前是不能截断的.
这里应注意的是由于表变量不支持truncate,所以完全清空对象结果集时临时表有明显优势,而表变量只能delete
事务支持
临时表:支持
表变量:不支持
我们通过简单的实例加以说明
复制代码
create table #t (i int)
declare @t table(i int)
BEGIN TRAN ttt
insert into #t select 1
insert into @t select 1
SELECT * FROM #t  ------returns 1 rows
SELECT * FROM @t  ------returns 1 rows
ROLLBACK tran ttt
SELECT * FROM #t    -------no rows
SELECT * FROM @t    -------still 1 rows
drop table #t       ----no use drop @t in session
复制代码

 

锁机制(select)
临时表 会对相关对象加IS(意向共享)锁
表变量 会对相关对象加SCH-S(架构共享)锁(相当于加了nolock hint)
可以看出虽说锁的影响范围不同,但由于作用域都只是会话或是batch中,临时表的IS锁虽说兼容性不如表变量的SCH-S但绝大多数情况基本无影响.
感兴趣的朋友可以用TF1200测试
索引支持
临时表  支持
表变量  条件支持(仅SQL2014)
没错,在sql2014中你可以在创建表的同时创建索引 图1-1
注:在sql2014之前表变量只支持创建一个默认的唯一性约束
code
复制代码
DECLARE @t TABLE 
(
col1 int index inx_1 CLUSTERED, 
col2 int  index index_2 NONCLUSTERED,
       index index_3 NONCLUSTERED(col1,col2)
)

复制代码

图1-1

 

用户自定义函数(UDFs)
临时表 不支持作为UDF的结果集返回
表变量 支持作为UDF的结果集返回
注:当表变量作为UDF的结果集返回时分为TVF(Table-Valued Function),TVP(Table-Valued Parameters)两种类型,只有TVF支持plan cache
如图1-2
Code
复制代码
CREATE FUNCTION TVP_Customers (@cust nvarchar(10))
RETURNS TABLE
AS
 RETURN
 (SELECT RowNum, CustomerID, OrderDate, ShipCountry
 FROM BigOrders
 WHERE CustomerID = @cust);
GO
CREATE FUNCTION TVF_Customers (@cust nvarchar(10))
RETURNS @T TABLE (RowNum int, CustomerID nchar(10), OrderDate date,
 ShipCountry nvarchar(30))
AS
BEGIN
 INSERT INTO @T
  SELECT RowNum, CustomerID, OrderDate, ShipCountry
  FROM BigOrders
  WHERE CustomerID = @cust
  RETURN
END;

DBCC FREEPROCCACHE
GO
SELECT * FROM TVF_Customers('CENTC');
GO
SELECT * FROM TVP_Customers('CENTC');
GO
SELECT * FROM TVF_Customers('SAVEA');
GO
SELECT * FROM TVP_Customers('SAVEA');
GO

select b.text,a.execution_count,a.* from sys.dm_exec_query_stats a
cross apply sys.dm_exec_sql_text(a.sql_handle) b
where b.text like '%_Customers%'
复制代码

 

图1-2

 

其它方面
表变量不支持select into,alter,truncate,dbcc等
表变量不支持table hint 如(force seek)

 

执行计划预估
我想这里可能是引起使用何种方式争论比较突出的地方,由于表变量没有统计信息,无法添加索引等使得大家对其在执行计划中的性能表现嗤之以鼻,但实际情况呢?我们需要深入分析.
关于临时表的预估这里我就不做介绍了,主要对表变量的预估做详细阐述.
表变量在sql2000引入的一个原因就是为了在一些执行过程中减少重编译.以获得更好的性能.当然带来好处的同时也会带来一定弊端.由于其不涉及重编译,优化器其实并不知道表变量中的具体行数,此时他采取了保守的预估方式:预估行数为1行.如图2-1

Code

复制代码
declare @t table (i int)
select * from @t-----此时0行预估行数为1行
insert into @t select 1
select * from @t-----此时1行,预估行数仍为1行
insert into @t values (2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(14),(15),(16),(17),(18),(19),(20)
select * from @t ----此时19行,预估行数仍为1行

--....无论实际@t中有多少行,由于没有重编译,预估均为1行
复制代码

 

 

图2-1

所以当我们加上重编译的的操作,此时优化器就知道了表变量的具体行数.如图2-2

Code

 

复制代码
declare @t table (i int)
select * from @t option(recompile)-----此时0行预估行数为1行
insert into @t select 1
select * from @t  option(recompile)-----此时1行,预估行数为1行
insert into @t values (2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(14),(15),(16),(17),(18),(19),(20)
select * from @t  option(recompile)----此时19行,预估行数为19行
--....当加入重编译hint时,优化器就知道的表变量的行数.
复制代码

 

图2-2

 

至此,我们可以看到优化器知道了表变量中的行数.这样在表变量扫描的过程中,尤其针对数据量较大的情形,不会因为预估总是1而引起一些问题.
如果你刚知道这里的预估原理,现有的代码都加上重编译那工作量可想而知了..这里介绍一个新的跟踪标记,Trace Flag 2453.
TF2453可以一定程度上替代重编译Hint,但只是在非简单计划(trivial plans)的情形下
注:TF2453只在sql2012 SP2和SQL2014中的补丁中起作用

表变量谓词预估
由于表变量木有统计信息,在优化器知道整体行数的前提下将会根据谓词的情形
采用不同的规则"猜"来进行预估.
注:这里有些规则笔者未找到微软相应的算法文档,经过自己根据数据推算得出.
看到这里的朋友请为我点个赞J(很长时间推算得出.可能数学忘得差不多了)
注:由于检索对象本身及为变量,谓词为变量,或是常数无影响
常见谓词下预估算法:
a ">", "<" 运算符 按照表变量数据量的30%进行预估
b "like" 运算符 按照表变量数据量的10%进行预估
c "="  运算符 按照表变量数据量的0.75次方预估
实例如图2-3
code
复制代码
declare @i int
set @i=13
DECLARE @T TABLE(I INT);
INSERT INTO @T VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(14),(15),(16),(17),(18),(19),(20)
------表变量中存在个数字
select * from @T where I < 1  option(recompile) ------20*30% 预估数为6
select * from @T where I > @i option(recompile) --------20*30%预估数为6
select * from @T where I like @i  option(recompile) --------20*10% 预估数为2
select * from @T where I like 1  option(recompile)  --------20*10 预估数为2
select * from @T where I = @i  option(recompile) --------POWER(20.00000,0.75) 预估数为9.45742
select * from @T where I = 1  option(recompile)  --------POWER(20.00000,0.75) 预估数为9.45742

insert into @T
select DatabaseLogID from AdventureWorks2008R2.dbo.DatabaseLog------insert new records
select * from @T option(recompile) ------------此时数据为行
select * from @T where I = 1  option(recompile)--------------------POWER(1617.00000,0.75) 预估数为254.99550
复制代码

 

图2-3

可以看出根据不同的谓词优化器会采用不同的预估方式,虽然它不如统计信息下的密度,直方图等来的精确(尤其是等值预估,在数据量巨大的情形下,其效果可能接近统计信息),但在了解数据的前提下如果适合表变量我们还是可以大胆使用的.

Tempdb竞争
tempdb的竞争本身涵盖的知识面比较大,这里我们只讨论临时表与表变量的孰优孰劣.
通过前面的介绍我们知道临时表是支持事务的,而表变量时不支持的.正因如此很多人放弃了表变量的使用.但任何事情都有两方面,支持就一定好吗?由于临时表对事务的支持,在高并发的情形中可能正因为其事务的支持造成系统表锁,总而影响并发.

我们通过一个简单的实例来说明

日常管理中,我发现很多开发人员在使用临时表时采用select * into #t from …的语法,这样的写法如果数据量稍大,将会造成事务持有系统表锁的时间变长,从而影响并发,吞吐.我们通过一个简单的实例说明.如图3-1

 

Code 我们通过SQLquerystress模拟并发

复制代码
----SSMS测试数据
Use tempdb
create table t
( id int identity,str1 char(8000))----more pages for many records

insert into t select 'a'
go 100

----sqlquerystress
select * into #t
from t----57s

----sqlquerystress
declare @t table
( id int,str1 char(8000))
insert into @t
select * from t-----1s
复制代码

 

 

图3-1

 

通过图3-1可以看出上述情形中临时表简直不堪重负.临时表与表变量到底该如何应用不是看谁比谁的优点多,应视具体情形而定

结语:借用火影忍者中宇智波. 鼬的一句名言:”任何术都是有缺陷的” 同样,在数据库的世界里没有哪项技术是完美无缺的.根据实际的场景,情形,选择合理的实现方式才是我们的初衷.

在网页中在线浏览ppt文档 - 每天进步多一点 - 博客园

mikel阅读(1707)

来源: 在网页中在线浏览ppt文档 – 每天进步多一点 – 博客园

方法一:

把ppt文件的扩展名直接修改为pps,嵌入到网页中

缺点:这种方式浏览器会提示是打开,还是下载,选择打开的话会直接在浏览器中打开,并且客户端一定要安装Office PowerPoint才能打开

方法二:

把ppt文件在PowerPoint中另存为网页,保存类型为htm,可以直接在网页中打开

缺点:每一个ppt文档都需要在PowerPoint中另存为网页,因为需要转换格式,在特殊场景下可能不适用

方法三:

使用微软提供的ocx控件

下载地址:http://www.microsoft.com/downloads/details.aspx?FamilyId=CE2CA4FD-2169-4FAC-82AF-770AA9B60D77

介绍地址:http://support.microsoft.com/default.aspx?scid=kb;en-us;311765

缺点:学习难度较大,需要修改浏览器权限,琢磨了许久,不得不放弃

方法四:

使用第三方软件,如SOAOffice、iweboffice、ntko等

没有做具体研究

方法五:

用PPT To Video Scout转换成avi,flv等影片格式

缺点:不能手动控制要跳转到哪个页面

方法六:

使用PowerPoint to Flash将ppt文件转换为flash文件,转换的flash用鼠标点击flash可以播放影片,并且可以通过选项设定在flash上添加前进或后退的按钮

 方法七:

将ppt转换为图片,但是对于ppt中存在视频的话,可能不适用,而且截图会存在像素的问题,所以播放图片会模糊。

我暂时采用了方法二的方式,查看了一下数字标牌的播放方式,找到了它存储的temp文件,发现为htm格式的网页,但是我用ppt转换为的htm与他存储的htm格式的图标不同,而且由ppt转换后的htm会存在大纲,通过下边的方法我将大纲去掉了,不知道和网页格式htm还会不会存在区别,暂定这种方式把!

去掉大纲的方法:

不显示大纲可以实现!当你将ppt文件保存为html文件时,它会自动生成一个文件夹,到里面找到frame.html,并用记事本打开它。你会看到有下面这段代码。其实只要改动两个地方就行了:第一去掉这句“ <frame src=outline.htm title=”大纲” name=PPTOtl>”。再将“<frameset cols=”25%,*” onload=”Load()” id=PPTHorizAdjust framespacing=1 frameborder=1>”中的cols=”25%”改为cols=”100%”。

第二把这句<frame src=outline.htm title=”大纲导航栏” name=PPTOtlNav scrolling=no noresize>去掉,再把<frameset cols=”25%,*” framespacing=1>
这句中的cols=”25%”改为cols=”100%”。

MYSQL limit用法 - 就是你baby - 博客园

mikel阅读(1005)

来源: MYSQL limit用法 – 就是你baby – 博客园

1、MySQL的limit用法

 

在我们使用查询语句的时候,经常要返回前几条或者中间某几行数据,这个时候怎么办呢?不用担心,mySQL已经为我们提供了这样一个功能。

 

  1. SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset  
SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset

 

LIMIT 子句可以被用于强制 SELECT 语句返回指定的记录数。LIMIT 接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。初始记录行的偏移量是 0(而不是 1): 为了与 PostgreSQL 兼容,MySQL 也支持句法: LIMIT # OFFSET #。

 

  1. mysql> SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15   
  2. //为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:
  3. mysql> SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.   
  4. //如果只给定一个参数,它表示返回最大的记录行数目:
  5. mysql> SELECT * FROM table LIMIT 5; //检索前 5 个记录行   
  6. //换句话说,LIMIT n 等价于 LIMIT 0,n。
mysql> SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15

//为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1: 
mysql> SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.

//如果只给定一个参数,它表示返回最大的记录行数目: 
mysql> SELECT * FROM table LIMIT 5; //检索前 5 个记录行

//换句话说,LIMIT n 等价于 LIMIT 0,n。

 

引用,路人乙:Mysql中limit的用法详解

 

2、Mysql的分页查询语句的性能分析

 

 

MySql分页sql语句,如果和MSSQL的TOP语法相比,那么MySQL的LIMIT语法要显得优雅了许多。使用它来分页是再自然不过的事情了。

 

2.1最基本的分页方式:

 

  1. SELECT … FROM … WHERE … ORDER BY … LIMIT …  
 
SELECT ... FROM ... WHERE ... ORDER BY ... LIMIT ...

 

在中小数据量的情况下,这样的SQL足够用了,唯一需要注意的问题就是确保使用了索引:

举例来说,如果实际SQL类似下面语句,那么在category_id, id两列上建立复合索引比较好:

 

  1. SELECT * FROM articles WHERE category_id = 123 ORDER BY id LIMIT 50, 10  
SELECT * FROM articles WHERE category_id = 123 ORDER BY id LIMIT 50, 10

 

 

2.2子查询的分页方式:

 

随着数据量的增加,页数会越来越多,查看后几页的SQL就可能类似:

  1. SELECT * FROM articles WHERE category_id = 123 ORDER BY id LIMIT 10000, 10  
SELECT * FROM articles WHERE category_id = 123 ORDER BY id LIMIT 10000, 10

 

一言以蔽之,就是越往后分页,LIMIT语句的偏移量就会越大,速度也会明显变慢。

此时,我们可以通过子查询的方式来提高分页效率,大致如下:

  1. SELECT * FROM articles WHERE  id >=   
  2.  (SELECT id FROM articles  WHERE category_id = 123 ORDER BY id LIMIT 10000, 1) LIMIT 10  
SELECT * FROM articles WHERE  id >=
 (SELECT id FROM articles  WHERE category_id = 123 ORDER BY id LIMIT 10000, 1) LIMIT 10

 

 

2.3JOIN分页方式

 

  1. SELECT * FROM `content` AS t1    
  2. JOIN (SELECT id FROM `content` ORDER BY id desc LIMIT “.($page-1)*$pagesize.”, 1) AS t2    
  3. WHERE t1.id <= t2.id ORDER BY t1.id desc LIMIT $pagesize;   
SELECT * FROM `content` AS t1 
JOIN (SELECT id FROM `content` ORDER BY id desc LIMIT ".($page-1)*$pagesize.", 1) AS t2 
WHERE t1.id <= t2.id ORDER BY t1.id desc LIMIT $pagesize;

 

经过我的测试,join分页和子查询分页的效率基本在一个等级上,消耗的时间也基本一致。

explain SQL语句:

id select_type table type possible_keys key key_len ref rows Extra

1 PRIMARY <derived2> system NULL NULL NULL NULL 1

1 PRIMARY t1 range PRIMARY PRIMARY 4 NULL 6264 Using where

2 DERIVED content index NULL PRIMARY 4 NULL 27085 Using index

 

—————————————-

 

为什么会这样呢?因为子查询是在索引上完成的,而普通的查询时在数据文件上完成的,通常来说,索引文件要比数据文件小得多,所以操作起来也会更有效率。

 

实际可以利用类似策略模式的方式去处理分页,比如判断如果是一百页以内,就使用最基本的分页方式,大于一百页,则使用子查询的分页方式。

【引用原文,energy1010的空间:MySql分页sql语句

 

 

3、Oracle分页查询语句

Oralce数据库

从数据库表中第M条记录开始检索N条记录

  1. SELECT * FROM (SELECT ROWNUM r,t1.* From 表名称 t1 where rownum < M + N) t2    
  2.  where t2.r >= M   
SELECT * FROM (SELECT ROWNUM r,t1.* From 表名称 t1 where rownum < M + N) t2 
 where t2.r >= M

例如从表Sys_option(主键为sys_id)中从第10条记录开始检索20条记录,语句如下:

  1. SELECT * FROM (SELECT ROWNUM R,t1.* From Sys_option where rownum < 30 ) t2    
  2. Where t2.R >= 10   
SELECT * FROM (SELECT ROWNUM R,t1.* From Sys_option where rownum < 30 ) t2 
Where t2.R >= 10 

 

3、MSSQLServer分页查询语句

 

SQL Server主要利用 SELECT TOP语句分页,具体方案,请参考

 

————————————-

 

分页方案一:(利用Not In和SELECT TOP分页)

语句形式:

 

  1. SELECT TOP 10 *    
  2. FROM TestTable    
  3. WHERE (ID NOT IN    
  4. (SELECT TOP 20 id    
  5. FROM TestTable    
  6. ORDER BY id))    
  7. ORDER BY ID   
SELECT TOP 10 * 
FROM TestTable 
WHERE (ID NOT IN 
(SELECT TOP 20 id 
FROM TestTable 
ORDER BY id)) 
ORDER BY ID

 

  1. SELECT TOP 页大小 *    
  2. FROM TestTable    
  3. WHERE (ID NOT IN    
  4. (SELECT TOP 页大小*页数 id    
  5. FROM 表    
  6. ORDER BY id))    
  7. ORDER BY ID    
  8. SELECT TOP 页大小 *   
SELECT TOP 页大小 * 
FROM TestTable 
WHERE (ID NOT IN 
(SELECT TOP 页大小*页数 id 
FROM 表 
ORDER BY id)) 
ORDER BY ID 
SELECT TOP 页大小 *
  1. FROM TestTable    
  2. WHERE (ID >    
  3. (SELECT MAX(id)    
  4. FROM (SELECT TOP 页大小*页数 id    
  5. FROM 表    
  6. ORDER BY id) AS T))    
  7. ORDER BY ID   
FROM TestTable 
WHERE (ID > 
(SELECT MAX(id) 
FROM (SELECT TOP 页大小*页数 id 
FROM 表 
ORDER BY id) AS T)) 
ORDER BY ID

 

————————————-

 

分页方案二:(利用ID大于多少和SELECT TOP分页)

语句形式:

  1. SELECT TOP 10 *    
  2. FROM TestTable    
  3. WHERE (ID >    
  4. (SELECT MAX(id)    
  5. FROM (SELECT TOP 20 id    
  6. FROM TestTable    
  7. ORDER BY id) AS T))    
  8. ORDER BY ID   
SELECT TOP 10 * 
FROM TestTable 
WHERE (ID > 
(SELECT MAX(id) 
FROM (SELECT TOP 20 id 
FROM TestTable 
ORDER BY id) AS T)) 
ORDER BY ID

 

————————————-

分页方案三:(利用SQL的游标存储过程分页)

 

  1. create procedure XiaoZhengGe    
  2. @sqlstr nvarchar(4000), –查询字符串    
  3. @currentpage int, –第N页    
  4. @pagesize int –每页行数    
  5. as    
  6. set nocount on    
  7. declare @P1 int, –P1是游标的id    
  8. @rowcount int    
  9. exec sp_cursoropen @P1 output,@sqlstr,@scrollopt=1,@ccopt=1,@rowcount=@rowcount output    
  10. select ceiling(1.0*@rowcount/@pagesize) as 总页数–,@rowcount as 总行数,@currentpage as 当前页    
  11. set @currentpage=(@currentpage-1)*@pagesize+1    
  12. exec sp_cursorfetch @P1,16,@currentpage,@pagesize    
  13. exec sp_cursorclose @P1    
  14. set nocount off   
create procedure XiaoZhengGe 
@sqlstr nvarchar(4000), --查询字符串 
@currentpage int, --第N页 
@pagesize int --每页行数 
as 
set nocount on 
declare @P1 int, --P1是游标的id 
@rowcount int 
exec sp_cursoropen @P1 output,@sqlstr,@scrollopt=1,@ccopt=1,@rowcount=@rowcount output 
select ceiling(1.0*@rowcount/@pagesize) as 总页数--,@rowcount as 总行数,@currentpage as 当前页 
set @currentpage=(@currentpage-1)*@pagesize+1 
exec sp_cursorfetch @P1,16,@currentpage,@pagesize 
exec sp_cursorclose @P1 
set nocount off

 

其它的方案:如果没有主键,可以用临时表,也可以用方案三做,但是效率会低。

建议优化的时候,加上主键和索引,查询效率会提高。

 

通过SQL 查询分析器,显示比较:我的结论是:

分页方案二:(利用ID大于多少和SELECT TOP分页)效率最高,需要拼接SQL语句

分页方案一:(利用Not In和SELECT TOP分页) 效率次之,需要拼接SQL语句

分页方案三:(利用SQL的游标存储过程分页) 效率最差,但是最为通用

 

在实际情况中,要具体分析。

mysql select into的用法_zwldx的博客-CSDN博客

mikel阅读(1052)

来源: mysql select into的用法_zwldx的博客-CSDN博客

MySQL不支持Select Into语句直接备份表结构和数据,一些种方法可以代替,如下:

#MYSQL不支持:
Select * Into new_table_name from old_table_name; 这是sql server中的用法
#替代方法:
Create table new_table_name (Select * from old_table_name);

————————————————
版权声明:本文为CSDN博主「zwldx」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zwldx/article/details/82227533

Lock wait timeout exceeded; try restarting transaction_萌神30号 ↝-CSDN博客

mikel阅读(891)

来源: Lock wait timeout exceeded; try restarting transaction_萌神30号 ↝-CSDN博客

目录

一、问题抛出

二、解决方案

三、三张表字段说明

innodb_trx

innodb_locks

innodb_lock_waits

四、终极方法

小结


一、问题抛出

在做查询语句时,MySQL 抛出了这样的异常:

  1. MySQL server error report:Array
  2. (
  3. [0] => Array
  4. (
  5. [message] => MySQL Query Error
  6. )
  7. [1] => Array
  8. (
  9. => SELECT * FROM taobao_trade WHERE order_status = 1 and orderID =‘2018061812306547’ AND is_tran_success=0 for update
  10. )
  11. [2] => Array
  12. (
  13. [error] => Lock wait timeout exceeded; try restarting transaction
  14. )
  15. [3] => Array
  16. (
  17. [errno] => 1205
  18. )
  19. )

Lock wait timeout exceeded; try restarting transaction的异常,错误提示的意思,很明显,是因为这条语句被锁住了,所以释放这个锁。

二、解决方案

我们可以通过到information_schema 中来进行查找被锁的语句。

解释: information_schema这张数据表保存了MySQL服务器所有数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权限等。再简单点,这台MySQL服务器上,到底有哪些数据库、各个数据库有哪些表,每张表的字段类型是什么,各个数据库要什么权限才能访问,等等信息都保存在information_schema表里面。

我们可以用下面三张表来查原因:

  • innodb_trx 当前运行的所有事务
  • innodb_locks 当前出现的锁
  • innodb_lock_waits 锁等待的对应关系

如果数据库中有锁的话,我们可以使用这条语句来查看:

select * from information_schema.innodb_trx

clipboard.png

图中红色语句 LOCK WAIT为占用系统资源的语句,我们需要杀掉这个锁,执行 kill 线程id号。上面这条记录的id为199120823069, trx_mysql_thread_id 为 738178711, 所以我们执行:kill 738178711杀掉这个MySQL语句的线程即可。

执行之后:

  1. kill 738178711
  2. // 查询线程
  3. // SELECT * from information_schema.processlist WHERE id = 738178711;
  4. // show full processlist;

clipboard.png

其他的记录不需要关注,因为其他的记录状态为“RUNNING” 即正在执行的事务,并没有锁。

三、三张表字段说明

innodb_trx

desc information_schema.innodb_trx;

clipboard.png

innodb_locks

desc information_schema.innodb_locks;

clipboard.png

innodb_lock_waits

desc information_schema.innodb_lock_waits

clipboard.png

四、终极方法

如果以上方法杀掉线程,但还是不能解决,则我们就可以查找执行线程用时比较久的用户,然后直接干掉。

  1. SELECT * from information_schema.`PROCESSLIST` WHERE Time > 1000 AND USER = ‘wonguser’ ORDER BY TIME desc;
  2. kill 740097562

这样把所有耗时比较久的任务干掉,就可以解决这个问题了。

小结

关于我的那个问题,我通过这个方法 select * from information_schema.innodb_trx 已经杀掉了线程,但通过表直接修改那个id对应的数据,还是会弹出Lock wait timeout exceeded; try restarting transaction这样的异常,在网上找了许多未找出具体的解决方法,后来自己灵光一现,可以找出那些好事比较久的线程,然后把那些可疑的线程杀掉,没想到这个问题就解决了,可以正常对这行数据进行操作了。

[转]MySQL select into,SQLServer select into - 阿彬 - 博客园

mikel阅读(870)

来源: [转]MySQL select into,SQLServer select into – 阿彬 – 博客园

以下的文章主要介绍的是MySQL select into 与 SQLServer select  into,我们大家都知道MySQL数据库是对Select Into 语句的直接备份表的结构与相关实际应用数据是不支持的,由于工作中的需要在网上找到一种方法可以代替, 也有其它方法可以处理,总结如下:

方法1:

MYSQL不支持:

  1. Select * Into new_table_name from old_table_name

替代方法:

  1. Create table new_table_name (Select * from old_table_name);

方法2:

1.先备份表结构和数据

导出命令 -u用户名 -p密码 -h主机IP地址 数据库名 表名1 > 导出文件.SQL

  1. mysqldump -uroot -proot -h192.168.0.88 ok_db oktable2 > ok_db.sql

2.修改备份表的名字

3.登录MySQL

4.选择数据库

5.执行: Source 备份表的路径 如:Source d:\ok_db.sql 回车即可。

6.完成.

SQLServer支持 MySQL Select into语句

1.备份表直接执行如下就可以了。

  1. Select * Into new_table_name from old_table_name;

MySQL Select into outfile用于导出指定的查询数据到文件如下:

1.导出表中所有数据到C盘根目录outfile.txt中如下:

  1. Select * into outfile ‘c:\\outfile.txt’ from test;

2.导出表中指定查询条件2005-06-08号的数据到C盘根目录outfile1.txt中如下:

  1. Select * into outfile ‘c:\\outfile.txt’ from test where beginDate=‘2008-06-08’;

mysql update from 子查询_ni_hao_ya的专栏-CSDN博客

mikel阅读(1094)

来源: mysql update from 子查询_ni_hao_ya的专栏-CSDN博客

这个破问题纠结了我好久,查了mySQL的好多资料,最后还是在别人那里得到结果,特此记录一下,留个备份吧!

msSQL 子查询更新

update log set uin= b.uin
from log a,logs b
where a.accountuin = b.accountuin

mySQL 不支持 update 子查询更新

找了半天资料 终于搞定了…

update `log` a inner join `logs` b on a.`accountuin` = b.`accountuin`
set a.`uin` = b.`uin` where a.`accountuin` = b.`accountuin`

这样就能 将查询出来的字段批量更新过去了…

tp 中save保存,数据没修改,保存失败的处理_一份耕耘 一份收货-CSDN博客

mikel阅读(652)

来源: tp 中save保存,数据没修改,保存失败的处理_一份耕耘 一份收货-CSDN博客

$model->save();
if($model->geterror!=”){//判断数据执行有错误;查看tp save流程
$this->error(‘修改失败’);
return false;
}
$model->geterror() 不为空的情况,就是数据保存失败情况,其中不包含数据未修改的情况;
—————————————————————————————-
亲测 ; $model->getError(); 如果SQL语句有问题,也不会报错的;
$model->getDbError(); 如果SQL语句有错误 ,会报错的;
save() 执行失败没有返回值;
执行成功: 有修改,返回1
没修改 ,返回0
参考:
$flag=$match->where(‘id=’.$id)->save($data);
//echo $match->getDbError();
if ($flag ||($flag === 0)){ //如果save执行错误返回空,==也是相等的;要===才能区别开来;
//$id=base64_encode($id);
$json->setErr(0,’添加完成’);//添加成功
$json->Send();
}else{
$json->setErr(10001,’添加失败’);
$json->Send();

————————————————
版权声明:本文为CSDN博主「qq_34242581」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34242581/article/details/52042505