SQL SERVER 基础使用技巧
阅读原文时间:2023年08月21日阅读:2

1 .编写目的

本人总结了一些实际使用中常常因为疏忽大意而出现各种意想不到的问题,本文档总结相关经验(有些并未得到验证),便于交流学习。

基础

(1)定义:

char:固定长度,存储ANSI字符,不足的补英文半角空格。

nchar:固定长度,存储Unicode字符,不足的补英文半角空格

varchar:可变长度,存储ANSI字符,根据数据长度自动变化。

nvarchar:可变长度,存储Unicode字符,根据数据长度自动变化。

nvarchar(n) :包含 n个字符的可变长度 Unicode 字符数据。n 的值必须介于 1  与  4,000 之间。字节的存储大小是所输入字符个数的两倍。所输入的数据字符长度可以为零。

varchar[(n)]:长度为 n 个字节的可变长度且非 Unicode的字符数据。n 必须是一个介于   1 和 8,000之间的数值。存储大小为输入数据的字节的实际长度,而不是 n 个字节。所输入的数据字符长度可以为零。

注意:ANSI主要是以单字节来存储数据,一般适合英文。而我们常用的汉字需要用两个字节来存储,所以就要使用unicode的数据类型,不然读取出来的数据可能会乱码。

(2)区别:

①从存储方式上,nvarchar是按字符存储的,而 varchar是按字节存储的;

②从存储量上考虑, varchar比较节省空间,因为存储大小为字节的实际长度,而 nvarchar是双字节存储;

③在使用上,如果存储内容都是英文字符而没有汉字等其他语言符号,建议使用varchar;含有汉字的使用nvarchar,因为nvarchar是使用Unicode编码,即统一的字符编码标准,会减少乱码的出现几率;

④   如果你做的项目可能涉及不同语言之间的转换,建议用nvarchar。

(3)优缺点:

Nvarchar优点:判断字符串的时候可以不需要考虑中英文两种字符的差别,可以避免程序中乱码的问题。

缺点:存储英文字符会增大一倍的存储空间.但是在存储代价已经很低廉的情况下,优先考虑兼容性会给你带来更多好处的,效率没有varchar高。

(4)为什么要用nvarchar?

有n前缀的,n表示Unicode字符,即所有字符都占两个字节,nchar,nvarchar

字符中,英文字符只需要一个字节存储就足够了,但汉字众多,需要两个字节存储,英文与汉字同时存在时容易造成混乱,Unicode字符集就是为了解决字符集这种不兼容的问题而产生的,它所有的字符都用两个字节表示,即英文字符也是用两个字节表示。

(5)有关var的简单介绍:

有var前缀的,表示是实际存储空间是变长的,varchar,nvarchar

所谓定长就是长度固定的,当输入的数据长度没有达到指定的长度时将自动以英文空格在其后面填充,使长度达到相应的长度;而变长字符数据则不会以空格填充,比较例外的是,text存储的也是可变长。

(6)如何使用这些类型?

如果你肯定存储的数据长度,而且不包中文的,可以选择char类型。

如果肯定存储的数据长度,但可能包括中文,可以选择nchar类型。

如果不确定存储的数据长度,存储只有英文、数字的最好用varchar

如果不确定存储的数据长度,也有可能有中文,可以选择nvarchar类型

查看以上语句执行结果差异,同时也需要特别注意变量@TestId未能成功赋值(即由于查询记录不存在,导致@TestId变量还是默认初始值AAAAA),在实施中经常会出现莫名奇妙问题,很多时候就是这原因导致的,特别是在写循环语句内,通过SELECT查询对变量赋值,一定需要先赋默认值(根据变量类型分别建议设置=’’或=0)。

先看几个例子,大家看下查询结果是否与想想的是一致的,如下为对应创建表结构语句及表初始数据。

CREATE TABLE [dbo].[A](

[AId] [nvarchar](50) NULL,

[CName] [nvarchar](50) NULL

)

