数据类型的选择帮助优化查询,比如针对 int 类型列和针对文本类型列可能会生成完全不同的查询计划
三种数据类型:
别名数据类型:用户可以为系统数据类型提供一个别名,并且可以对数据类型做进一步限制,这样可以保持数据库和应用程序的一致性
用户自定义数据类型:引入的托管代码中的数据类型,包括 CLR 中的系统数据类型和 CLR 中用户自定义数据类型
整数数据类型:
tinyint 存储的是一个字节( 8 位),值范围是 0-255
smallint 存储的是两字节( 16 位),值范围是 -32768-32767
int 存储的是四字节( 32 位),值范围是 -2147483648-2147483647
bigint 存储的是八字节,值范围是 -2^63 - 2^63-1 ,存储的是非常大的整数值
分数数据类型:
decimal : ANSI 兼容,允许指定整数位和小数位
numeric :功能上和 decimal 是一样的
money 、 smallmoney :以前遗留下来的数据类型,用来存储货币值,四个小数位精度,通常使用 decimal 代替这个数据类型
bit 数据类型:存储 1 位值,存储上有优化,如果一张表中少于 8 个的 bit 列,他们将被存储在一个字节中,通常用来存储 boolean 值;在 SQL Server 中 bit 值没有字符串形式, True 和 False 只是可以转化为 bit 值,就像整数值 1 、 0 可以转化为 bit 值一样,底层 bit 值存储的不是 True 或 False ,也不是整数值 1 、 0 ,而只是一位值而已 ;高级语言中 False 都使用 0 来存储,而 True 存储根据语言不同使用不同的值,因此在检测 boolean 值时,使用 @inputvalue=0 来检测 false ,使用 @inputvalue<>0 来检测 true ,不要使用 @inputvalue=1 来检测 true 值,在有的语言中得不到预期的结果; bit 类型值还可以存储 null 值,也就是说 bit 类型值共有三种状态 0,1,null
IDENTITY 和 int 或者 bitint 列一起使用,通过 SET IDENTITY_INSERT ON 开关,允许在 IDENTITY 列显式插入值,具有 IDENTITY 属性的列不能够保证值都唯一,因为可以显式插入值;可以通过系统变量 @@IDENTITY 检索最近一次 IDENTITY 值,使用 @@IDENTITY 的问题是:当插入一条记录到 Customer 表中时,这个 customer 被赋予一个新的 identity ,但是如果这个表有一个触发器,这个触发器在插入时将这个条目写入到 audit logging table 中, @@IDENTITY 返回的是 logging table 值,而不是 Customer 表的值;此时需要使用 SCOPE_IDENTITY 函数,它提供的是当前范围 Scope 内最新 identity 值;当一次插入多行记录时,通过 OUTPUT 语句检索 IDENTITY 值(通过检索 inserted 表来检索该值),插入一条记录时,使用这种方法也可以检索插入的值
近似数据类型通常用在科学计算应用程序中,而不是商业应用程序中,不要使用近似数据类型存储货币值这样的商业值
real :存储的是 4 字节( 32 位),精确到 7 位小数
float(n) : n 代表用来存储尾数的位数, n 默认等于 53 ,当 n 在 1-24 范围时, n=24 ,即精度为 7 位小数,使用 4 字节存储,也就是 real 数据类型的值;当 n 在 25-53 范围时, n=53 ,即精度 15 位小数,使用 8 字节存储
SQL Server 支持丰富的日期和时间数据类型,需要非常小心每个类型的字符串形式,大量函数可用来处理这些类型
date :符合 ANSI 标准,范围 0001-01-01 to 9999-12-31 ,精确到天
time :符合 ANSI SQL 标准,范围 00:00:00.0000000 to 23:59:59.9999999 ,精确到 100 纳秒,允许指定小数位数,即定义纳秒的位数
datetime2 :是 date 和 time 类型的组合
datetime :旧版本的日期时间类型,范围 1753-01-01 to 9999-12-31 ,精确到 3.33 毫秒, datetime 类型根据语言设置将字符串转化为日期, "YYYYMMDD" 总是可以转化为正确的日期时间类型, "YYYY-MM-DD" 可能会被当做 "YYYY-DD-MM" 格式进行转化,取决于设置, date 类型不会有这样的行为
datetimeoffset : datetime2 和 timezone offset 的组合,日期时间部分代表本地时间, timezone offset 范围 -14:00 to +14:00 ,表示和 UTC 时间的偏移量,注意 date 类型是和时区无关的
GUID 就是一个数字分配器,保证一个程序生成的值和其他程序生成的值不冲突,是 128 位值;在 SQL Server 中对应的数据类型就是 uniqueidentifier ,该类型允许的操作只包括 = 、 <> 、 < 、 > 、 <= 、 >= 以及是否为 null 的检测 IS NULL 、 IS NOT NULL ;该数据类型定义的列上不允许使用 IDENTITY ;通过 NEWID() 函数生成新的 GUID 值,该函数生成的 GUID 值具有非常大的随机性,有性能问题,NEWSEQUENTIALID () 函数试图解决 NEWID() 函数的随机性,不过它是以牺牲唯一性为代价的
NULL 是数据库列的状态,不是数据类型的值,使用 IS NULL 检测是否为 null ,而不是使用 =NULL 检测是否等于 null ;为数据库列指定不当的 nullability 是常见的设计错误
Unicode 常量要求在前面都加上 N 前缀,使用 LEN() 函数返回字符的数量,使用 DATALENGTH() 返回字节数量
字符数据类型:
char 和 nchar :存储固定长度的字符,如果这两个类型值的字符长度不够的话,在尾部使用空格填充,使用时通常都需要去掉尾部的空格, char 使用单字节存储, nchar 采用双字节存储
varchar 和 nvarchar :用来存储变长字符串,允许你指定一个字符串的最大长度, nvarchar 用来存储 unicode 字符(即双字节字符), varchar 和 nvarchar 限制在 8000 和 4000 个字符上限,正好是数据库中一个数据页的大小
varchar(max) 和 nvarchar(max) :存储更长的字符,它们允许存储高达 2GB 的数据
text 和 ntext :旧数据类型,被启用,使用 varchar(max) 和 nvarchar(max) 替换这两个数据类型
sysname : SQL Server 中对象名称通常都是该类型,是 nvarchar(128) 的别名
Collation 用来控制在 SQL Server 中存储的非 Unicode 数据使用的代码页,以及 SQL Server 中排序和比较字符值时使用的规则
code pages 代码页:在计算机中每个字符使用一个字节,共有 256 个字符, 0-31 作为控制字符,比如回车( 8 )、 Tab ( 9 )、空格( 32 ),一直到 Delete ( 127 ); 127 以上的值则根据标准不同表示不同的含义,这些标准在 MSDOS 操作系统中就被称为代码页,一个代码页就定义 128-255 之间的字符的用途,这些代码页 0-127 之间的字符定义都是相同的
共有两类 Collations : SQL Server Collations 和 Windows Collations , SQL Server Collations 用来保持向后兼容性,鼓励使用 Windows Collations , SQL Server Collations 名称采用如下形式: SQL_SortRules[_Pref]_CPCodePage_ComparisonStyle
CodePage :代码页, 1-4 位整数,由于历史原因 CP1 使用的是 1252 代码页,其他正常
Comparison Style : BIN 表示按二进制排序或者 case 、 accent 是否敏感的组合, CI 不区分大小写, CS 区分大小写, AI 不区分语调, AS 区分语调
可以在服务器、数据库、列级别设置 Collations ,当比较使用不同的 Collations 存储的数据时,要求指定用于比较的 Collations
CAST 是 ANSI SQL 中标准的数据类型转换操作符, Convert 则提供了更加强大的功能,允许你指定如何执行类型转换,即允许指定转换的格式 Style ,注意 Style 通常是关于字符串输出的,但是也可以用来处理字符串输入,即解析字符串
尽量避免隐式转换,因为隐式转换可能会带来非期望的结果,隐式转换经常出现的问题:
为目标类型指定了不恰当的值,比如不恰当字符串转换为 int 值
为目标类型指定值超出了该类型的范围,比如为 tinyint 类型指定了 300
在转换时值被截断(有时是悄悄的),比如 CONVERT(varchar(6),SYSDATETIME(),112) , 112 格式通常返回 8 位字符串,当转换为 6 位字符串时,就被悄悄截断
在转换时值被舍入(有时是悄悄的), 比如 datetime 类型值 20051231 23:59:59.999 被悄悄转换为 20060101 00:00:00.000
在转换时值被修改(有时是悄悄的), SELECT 5ee ,被悄悄修改为 5
对数据类型内部存储做假设,依赖于数据类型内部存储是非常危险的,因为内部表示随着时间会发生变化
一些日期类型转换依赖于语言设置,比如 2010-05-04 即可以转换为 2010 年 5 月 4 日,也可以转换为 2010 年 4 月 5 日
有一些解析问题难以理解,比如 SELECT 5ee
rowversion 数据类型是用来帮助处理并发的,之前 SQL Server 使用的是 timestamp ,现在 timestamp 已经被弃用;在每次修改数据行时, rowversion 值都自动被修改,当你要提交数据修改时,首先检查是否存在 rowversion 值的行,如果不存在,则说明数据已经被修改,此时在使用相应的策略,注意这里存在竟态条件,即你检查时没有发现数据被修改,但是提交前数据却被修改; rowversion 在数据库中保存一个计数器,所有修改都会让计数器增加,可以使用系统变量 @@DBTS 检索返回的 rowversion 值
Alias data types 就是系统内置数据类型的子类型,使用 CREATE TYPE 语句创建,包含了是否可以为 null 的说明,通常用来维护应用程序中使用的数据类型的一致性(比如定义了 ProductNum 类型,其为 nvarchar(20) ,则使用 ProductNum 定义的列都是 nvarchar(20) ,不会出现在一个表中 product number 是 nvarchar(20) ,在另一个表中是 nvarchar(22) );数据库公共角色都自动被授予对 Alias data types 的 REFERENCES 权限,即公共角色都自动可以引用创建 的 Alias data types
其他数据类型:
binary 、 varbinary 、 varbinary(max) :用来存储任意的大的二进制对象( blobs )
image :已经被遗弃,应该使用 varbinary(max)
hierarchyid :是在托管代码中实现的,代表树中的节点
sql_variant :用来存储未知类型的数据,很少使用
cursor :在构造基于游标的代码时,保存对游标的引用