干了这么多年 SQL Server,我发现自己挨过的骂,有一大半都和数据类型的粗心脱不了干系。数据类型这四个字听起来像基础课目录,可它决定的不只是字段“存不存得下”,而是整张表的存储特点、查询能不能走索引、应用接口会不会返错、报表统计是否对得上。上周刚帮同事看了一个线上案例:手机号被设计成 int 列,带 0 开头号码的用户全被吞了前导位;还有一个老系统把下单时间塞进 varchar,导致所有跨年统计错位。这类问题往往不是当场抛异常,而是等上线跑了一段时间才被发现,到了那个时候,锅基本已经扣在写表的人头上了。
这篇文章就从 SQL Server 里最容易踩的数据类型坑说起,涵盖字段选型、类型转换、业务建模和排查手段。无论你是刚开始写 T-SQL 的开发者,还是要维护老库的运维,读完至少能提前绕开几个高频背锅场景。
1. 为什么SQL Server数据类型值得认真对待
1.1 字段类型决定的不只是存储,而是整条数据链路的稳定性
很多人习惯先建表、后调整,用的是“先跑起来再说”的思路。数据库的确允许你后期改列,类型也会按照一定规则自动转换,但这种自由恰恰是业务事故的温床。字段类型一旦定下来,它就会影响存储字节数、约束检查、索引结构、统计信息估算、执行计划生成,还会影响上游应用里 ORM 和接口的数据映射。
一个字段选 bit 还是 tinyint,看起来只是“0/1”和“0~255”的差异。bit 有真正的三态逻辑(true、false、NULL),tinyint 则适合表达“未知、启用、禁用”之外的更多业务状态。如果你把支付状态存成 varchar(20) 中文,比如 '已支付',那数据库里所有判断都要写成 WHERE status = N'已支付'。一旦排序规则或前端传入字符串大小写、全半角不一致,就会产生匹配漏洞,而且字符串状态列做索引的成本也远高于数值型。类型不只是存储容器,它本身就带有业务边界和校验能力。
再举一个我印象很深的例子。一张日志表用了 uniqueidentifier 做主键,生成方式是应用层直接 NEWID()。由于 GUID 完全随机,插入时聚集索引频繁页分裂,索引碎片率没过多久就飙到 50% 以上。后来改成 int IDENTITY,性能立刻改善。主键选型不是“能用就行”,它会直接影响写入吞吐和查询速度,SQL Server 在有聚集索引的情况下尤其敏感。
1.2 修改字段的隐性成本:设计时半小时,上线后好几天
我曾经帮业务方处理过一次“把用户表手机号字段从 varchar(20) 改纯数字类型”的需求。从 SQL 层面看,一句话就能改:ALTER TABLE dbo.Users ALTER COLUMN Phone BIGINT。但真执行的时候,才发现表里早就混进了 '138-0000-0000'、'未知' 这类脏数据,转换直接报错。就算数据干净,表上如果有大量索引、统计信息和关联对象,ALTER COLUMN 会让 SQL Server 重建整个表及相关结构,期间产生的架构锁足以让线上读写长时间阻塞。
这还只是数据库侧的问题。下游的存储过程、视图、报表、ORM 实体类、Excel 导出模板很可能都基于旧类型做了判断。改完字段,可能出现字符串拼接不再是 '138xxxx',而变成了 13800000000,前端展示、导出格式全乱。等到产品经理和客服一起找上门,你才会意识到类型设计本质上是全链路契约。设计表结构时多核对一轮,往往比上线后反复救火划算得多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 最容易翻车的几类字段选型陷阱
2.1 字符类型:varchar、nvarchar、char 的差异不只是“少俩字母”
SQL Server 里的字符类型有几个容易混淆的点,首先就是 varchar(n) 的 n 表示字节数,不是字符数。对于英文字母和数字,1 个字符占 1 字节;在中文简体字代码页下,一个汉字通常占 2 字节。也就是说,varchar(20) 在纯中文场景里最多只能存 10 个汉字。如果你在前端做了“最多 20 个汉字”的校验,后端插入时就会经常遇到 String or binary data would be truncated 的错误,或者更隐蔽地被静默截断,数据悄悄变短。
而 nvarchar(n) 的 n 表示字符数,一个中文字符固定占 2 字节(较新的补充字符会占 4 字节),所以 nvarchar(20) 确实可以存 20 个汉字。这也解释了为什么接收外部系统文本、用户昵称、地址等字段时,我更倾向用 nvarchar。如果表里字段主要存英文字符或纯 ASCII,varchar 能节省一半存储空间;但只要有中文、少见字符或不确定未来语言,直接用 nvarchar 更省心。很多老系统因为最初用 varchar 存中文,后来遭遇了生僻字插入失败或者字符集转换乱码,处理起来非常痛苦。
char(n) 是定长类型,如果值不足 n 会在尾部补空格。它适合存储长度固定的编号,比如身份证号、银行卡号。但要注意,SQL Server 对尾随空格的处理比较特殊,很多比较和去重逻辑会忽略尾部空格,所以 CHAR(10) 存 'ABC' 和 'ABC ' 在使用等值判断时可能会被认为相等,必要时用 DATALENGTH 判断真实存储长度。我曾经见过有人用 char(200) 存用户备注,每行固定占 200 字节,表体积飞速膨胀,最后不得不重建表才能优化。
另一个容易踩的是排序列的默认排序规则(Collation)。在 Chinese_PRC_CI_AS 这类大小写不敏感的排序规则下,WHERE name = 'abc' 能匹配到 'ABC'。业务上需要严格区分大小写时,最好在建列时指定二进制排序,比如 name NVARCHAR(50) COLLATE Chinese_PRC_CS_AS。还有临时表与正式表之间的 COLLATE 冲突,多表关联时经常报错 Cannot resolve the collation conflict,这种通常需要显式加 COLLATE DATABASE_DEFAULT。
2.2 数值类型:int、bigint、decimal 的边界比想象中更接近业务红线
数值类型最经典的问题是“先用 int 存,跑几年后溢出”。我知道的一张订单表,上线时每天单量不大,主键用了 int。三年后某个大促,应用写入突然开始报 Arithmetic overflow error converting IDENTITY to data type int,整个下单链路直接挂掉。因为 int 的上限只有 21 亿多,对互联网业务来说并不是一个遥不可及的天文数字。如果表的主键有明确的高速增长预期,一次性设计成 bigint 会省掉一次大表重建。
金额和浮点数是另一个重灾区。float 和 real 是近似数值类型,内部按二进制科学计数法存储,0.1 这类十进制小数无法被精确表达。用 float 计算金额会导致 0.1 + 0.2 = 0.30000000000000004 类似的问题,这在报表核对时非常致命。业务金额、单价、税率、账户余额,都应该用 decimal(p, s)。decimal(18,2) 表示总精度 18 位、小数 2 位,最大能表示万亿级别的金额。如果涉及汇率、积分类等高精度计算,可以考虑 decimal(18,4) 甚至小数位更多的设计,但小数位也不是越多越好,位数越多占存储越大、计算开销越高。
更麻烦的是 decimal 运算过程中的精度膨胀。SQL Server 对 decimal 加减乘除有一套自己的结果精度规则:加减法的结果精度会变大,乘法的结果精度约等于两个精度相加再加 1,除法尤其容易出现超高精度中间结果。比如一个 decimal(18,2) 除以另一个 decimal(18,2),SQL Server 会按极高的 scale 计算,如果你不显式控制,可能会出现“结果精度超过 38 位,最终被截断或报错”的现象。我的习惯是:任何除法、百分比计算,最后都要显式 CAST 或 ROUND 到目标精度,不要依赖隐式行为。
2.3 时间日期:datetime、datetime2、date 的选择直接决定查询准确性
SQL Server 的日期时间类型里,最容易坑人的是 datetime 和 datetime2 的精度差异。datetime 的精度是 3.33 毫秒,也就是秒后面只能按 0.000、0.003、0.007 步进;datetime2 的精度最高可达 100 纳秒。如果存的是业务流水时间,或者从 Java、Python 传过来的高精度时间戳,datetime 很容易产生细微的舍入误差。这种差异在“按时间倒序取最新一条”的查询里,可能让你拿到和你预期不一致的记录。
datetime 的另一个限制是年份范围只有 1753 到 9999,datetime2 则能覆盖公元 1 年到 9999 年。听上去有点钻牛角尖,但如果系统在处理历史档案、考古数字化或跨世纪日期数据,就必须用 datetime2。date 类型只保存日期,适合生日、入职日期、交易日期这类不需要时分秒的字段,能有效减少存储浪费。如果需要保存带时区的绝对时间点,可以使用 datetimeoffset。
我见过不少项目用 varchar 字符串存时间,理由是“方便前端展示”。这个习惯一旦养成,后期做范围统计就是灾难。字符串比较和日期语义并不一致,'2024-9-5' 和 '2024-09-05' 会被当成不同的字符序列;按月份排序时还可能得到 10 月排在 9 月之前。使用原生日期类型,除了能正确排序和聚合,还能让索引生效。SQL Server 里的默认时间函数也有区别:GETDATE() 返回 datetime,SYSDATETIME() 返回 datetime2(7)。如果你在 datetime2 列上插入 GETDATE(),不会有问题;但如果你在 datetime 列上插入 SYSDATETIME(),精度就会被悄悄丢弃,这种隐式截断不会报错,非常容易忽视。
2.4 高频字段类型速查表
| 场景 | 推荐类型 | 原始风险说明 |
|---|---|---|
| 主键、流水号 | bigint(增长明确)或 int(量级确定) |
int 可能溢出 |
| 手机号/电话号码 | varchar(20) 或 nvarchar(20) |
前导 0、区号、分机号被数字类型吞掉 |
| 身份证号 | char(18) |
含 X,存数字必炸 |
| 用户名/昵称 | nvarchar(50) |
中文需按字符计算 |
| 邮箱 | nvarchar(255) |
可能包含 +、. 号,varchar 换算易混淆 |
| URL/IP 地址 | nvarchar(255) / varchar(45) |
IPv6 长度较长 |
| 金额 | decimal(18,2) 或更高精度 |
float 有精度误差 |
| 状态/枚举 | tinyint / bit |
字符串状态匹配困难 |
| 创建时间/更新时间 | datetime2(3) / datetime2(7) |
datetime 精度不足 |
| 大文本/文件 | nvarchar(max) / varbinary(max) |
短文本用 max 会影响性能和行结构 |
这张表不是万能答案,但可以帮你把每个字段选的类型先和“极端情况”对照一遍。如果某个字段只能想到常规范围,没考虑几年后的增长和国内外的字符集,往往就会成为隐患。
3. 类型转换:看起来能跑,实际埋雷的重灾区
3.1 隐式转换带来的性能事故
SQL Server 在遇到两边的类型不一致时,会自动做隐式转换。这个特性省事,但也隐藏了大量性能问题。最典型的是 nvarchar 与 varchar 的比较。按照 SQL Server 数据类型优先级,nvarchar 比 varchar 等级更高,所以当 varchar 列和 nvarchar 参数比较时,查询优化器通常会先将列转换为 nvarchar 再比较。一旦列被包进转换函数,这一列上的索引就很难被有效利用,最终执行计划里出现 CONVERT_IMPLICIT,全表扫描随之而来。
类似的情况也出现在“字符串列和数字列比较”中。比如表中 order_no 是 varchar,查询条件写成 WHERE order_no = 123456,此时 SQL Server 会把字符串列转换为数字,同样相当于对列施加了转换,索引就废掉了。反过来如果 order_no 是 int 列,查询条件写 WHERE order_no = '123456',SQL Server 会把常量字符串转成 int,这通常不会破坏索引。关键准则是:尽量不要让“列”去适应“条件”,而是让条件在传入前就匹配列的类型。
我在排查慢查询时,第一反应就是看执行计划里有没有带感叹号的 CONVERT_IMPLICIT。它经常出现在关联条件、筛选条件、参数化查询不一致的场景。比如存储过程参数定义为 @name NVARCHAR(50),但表里列是 VARCHAR(50),又没主动做转换,那么所有 WHERE name = @name 都可能触发隐式转换。这类查询在数据量小的测试环境里毫无感知,一上生产跑几千万行就原形毕露。
3.2 CAST、CONVERT 与 TRY_CAST 的正确使用方式
显示转换要优先于隐式转换,因为至少你知道会发生什么。CAST(expression AS data_type) 是标准 SQL 语法;CONVERT(data_type, expression, style) 是 SQL Server 特有语法,好处是第三个参数可以指定格式化样式,尤其是日期字符串转换时很有用。
日期转换的样式非常实用。比如 CONVERT(VARCHAR(10), GETDATE(), 112) 会生成 YYYYMMDD,CONVERT(VARCHAR(10), GETDATE(), 120) 会生成 YYYY-MM-DD。我曾多次使用这些样式来避免日期歧义问题,因为不同语言环境的日期格式差异很大,用字符串拼接日期再传给数据库,容易产生 1/2/2024 这种让解析器猜不透的值。
遇到不确定是否能转换成功的字符串,要用 TRY_CAST 或 TRY_CONVERT。它们不会抛异常,转换失败时返回 NULL。在清洗历史脏数据时,这个特性太有用了。比如某列混入了大量非数字文本,你想把它转成数字,可以直接 SELECT TRY_CAST(value AS INT) FROM ...,然后过滤掉 NULL,就能快速看到哪些数据不合法。反之如果直接用 CAST,报错后可能整个批处理中断,排查半天才知道是哪行脏数据出了问题。除了 TRY_ 系列,PARSE 也可以做基于区域性文化的转换,但在绝大多数场景下没必要用它,性能和可控性都不如 CAST/CONVERT。
3.3 条件表达式与合并逻辑里容易被忽略的截断
在生产上,我还见过一种“SELECT 执行好好的,insert 到正式表就报错”的问题。原因是 SELECT 阶段的表达式扩展出了更大的精度或长度,但目标表的列类型放不下。比如临时表里某列是 VARCHAR(100),你计算 LEFT(remark, 200),SQL Server 可能推断出结果长度是 200,插入目标列 VARCHAR(100) 时直接报截断错误。避免方案是在写 SELECT 前就显式做一次 CAST(... AS VARCHAR(100)),不能让最终结果集“自带超长属性”。
CASE 表达式也有类似问题。CASE 所有分支的返回类型必须兼容,SQL Server 取优先级最高的类型作为结果类型。比如一个分支返回 INT,另一个分支返回 VARCHAR,结果列可能被推断为 INT,导致一些字符串分支被隐式转换。如果你在字符串分支里放了无法转成数字的文本,查询可能直接报错。这种问题在代码评审里很难发现,需要靠执行计划或小数据集验证。我的经验是:CASE 里每个 WHEN 分支都显式转成相同的数据类型,别让数据库猜。
4. 一张业务表的设计复盘:从字段到索引的实操记录
4.1 以一个用户订单表为例,逐字段分析选型
下面是我最近参与设计的一张 OrderInfo 订单表,用它来说明一个真正合理的类型设计是什么样的。这个表非常典型,覆盖了大多数业务系统的核心字段。
sql复制CREATE TABLE dbo.OrderInfo
(
OrderId BIGINT IDENTITY(1,1) NOT NULL,
OrderNo VARCHAR(32) NOT NULL,
UserId BIGINT NOT NULL,
ReceiverName NVARCHAR(30) NOT NULL,
ReceiverMobile VARCHAR(20) NOT NULL,
OrderStatus TINYINT NOT NULL,
PayAmount DECIMAL(18,2) NOT NULL,
DiscountAmount DECIMAL(18,2) NOT NULL,
CouponId BIGINT NULL,
ProvinceCode CHAR(6) NULL,
AddressDetail NVARCHAR(120) NOT NULL,
OrderTime DATETIME2(3) NOT NULL,
PayTime DATETIME2(3) NULL,
Remark NVARCHAR(200) NULL,
CONSTRAINT PK_OrderInfo PRIMARY KEY CLUSTERED (OrderId)
);
我逐条解释设计意图:
OrderId用bigint identity,而不是int。订单量在长期增长下很可能突破 int 上限,提前扩大主键代价最小。虽然主键变大可能让聚集索引占用空间增加,但比两年后做类型修改要稳妥得多。OrderNo是给业务方看的订单号,保留成字符型,方便记录前缀和外部系统交换。定义为varchar(32)而不是nvarchar(32),是因为这里统一存字母数字,不出现中文。ReceiverName用nvarchar(30)。收货人可能是中文,也可能有少数民族名字中的特殊字符,varchar难以控制字符数预期。ReceiverMobile用varchar(20),我只存手机号、座机和分机文本,不用数字类型是怕前导 0 和超大数值被吞。OrderStatus用tinyint加注释枚举。tinyint 只占 1 字节,查询和索引效率高,而且通过CHECK约束能防止非法状态写入。- 金额字段统一
decimal(18,2)。订单支付金额和优惠金额都要求精确计算,不能出现float的尾差。 CouponId用bigint,允许 NULL,表示优惠券可能为空。注意这里的 NULL 不代表“没参加活动”,只代表当前订单没有绑定券,语义上是独立的。ProvinceCode用char(6),存行政区划代码,固定六位。OrderTime、PayTime用datetime2(3)。业务只需要毫秒级精度,3 位小数即可给排序和展示留足空间,datetime2也比较贴合 Java 8 之后的LocalDateTime映射。Remark用nvarchar(200)而不是nvarchar(max)。备注有明确长度限制既是对业务的约束,也避免每个可变长字段都进入 LOB 存储,影响行内空间分配。
4.2 常见的“先凑合后返工”设计我会怎么拦下来
在评审时,我最常说的一句话是:类型就是约束。把字段设计成 varchar(200),就是允许任何人往里填 200 字节的内容,即便业务原本想限制 10 个字符。宽松的类型会让脏数据有了栖息地。比如状态位如果一开始放 varchar(10),你能存 '已取消',也能存 'cancel'、'Cancel'、'已取消 ',后面排查数据时会跪着看字典表。
另一种常见返工来自外部接口。上游系统返回一个整型字段,你以为存 int 没毛病,结果有一天对方开始返回超长字符串编号,于是你不得不把该字段改成 varchar 并重新处理线上数据。设计时留出一定的字符扩展空间并没错,但你需要为每一个字段想清楚“它和外部系统的映射边界在哪里”。如果字段依赖外部系统,更建议预留一个映射表或者对底层字段加一层版本,避免某天上游改了类型而数据库完全没用缓冲时间。
4.3 如果真的要修改已有字段类型,标准操作顺序是什么
一旦业务已经跑了一个月,你发现某个字段类型选错了,尽量按照下面的步骤来,最大程度减少生产事故。
第一,先在测试库完整跑一遍 ALTER COLUMN。这里不光执行 SQL,还要检查所有引用该列的对象。可以通过视图 sys.sql_expression_dependencies 或直接搜索存储过程文本,把依赖对象一次性找全。第二,检查列里的存量数据是否能合法转换。例如要把 VARCHAR 改为 BIGINT,先用 TRY_CONVERT(BIGINT, col) 找出所有转换失败的行,第一时间回报数据质量问题。第三,评估锁和空间。修改大表字段会重建表,你需要在低峰期执行,并且确保磁盘有足够剩余空间。第四,准备回滚计划。SQL Server 的 ALTER COLUMN 不一定能轻松回滚,所以改动前要建好新列装载数据,切换应用读取路径后再做旧列下线的操作。
sql复制-- 先定位不可转换的数据,避免 ALTER 中途报错
SELECT OrderNo, ReceiverMobile
FROM dbo.OrderInfo
WHERE TRY_CONVERT(BIGINT, ReceiverMobile) IS NULL;
提示:
ALTER TABLE之前一定要备份表结构定义、索引脚本和相关触发器。数据库项目里最有价值的不是“一句话改字段”,而是你手里那套能复现完整结构的版本脚本。
5. 高频翻车现场与排查技巧实录
5.1 经典报错的成因与快速处理速查
下面这些错误,应该有很多人看着眼熟。它们和数据类型的关联非常直接,我把典型的报错、原因和推荐解法整理成一个表,遇到时可以直接对着排查。
| 错误信息 | 常见原因 | 推荐处理 |
|---|---|---|
| String or binary data would be truncated | 插入值超过列长度,或 varchar 中文按字节超长 |
核对字段长度设计,确认是 varchar 还是 nvarchar;必要时用 LEFT 或业务层校验截断 |
| Arithmetic overflow error converting ... to data type int | 数值运算或赋值超出 int 范围 | 把目标字段或变量升级为 bigint,先定位是哪一步运算溢出 |
| Conversion failed when converting date and/or time from character string | 字符串无法转为日期时间 | 用 TRY_CONVERT 找出脏数据,检查日期格式与 style 参数是否匹配 |
| Conversion failed when converting the varchar value 'xx' to data type int | 把含字母的字符串转为数字 | 清洗数据,或改为 TRY_CAST,避免程序直接对脏字段执行数字转换 |
| Cannot resolve the collation conflict ... | 关联的两列或临时表排序规则不同 | 在显式比较中给其中一列加 COLLATE DATABASE_DEFAULT |
| The data types nvarchar and varchar are incompatible in the equal to operator | 两边类型不匹配且无法隐式转换 | 统一两边的数据类型,推荐把列和参数都做成 nvarchar 或在比较中统一转换 |
| Error Code 8115: 将 numeric 转换为数据类型 numeric 时发生算术溢出错误 | 目标 decimal 精度不足 | 增大 decimal 总精度或调整小数位序列 |
说实话,最后一种在财务系统里出现频率非常高。发券、分摊金额、折扣分摊算出来的中间小数位数可能远超目标列的精度,入库前没有显式 CAST,就会报 8115。这类问题最佳防线是数据库设计评审阶段就把每个 decimal 列的精度写得明明白白。
5.2 排查字段元数据和隐式转换的几条实用 SQL
排查字段类型问题时,不要靠肉眼去翻 SSMS 的表设计界面。直接查 sys.columns 更准确,也方便批量导出。
sql复制SELECT
c.name AS column_name,
t.name AS data_type,
c.max_length,
c.precision,
c.scale,
c.is_nullable,
c.collation_name
FROM sys.columns c
INNER JOIN sys.types t
ON c.user_type_id = t.user_type_id
WHERE c.object_id = OBJECT_ID(N'dbo.OrderInfo')
ORDER BY c.column_id;
这个查询会直接返回每个列的类型、最大长度、精度、小数位数以及排序规则。我通常在项目评审前导出所有表的设计清单,然后根据之前的速查表逐列核对。
max_length 需要留意它的计量单位不一定是字符数。对 varchar 和 nvarchar 来说,max_length 是字节数,所以一个 nvarchar(30) 列返回的 max_length 是 60。如果你在界面里看到 nvarchar(30),脚本里却是 60,别慌,就是因为它按字节统计。对于 varchar(20) 则是 20。这个细节也解释了为什么中文长度错误那么常见——你看到的字符数和它占用的字节数完全不是一回事。
排查查询是否存在隐式转换,最直接的方法是抓执行计划。在 SSMS 里开启“包含实际执行计划”,然后找到带有感叹号的运算符。如果出现 CONVERT_IMPLICIT,说明某处发生了隐式类型转换。再结合字段类型和参数的差异,基本就能定位到具体是哪条代码写得不严谨。
5.3 让数据类型问题在开发阶段就暴露的团队习惯
我复盘了这些年踩过的坑,发现很多类型问题不是不能避免,而是缺少早期的自动检查机制。下面几条是我在团队里强烈建议的规范,执行后能显著减少低级错误。
第一,数据库版本脚本必须经过评审。评审清单至少包含:是否有金额字段误用 float、状态字段是否用了难以维护的字符串、主键是否超出合理量级、时间列是否都用了正确的时间类型。第二,编写 ORM 映射时,不允许让程序通过隐式转换猜测数据库类型。C# 的 decimal、Java 的 BigDecimal,都应当和 SQL Server 的 decimal 明确对应;C# 的 string 也要和 nvarchar 保持风格统一。第三,提交的 SQL 代码里不能出现字符串拼接日期、字符串拼接数字的写法,无论是查询条件还是更新条件,都要求参数化。第四,给文本列设计一个“最大长度”的沟通机制。产品需求文档里只写了“收货人”三个字是不够的,你要自行判断该字段是否可能有中文、是否有特殊字符、是否会被外部系统扩展,然后把长度定义和技术细节写进表注释。
另外,关于数据导入和 ETL,我有一个小建议:每次批量导入前,不要直接来源数据塞入目标表,先做一轮 TRY_CONVERT 数据质量检查。尤其是日期、金额和电话号码这类字段,源系统常会出现 '189****1234'、'--'、0 等特殊值。只要先写一个探针查询,确认所有值都能成功转换,再执行正式的 INSERT 或 MERGE,就能避免一个脏数据把整批任务打断。
写在最后
我自己在设计表结构前,会习惯性地问三个问题:这个字段未来最大可能容纳什么值?它会不会被用来做排序、分组或关联?它和外部系统交互时,类型是否会被改变?三个问题过完,再把手放到键盘上建表,出错率会低很多。学数据类型很像学交通规则,大部分知识点单独看都很简单,难的是在不同路口都条件反射地做出正确的选择。希望这篇 SQL Server 数据类型的经验总结,能帮你减少几次被业务追着问“为什么数据又错了”的尴尬时刻。
