SQL Server 基础语法详解:从建库建表到约束与增删改查

数据库基础语法的学习,关键在于理解每条语句操作的对象:是数据库、表结构,还是表中的记录。对象分清楚,建库、建表、添加约束与增删改查之间的关系就会自然串联起来。

本文以学生信息表 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 避免对不存在的数据库执行删除。存在性判断不代表数据库一定可以删除;如果仍有其他连接正在使用它,删除操作可能失败。

从建库、建表到约束与增删改查,每条语句都有清晰的职责。实际练习时,先确定操作的是结构还是数据,再确认字段、条件与数据库上下文,才能把这些基础语法准确地用在一起。

相关推荐
这个DBA有点耶1 小时前
MySQL 8.0不可见索引:删索引怕出事,不删又拖慢写入——中间态怎么用?
数据库·mysql·代码规范
测试界的小码农2 小时前
【图数据库实战进阶(01):从零认识 Neo4j,搭建第一个图数据库项目】
数据库·neo4j
Android系统攻城狮3 小时前
Linux FFmpeg深度解析之avio_open调用流程与实战(三十八)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
x_SpiderMan3 小时前
搭建Oracle RAC 集群
数据库·oracle
测试界的小码农3 小时前
【图数据库实战进阶(03):Neo4j 图数据建模实战——节点、关系与属性应该怎么设计?】
数据库·oracle·neo4j
瑞克-桑切斯3 小时前
redis
数据库·redis·缓存
freejackman4 小时前
【工程化】从“单兵作战”到“多引擎架构”:基于门面模式的重构实战
大数据·数据库·hive·sql·测试工具·设计模式·架构
bksczm4 小时前
InnoDB 内存结构总览:Change Buffer、AHI 与 Log Buffer
数据库·mysql
花间相见4 小时前
【计算基础|Redis02】【上手】五大数据类型命令逐条实操
数据库·redis·后端