CREATE TABLE [dbo].[B](

[BId] [nvarchar](50) NULL,

[AId] [nvarchar](50) NULL,

[CValues] [nvarchar](50) NULL

)

INSERT INTO A ( AId, CName ) VALUES ('1','王XX')

INSERT INTO A ( AId, CName ) VALUES ('2','陈XX')

INSERT INTO A ( AId, CName ) VALUES ('3','李XX')

INSERT INTO B ( BId,AId, CValues ) VALUES ('B00001','1','B1')

INSERT INTO B ( BId,AId, CValues ) VALUES ('B00002','1','B2')

INSERT INTO B ( BId,AId, CValues ) VALUES ('B00003','2','B3')

内连接与左连接的差异:

SELECT A.*,B.* FROM A INNER JOIN B ON A.AId = B.Aid

(内连接也经常这样写:SELECT A.*,B.* FROM A,B WHERE A.AId = B.Aid )

SELECT A.*,B.* FROM A LEFT JOIN B ON A.AId = B.Aid

左连接带查询条件一:

ON涉及增加连接右边表的条件

SELECT A.*,B.* FROM A LEFT JOIN B ON A.AId = B.AId

SELECT A.*,B.* FROM A LEFT JOIN B ON A.AId = B.AId AND B.CValues='B3'

左表A数据都有,右表B数据只有满足B.CvaluesB3的才有数据,其他为null。相当于只关联符合B.Cvalues=’B3’的B,其他为null,得到的结果。

左连接带查询条件二:

ON涉及连接左边表的条件。

SELECT A.*,B.* FROM A LEFT JOIN B ON A.AId=B.AId AND A.CName=''

SELECT A.*,B.* FROM A LEFT JOIN B ON A.AId=B.AId AND A.CName='王XX'

SELECT A.*,B.* FROM A LEFT JOIN B ON A.AId=B.AId AND A.CName='陈XX'

始终记住left join,左表A数据都有,如果加入左表A条件CName='王XX',则只有满足A. CName='王XX',对应的右表B. AIdA. CName='王XX'对应AId的才有数据,其他为null。相当于只关联符合A. CName='王XX'的AId,其他为null,得到的结果。

ONWHERE过滤差异:

SELECT A.*,B.* FROM A LEFT JOIN B ON A.AId = B.AId AND B.CValues='B3'

SELECT A.*,B.* FROM A LEFT JOIN B ON A.AId = B.AId WHERE B.CValues='B3'

其他:

对表连接查询,所有字段都必须指定对应表名,不然会到后续表变更涉及相同字段时执行语句就出问题。表名很长可以增加对应别名,如:

SELECT B.BoxSN,A.CreateDate

FROM Lot A,WW_RMInventory B

WHERE A.LotSN='1' AND A.LotSN=B.LotSN

代码中INSERT语句必须写出全部列名(禁止不写列名的写法),以保证表增加删除字段后语句执行不受影响。

如:INSERT INTO TAB (COL1,COL2) VALUES (COL1_VAL,COL2_VAL);。

WHERE查询条件先后顺序不同影响到执行效率。查询字段尽可能基于索引字段。

如我们对Lot表做查询操作:

SELECT *

FROM Lot

WHERE LotId='LOT1000196XG' AND ProductId='PRD10002HHWA'

/*

在都建有单个字段索引的情况下,优于下面语句,

因为通过LotId=' LOT1000196XG '第一条件过滤后的数据行少*/

SELECT *

FROM Lot

WHERE ProductId='PRD10002HHWA' AND LotId='LOT1000196XG'

建议对可能为空的字段设置默认值,这样就不会导致后续查询的不方便。

ORDER BY子句的性能取决于参与排序操作的数据量多少(排序操作会在数据筛选完以后,对筛选出的结果集进行排序)。因此尽量减少返回数据。

对于循环次数较多场景下,不建议使用游标,可用其他循环如WHILE代替,因为游标相对执行耗时更长。

索引的正确使用

