数据库基础语法的学习,关键在于理解每条语句操作的对象:是数据库、表结构,还是表中的记录。对象分清楚,建库、建表、添加约束与增删改查之间的关系就会自然串联起来。
本文以学生信息表 Students 为主线,将基础操作整理为 8 个部分。每部分结合 SQL 示例说明语句含义和执行结果,覆盖数据库创建与删除、表结构、数据约束、插入、查询、修改及清空操作。
适用环境与阅读约定 :本文使用 SQL Server 的 T-SQL 语法,可在 SQL Server Management Studio(SSMS)中分段执行。建库与删库示例使用
StudentManagerDB,学生表相关示例使用已存在的PeoplesDB,两者是独立练习。若希望在新建数据库中完成表操作,请将对应的USE PeoplesDB统一替换为USE StudentManagerDB。代码按知识点展示,不建议将全文一次性执行。
1. 创建数据库:区分数据库名称、逻辑文件名与物理路径
创建数据库使用 CREATE DATABASE。下面的示例在创建 StudentManagerDB 时,同时指定主数据文件与事务日志文件的名称、存储位置、初始大小和自动增长量。
sql
USE master;
GO
CREATE DATABASE StudentManagerDB
ON PRIMARY
(
NAME = N'StudentManagerDB_data',
FILENAME = N'D:\SQLData\StudentManagerDB_data.mdf',
SIZE = 10MB,
FILEGROWTH = 5MB
)
LOG ON
(
NAME = N'StudentManagerDB_log',
FILENAME = N'D:\SQLData\StudentManagerDB_log.ldf',
SIZE = 3MB,
FILEGROWTH = 1MB
);
GO
执行前需要把 D:\SQLData\ 改为 SQL Server 所在计算机上实际存在、且 SQL Server 服务账户有写入权限的目录;数据库名也不能与已有数据库重复。
| 语法或参数 | 含义 | 本例中的配置 |
|---|---|---|
USE master |
将当前连接的数据库上下文切换为 master |
在系统数据库上下文中执行建库操作 |
ON PRIMARY |
在主文件组中定义数据文件 | 配置主数据文件 |
LOG ON |
定义事务日志文件 | 配置日志文件 |
NAME |
SQL Server 内部使用的逻辑文件名 | StudentManagerDB_data、StudentManagerDB_log |
FILENAME |
文件在服务器上的物理路径 | 数据文件 .mdf、日志文件 .ldf |
SIZE |
文件的初始大小 | 数据文件 10MB,日志文件 3MB |
FILEGROWTH |
文件空间不足时的自动增长量 | 数据文件每次 5MB,日志文件每次 1MB |
数据库名、逻辑文件名和物理文件名是三个不同概念。 StudentManagerDB 是数据库名,NAME 指定逻辑文件名,FILENAME 指定实际文件的位置。逻辑文件名中的 _data、_log 是便于识别的命名方式,并非强制后缀。
主数据文件通常使用 .mdf,日志文件通常使用 .ldf;.mdf 是数据库数据文件,不能将它解释成备份格式。FILEGROWTH = 5MB 表示每次增长 5MB,也不代表文件最大只能增长到 5MB。
另外,GO 是 SSMS 等工具识别的批处理分隔符 ,不是发送给 SQL Server 引擎执行的 T-SQL 语句。在 SSMS 中,按 F5 才是执行脚本的操作;-- 表示单行注释。
2. 创建学生表:理解字段类型、非空与自增
进入业务数据库后,使用 CREATE TABLE 定义学生表。以下示例沿用原有字段设计,并显式写出可空列的 NULL,使表结构更清楚。
sql
USE PeoplesDB;
GO
CREATE TABLE dbo.Students
(
s_Id INT IDENTITY(1, 1) NOT NULL,
s_Name VARCHAR(20) NOT NULL,
s_Gender CHAR(2) NOT NULL,
s_Birthday DATETIME NOT NULL,
s_IdNo NUMERIC(18, 0) NOT NULL,
s_Age INT NOT NULL,
s_PhoneNumber VARCHAR(50) NULL,
s_Address VARCHAR(50) NULL
);
GO
| 字段 | 类型与定义 | 含义 |
|---|---|---|
s_Id |
INT IDENTITY(1, 1) |
整数编号,从 1 开始,每次递增 1 |
s_Name |
VARCHAR(20) NOT NULL |
姓名,不能为空 |
s_Gender |
CHAR(2) NOT NULL |
性别,不能为空 |
s_Birthday |
DATETIME NOT NULL |
出生日期,不能为空 |
s_IdNo |
NUMERIC(18, 0) NOT NULL |
精度为 18、小数位数为 0 的数值 |
s_Age |
INT NOT NULL |
年龄,不能为空 |
s_PhoneNumber |
VARCHAR(50) NULL |
手机号,允许为空 |
s_Address |
VARCHAR(50) NULL |
地址,允许为空 |
VARCHAR 是变长字符串类型,CHAR 是定长字符串类型。这里的长度参数按字节理解,不能简单认为 VARCHAR(20) 一定可以保存 20 个汉字。示例沿用 VARCHAR、CHAR,中文保存能力取决于相应排序规则的编码;其中 CHAR(2) 保存"男""女"需要编码能够在两个字节内表示这些字符。
NOT NULL 只说明不能保存空值 NULL,并不等于字符串不能是空字符串。IDENTITY(1, 1) 负责生成编号,不会自动建立主键约束;主键将在下一节单独添加。
NUMERIC(18, 0) 中,18 是总有效数字位数,0 是小数位数。此处保留数值型证件号示例,但该定义只能存储数值,不能覆盖包含 X 等字符的证件号。
如果练习过程中需要删除旧表后重建,可先单独执行下面的语句,再执行建表代码。删除表会同时删除表结构和其中的数据。
sql
USE PeoplesDB;
GO
IF EXISTS
(
SELECT *
FROM sysobjects
WHERE name = N'Students'
AND xtype = 'U'
AND id = OBJECT_ID(N'dbo.Students')
)
BEGIN
DROP TABLE dbo.Students;
END;
GO
IF EXISTS 用来判断查询是否返回记录。这里沿用素材中的 sysobjects 写法,它提供当前数据库中的对象信息;xtype = 'U' 用于限定用户表,而非查询表内的学生记录。
3. 添加数据约束:让字段遵守明确规则
表结构规定了"可以保存什么类型的数据",约束进一步规定"哪些数据可以被接受"。本例围绕编号、手机号和地址,分别添加主键、唯一、检查与默认约束。
| 约束 | 本例名称 | 作用 |
|---|---|---|
PRIMARY KEY |
pk_s_Id |
将编号作为唯一标识,不能重复,也不能为 NULL |
UNIQUE |
uq_s_PhoneNumber |
限制手机号重复 |
CHECK |
ck_s_PhoneNumber |
检查手机号长度是否满足条件 |
DEFAULT |
df_s_Address |
插入时未提供地址,或指定 DEFAULT 时使用默认值 |
下面的脚本先判断同名约束是否存在,再删除并重新添加,便于理解约束的维护过程。parent_obj 将判断范围限定在当前学生表,避免仅凭名称识别对象。
sql
USE PeoplesDB;
GO
-- 主键约束
IF EXISTS
(
SELECT * FROM sysobjects
WHERE name = N'pk_s_Id'
AND parent_obj = OBJECT_ID(N'dbo.Students')
)
ALTER TABLE dbo.Students DROP CONSTRAINT pk_s_Id;
ALTER TABLE dbo.Students
ADD CONSTRAINT pk_s_Id PRIMARY KEY (s_Id);
GO
-- 唯一约束
IF EXISTS
(
SELECT * FROM sysobjects
WHERE name = N'uq_s_PhoneNumber'
AND parent_obj = OBJECT_ID(N'dbo.Students')
)
ALTER TABLE dbo.Students DROP CONSTRAINT uq_s_PhoneNumber;
ALTER TABLE dbo.Students
ADD CONSTRAINT uq_s_PhoneNumber UNIQUE (s_PhoneNumber);
GO
-- 检查约束
IF EXISTS
(
SELECT * FROM sysobjects
WHERE name = N'ck_s_PhoneNumber'
AND parent_obj = OBJECT_ID(N'dbo.Students')
)
ALTER TABLE dbo.Students DROP CONSTRAINT ck_s_PhoneNumber;
ALTER TABLE dbo.Students
ADD CONSTRAINT ck_s_PhoneNumber
CHECK (LEN(s_PhoneNumber) = 11);
GO
-- 默认约束
IF EXISTS
(
SELECT * FROM sysobjects
WHERE name = N'df_s_Address'
AND parent_obj = OBJECT_ID(N'dbo.Students')
)
ALTER TABLE dbo.Students DROP CONSTRAINT df_s_Address;
ALTER TABLE dbo.Students
ADD CONSTRAINT df_s_Address
DEFAULT (N'地址不详') FOR s_Address;
GO
约束的作用需要按实际定义理解。 UNIQUE 控制重复,CHECK (LEN(...) = 11) 控制长度,两者不会自动验证手机号是否真实有效。SQL Server 的 LEN 不计尾随空格;此处检查也不要求字符必须全部为数字。
手机号列允许为 NULL,上述 CHECK 本身不会阻止 NULL。不过,在 SQL Server 中,这种单列 UNIQUE 约束最多允许一行使用 NULL,不能理解为任意多行都可以留空。
默认约束同样有明确的触发条件:插入时省略地址列,或为它指定 DEFAULT,才会采用"地址不详";显式插入 NULL 不会被默认值替换。默认值是否生效,也不以该列允许为空为前提。
添加约束时,已有记录必须满足相应规则。例如,已经存在重复手机号,唯一约束就无法成功添加。为表设置主键前,还应确认没有其他名称的主键;一个表只能有一个主键。
4. 插入与基础查询:让列和值一一对应
插入数据使用 INSERT INTO ... VALUES ...。显式列出字段后,VALUES 中的数据必须与这些字段按位置对应。
sql
USE PeoplesDB;
GO
INSERT INTO dbo.Students
(
s_Name,
s_Gender,
s_Birthday,
s_IdNo,
s_Age,
s_PhoneNumber,
s_Address
)
VALUES
(
N'李四',
N'男',
'19900202',
4182231990020033,
36,
'15555555555',
N'河南省开封'
);
GO
这里没有为 s_Id 提供值,因为它由自增属性生成。生日使用 YYYYMMDD 形式表达,手机号作为字符串保存;带有 N 前缀的中文常量表示 Unicode 字符串,但写入 VARCHAR、CHAR 列时仍受目标列编码限制。示例数据仅用于说明字段与值的对应关系。
插入完成后,可以查询整行数据,也可以只返回需要的列。
sql
-- 查询全部列
SELECT *
FROM dbo.Students;
-- 只查询姓名、年龄和地址
SELECT s_Name, s_Age, s_Address
FROM dbo.Students;
GO
SELECT 决定返回哪些列,FROM 决定从哪张表读取数据。* 表示所有列;显式列名则让结果只包含指定字段。
在新建的空表中执行一次插入语句后,上述查询会返回这条学生记录。如果已经添加手机号唯一约束,再次插入同一手机号会因重复而失败。
5. 条件与模糊查询:准确描述要找的数据
WHERE 用来筛选符合条件的记录。比较运算适合单个条件,AND 用来组合条件,IS NULL、LIKE、BETWEEN 与 IN 分别处理空值、模糊匹配、范围与集合匹配。
sql
-- 年龄大于 26
SELECT s_Name, s_Age, s_Address
FROM dbo.Students
WHERE s_Age > 26;
-- 年龄等于 26
SELECT s_Name, s_Age, s_Address
FROM dbo.Students
WHERE s_Age = 26;
-- 性别为男
SELECT *
FROM dbo.Students
WHERE s_Gender = N'男';
-- 同时满足"性别为男"和"年龄大于 20"
SELECT *
FROM dbo.Students
WHERE s_Gender = N'男' AND s_Age > 20;
-- 手机号为空
SELECT *
FROM dbo.Students
WHERE s_PhoneNumber IS NULL;
GO
SQL 中相等比较使用 =。判断空值应写成 IS NULL,不能用 = NULL 替代;NULL 表示缺失或未知,也不等同于空字符串。
模糊查询使用 LIKE。% 可以匹配零个或多个字符,放置位置决定了匹配方式。
sql
-- 地址中包含"南"
SELECT *
FROM dbo.Students
WHERE s_Address LIKE N'%南%';
-- 姓名以"张"开头
SELECT *
FROM dbo.Students
WHERE s_Name LIKE N'张%';
-- 年龄处于 20 到 30 之间,包含两端
SELECT *
FROM dbo.Students
WHERE s_Age BETWEEN 20 AND 30;
-- 年龄为 23 或 26
SELECT *
FROM dbo.Students
WHERE s_Age IN (23, 26);
-- 姓名为指定名单中的任意一个
SELECT *
FROM dbo.Students
WHERE s_Name IN (N'张凯', N'李四', N'赵六');
GO
这里需要区分"范围"和"名单":BETWEEN 20 AND 30 包含区间内的值及两个端点;IN (23, 26) 只匹配列出的两个值。LIKE N'%南%' 则是在字符串中查找片段,与精确相等的含义不同。
6. 整理查询结果:别名、拼接、固定列与计算列
查询不仅可以读取原始字段,还可以为列设置显示名称、拼接多个字段,或生成临时的固定列与计算列。
sql
-- 使用 AS 设置列别名
SELECT
s_Id AS [编号],
s_Name AS [名称],
s_Gender AS [性别]
FROM dbo.Students;
-- 使用"别名 = 表达式"设置显示名称,并拼接字符串
SELECT
[学生姓名] = s_Name,
[地址和电话] = s_Address + N'----[' + s_PhoneNumber + N']'
FROM dbo.Students;
-- 在结果中增加固定内容的列
SELECT
s_Name,
s_Gender,
[学校] = N'传鼎'
FROM dbo.Students;
-- 在结果中计算年龄加 5
SELECT
s_Id,
s_Name,
s_Gender,
s_Address,
(s_Age + 5) AS Age
FROM dbo.Students;
GO
第一条查询将结果列显示为"编号""名称""性别",表中的实际字段名不变。第二条查询把地址与电话合并展示,例如 河南省开封----[15555555555],这属于字符串拼接,并不是把多张表合并查询。
在当前常见 SQL Server 环境中,使用 + 拼接字符串时,只要参与拼接的值为 NULL,整个拼接表达式通常也会返回 NULL。因此,字段允许为空时,不能假设拼接结果一定完整。
[学校] = N'传鼎' 只是在结果中增加一列相同文本,不会给表新增字段;(s_Age + 5) AS Age 也只计算展示值,不会修改已保存的年龄。
结果加工与数据修改的区别就在这里: SELECT 中的别名、拼接和计算改变的是返回结果;要改变表内数据,需要使用 UPDATE。
7. 限制返回数量与排序:正确理解 TOP 和 ORDER BY
TOP 用于限制返回的记录数量,ORDER BY 用于规定结果顺序。两者结合时,才可以清楚地表达"按什么顺序取前几条"。
sql
-- 按编号升序取前 2 条
SELECT TOP (2)
s_Name,
s_Gender,
[学校] = N'传鼎'
FROM dbo.Students
ORDER BY s_Id ASC;
-- 按编号升序取记录总数的前 80%
SELECT TOP (80) PERCENT
s_Name,
s_Gender,
[学校] = N'传鼎'
FROM dbo.Students
ORDER BY s_Id ASC;
GO
TOP (2) 表示最多返回 2 行。TOP (80) PERCENT 表示按比例取行,计算结果不是整数时会向上取整。例如,5 行取 80% 返回 4 行,3 行取 80% 返回 3 行。
TOP 本身不是分页。 它只限制一次查询返回的数量,并没有表达"第几页"。如果省略 ORDER BY,也不能把"前两条"理解为固定的两条记录。
排序可以使用字段,也可以使用查询结果中的计算列别名。下面沿用年龄加 5 的表达式,分别进行升序与降序排列。
sql
-- ASC:升序
SELECT
s_Id, s_Name, s_Gender, s_Address,
(s_Age + 5) AS Age
FROM dbo.Students
ORDER BY Age ASC;
-- DESC:降序
SELECT
s_Id, s_Name, s_Gender, s_Address,
(s_Age + 5) AS Age
FROM dbo.Students
ORDER BY Age DESC;
GO
ASC 表示从小到大,DESC 表示从大到小。两条语句都不会改变表中年龄的存储值;当多行的排序值相同时,仅按该值排序不能保证这些行之间的先后顺序。
8. 修改、删除与清空:分清操作范围
UPDATE 修改记录,DELETE 删除记录,TRUNCATE TABLE 清空整张表的数据,DROP DATABASE 则删除整个数据库。执行这些语句前,必须先确认操作对象和条件。
sql
USE PeoplesDB;
GO
-- 将编号为 2 的学生姓名修改为"行名了"
UPDATE dbo.Students
SET s_Name = N'行名了'
WHERE s_Id = 2;
-- 查看修改后的目标记录
SELECT *
FROM dbo.Students
WHERE s_Id = 2;
GO
SET 指定要修改的字段和值,WHERE 指定修改范围。在本例已经设置编号主键的前提下,编号为 2 的记录至多一条;如果该记录不存在,更新影响 0 行。省略 WHERE 则会修改整张表中所有记录的对应字段。
按条件删除使用 DELETE。下面只删除编号为 3 的学生,条件没有匹配记录时,删除影响 0 行。
sql
DELETE FROM dbo.Students
WHERE s_Id = 3;
GO
如果目的是清空整张学生表,可以单独执行 TRUNCATE TABLE。它不能带 WHERE,执行后保留表结构,并将本例的自增编号重置到种子值。
sql
-- 清空操作:确认不再需要表内全部数据后,单独执行
TRUNCATE TABLE dbo.Students;
SELECT *
FROM dbo.Students;
GO
下面的对照可以帮助判断该使用哪种语句。
| 语句 | 操作对象 | 是否可以按 WHERE 筛选 | 执行后的主要结果 |
|---|---|---|---|
UPDATE ... SET ... |
表中的记录 | 可以 | 修改指定字段,记录仍然存在 |
DELETE FROM ... |
表中的记录 | 可以 | 删除匹配记录,保留表结构 |
TRUNCATE TABLE ... |
整张表的数据 | 不可以 | 清空数据,保留表结构,重置自增计数 |
DROP TABLE ... |
整张表 | 不可以 | 删除表结构及数据 |
DROP DATABASE ... |
整个数据库 | 不可以 | 删除整个数据库 |
最后,若要删除第 1 节创建的练习数据库,可切换到 master 后判断数据库是否存在,再执行删除。此处操作的是 StudentManagerDB,不是前面学生表所在的 PeoplesDB。
sql
-- 删库操作:确认目标数据库不再需要后,单独执行
USE master;
GO
IF EXISTS
(
SELECT *
FROM sysdatabases
WHERE name = N'StudentManagerDB'
)
BEGIN
DROP DATABASE StudentManagerDB;
END;
GO
sysdatabases 提供数据库信息,IF EXISTS 避免对不存在的数据库执行删除。存在性判断不代表数据库一定可以删除;如果仍有其他连接正在使用它,删除操作可能失败。
从建库、建表到约束与增删改查,每条语句都有清晰的职责。实际练习时,先确定操作的是结构还是数据,再确认字段、条件与数据库上下文,才能把这些基础语法准确地用在一起。