sqlserver中的锁
共享锁(s)/更新锁(U)/排它锁(独占锁X)
共享 (S) 锁允许并发事务读取 (SELECT) 一个资源。资源上存在共享 (S) 锁时,任何其它事务都不能修改数据。
更新 (U) 锁可以防止通常形式的死锁。若要避免这种潜在的死锁问题,请使用更新 (U) 锁。一次只有一个事务可以获得资源的更新 (U) 锁。
如果事务修改资源,则更新 (U) 锁转换为排它 (X) 锁。否则,锁转换为共享锁。
排它 (X) 用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。
排它 (X) 锁可以防止并发事务对资源进行访问。其它事务不能读取或修改排它 (X) 锁锁定的数据
增删改查使用:
SELECT 语句在默认隔离级别下会产生共享锁,该锁查询完就释放,不需要等待所在的事务提交。
UPDATE 语句先使用更新锁锁定数据,然后升级成独占锁(事务级别,非命令级别)
INSERT,UPDATE,DELETE 语句使用独占锁
UPDLOCK.UPDLOCK 的优点是允许您读取数据(不阻塞其它事务)并在以后更新数据,同时确保自从上次读取数据后数据没有被更改。
当我们用UPDLOCK来读取记录时可以对取到的记录加上更新锁,从而加上锁的记录在其它的线程中是不能更改的只能等本线程的事务结束后才能更改.
测试:
在另一个查询里:
updlock/TABLOCKX(如果添加transaction则是事务级别,非命令级别)(Shared Lock是命令级别,即使添加transaction)
BEGIN TRANSACTION
SELECT * FROM myTable WITH (UPDLOCK) WHERE Id in (1,2,3)
waitfor delay '00:00:10'
update myTable set [Name]='ZZ' where Id in (1,2,3)
commit TRANSACTION
在另一个查询里:
SELECT * FROM myTable WHERE Id in (1,2,3)
可以马上查询到数据。
但如果要更新数据,必须等其他更新锁释放后才能执行。
update myTable set [Name]='ZZ' where Id in (1,2,3)
这就说明,有时候需要控制某条记录在我读取后就不许再进行更新,那么我就可以将所有要处理当前记录的查询都加上更新锁,以防止查询后被其它事务修改.将事务的影响降低到最小
select * from dbo.Employee with(holdlock); with关键字来设置锁表的方式。
NOLOCK(不加锁)
NOLOCK 语句执行时不发出共享锁,允许脏读 ,等于 READ UNCOMMITTED事务隔离级别
UPDLOCK(修改锁 IX)此选项被选中时,SQL Server 在读取数据时使用修改锁来代替共享锁,并将此锁保持至整个事务(事务级别,非命令级别)或命令结束。使用此选项能够保证多个进程能同时读取数据但只有该进程能修改数据。
HOLDLOCK 持有共享锁,直到整个事务完成,应该在被锁对象不需要时立即释放,等于SERIALIZABLE事务隔离级别
HOLDLOCK(保持锁 S)此选项被选中时,SQL Server 会将此共享锁保持至整个事务结束,而不会在途中释放。
TABLOCK(表锁 S)此选项被选中时,SQL Server 将在整个表上置共享锁直至该命令结束。 这个选项保证其他进程只能读取而不能修改数据。
TABLOCKX(排它表锁 X)此选项被选中时,SQL Server 将在整个表上置排它锁直至该命令或事务结束。这将防止其他进程读取或修改表中的数据。
ROWLOCK(IS)此选项被选中时,SQL Server 在读取数据时使用修改锁来代替共享锁,并将此锁保持至整个事务或命令结束。使用此选项能够保证多个进程能同时读取数据但只有该进程能修改数据。
PAGLOCK(页锁 IS)此选项为默认选项, 当被选中时,SQL Server 使用共享页锁。
READPAST 让sql server跳过任何锁定行,执行事务,适用于READ UNCOMMITTED事务隔离级别只跳过RID锁,不跳过页,区域和表锁
ROWLOCK 强制使用行锁
行锁使用注意事项
1、ROWLOCK行级锁确保在用户取得被更新的行,到该行进行更新,这段时间内不被其它用户所修改
因而行级锁即可保证数据的一致性,又能提高数据操作的并发性。
2、ROWLOCK告诉SQL Server只使用行级锁,ROWLOCK语法可以使用在SELECT,UPDATE和DELETE语句中。
3、ROWLOCK指定通常采用页锁或表锁时,采用行锁。 在从 SNAPSHOT 隔离级别操作的事务中指定时,
除非将 ROWLOCK 与需要锁的其他表提示(例如,UPDLOCK 和 HOLDLOCK)组合,否则不会取得行锁。
4、UPDLOCK指定采用更新锁并保持到事务完成。UPDLOCK 仅对行级别或页级别的读操作采用更新锁。
如果将 UPDLOCK 与 TABLOCK 组合使用或出于一些其他原因采用表级锁,将采用排他 (X) 锁。
指定 UPDLOCK 时,忽略 READCOMMITTED 和 READCOMMITTEDLOCK 隔离级别提示。
例如,如果将会话的隔离级别设置为 SERIALIZABLE 且查询指定 (UPDLOCK, READCOMMITTED),则忽略 READCOMMITTED 提示且使用 SERIALIZABLE 隔离级别运行事务。
5、HOLDLOCK等同于SERIALIZABLE。HOLDLOCK 仅应用于那些为其指定了 HOLDLOCK 的表或视图,
并且仅在使用了 HOLDLOCK 的语句定义的事务的持续时间内应用。 HOLDLOCK 不能被用于包含 FOR BROWSE 选项的 SELECT 语句。
ROWLOCK告诉 SQL Server只使用行级锁。ROWLOCK语法可以使用在SELECT, UPDATE和DELETE语句中,不过 我习惯仅仅在UPDATE和DELETE语句中使用。
如果在UPDATE语句中有指定的主键,那么就总是会引发行级锁的。但是当SQL Server对几个这种UPDATE进行批处理时,某些数据正好在同一个页面(page),
这种情况在当前情况下 是很有可能发生的,这就象在一个folder中,创建一个新文件需要较长的时间,而同时你又要去更新该folder中的某些文件。当页面锁引发后,
事情就开始变得糟糕了。而如果在UPDATE或者DELETE 时,没有指定主键,数据库当然认为很多数据会收到影响,那样 就会直接引发页面锁,事情同样变得糟糕
通过指定使用行级锁,这种情况可以得到避免。但是需要小心的是,如果你错误地使用在过多行上,数据库并不会聪明到自动将行级锁升级到页面锁,服务器也会因为行级锁的开销而消耗大量的内存和CPU,直至无法响应。尤其主要留意的是 企业管理器中"管理/当前活动"(Management /Current Activity)这一项。该项会花较长的时间来载入锁的信息。这些信息是十分有用的,当你使用行级锁后,你如果在"锁/处理" (Locks/Processes)下看到几百个锁,一点都不奇怪,而恰恰应该庆幸锁超时和死锁的问题减少了。
锁的分类
1.按锁定的对象来分
有表锁、行锁、数据库锁等,表锁定是对整个表进行锁定,行锁定是对表中特定行进行锁定,数据库锁是锁定整个数据库。
2.从数据库系统角度分为三种:
排他锁、共享锁、更新锁。
3.从程序员角度分为两种:
一种是悲观锁,一种乐观锁
悲观锁(Pessimistic Lock)
传统的关系数据库里用到了很多这种锁机制,比如行锁、表锁、读锁、写锁等,都是在操作之前先上锁。
共享锁(Share Lock)
S锁,也叫读锁,用于所有的只读数据操作。共享锁是非独占的,允许多个并发事务读取其锁定的资源。
性质
1. 多个事务可封锁同一个共享页;
2. 任何事务都不能修改该页;
3. 通常是该页被读取完毕,S锁立即被释放。
在SQL Server中,默认情况下,数据被读取后,立即释放共享锁。
例如,执行查询语句“SELECT * FROM my_table”时,首先锁定第一页,读取之后,释放对第一页的锁定,然后锁定第二页。这样,就允许在读操作过程中,修改未被锁定的第一页。
例如,语句“SELECT * FROM my_table HOLDLOCK”就要求在整个查询过程中,保持对表的锁定,直到查询完成才释放锁定。
排他锁(Exclusive Lock)
X锁,也叫写锁,表示对数据进行写操作。如果一个事务对对象加了排他锁,其他事务就不能再给它加任何锁了。(某个顾客把试衣间从里面反锁了,其他顾客想要使用这个试衣间,就只有等待锁从里面打开了。)
性质
1. 仅允许一个事务封锁此页;
2. 其他任何事务必须等到X锁被释放才能对该页进行访问;
3. X锁一直到事务结束才能被释放。
产生排他锁的SQL语句如下:select * from ad_plan for update;
更新锁
U锁,在修改操作的初始化阶段用来锁定可能要被修改的资源,这样可以避免使用共享锁造成的死锁现象。
因为当使用共享锁时,修改数据的操作分为两步:
1. 首先获得一个共享锁,读取数据,
2. 然后将共享锁升级为排他锁,再执行修改操作。
这样如果有两个或多个事务同时对一个事务申请了共享锁,在修改数据时,这些事务都要将共享锁升级为排他锁。这时,这些事务都不会释放共享锁,而是一直等待对方释放,这样就造成了死锁。
如果一个数据在修改前直接申请更新锁,在数据修改时再升级为排他锁,就可以避免死锁。
性质
1. 用来预定要对此页施加X锁,它允许其他事务读,但不允许再施加U锁或X锁;
2. 当被读取的页要被更新时,则升级为X锁;
3. U锁判断不符合条件就释放,符合就转为X锁。
悲观锁按作用范围划分为:行锁、表锁。(这部分摘自https://www.jianshu.com/p/eb41df600775 )
行锁
锁的作用范围是行级别。
表锁
锁的作用范围是整张表。
数据库能够确定那些行需要锁的情况下使用行锁,如果不知道会影响哪些行的时候就会使用表锁。
举个例子,一个用户表user,有主键id和用户生日birthday。
当你使用update … where id=?这样的语句时,数据库明确知道会影响哪一行,它就会使用行锁;
当你使用update … where birthday=?这样的的语句时,因为事先不知道会影响哪些行就可能会使用表锁。
sqlserver中的隔离级别
SQL Server
的四种隔离级别知识点整理,特别制作了流程图,⽅便以后查看!
SET TRANSACTION ISOLATION LEVEL
{
READ UNCOMMITTED
| READ COMMITTED
| REPEATABLE READ 相当于(HOLDLOCK)):
| SERIALIZABLE 它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。
}
未提交读READ UNCOMMITTED(脏读)相当于with(nolock)
演示:
1)用户B:BEGIN TRAN
UPDATE test SET age=25 WHERE name = ‘AA’
2)用户A:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED(此句不写即默认为READ COMMITTED模式)
SELECT * FROM test(此时将查到AA的age值为25)
3)用户B: ROLLBACK
(此时撤消了步骤1的UPDATE操作,则⽤户A读到的错误数据被称为脏读)
提交读(READ COMMITTED)
意义:指定在读取数据时控制共享锁以避免脏读。此隔离等级的主要作⽤是避免脏读。
1)用户B:BEGIN TRAN
UPDATE test SET age=25 WHERE name = ‘AA’
2)用户A: SET TRANSACTION ISOLATION LEVEL READ COMMITTED
SELECT * FROM test (上句设置了提交读模式,则此时将会查不到数据,显示查询等待中,直到用户B进行了ROLLBACK或者COMMIT操作后,此语句才会生效)
三、不⼀致的分析
REPEATABLE READ(重复读)
意义:在多用户环境下,用户A开了一个事务,并且先对test表的某条记录做了查询(select * from test where name = ‘AA’),
接着用户B对test表做了更新并提交(update test set age=25 where name=’AA’)
,这时A再去查test表中的这条记录,第一次读到的age值为12,第二次为25,两次读到的数据不一样,称之为重复读。(如图演⽰)
SERIALIZABLE事务隔离级别
排它锁不受隔离级别控制,共享锁受隔离级别控制
结论如下:
在任何隔离级别下,事务在执行写操作时都申请排它锁(exclusive lock),持有排它锁直到事务结束,排它锁不受隔离级别的控制;
而共享锁(Shared Lock)受到隔离级别的控制,隔离级别影响Shared Lock的申请和释放:
在 Read Uncommitted隔离级别下,读操作不会申请Shared Lock;
在 Read Committed(不使用row-versioning),Repeatable Read 和 Serializable隔离级别下,都会申请Shared Lock;
在 Read Committed(不使用row-versioning) 隔离级别下,在读操作执行时,申请和持有Share Lock;一旦读操作完成,释放Shared Lock;
在 Repeatable Read 和 Serializable隔离级别下,事务会持有Shared Lock,直到事务结束(提交或回滚);
在Serializable隔离级别下,事务会持有范围Shared Lock(Range Lock),锁定一个范围,在事务活跃期间,其他事务不允许在该范围中进行更新(Insert 或 delete)操作;
SQLServer默认隔离级别: Read Committed.
更新锁和更新锁互斥。共享锁和和共享锁可以共享。共享锁和和更新锁可以共享。排它锁独占。
SQLServer隔离级别只会影响共享锁。
事务(可以是数据库事务,可以是程序事务)在执行写操作时都申请排它 (X) 锁(exclusive lock),持有排它锁直到事务结束,排它锁不受隔离级别的控制;
事务的分类 (分类)
事务分为三类:显式事务、隐式事务、自动提交事务
(1) 显式事务:用 begin transaction 明确指定事务的开始,由 commit transaction 提交事务、rollback transaction 回滚事务到事务结束。
(2) 隐式事务:通过设置 set implicit_transactions on 语句,将隐式事务模式设置为打开。
当以隐式事务模式操作时,不必使用 begin transaction 开启事务,当一个事务结束后,这个模式会自动启用下一个事务,
只需使用 commit transaction 提交事务或 Rollback Transaction 回滚事务即可。
(3) 自动提交事务: 这是 SQL Server 的默认模式,它将每条单独的 T-SQL 语句视为一个事务。如果成功执行,则自动提交。如果错误,则自动回滚。
如何防止死锁
表设计:
1 尽可能使用分区表,把数据放到不同的文件中,尽可能以分区表或分区视图的方式拆分包含大量数据的表,
将它们保存在不同的物量磁盘和文件组中。在访问数据时,可以分散访问保存在不同分区的数据,从而减少因为在大型表中放置锁而造成其它事务长时间等待的概率
2横向分割表设计,将表按照某种原则(可按照字段读写频率来设计)设计成相对应的几个表,之间采用主(外)键关联;
3 使用较低的隔离级别,尽可能地为用户连接指定具有最少限制的事务隔离级别,而不是总是使用默认的READ COMMITTED
对一些数据不需要及时读取更新值的表在写SQL的时候在表后台加上(nolock),如:Select * from tableA(nolock)
4 对于频繁使用的表使用集簇化的索引;
5 减少数据库并发量,死锁是由于并发访问数据库资源造成的,减少死锁就应该限制应用系统的并发访问量。我们应该合理设置后台服务的线程数,
update/delete/insert
1 将大量数据的操作分解,分步骤,分阶段的执行。也应该避免在用户量大的时候大规模的进行后台数据库操作,应该将大规模的数据库操作放在用户量最少的时候进行。
2设法让UPDATE和DELETE语句使用索引;
select
1 select 会获取共享锁,进而会让排它锁等待,所以查询时要添加where条件或者top N限制
事务:
1 按照同一顺序访问数据库资源,(A 先访问tb1,再访问tb2,B先访问tb1,再访问tb2)
2 保持事务时间短,如果可能的话,应将较复杂的事务分割成多个较简单的事务
3 所有条件逻辑、变量赋值以及其他相关的预备设置操作应当在事务之外完成,而不应该放到事务之内。永远不要为了接受用户输入而暂停某个事务,用户输入应当总是在事务之外完成
4 能够在死锁出现并中止用户事务之后,以随机的时间间隔自动重新提交事务。这里等待时间的随机性非常重要,这是因为另一个竞争的事务也可能在等待,
我们不应该让两个竞争的事务等待同样的时间,然后再在同一时间执行它们,这样的话将导致新的死锁。
10 如果客户程序需要先用一定的时间检查数据,然后可能更新数据,也可能不更新数据,那么最好不要在整个记录检查期间都锁定记录。
假设大部分时间都是检查数据而不是更新数据,那么处理这种特殊情况的一种方法就是:先选择出记录(不加UPDATE子句。
UPDATE子句将在记录上加上共享锁),然后把它发送给客户。
如果用户只查看记录但从来不更新它,程序可以什么也不做;反过来,如果用户决定更新某个记录,
那么他可以通过一个WHERE子句检查当前的数据是否和以前提取的数据相同,然后执行UPDATE。
类似地,我们还可以检查记录中的时间标识列(如果它存在的话)。如果数据相同,则执行UPDATE操作;如果记录已经改变,则应用应该提示用户以便用户决定如何处理。
虽然这种方法需要编写更多的代码,但它能够减少加锁时间和次数,提高应用的整体性能。
11 使用游标会降低并发性。为避免这一点,如果可以使用只读的游标则应该使用READ_ONLY游标选项,否则如果需要进行更新,
尝试使用OPTIMISTIC游标选项以减少加锁。设法避免使用SCROLL_LOCKS游标选项,该选项会增加由于记录锁定引起的问题。
12使用sp_who和sp_who2来确定可能是哪些用户阻塞了其他用户
9. 数据设计时,尽量避免 update/delete. 举例来说,如果是一个请假条的审批流程,
把请假条申请设计成一个表,领导批复设计成另一个表。尽量避免设计时合并成一个表,
把批准状态(同意/否决)、批准时间当成“请假条申请”的属性。
说极端一点,最好从数据库设计上,避免后续编程有 update/delete, 只有 insert。 好像现在流行的 NoSQL 也是这么个思路。
7 设置锁超时时间,一但超时,阻塞语句自动取消,释放资源,报1222错误,但当天事务报错,不会回滚,会造成数据错误,要手工处理
查看超时时间
select @@lock_timeout (-1)
set @@lock_timeout=0
select @@TRANCOUNT
@@error代表最近的一个T-SQL语句的报错号
select @@MAX_CONNECTIONS
并发数
查看锁活动情况
select * from sys.dm_tran_locks
select object_name(resource_associated_entity_id),* from sys.dm_tran_locks
where resource_type='object' and object_name(resource_associated_entity_id)='test222'
查看事务活动情况
dbcc opentran
10亿表如何更新500W数据,一次只能更新5000
1 run in un business time
2 分批次跑,一次跑5000行左右,每次commit,要添加try catch ,try 中加transaction/commit catch中加rollcack
3 对表用行级锁
4可以建个临时表,表中有ID和要更新列,然后用临时表update target表
聚集索引和非聚集索引区别
SQL SERVER中索引类型包括的三种类型分别是
唯一索引(UNIQUE),聚集索引(CLUSTERED) ,非聚集索引(NONCLUSTERED)
主键与唯一索引的区别
主键是一种约束,唯一索引是一种索引,两者在本质上是不同的。 主键创建后一定包含一个唯一性索引,唯一性索引并不一定就是主键。 唯一性索引列允许空值,而主键列不允许为空值。 主键列在创建时,已经默认为空值 + 唯一索引了。
对一个大表(5亿)添加两列数据
phone char(11) 手机号 (如果用alter方式的话里面也是存储的NULL,建表也很快,但是char会占用空间,即使是NULL值也占用空间 )
这种方式可以再单独设计一个表,然后里面存储phone char(11),然后两张表中存共同的ID号
address nvarchar(100) 可以用alter table add column address nvarchar(100),这种方式不会对历史数据行赋值(全部为null)
执行计划怎么看
表扫描方式(全表,索引)
join方式
是否用了索引
1、越粗表示扫描影响的行数愈多。
2、Actual Number of Rows 扫描中实际影响的的行数。
3、Estimated Number of Rows 预估扫描影响的行数。
4、Estimated row size 操作符生成的行的估计大小(字节)。
5、Estimated Data Size 预估影响的数据的大小。
char/varchar/nvarchar区别
Hint主要分为三类应用:查询Hint、表Hint、连接Hint
查询Hint
{ HASH | ORDER } GROUP
HASH GROUP
HASH JOIN也好,HASH GROUP也好,HASH都是我认为可能还存在使用场景的。因为从过往经验看,
优化器容易选择错误(造成性能大幅下降)的还是排序或者循环,这个对资源消耗是最大的,那么HASH对于查询是有大表参与的情况下是相对比较友好的。
TABLE HINT(table,INDEX(索引名), FORCESEEK)
指定查询使用某个表索引,FORCESEEK要求使用检索而不是表扫描
HOLDLOCK|NOLOCK|READPAST|ROWLOCK|TABLOCK|XLOCK
这几个可能是和锁有关的几个提示可能会用到的。HOLDLOCK主要是用到存储过程事务里面,先对表加锁后一直到事务提交,
SELECT查询用到。NOLOCK再常见不过。像生产环境查询表数据通常加NOLOCK避免造成堵塞影响生产上的查询,另外一个就是ETL抽数据也是加NOLOCK。
REAPAST的使用场景是对于超高并发下更新数据,跳过已经加了锁的行而取下一行。这个就是电商的秒杀场景或者取号场景。ROWLOCK也是针对高并发更新数据场景,
对行级别加锁而避免SQL SERVER对表级别加锁。TABLOCK就是相反,加表锁是当前事务会在多处对数据进行更新,避免更新完后后面有其他的并发事务更新了表的行
(如果不加表锁,前面的数据更新是采用页锁或者行锁,但是这样子会影响当前存储过程事务在后面对表的二次更新的数据结果,就需要加表锁。)。
TABLOCK另外一个变体是TABLOCKX,这个是直接排他,就是其他连接连表数据都不能查询,直接堵塞其他对表的查询请求。XLOCK是最高级别的锁,
直接排他,指定XLOCK要同时指定ROWLOCK, PAGLOCK 或 TABLOCK。
FROM t WITH (TABLOCK, INDEX(myindex))
连接Hint: merge/hash/nest loop/
table with(nolock)
index hint
表变量和临时表
临时表
它存储在Tempdb中,显示删除(DROP)或连接断开时数据才会被删除。
本地临时表:以#为前缀,只对本地当前的用户连接可见,连接断开时被删除。
全局临时表:以##为前缀,对所有用户可见,当所有引用该表的用户断开连接时被删除。
表变量
表变量在批处理结束后自动被删除
本地表变量:以@为前缀
全局表变量:以@@为前缀,一般都是系统的全局变量,如:@@Error、@@RowCount
表变量和临时表的区别
表变量是存储在内存中,不产生日志
表变量不允许有非聚集索引
表变量是不允许有默认值,也不允许有约束
临时表中是有锁的机制,表变量没有
表变量存放在内存中,正是因为这一点所有用户访问表变量的时候SQL Server是不需要生成日志。
表变量创建的语法类似于临时表,区别就在于创建的时候,必须要为之命名。表变量是变量的一种,
表变量也分为本地及全局的两种,本地表变量的名称都是以“@”为前缀,只有在本地当前的用户连接中才可以访问。全局的表变量的名称都是以“@@”为前缀,
一般都是系统的全局变量,像我们常用到的,如@@Error代表错误的号,@@RowCount代表影响的行数。
表变量的行为类似于局部变量,有明确定义的作用域。该作用域为声明该变量的函数、存储过程或批处理。
在其作用域内,表变量可像常规表那样使用。该变量可应用于 SELECT、INSERT、UPDATE 和 DELETE 语句中用到表或表的表达式的地方。但是,表不能用在下列语句中:
INSERT INTO table_variable EXEC 存储过程。
SELECT select_list INTO table_variable 语句。
在定义表变量的函数、存储过程或批处理结束时,自动清除表变量。
表类型声明中的 CHECK 约束、DEFAULT 值和计算列不能调用用户定义函数。
在存储过程中使用表变量与使用临时表相比,减少了存储过程的重新编译量。
涉及表变量的事务只在表变量更新期间存在。这样就减少了表变量对锁定和记录资源的需求。
不支持在表变量之间进行赋值操作。
declare @t1 table(t1 int)
declare @t2 table(t2 int)
set @t1=@t2 –错误
另外,由于表变量作用域有限,并且不是持久数据库的一部分,因而不受事务回滚的影响。
临时表:
临时表与永久表相似,只是它的创建是在Tempdb中,它只有在一个数据库连接结束后或者由SQL命令DROP掉,才会消失,否则就会一直存在。临时表在创建的时候都会产生SQL Server的系统日志,虽它们在Tempdb中体现,是分配在内存中的,它们也支持物理的磁盘,但用户在指定的磁盘里看不到文件。
临时表分为本地和全局两种,本地临时表的名称都是以“#”为前缀,只有在本地当前的用户连接中才是可见的,当用户从实例断开连接时被删除。全局临时表的名称都是以“##”为前缀,创建后对任何用户都是可见的,当所有引用该表的用户断开连接时被删除
用法:
表变量:无表关联操作,只作为中间集进行数据处理;临时表:有表关联,且不能确定数据量大小的情况下。理论如此,真正用法要按照实际项目需求而定。
如何分析定位问题
create table test222
(id int,
name varchar(10)
,phone char(11))
drop table test222
create table test222
(id int,
name varchar(10)
)
insert into test222 values(1,'11')
insert into test222 values(2,'22')
insert into test222 values(3,'33')
select * ,len(phone) from test222
alter table test222 add phone char(11)
alter table test222 add address nvarchar(100)
关于数据库表的主键设计,一般而言,是根据业务需求情况,以业务逻辑为基础,形成主键。
比如,销售时要记录销售情况,一般需要两个表,一个是销售单的概要描述,记录诸如销售单号、总金额一类的情况,
另外一个表记录每种商品的数量和金额。对于第一个表(主表),通常我们以单据号为主键;对于商品销售的明细表(从表),
我们就需要将主表的单据号也放入到商品的明细表中,使其关联起来形成主从关系。同时该单据号与商品的编码一起,形成明细表的联合主键。
这只是一般情况,我们稍微将这个问题延伸一下:假如在明细中,我们每种商品又可能以不同的价格方式销售。有部分按折扣价格销售,有部分按正常价格销售。
要记录这些情况,那么我们就需要第三个表。而这第三个表的主键就需要第一个表的单据号以及第二个表的商品号再加上自身需要的信息一起构成联合主键;
又或者其他情况,在第一个主表中,本身就是以联合方式构成联合主键,那么也需要在从表中将主表的多个字段添加进来联合在一起形成自己的主键。
解决方案
从上面,我们已经看到现有结构存在着相当多的弊端,主要是导致程序复杂、效率降低并且不利于分页。
为解决上述问题,本文提出:当应用系统后台数据库表间存在主从关系时,数据库表额外增加一非业务字段作为主键,该字段为数值型;或者当该表需要在应用中进行分页查询时,也应考虑如此设计。一般地,我们也可以几乎为任何表增加一个与业务逻辑无关的字段作为该表的主键字段。
由于该字段要作为表的主键,那么其首要条件是要保证在该表中要具有唯一性。