合理的使用正确的索引是提高系统执行效率的关键因素,对索引的使用需要注意以下一些问题:

u 过度索引

一般情况下,使用索引可以缩短查询语句的执行时间,提高系统的执行效率,但是要避免过度索引的情况出现,对一个表建立了过多的索引,从而造成维护索引所需要的时间超过使用索引所降低的时间,从而造成整个系统效率的下降,这一般发生在对一些进行大量更新的表上面。

u 复合索引

复合索引是使用多个数据列的索引,第一个字段的数据区分度非常重要,也是影响一个联合索引效率的关键所在。

u LIKE运算符

LIKE尽量前端匹配,在应用程序中为了使用方便,对字符型变量进行比较时经常使用LIKE运算符进行字符模式的匹配。需要注意的是对于LIKE运算,如果通配符%出现在字符串的尾部或中间,LIKE运算符将可以使用索引进行字符串的匹配,否则如果通配符%出现在字符串的开始,则LIKE必须使用全表扫描的方式去匹配字符串,这将产生较大的系统负荷。一般情况下,为了提高系统的效率,我们希望用户能够在通配符的左端提供较多的数据信息以降低查询的数量。

u 条件中不要使用函数

不要对有索引的字段使用任何计算,包括函数。因为这会导致无法使用索引进行数据检索,从而导致扫描(全表)操作。

SELECT LotId,LotSN,CreateDate

FROM Lot

WHERE DATEDIFF(DD,CreateDate,'2010-08-09 12:30:30')>=0

修改为:

SELECT LotId,LotSN,CreateDate

FROM Lot

WHERE '2010-08-09 12:30:30'>=CreateDate

还有其他函数等等:

select id from t where substring(name,1,3)='abc'

u 避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。

select id from t where num/2=100

修改为:

select id from t where num=100*2

u 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描(有的资料说is null不会导致索引失效),如:

select id from t where num is null

可以在num上设置默认值0或'',确保表中num列没有null值,然后这样查询:

select id from t where num=0

select id from t where num= ''

u 其他

尽量不要使用!= 或“<>”

尽量避免使用OR

尽量避免使用in 和 not in

u 并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。

避免使用复合SQL语句(如:SELECT A,B FROM TABLE1 WHERE A IN (SELECT A1 FROM TABLE2))

尽量避免使用DISTINCT

不要在存储过程或函数中反复访问同一张表,如果需要,可以将数据放在临时表中使用。

如果能从一条SQL语句中获取多个想要的数据,就不要分多条SQL语句分分次获取

使用EXISTS代替IN,NOT EXISTS 代替NOT IN

查询的WHERE过滤原则,应使过滤记录数最多的条件

多表连接查询时,可以先按条件过滤在进行连接

select、insert子句的代码中不允许出现“*”以代替查询所有字段,必须用实际的字段名代替

提高GROUP BY的执行效率,在进行GROUP BY 之前将不需要条件过滤掉

尽量使用“>=” “<=”不要使用“>” “<”。

技巧

select distinct name

from syscomments a,sysobjects b

where a.id=b.id and b.xtype='p' and text like '%被调用存储过程名%'

select distinct object_name(id) from syscomments

where id in(select id from sysobjects where type ='P') and text like '%DCMoveStationKN%'

--本周的第一天

SELECT DATEADD(wk, DATEDIFF(wk,0,getdate()), 0)

--本年的第一天

SELECT DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)

--本季度的第一天

SELECT DATEADD(qq, DATEDIFF(qq,0,getdate()), 0)

--当天的凌晨

SELECT DATEADD(dd, DATEDIFF(dd,0,getdate()), 0)

--上个月的最后一天

SELECT dateadd(ms,-3,DATEADD(mm, DATEDIFF(mm,0,getdate()), 0))

--上一年的最后一天

SELECT dateadd(ms,-3,DATEADD(yy, DATEDIFF(yy,0,getdate()), 0))

--本月的第一天

