1. 从一个实际场景说起:为什么第二篇要盯着“操作”讲
很多朋友学 SQL Server 2019,第一篇还饶有兴致地装环境、建实例、连 SSMS,到第二篇就开始发懵:建好的库到底怎么用?表结构怎么设计才不返工?增删改查明明看着简单,为什么数据一多就卡成幻灯片?这篇就是来解决这些问题的。
我见过太多半路出家的开发者和运维,SQL 语法背得滚瓜烂熟,真上了生产环境却连“为什么这条 DELETE 走了全表扫描”“为什么加了索引还慢”都说不清。所以这一篇我不打算照着微软官方文档念一遍,而是把 SQL Server 2019 里和日常操作强相关的核心动作拆开讲:建库建表、增删改查、索引与性能、视图与存储过程、备份恢复、常见坑排查。尽量沿用前一篇的节奏,每一步都讲清“为什么这么做”,再给可以直接抄的写法。
如果你是刚装了 SQL Server 2019、正准备做课程设计或者公司小项目的人,恭喜你,这篇基本覆盖了你未来三个月最常用的数据库操作。如果你已经写了两年代码但对数据库底层的理解还停留在“能跑就行”,这篇也能帮你把很多模糊的概念理清楚。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 建库与建表:把地基打得稳,后面才不折腾
2.1 CREATE DATABASE 不只是“建个库名”那么简单
很多初学者建库就是右键“新建数据库”,填个名字点确定,完事。这在学习环境里没毛病,但你稍微认真一点就会发现,SSMS 的图形界面背后其实给你塞了一堆默认值。当我们用 T-SQL 手动建库时,才能真正理解这些默认值意味着什么。
sql复制CREATE DATABASE ShopDB
ON PRIMARY
(
NAME = N'ShopDB',
FILENAME = N'D:\SQLData\ShopDB.mdf',
SIZE = 128MB,
MAXSIZE = 4GB,
FILEGROWTH = 64MB
)
LOG ON
(
NAME = N'ShopDB_log',
FILENAME = N'D:\SQLData\ShopDB_log.ldf',
SIZE = 64MB,
MAXSIZE = 2GB,
FILEGROWTH = 64MB
);
这里有几个点值得说道说道。
- 文件路径:mdf 是数据文件,ldf 是日志文件,两者尽量放在不同的物理磁盘上。日志文件是顺序写入的,数据文件是随机读写,混在一个盘里会互相拖慢。学习环境只有一块盘就算了,生产环境这是基本要求。
- 初始大小:我见过不少人默认 8MB 起步,结果数据一多频繁自动增长,每次增长还会产生 IO 抖动。SQL Server 的自动增长是“用完了才长”,增长时会有短暂的性能毛刺。所以宁可把初始大小给得宽松点,也别让它一天涨八回。
- MAXSIZE:不给上限会怎样?理论上可以一直涨到磁盘满。给一个上限对后续监控有好处,但别给得太小,否则业务高峰期报“数据库文件已满”你就等着挨骂吧。
再说一个经常被忽略的操作:建完库立刻做一次完整备份,并把数据库设为“简单恢复模式”或“完整恢复模式”,取决于你的数据安全性要求。别等到数据出问题了才想起来恢复模式这回事。
2.2 表结构设计:命名规范与类型选择是经验的体现
表设计是数据库操作里最能看出一个人水平的地方。SQL Server 2019 里 CREATE TABLE 的语法本身不复杂,复杂的是“你打算怎么组织你的数据”。
以一个简单的订单表为例:
sql复制CREATE TABLE dbo.Orders
(
OrderId INT IDENTITY(1,1) PRIMARY KEY,
OrderNo VARCHAR(32) NOT NULL,
CustomerId INT NOT NULL,
OrderStatus TINYINT NOT NULL DEFAULT 0,
TotalAmount DECIMAL(18,2) NOT NULL,
CreatedTime DATETIME2(3) NOT NULL CONSTRAINT DF_Orders_CreatedTime DEFAULT(SYSDATETIME()),
UpdatedTime DATETIME2(3) NULL
);
几个容易被忽略的细节:
- 为什么用 IDENTITY 而不是 GUID 做主键?因为聚集索引默认建在主键上,整数自增列做聚集索引时写入顺序和物理顺序基本一致,页拆分少。业务上需要对外暴露不可猜测的编号时,可以单独加一个业务编号列并建唯一索引。
- OrderStatus 用 TINYINT 而不是 VARCHAR(50) 存“待支付、已支付、已发货”?这是典型的枚举字段处理问题。TINYINT 占 1 字节,检索时走整数比较,性能远好于字符串比较。状态含义可以在前端或应用层映射,也可以建一张状态字典表。
- 金额用 DECIMAL(18,2),不用 FLOAT。这个我强调多少次都不够:FLOAT 是近似值,存金额算总账时会出现 0.1 + 0.2 = 0.30000000000000004 的尴尬。DECIMAL 是精确数值类型,才是钱的正经选择。
- 时间用 DATETIME2(3),而不是老旧的 DATETIME。DATETIME 的精度只有 3.33 毫秒,而且 2038 年问题虽然不直接命中它(它能到 9999 年),但精度和时区支持都不如 DATETIME2。SQL Server 2019 里习惯上新库一律 DATETIME2。
2.3 外键、索引与约束:数据库帮你兜底,而不是靠应用自觉
我经常看到一种论调:“外键影响性能,所以不用外键,靠应用层保证。”这种说法不是完全没道理,但要注意适用范围。大型互联网高并发场景下,外键确实可能成为写入瓶颈,但普通企业应用、课程设计、中小型系统,外键带来的完整性收益远大于那一点点性能损耗。
sql复制ALTER TABLE dbo.Orders
ADD CONSTRAINT FK_Orders_CustomerId
FOREIGN KEY (CustomerId) REFERENCES dbo.Customers(CustomerId);
这条 SQL 的意思是:Orders 表里的 CustomerId 必须在 Customers 表里存在。它防止你写出“查不到客户的订单”。如果没有外键,应用层做个判断也能拦住大部分情况,但总会有漏网之鱼——比如测试脚本灌数据时、运维直接改库时、两个应用共用一张表时。外键是数据库在说“这事我帮你盯着”,何乐而不为。
不过外键有一个坑:删除父表数据时,如果子表有引用,DELETE 会直接报错。你需要在删除前理清业务逻辑,要么先删子表,要么用逻辑删除(加 IsDeleted 字段标记),生产环境尤其不建议物理删除有业务关联的数据。
索引的话题我留到后面专门讲,建表阶段只要记住一句话:主键、唯一约束、外键会自动建索引,频繁查询的列可以随后按需补索引,千万别盲目“每个字段都建索引”,那会让插入变慢、磁盘膨胀、查询优化器选择困难。
