前言
数据库是应用程序的“数据心脏”,没有数据库,程序只能处理临时数据。这一周我们从“什么是数据库”开始,逐步深入到表的创建、约束的定义、数据的增删改查,再到多表关联查询和事务处理,帮助零基础读者快速上手。
一、数据库基础概念
1.1 什么是数据库
数据库(DB,DataBase):按照数据结构来组织、存储和管理数据的仓库。
简单说,就像 Excel 文件,但数据库能处理更大规模的数据,并且提供强大的查询和管理能力。
1.2 关键术语
| 术语 | 全称 | 含义 |
|---|
| DB | DataBase | 数据库 | | DBMS | DataBase Management System | 数据库管理系统(如 SQL Server、MySQL) | | DBA | DataBase Administrator | 数据库管理员 | | SA | Super Administrator | 超级管理员(SQL Server 中的最高权限账户) |
1.3 主流关系型数据库
| 数据库 | 厂商 | 特点 |
|---|
| Oracle | 甲骨文 | 功能强大,收费,大型企业 | | MySQL | 甲骨文 | 开源免费,中小型项目 | | SQL Server | 微软 | 与 .NET 集成好,中小企业 | | DB2 | IBM | 稳定性高,大型企业 |
💡 我们学习的是 SQL Server 2014,它是微软推出的关系型数据库管理系统,非常适合与 C#/.NET 配合开发。
二、SQL Server 环境与数据库操作
2.1 启动服务
可通过两种方式启动 SQL Server 服务:
2.2 连接服务器(SSMS)
打开 SQL Server Management Studio(SSMS):
2.3 系统数据库
SQL Server 安装后自带四个系统数据库:
-
master:记录所有系统信息 -
model:新数据库的模板 -
msdb:代理服务(计划任务、备份等) -
tempdb:临时数据存储(重启后清空)
2.4 创建用户数据库 - -- 也可以使用图形界面:右键“数据库” → 新建数据库
- CREATE DATABASE MySchool
- ON PRIMARY
- (
- NAME = MySchool_data,
- FILENAME = 'C:\Program Files\MySchool.mdf',
- SIZE = 5MB,
- MAXSIZE = 100MB,
- FILEGROWTH = 10%
- )
- LOG ON
- (
- NAME = MySchool_log,
- FILENAME = 'C:\Program Files\MySchool.ldf',
- SIZE = 2MB,
- MAXSIZE = 50MB,
- FILEGROWTH = 1MB
- );
复制代码
-
mdf:主数据文件(只能有一个) -
.ndf:次要数据文件(可有多个) -
.ldf:事务日志文件(至少一个)
三、数据类型详解
创建表时必须为每列指定数据类型,这是域完整性的第一道防线。
3.1 字符类型
| 类型 | 说明 | 存储方式 |
|---|
| char(n) | 固定长度非 Unicode | 长度固定 n,不足补空格,效率高 | | varchar(n) | 可变长度非 Unicode | 长度可变,节省空间,效率略低 | | nchar(n) | 固定长度 Unicode | 支持多语言,长度固定 | | nvarchar(n) | 可变长度 Unicode | 支持多语言,节省空间 |
选择建议:如果确定长度且不需要多语言,用 char;不确定长度用 varchar;需要存储中文等多字节字符,用 nchar/nvarchar。
3.2 数值类型
| 类型 | 说明 |
|---|
| int | 整数(-21亿 ~ 21亿) | | smallint | 小整数 | | float / real | 浮点数 | | money | 货币类型,默认4位小数 | | bit | 布尔值(0 或 1) |
⚠️ 身份证号不能存为 int(因为有 X,且长度可能超过 int 范围),应用 varchar(18)。
3.3 日期时间
3.4 其他
四、表的创建与约束(数据完整性)
(在SQL中并不区分大小写,所以不必特意大写。)
数据完整性指的是数据的准确性和可靠性,它是数据库设计的核心。完整性分为四类:
4.1 实体完整性(行级)
保证每一行数据是唯一的、可识别的。
-
主键约束(Primary Key):唯一且非空。一张表只能有一个主键。 -
唯一约束(Unique):值唯一,但允许有一个 NULL。 -
标识列(Identity):自动生成数字,无需手动输入。如 identity(1,1) 表示从1开始,每次增1。 - -- 创建学生表
- CREATE TABLE Student
- (
- Id INT PRIMARY KEY IDENTITY(1,1), -- 主键+标识列
- StuNo VARCHAR(20) UNIQUE, -- 学号唯一
- Name NVARCHAR(20) NOT NULL -- 非空
- );
复制代码
4.2 域完整性(列级)
保证列的数据符合预期格式和范围。
-
数据类型(上面已讲) -
非空约束(NOT NULL) -
默认值约束(DEFAULT) -
检查约束(CHECK):自定义表达式验证数据。 - CREATE TABLE Employee
- (
- Id INT PRIMARY KEY IDENTITY,
- Name NVARCHAR(20) NOT NULL,
- Gender CHAR(2) CHECK (Gender IN ('男', '女')), -- 性别只能男或女
- Age INT CHECK (Age BETWEEN 18 AND 60), -- 年龄18~60
- IDCard CHAR(18) CHECK (LEN(IDCard) = 18) -- 身份证18位
- );
复制代码
4.3 引用完整性(表间)
通过外键(Foreign Key) 保证两个表之间的数据一致性。
-
外键引用的列必须是主键或唯一键(通常为主键)。 -
数据类型必须一致。 -
先有主键值,才能添加外键值。 -
删除主键值时,必须先删除所有引用的外键值。 - -- 部门表(主键表)
- CREATE TABLE Dept
- (
- Id INT PRIMARY KEY IDENTITY,
- Name NVARCHAR(20)
- );
- -- 员工表(外键表)
- CREATE TABLE Emp
- (
- Id INT PRIMARY KEY IDENTITY,
- Name NVARCHAR(20),
- DeptId INT FOREIGN KEY REFERENCES Dept(Id) -- 外键
- );
复制代码
4.4 自定义完整性
通常由业务逻辑保证(如触发器、存储过程等),此阶段暂不深入。
五、T-SQL 数据操作语言(DML)
T-SQL 是 SQL Server 对标准 SQL 的扩展,主要包括 DDL(数据定义)、DML(数据操作)、DCL(数据控制)。这里重点学习 DML。
5.1 插入数据(INSERT) - -- 指定列插入(推荐)
- INSERT INTO Student (Name, Gender, Age) VALUES ('张三', '男', 20);
- -- 不指定列(需按表定义顺序)
- INSERT INTO Student VALUES ('李四', '女', 22); -- 若表有标识列,则忽略
- -- 批量插入
- INSERT INTO Student (Name, Gender, Age) VALUES
- ('王五', '男', 21),
- ('赵六', '女', 23);
复制代码
注意:
-
字符串用单引号包裹。 -
标识列不能手动插入。 -
默认值可用 DEFAULT 关键字。
5.2 更新数据(UPDATE) - -- 没有加上 WHERE 条件,会更新整张表(危险!)
- UPDATE Student SET Age = Age + 1;
- -- 带条件的更新(推荐)
- UPDATE Student SET Age = Age + 1 WHERE Name = '张三';
- -- 多列更新
- UPDATE Student SET Gender = '女', Age = 25 WHERE Id = 1;
复制代码
5.3 删除数据(DELETE / TRUNCATE) - -- 删除指定行
- DELETE FROM Student WHERE Id = 5;
- -- 删除所有行(保留表结构,日志记录每条删除)
- DELETE FROM Student;
- -- 快速清空表(释放空间,重置标识列,不可恢复)
- TRUNCATE TABLE Student;
- -- 删除表(表结构也被删除)
- DROP TABLE Student;
复制代码
| 操作 | 速度 | 日志 | 标识列 | 可恢复 |
|---|
| DELETE | 慢 | 逐行记录 | 不重置 | 可以 | | TRUNCATE | 快 | 页释放记录 | 重置 | 不可以 | | DROP | 最快 | 少 | 删表 | 不可以 |
5.4 查询数据(SELECT)
基础查询: - -- 查询所有列
- SELECT * FROM Student;
- -- 查询指定列
- SELECT Name, Age FROM Student;
- -- 使用别名(AS可省略)
- SELECT Name AS 姓名, Age 年龄 FROM Student;
- -- 带条件查询(WHERE)
- SELECT * FROM Student WHERE Age > 20 AND Gender = '男';
- -- 范围查询(BETWEEN)
- SELECT * FROM Student WHERE Age BETWEEN 18 AND 25;
- -- 模糊查询(LIKE)
- -- % 代表任意个字符,_ 代表一个字符
- SELECT * FROM Student WHERE Name LIKE '张%'; -- 姓张的
- SELECT * FROM Student WHERE Name LIKE '李_'; -- 两个字姓李的
- -- 排序(ORDER BY)
- SELECT * FROM Student ORDER BY Age DESC; -- 降序
- -- 空值判断(IS NULL / IS NOT NULL)
- SELECT * FROM Student WHERE Bonus IS NULL;
- -- 前N条(TOP)
- SELECT TOP 5 * FROM Student ORDER BY Age DESC; -- 年龄最大的5个
- SELECT TOP 50 PERCENT * FROM Student; -- 前50%
复制代码
六、多表查询(核心难点)
实际开发中,数据往往分布在多张表中,需要通过连接查询将它们组合起来。
6.1 内连接(INNER JOIN)
只返回两个表中匹配的行。 - -- 隐式内连接(使用 WHERE)
- SELECT e.Name, d.Name
- FROM Emp e, Dept d
- WHERE e.DeptId = d.Id;
- -- 显式内连接(推荐)
- SELECT e.Name, d.Name
- FROM Emp e
- INNER JOIN Dept d ON e.DeptId = d.Id;
复制代码
6.2 外连接(LEFT / RIGHT JOIN)
返回左表(或右表)的所有行,即使匹配不上。 - -- 左外连接:返回左表全部,右表匹配的字段,没匹配则为 NULL
- SELECT e.Name, d.Name
- FROM Emp e
- LEFT JOIN Dept d ON e.DeptId = d.Id;
- -- 右外连接同理
复制代码
6.3 子查询(嵌套查询)
将一个查询结果作为另一个查询的条件或临时表。
单行单列(作为条件): - -- 查询工资最高的员工
- SELECT * FROM Emp
- WHERE Salary = (SELECT MAX(Salary) FROM Emp);
复制代码
多行多列(作为虚拟表): - -- 查询入职日期在2011-11-11之后的员工及部门信息
- SELECT *
- FROM Dept d
- JOIN (SELECT * FROM Emp WHERE JoinDate > '2011-11-11') e
- ON d.Id = e.DeptId;
复制代码
6.4 综合案例:多表连接练习
我们建立了一张完整的员工-部门-职务-工资等级表,以下是几个典型查询: - -- 1. 查询员工编号、姓名、工资、职务名称、职务描述
- SELECT e.Id, e.Ename, e.Salary, j.Jname, j.Description
- FROM Emp e
- JOIN Job j ON e.JobId = j.Id;
- -- 2. 查询员工姓名、工资、工资等级(使用 BETWEEN)
- SELECT e.Ename, e.Salary, g.Grade
- FROM Emp e
- JOIN SalaryGrade g ON e.Salary BETWEEN g.Losalary AND g.Hisalary;
- -- 3. 查询每个部门的编号、名称、位置及员工人数
- SELECT d.Id, d.Dname, d.Loc, COUNT(e.Id) AS EmpCount
- FROM Dept d
- LEFT JOIN Emp e ON d.Id = e.DeptId
- GROUP BY d.Id, d.Dname, d.Loc;
- -- 4. 查询所有员工及其直接上级(自连接 + 左外连接)
- SELECT e1.Ename AS 员工, e2.Ename AS 上级
- FROM Emp e1
- LEFT JOIN Emp e2 ON e1.Mgr = e2.Id;
复制代码
七、事务处理(Transaction)
事务用于保证一组操作要么全部成功,要么全部回滚。经典的转账案例: - -- 创建账户表
- CREATE TABLE Bank
- (
- UName VARCHAR(20),
- UMoney MONEY CHECK (UMoney >= 1)
- );
- INSERT INTO Bank VALUES ('班长', 10000), ('学委', 100);
- -- 模拟转账(班长转5000给学委)
- BEGIN TRANSACTION
- UPDATE Bank SET UMoney = UMoney - 5000 WHERE UName = '班长';
- UPDATE Bank SET UMoney = UMoney + 5000 WHERE UName = '学委';
- COMMIT TRANSACTION
复制代码
但如果在转账过程中发生断电或错误,会导致数据不一致。使用事务回滚: - DECLARE @ErrorCount INT = 0;
- BEGIN TRANSACTION
- UPDATE Bank SET UMoney = UMoney - 5000 WHERE UName = '班长';
- SET @ErrorCount = @ErrorCount + @@ERROR; -- @@ERROR 记录错误号
- UPDATE Bank SET UMoney = UMoney + 5000 WHERE UName = '学委';
- SET @ErrorCount = @ErrorCount + @@ERROR;
- IF @ErrorCount > 0
- BEGIN
- ROLLBACK TRANSACTION; -- 回滚
- PRINT '转账失败';
- END
- ELSE
- BEGIN
- COMMIT TRANSACTION; -- 提交
- PRINT '转账成功';
- END
复制代码
事务四特性(ACID):
-
原子性(Atomicity):要么全部完成,要么全部不完成。 -
一致性(Consistency):事务前后数据状态一致。 -
隔离性(Isolation):并发事务互不干扰。 -
持久性(Durability):提交后数据永久保存。
八、学习心得与总结
这一周我们从零开始迈入了数据库世界,核心感悟如下:
-
约束是数据的守护神:没有约束的表就像没有门锁的房子,数据随时可能“被入侵”。一定要为每个表设计好主键、外键、检查约束等。 -
T-SQL 是 C# 开发者的必备技能:无论使用 ADO.NET 还是 Entity Framework,底层都是 SQL 语句。写一手高效的 SQL 能极大提升程序性能。 -
多表查询是进阶关键:内连接、外连接、子查询三种方式各有适用场景。实际项目中,80% 的查询都涉及多张表,熟练使用连接是区分初级和中级开发者的重要标志。 -
事务是数据一致性的保障:在涉及金额、库存等敏感数据时,一定要使用事务,避免半途而废导致数据错乱。 -
善用图形界面,也要懂 SQL 脚本:虽然 SSMS 提供了友好的可视化界面,但学会写 SQL 脚本可以更高效地管理数据库,也更容易迁移和备份。
|