SELECT DATEADD(mm, DATEDIFF(mm,0,getdate()), 0)

--本月的最后一天

SELECT dateadd(ms,-3,DATEADD(mm, DATEDIFF(m,0,getdate())+1, 0))

--本年的最后一天

SELECT dateadd(ms,-3,DATEADD(yy, DATEDIFF(yy,0,getdate())+1, 0))

--本月的第一个星期一

select DATEADD(wk, DATEDIFF(wk,0, dateadd(dd,6-datepart(day,getdate()),getdate())), 0)

Select CONVERT(varchar(100), GETDATE(), 0): 05 16 2006 10:57AM

Select CONVERT(varchar(100), GETDATE(), 1): 05/16/06

Select CONVERT(varchar(100), GETDATE(), 2): 06.05.16

Select CONVERT(varchar(100), GETDATE(), 3): 16/05/06

Select CONVERT(varchar(100), GETDATE(), 4): 16.05.06

Select CONVERT(varchar(100), GETDATE(), 5): 16-05-06

Select CONVERT(varchar(100), GETDATE(), 6): 16 05 06

Select CONVERT(varchar(100), GETDATE(), 7): 05 16, 06

Select CONVERT(varchar(100), GETDATE(), 8): 10:57:46

Select CONVERT(varchar(100), GETDATE(), 9): 05 16 2006 10:57:46:827AM

Select CONVERT(varchar(100), GETDATE(), 10): 05-16-06

Select CONVERT(varchar(100), GETDATE(), 11): 06/05/16

Select CONVERT(varchar(100), GETDATE(), 12): 060516

Select CONVERT(varchar(100), GETDATE(), 13): 16 05 2006 10:57:46:937

Select CONVERT(varchar(100), GETDATE(), 14): 10:57:46:967

Select CONVERT(varchar(100), GETDATE(), 20): 2006-05-16 10:57:47

Select CONVERT(varchar(100), GETDATE(), 21): 2006-05-16 10:57:47.157

Select CONVERT(varchar(100), GETDATE(), 22): 05/16/06 10:57:47 AM

Select CONVERT(varchar(100), GETDATE(), 23): 2006-05-16

Select CONVERT(varchar(100), GETDATE(), 24): 10:57:47

Select CONVERT(varchar(100), GETDATE(), 25): 2006-05-16 10:57:47.250

Select CONVERT(varchar(100), GETDATE(), 100): 05 16 2006 10:57AM

Select CONVERT(varchar(100), GETDATE(), 101): 05/16/2006

Select CONVERT(varchar(100), GETDATE(), 102): 2006.05.16

Select CONVERT(varchar(100), GETDATE(), 103): 16/05/2006

Select CONVERT(varchar(100), GETDATE(), 104): 16.05.2006

Select CONVERT(varchar(100), GETDATE(), 105): 16-05-2006

Select CONVERT(varchar(100), GETDATE(), 106): 16 05 2006

Select CONVERT(varchar(100), GETDATE(), 107): 05 16, 2006

Select CONVERT(varchar(100), GETDATE(), 108): 10:57:49

Select CONVERT(varchar(100), GETDATE(), 109): 05 16 2006 10:57:49:437AM

Select CONVERT(varchar(100), GETDATE(), 110): 05-16-2006

Select CONVERT(varchar(100), GETDATE(), 111): 2006/05/16

Select CONVERT(varchar(100), GETDATE(), 112): 20060516

Select CONVERT(varchar(100), GETDATE(), 113): 16 05 2006 10:57:49:513

Select CONVERT(varchar(100), GETDATE(), 114): 10:57:49:547

Select CONVERT(varchar(100), GETDATE(), 120): 2006-05-16 10:57:49

Select CONVERT(varchar(100), GETDATE(), 121): 2006-05-16 10:57:49.700

Select CONVERT(varchar(100), GETDATE(), 126): 2006-05-16T10:57:49.827

Select CONVERT(varchar(100), GETDATE(), 131): 18/04/1427 10:57:49:920AM