# SQL基础知识学习指南

1. SQL简介

SQL(Structured Query Language,结构化查询语言)是一种专门用于管理关系型数据库的标准编程语言。它由IBM在20世纪70年代开发,现在已经成为数据库管理系统的国际标准语言。

SQL之所以如此重要,是因为它提供了一套统一的方式来与数据库进行交互。无论你使用的是MySQL、SQL Server、PostgreSQL还是Oracle,SQL的基本语法都是相通的,这使得开发者可以轻松地在不同的数据库系统之间切换。

SQL的主要功能包括:

  • 查询数据(SELECT): 从数据库中检索数据,这是SQL最常用的功能。你可以查询单个表的数据,也可以跨多个表进行复杂查询
  • 插入数据(INSERT): 将新数据添加到数据库表中。可以一次插入一条记录,也可以批量插入多条记录
  • 更新数据(UPDATE): 修改数据库中已存在的数据。可以更新单个字段的值,也可以同时更新多个字段
  • 删除数据(DELETE): 从数据库中移除不再需要的数据。可以删除特定条件的记录,也可以删除所有记录
  • 创建和修改数据库结构(DDL): 定义数据库、表、视图、索引等数据库对象的结构。包括创建、修改和删除这些对象的操作

SQL语言具有以下特点:

  • 声明式语言: 你只需要告诉数据库"要什么",而不需要告诉它"怎么做"
  • 面向集合: SQL操作通常作用于整个数据集,而不是单条记录
  • 非过程化: 不需要编写复杂的控制流程,SQL会自动优化执行路径
  • 标准化: 大部分SQL语法在不同数据库中都是兼容的

2. 基本概念

2.1 数据库表

数据库表是数据存储的基本单位,它以一种类似Excel表格的结构来组织数据。表由行(记录)和列(字段)组成,每一行代表一条完整的数据记录,每一列代表一种特定的数据属性。

复制代码
表名: Students(学生表)
+----+--------+------+----------------------+
| ID | Name   | Age  | Email                |
+----+--------+------+----------------------+
| 1  | 张三   | 20   | zhangsan@example.com |
| 2  | 李四   | 21   | lisi@example.com     |
| 3  | 王五   | 22   | wangwu@example.com   |
+----+--------+------+----------------------+

在这个示例中:

  • Students 是表的名称,用于存储学生信息
  • ID、Name、Age、Email 是列名,分别代表学号、姓名、年龄和邮箱
  • 每一行代表一个学生的完整信息
  • 表的第一行通常是列名,用于描述每一列的含义

设计良好的表结构应该遵循以下原则:

  • 每一列应该具有单一明确的意义
  • 每一行的数据应该是完整且独立的
  • 避免在表中存储重复数据
  • 为每个表设置主键以唯一标识每一行

2.2 数据类型

选择合适的数据类型对于数据库性能和数据完整性至关重要。不同的数据类型占用不同的存储空间,并且支持不同的操作。

  • INT(整数): 用于存储整数值,如年龄、数量、ID等。根据数据库的不同,INT类型可能有不同的取值范围,通常为-2,147,483,648到2,147,483,647

  • VARCHAR(n)(可变长度字符串): 用于存储可变长度的文本数据,如姓名、地址等。n表示最大字符数,实际占用的存储空间取决于实际存储的字符数。例如,VARCHAR(50)最多可以存储50个字符

  • CHAR(n)(固定长度字符串): 用于存储固定长度的文本数据。无论实际存储的字符数是多少,都会占用n个字符的存储空间。CHAR类型适合存储长度固定的数据,如电话号码、邮政编码等

  • DECIMAL(m,d)(精确数值): 用于存储精确的小数,m表示总位数,d表示小数位数。例如,DECIMAL(10,2)表示总共10位数字,其中2位小数,可以存储范围从-99999999.99到99999999.99的数值。这种类型适合存储金额、比例等需要精确计算的数据

  • DATE(日期): 用于存储日期值,格式通常为YYYY-MM-DD,如2026-03-11。可以存储从1000-01-01到9999-12-31之间的日期

  • DATETIME(日期和时间): 用于存储日期和时间信息,格式通常为YYYY-MM-DD HH:MM:SS。例如,2026-03-11 14:30:00表示2026年3月11日下午2点30分

  • BOOLEAN(布尔值): 用于存储真或假的逻辑值,通常用于表示开关状态、是否有效等二元选择

选择数据类型时需要考虑以下因素:

  • 数据的实际范围和精度要求
  • 存储空间的考虑
  • 预期的查询类型和索引需求
  • 不同数据库系统的具体实现

3. SQL语句分类

SQL语句按照功能可以分为三大类:DDL(数据定义语言)、DML(数据操作语言)和DQL(数据查询语言)。此外,还有DCL(数据控制语言)用于权限管理。

3.1 DDL(数据定义语言)

DDL语句用于定义和修改数据库的结构,包括创建、修改和删除数据库对象。这些操作会影响数据库的模式(Schema),通常由数据库管理员或开发者在应用设计阶段执行。

sql 复制代码
-- 创建数据库
-- 在开始使用数据库之前,首先需要创建一个数据库容器
CREATE DATABASE mydb;

-- 创建表
-- 定义表的结构,包括列名、数据类型和各种约束
CREATE TABLE Students (
    ID INT PRIMARY KEY,                    -- ID是主键,唯一标识每个学生
    Name VARCHAR(50) NOT NULL,             -- 姓名不能为空
    Age INT,                               -- 年龄可以是整数
    Email VARCHAR(100) UNIQUE,             -- 邮箱地址必须唯一
    CreateTime DATETIME DEFAULT NOW()      -- 创建时间默认为当前时间
);

-- 删除表
-- 永久删除表及其所有数据,此操作不可恢复
DROP TABLE Students;

-- 修改表结构
-- 可以添加、删除或修改表的列
ALTER TABLE Students ADD COLUMN Gender VARCHAR(10);    -- 添加性别列
ALTER TABLE Students MODIFY COLUMN Age INT NOT NULL;   -- 修改年龄列为必填
ALTER TABLE Students DROP COLUMN CreateTime;          -- 删除创建时间列

DDL操作需要谨慎,因为它们会直接影响数据库结构。在生产环境中,通常需要在执行DDL操作前备份数据,并在测试环境中验证。

3.2 DML(数据操作语言)

DML语句用于操作表中的数据,包括插入、更新和删除记录。这些是日常开发中最常用的SQL操作。

sql 复制代码
-- 插入数据
-- 将新的学生记录添加到表中
INSERT INTO Students (ID, Name, Age, Email)
VALUES (1, '张三', 20, 'zhangsan@example.com');

-- 插入数据时省略某些列
-- 如果列有默认值或允许NULL,可以省略
INSERT INTO Students (ID, Name, Email)
VALUES (2, '李四', 'lisi@example.com');

-- 批量插入
-- 一次插入多条记录,提高效率
INSERT INTO Students (ID, Name, Age)
VALUES 
    (3, '王五', 22),
    (4, '赵六', 21),
    (5, '孙七', 23);

-- 更新数据
-- 修改表中已存在的记录,通常需要使用WHERE子句来限定范围
UPDATE Students SET Age = 21 WHERE ID = 1;

-- 同时更新多个字段
UPDATE Students 
SET Age = 22, Email = 'newemail@example.com' 
WHERE Name = '张三';

-- 批量更新
-- 更新所有年龄大于20岁的学生
UPDATE Students SET Age = Age + 1 WHERE Age > 20;

-- 删除数据
-- 从表中移除记录,同样需要使用WHERE子句来限定范围
DELETE FROM Students WHERE ID = 3;

-- 批量删除
-- 删除所有年龄小于18岁的学生记录
DELETE FROM Students WHERE Age < 18;

-- 警告:删除所有数据
-- 如果不使用WHERE子句,会删除表中的所有记录
-- DELETE FROM Students;

在使用DML语句时,务必注意:

  • UPDATE和DELETE操作必须使用WHERE子句,否则会影响所有记录
  • 对于重要数据的修改操作,建议先备份数据
  • 可以使用事务来确保多个相关操作的原子性
  • 执行前可以先使用SELECT语句验证将要影响的记录

3.3 DQL(数据查询语言)

DQL语句用于从数据库中检索数据,这是SQL中最常用的功能。通过SELECT语句,你可以获取所需的信息,并进行排序、分组和筛选。

sql 复制代码
-- 查询所有数据
-- 星号(*)表示选择所有列,但在生产环境中不推荐使用
SELECT * FROM Students;

-- 查询指定列
-- 只选择需要的列,可以提高查询效率并减少数据传输
SELECT Name, Age FROM Students;

-- 条件查询
-- 使用WHERE子句过滤记录
SELECT * FROM Students WHERE Age > 20;

-- 复杂条件查询
-- 使用AND、OR组合多个条件
SELECT * FROM Students 
WHERE Age > 18 AND Age < 25 
  AND (Name LIKE '张%' OR Name LIKE '李%');

-- 排序
-- 使用ORDER BY对结果进行排序,DESC表示降序,ASC表示升序(默认)
SELECT * FROM Students ORDER BY Age DESC;

-- 多列排序
-- 先按年龄升序排序,年龄相同的按姓名降序排序
SELECT * FROM Students ORDER BY Age ASC, Name DESC;

-- 限制结果数量
-- 使用LIMIT限制返回的记录数,常用于分页查询
SELECT * FROM Students LIMIT 5;

-- 分页查询
-- OFFSET指定从第几条记录开始返回,常用于实现分页功能
-- 每页显示10条记录,获取第3页的数据
SELECT * FROM Students LIMIT 10 OFFSET 20;

-- 去重查询
-- 使用DISTINCT消除重复的记录
SELECT DISTINCT Age FROM Students;

-- 计算字段
-- 可以在查询中进行计算
SELECT Name, Age, Age + 5 AS FutureAge FROM Students;

-- 聚合查询
-- 对数据进行统计汇总
SELECT 
    COUNT(*) as TotalStudents,
    AVG(Age) as AverageAge,
    MAX(Age) as MaxAge,
    MIN(Age) as MinAge
FROM Students;

查询优化技巧:

  • 避免使用SELECT *,明确指定需要的列
  • 为WHERE和JOIN中使用的列创建索引
  • 合理使用LIMIT限制结果数量
  • 在大数据量查询时考虑分页处理
  • 避免在WHERE子句中对列使用函数

4. 高级查询

4.1 WHERE子句

WHERE子句用于过滤查询结果,只返回满足条件的记录。它是SQL查询中最常用的子句之一。

sql 复制代码
-- 比较运算符
-- =: 等于
-- != 或 <>: 不等于
-- >: 大于
-- <: 小于
-- >=: 大于等于
-- <=: 小于等于
SELECT * FROM Students WHERE Age = 20;
SELECT * FROM Students WHERE Age >= 18;
SELECT * FROM Students WHERE Name != '张三';

-- 逻辑运算符
-- AND: 所有条件都必须满足
-- OR: 任一条件满足即可
-- NOT: 对条件取反
SELECT * FROM Students WHERE Age > 18 AND Age < 25;
SELECT * FROM Students WHERE Age = 20 OR Age = 21;
SELECT * FROM Students WHERE NOT (Age < 18);

-- 范围查询
-- BETWEEN: 在指定范围内(包含边界值)
SELECT * FROM Students WHERE Age BETWEEN 18 AND 22;

-- IN: 值在列表中
SELECT * FROM Students WHERE Age IN (18, 19, 20);

-- 模糊匹配
-- LIKE: 用于字符串匹配
-- %: 匹配任意长度的任意字符
-- _: 匹配单个字符
SELECT * FROM Students WHERE Name LIKE '张%';    -- 姓张的学生
SELECT * FROM Students WHERE Name LIKE '%三';    -- 名字以三结尾的学生
SELECT * FROM Students WHERE Name LIKE '张_';    -- 姓张且名字为两个字的学生

-- NULL值判断
-- IS NULL: 值为NULL
-- IS NOT NULL: 值不为NULL
SELECT * FROM Students WHERE Email IS NULL;
SELECT * FROM Students WHERE Email IS NOT NULL;

-- 子查询
-- WHERE子句中可以使用子查询
SELECT * FROM Students 
WHERE Age > (SELECT AVG(Age) FROM Students);

4.2 聚合函数

聚合函数对一组值进行计算,返回单个值。常用于数据统计和分析。

sql 复制代码
-- COUNT: 统计记录数
-- COUNT(*): 统计所有记录数
-- COUNT(column): 统计指定列非NULL的记录数
SELECT COUNT(*) FROM Students;                  -- 统计学生总数
SELECT COUNT(Email) FROM Students;              -- 统计有邮箱的学生数

-- AVG: 计算平均值
SELECT AVG(Age) FROM Students;                  -- 计算平均年龄

-- MAX: 获取最大值
SELECT MAX(Age) FROM Students;                  -- 获取最大年龄

-- MIN: 获取最小值
SELECT MIN(Age) FROM Students;                  -- 获取最小年龄

-- SUM: 计算总和
SELECT SUM(Age) FROM Students;                  -- 计算年龄总和

-- 组合使用聚合函数
SELECT 
    COUNT(*) as 学生总数,
    AVG(Age) as 平均年龄,
    MAX(Age) as 最大年龄,
    MIN(Age) as 最小年龄
FROM Students;

-- 与DISTINCT结合使用
SELECT COUNT(DISTINCT Age) FROM Students;       -- 统计不同年龄的数量

聚合函数注意事项:

  • 聚合函数忽略NULL值(除了COUNT(*))
  • 在使用聚合函数时,其他非聚合列必须出现在GROUP BY子句中
  • 可以给聚合结果起别名,使结果更易读

4.3 GROUP BY 分组

GROUP BY子句将查询结果按照一个或多个列进行分组,通常与聚合函数一起使用,用于对数据进行分类统计。

sql 复制代码
-- 基本分组
-- 按年龄分组,统计每个年龄的学生数量
SELECT Age, COUNT(*) as Count
FROM Students
GROUP BY Age;

-- 多列分组
-- 按年龄和性别分组
SELECT Age, Gender, COUNT(*) as Count
FROM Students
GROUP BY Age, Gender;

-- 分组后使用多个聚合函数
SELECT Age, 
       COUNT(*) as 学生数量,
       AVG(Age) as 平均年龄,
       MAX(Age) as 最大年龄
FROM Students
GROUP BY Age;

-- HAVING子句
-- HAVING用于过滤分组后的结果,类似于WHERE,但作用于分组
-- 查找学生数量大于1的年龄组
SELECT Age, COUNT(*) as Count
FROM Students
GROUP BY Age
HAVING COUNT(*) > 1;

-- HAVING和WHERE的区别
-- WHERE在分组前过滤记录,HAVING在分组后过滤分组
-- 下面的查询先筛选年龄大于18的学生,再分组统计
SELECT Age, COUNT(*) as Count
FROM Students
WHERE Age > 18
GROUP BY Age
HAVING COUNT(*) > 1;

-- WITH ROLLUP
-- 生成分组的汇总行
SELECT Age, COUNT(*) as Count
FROM Students
GROUP BY Age WITH ROLLUP;

GROUP BY使用要点:

  • SELECT中的非聚合列必须出现在GROUP BY中
  • WHERE在GROUP BY之前执行,HAVING在GROUP BY之后执行
  • 可以按多个列进行分组
  • 分组列的顺序会影响结果的组织方式

4.4 连接查询

连接查询用于从多个相关表中检索数据。连接是关系型数据库的核心特性,它允许你通过表之间的关系来整合数据。

sql 复制代码
-- 内连接(INNER JOIN)
-- 只返回两个表中匹配的记录
-- 例如:查询学生及其选课信息
SELECT s.Name, c.CourseName
FROM Students s
INNER JOIN Courses c ON s.ID = c.StudentID;

-- 左连接(LEFT JOIN)
-- 返回左表的所有记录,以及右表中匹配的记录
-- 如果右表没有匹配记录,则显示NULL
-- 例如:查询所有学生及其选课信息,包括没有选课的学生
SELECT s.Name, c.CourseName
FROM Students s
LEFT JOIN Courses c ON s.ID = c.StudentID;

-- 右连接(RIGHT JOIN)
-- 返回右表的所有记录,以及左表中匹配的记录
-- 如果左表没有匹配记录,则显示NULL
-- 例如:查询所有课程及其选修学生,包括没有学生选修的课程
SELECT s.Name, c.CourseName
FROM Students s
RIGHT JOIN Courses c ON s.ID = c.StudentID;

-- 全连接(FULL JOIN)
-- 返回两个表中的所有记录,无论是否匹配
-- 如果没有匹配记录,则显示NULL
-- 注意:MySQL不支持FULL JOIN,可以用UNION ALL模拟
SELECT s.Name, c.CourseName
FROM Students s
FULL JOIN Courses c ON s.ID = c.StudentID;

-- 自连接
-- 表与自身进行连接
-- 例如:查询学生及其同班同学
SELECT s1.Name as 学生姓名, s2.Name as 同班同学
FROM Students s1
INNER JOIN Students s2 ON s1.ClassID = s2.ClassID AND s1.ID != s2.ID;

-- 多表连接
-- 可以连接多个表
SELECT s.Name, c.CourseName, t.TeacherName
FROM Students s
INNER JOIN Courses c ON s.ID = c.StudentID
INNER JOIN Teachers t ON c.TeacherID = t.ID;

-- 连接条件
-- 除了ON子句,还可以使用WHERE进行额外筛选
SELECT s.Name, c.CourseName
FROM Students s
INNER JOIN Courses c ON s.ID = c.StudentID
WHERE c.CourseName LIKE '数学%';

连接查询最佳实践:

  • 始终为表设置别名,提高可读性
  • 明确指定连接条件,避免隐式连接
  • 根据需求选择合适的连接类型
  • 注意NULL值的处理
  • 合理使用索引提高连接性能

5. 约束

约束是数据库用来保证数据完整性和一致性的规则。它们确保存储在数据库中的数据符合业务规则和逻辑要求。

5.1 主键约束(PRIMARY KEY)

主键用于唯一标识表中的每一行记录。一个表只能有一个主键,但主键可以由多个列组成(复合主键)。

sql 复制代码
-- 单列主键
CREATE TABLE Students (
    ID INT PRIMARY KEY,
    Name VARCHAR(50)
);

-- 复合主键
-- 当单个列无法唯一标识记录时使用
CREATE TABLE StudentCourses (
    StudentID INT,
    CourseID INT,
    Score INT,
    PRIMARY KEY (StudentID, CourseID)
);

-- 主键的特点:
-- 1. 值必须唯一,不能重复
-- 2. 值不能为NULL
-- 3. 一个表只能有一个主键
-- 4. 主键会自动创建索引,提高查询效率

5.2 外键约束(FOREIGN KEY)

外键用于建立表与表之间的关系,确保参照完整性。外键值必须是父表中存在的主键值。

sql 复制代码
-- 创建外键约束
CREATE TABLE Courses (
    CourseID INT PRIMARY KEY,
    StudentID INT,
    CourseName VARCHAR(100),
    FOREIGN KEY (StudentID) REFERENCES Students(ID)
);

-- 外键的特点:
-- 1. 外键值必须在父表中存在(或为NULL)
-- 2. 可以定义级联操作(ON DELETE、ON UPDATE)
-- 3. 保证数据的参照完整性

-- 级联操作示例
CREATE TABLE Courses (
    CourseID INT PRIMARY KEY,
    StudentID INT,
    CourseName VARCHAR(100),
    FOREIGN KEY (StudentID) REFERENCES Students(ID)
    ON DELETE CASCADE        -- 删除学生时自动删除相关课程
    ON UPDATE CASCADE       -- 更新学生ID时自动更新相关课程
);

5.3 唯一约束(UNIQUE)

唯一约束确保列中的所有值都是唯一的,但允许NULL值(可以有多个NULL)。

sql 复制代码
CREATE TABLE Students (
    ID INT PRIMARY KEY,
    Email VARCHAR(100) UNIQUE,    -- 邮箱必须唯一
    Phone VARCHAR(20) UNIQUE      -- 电话号码必须唯一
);

-- 唯一约束与主键的区别:
-- 1. 主键不允许NULL,唯一约束允许NULL
-- 2. 一个表只能有一个主键,但可以有多个唯一约束
-- 3. 主键通常用于标识记录,唯一约束用于保证业务规则

5.4 非空约束(NOT NULL)

非空约束确保列不接受NULL值,必须提供具体的数据。

sql 复制代码
CREATE TABLE Students (
    ID INT PRIMARY KEY,
    Name VARCHAR(50) NOT NULL,    -- 姓名不能为空
    Age INT NOT NULL              -- 年龄不能为空
);

-- NOT NULL约束:
-- 1. 强制要求提供值
-- 2. 插入或更新时必须指定该列的值
-- 3. 提高数据完整性

5.5 默认值约束(DEFAULT)

默认值约束为列指定默认值,当插入数据时如果没有为该列提供值,则使用默认值。

sql 复制代码
CREATE TABLE Students (
    ID INT PRIMARY KEY,
    Name VARCHAR(50) NOT NULL,
    Age INT DEFAULT 18,                           -- 默认年龄为18
    Status VARCHAR(20) DEFAULT 'active',          -- 默认状态为active
    CreateTime DATETIME DEFAULT CURRENT_TIMESTAMP -- 默认为当前时间
);

-- 默认值约束:
-- 1. 简化数据插入操作
-- 2. 确保列始终有值
-- 3. 可以使用函数作为默认值

6. 索引

索引是数据库中用于提高查询性能的数据结构。它类似于书的目录,可以帮助快速定位数据,而不需要扫描整个表。

sql 复制代码
-- 创建普通索引
-- 为经常查询的列创建索引
CREATE INDEX idx_student_name ON Students(Name);

-- 创建复合索引
-- 为多个列的组合创建索引
CREATE INDEX idx_student_age_name ON Students(Age, Name);

-- 创建唯一索引
-- 确保列值的唯一性
CREATE UNIQUE INDEX idx_student_email ON Students(Email);

-- 创建全文索引
-- 用于文本搜索
CREATE FULLTEXT INDEX idx_student_description ON Students(Description);

-- 查看索引
SHOW INDEX FROM Students;

-- 删除索引
DROP INDEX idx_student_name ON Students;

-- 索引的优点:
-- 1. 大大提高查询速度
-- 2. 加速ORDER BY和GROUP BY操作
-- 3. 提高数据检索效率

-- 索引的缺点:
-- 1. 占用额外的存储空间
-- 2. 降低INSERT、UPDATE、DELETE的速度
-- 3. 需要维护索引结构

-- 索引使用建议:
-- 1. 为经常用于WHERE、JOIN、ORDER BY的列创建索引
-- 2. 不要为频繁更新的列创建过多索引
-- 3. 选择性高的列更适合创建索引
-- 4. 复合索引的列顺序很重要
-- 5. 定期分析索引使用情况,删除无用索引

7. 视图

视图是基于一个或多个表的虚拟表,它不存储实际数据,而是存储查询逻辑。视图可以简化复杂查询,提高数据安全性。

sql 复制代码
-- 创建视图
-- 创建一个只包含成年学生的视图
CREATE VIEW AdultStudents AS
SELECT ID, Name, Age, Email
FROM Students
WHERE Age >= 18;

-- 使用视图
-- 像使用普通表一样使用视图
SELECT * FROM AdultStudents;

-- 创建复杂视图
-- 包含多个表的连接和聚合
CREATE VIEW StudentStatistics AS
SELECT 
    s.ID,
    s.Name,
    COUNT(c.CourseID) as CourseCount,
    AVG(c.Score) as AverageScore
FROM Students s
LEFT JOIN Courses c ON s.ID = c.StudentID
GROUP BY s.ID, s.Name;

-- 创建可更新视图
-- 某些视图支持更新操作
CREATE VIEW StudentBasicInfo AS
SELECT ID, Name, Age
FROM Students;

-- 更新视图数据
UPDATE StudentBasicInfo SET Age = 21 WHERE ID = 1;

-- 查看视图定义
SHOW CREATE VIEW AdultStudents;

-- 删除视图
DROP VIEW AdultStudents;

-- 视图的优点:
-- 1. 简化复杂查询,提高可读性
-- 2. 提供数据安全性,限制用户访问敏感数据
-- 3. 封装业务逻辑,便于维护
-- 4. 实现数据逻辑独立性

-- 视图的限制:
-- 1. 某些视图不支持更新操作
-- 2. 视图查询可能有性能开销
-- 3. 复杂视图可能导致优化困难

8. 存储过程和函数

存储过程和函数是预编译的SQL代码块,可以重复执行,提高代码复用性和性能。

8.1 存储过程

存储过程是一组为了完成特定功能的SQL语句集合,可以接受参数并返回结果。

sql 复制代码
-- 创建简单存储过程
-- 根据年龄查询学生
DELIMITER //
CREATE PROCEDURE GetStudentsByAge(IN minAge INT)
BEGIN
    SELECT * FROM Students WHERE Age >= minAge;
END //
DELIMITER ;

-- 调用存储过程
CALL GetStudentsByAge(20);

-- 创建带输出参数的存储过程
-- 统计学生数量并返回结果
DELIMITER //
CREATE PROCEDURE GetStudentCount(OUT totalCount INT)
BEGIN
    SELECT COUNT(*) INTO totalCount FROM Students;
END //
DELIMITER ;

-- 调用带输出参数的存储过程
CALL GetStudentCount(@count);
SELECT @count;

-- 创建带输入输出参数的存储过程
DELIMITER //
CREATE PROCEDURE UpdateStudentAge(INOUT studentAge INT)
BEGIN
    SET studentAge = studentAge + 1;
END //
DELIMITER ;

-- 复杂存储过程示例
-- 根据条件查询学生并进行分页
DELIMITER //
CREATE PROCEDURE GetStudentsByPage(
    IN minAge INT,
    IN pageNum INT,
    IN pageSize INT,
    OUT totalRecords INT
)
BEGIN
    -- 获取总记录数
    SELECT COUNT(*) INTO totalRecords 
    FROM Students 
    WHERE Age >= minAge;
    
    -- 获取分页数据
    SELECT * 
    FROM Students 
    WHERE Age >= minAge
    LIMIT pageSize OFFSET (pageNum - 1) * pageSize;
END //
DELIMITER ;

-- 删除存储过程
DROP PROCEDURE GetStudentsByAge;

-- 存储过程的优点:
-- 1. 提高代码复用性
-- 2. 减少网络流量,只传递参数
-- 3. 提高性能,预编译和缓存执行计划
-- 4. 增强安全性,限制数据访问
-- 5. 简化复杂业务逻辑

8.2 函数

函数是返回单个值的可重用代码块,可以在SQL语句中像内置函数一样使用。

sql 复制代码
-- 创建标量函数
-- 计算学生的年级
DELIMITER //
CREATE FUNCTION GetGradeLevel(age INT) RETURNS VARCHAR(20)
DETERMINISTIC
BEGIN
    DECLARE grade VARCHAR(20);
    IF age < 18 THEN
        SET grade = '未成年';
    ELSEIF age < 22 THEN
        SET grade = '大学低年级';
    ELSE
        SET grade = '大学高年级';
    END IF;
    RETURN grade;
END //
DELIMITER ;

-- 使用函数
SELECT Name, Age, GetGradeLevel(Age) as GradeLevel 
FROM Students;

-- 创建聚合函数
-- 统计特定年龄段的学生数量
DELIMITER //
CREATE FUNCTION CountStudentsByAgeRange(minAge INT, maxAge INT) 
RETURNS INT
DETERMINISTIC
READS SQL DATA
BEGIN
    DECLARE count INT;
    SELECT COUNT(*) INTO count 
    FROM Students 
    WHERE Age >= minAge AND Age <= maxAge;
    RETURN count;
END //
DELIMITER ;

-- 使用函数
SELECT CountStudentsByAgeRange(18, 22) as StudentCount;

-- 删除函数
DROP FUNCTION GetGradeLevel;

-- 函数的特点:
-- 1. 必须返回单个值
-- 2. 可以在SQL语句中直接使用
-- 3. 比存储过程更简洁
-- 4. 适合进行计算和数据转换

-- 存储过程与函数的区别:
-- 1. 函数必须返回值,存储过程可以不返回
-- 2. 函数可以在SQL语句中调用,存储过程需要单独调用
-- 3. 函数通常用于计算,存储过程用于复杂操作
-- 4. 函数限制更多,不能执行某些操作

9. 事务

事务是数据库管理系统执行过程中的一个逻辑单位,由一系列操作组成。事务具有ACID特性,确保数据的完整性和一致性。

sql 复制代码
-- 开始事务
-- 标志着事务的开始
START TRANSACTION;
-- 或
BEGIN;

-- 执行多个操作
-- 这些操作要么全部成功,要么全部失败
INSERT INTO Students VALUES (6, '钱八', 24, 'qianba@example.com');
UPDATE Students SET Age = 25 WHERE ID = 1;
DELETE FROM Students WHERE ID = 3;

-- 提交事务
-- 确认所有操作,将更改永久保存到数据库
COMMIT;

-- 回滚事务
-- 取消所有操作,恢复到事务开始前的状态
ROLLBACK;

-- 事务的ACID特性:
-- 原子性(Atomicity):事务中的所有操作要么全部成功,要么全部失败
-- 一致性(Consistency):事务执行前后,数据库从一个一致性状态转换到另一个一致性状态
-- 隔离性(Isolation):并发执行的事务之间互不干扰
-- 持久性(Durability):事务提交后,其结果永久保存在数据库中

-- 事务隔离级别:
-- READ UNCOMMITTED:读未提交,可能读到未提交的数据(脏读)
-- READ COMMITTED:读已提交,避免脏读,但可能读到不可重复读的数据
-- REPEATABLE READ:可重复读,避免脏读和不可重复读,但可能读到幻读
-- SERIALIZABLE:串行化,最高隔离级别,避免所有并发问题

-- 设置隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 实际应用示例
-- 转账操作:从账户A转账100元到账户B
START TRANSACTION;

-- 检查账户A余额是否足够
SELECT Balance INTO @balanceA FROM Accounts WHERE ID = 1;

IF @balanceA >= 100 THEN
    -- 扣除账户A的余额
    UPDATE Accounts SET Balance = Balance - 100 WHERE ID = 1;
    
    -- 增加账户B的余额
    UPDATE Accounts SET Balance = Balance + 100 WHERE ID = 2;
    
    -- 提交事务
    COMMIT;
ELSE
    -- 余额不足,回滚事务
    ROLLBACK;
END IF;

-- 事务使用建议:
-- 1. 保持事务尽可能简短
-- 2. 避免在事务中进行用户交互
-- 3. 合理设置隔离级别
-- 4. 正确处理异常和错误
-- 5. 在关键操作中使用事务确保数据一致性

10. 实用技巧

10.1 常用SQL语句

这些语句在日常开发和维护中经常使用,可以帮助你快速获取数据库信息。

sql 复制代码
-- 查看所有数据库
SHOW DATABASES;

-- 使用指定数据库
USE mydb;

-- 查看当前数据库
SELECT DATABASE();

-- 查看所有表
SHOW TABLES;

-- 查看表结构
DESCRIBE Students;
-- 或
SHOW COLUMNS FROM Students;

-- 查看创建表的SQL
-- 用于了解表的结构和约束
SHOW CREATE TABLE Students;

-- 查看表的索引
SHOW INDEX FROM Students;

-- 查看数据库连接
SHOW PROCESSLIST;

-- 查看数据库状态
SHOW STATUS;

-- 查看数据库变量
SHOW VARIABLES;

-- 查看表大小
SELECT 
    table_name,
    ROUND(((data_length + index_length) / 1024 / 1024), 2) AS size_mb
FROM information_schema.TABLES
WHERE table_schema = 'mydb';

-- 查看表的行数
SELECT table_name, table_rows
FROM information_schema.TABLES
WHERE table_schema = 'mydb';

10.2 性能优化建议

数据库性能优化是一个复杂的主题,以下是一些实用的优化技巧。

sql 复制代码
-- 1. 为经常查询的列创建索引
CREATE INDEX idx_student_name ON Students(Name);
CREATE INDEX idx_student_age ON Students(Age);

-- 2. 避免使用 SELECT *,只查询需要的列
-- 不推荐
SELECT * FROM Students WHERE Age > 20;

-- 推荐
SELECT ID, Name, Age FROM Students WHERE Age > 20;

-- 3. 使用 LIMIT 限制返回结果数量
-- 特别是对于大数据量的查询
SELECT * FROM Students WHERE Age > 20 LIMIT 100;

-- 4. 合理使用 JOIN,避免过多表连接
-- 优化前:连接多个表
SELECT * FROM A 
JOIN B ON A.id = B.id 
JOIN C ON B.id = C.id 
JOIN D ON C.id = D.id;

-- 优化后:考虑是否真的需要所有表
SELECT * FROM A 
JOIN B ON A.id = B.id;

-- 5. 使用 EXISTS 代替 IN
-- IN 子查询可能性能较差
SELECT * FROM Students 
WHERE ID IN (SELECT StudentID FROM Courses WHERE CourseName = '数学');

-- EXISTS 通常性能更好
SELECT * FROM Students s 
WHERE EXISTS (SELECT 1 FROM Courses c WHERE c.StudentID = s.ID AND c.CourseName = '数学');

-- 6. 避免在 WHERE 子句中对列使用函数
-- 性能较差,无法使用索引
SELECT * FROM Students WHERE YEAR(CreateTime) = 2026;

-- 性能更好,可以使用索引
SELECT * FROM Students 
WHERE CreateTime >= '2026-01-01' AND CreateTime < '2027-01-01';

-- 7. 使用批量操作代替循环操作
-- 不推荐:多次插入
INSERT INTO Students VALUES (1, '张三', 20);
INSERT INTO Students VALUES (2, '李四', 21);
INSERT INTO Students VALUES (3, '王五', 22);

-- 推荐:批量插入
INSERT INTO Students VALUES 
    (1, '张三', 20),
    (2, '李四', 21),
    (3, '王五', 22);

-- 8. 定期清理无用数据
-- 删除不再需要的数据,减少表大小
DELETE FROM Students WHERE CreateTime < '2020-01-01';

-- 9. 使用 EXPLAIN 分析查询性能
EXPLAIN SELECT * FROM Students WHERE Age > 20;

-- 10. 合理设计表结构
-- 选择合适的数据类型
-- 避免过度规范化
-- 适当反规范化提高查询性能

10.3 安全性建议

数据库安全是开发中不可忽视的重要方面。

sql 复制代码
-- 1. 使用参数化查询防止SQL注入
-- 不安全:直接拼接SQL
string sql = "SELECT * FROM Students WHERE Name = '" + userName + "'";

-- 安全:使用参数化查询
string sql = "SELECT * FROM Students WHERE Name = @userName";
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
    cmd.Parameters.AddWithValue("@userName", userName);
}

-- 2. 使用最小权限原则
-- 为应用创建专门的数据库用户,只授予必要的权限
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'secure_password';
GRANT SELECT, INSERT, UPDATE ON mydb.Students TO 'app_user'@'localhost';

-- 3. 加密敏感数据
-- 对于密码等敏感信息,应该加密存储
INSERT INTO Users (Username, PasswordHash) 
VALUES ('user1', HASH('sha256', 'password123'));

-- 4. 定期备份数据
-- 设置自动备份计划
mysqldump -u root -p mydb > backup_20260311.sql

-- 5. 限制远程访问
-- 只允许来自特定IP的连接
GRANT ALL PRIVILEGES ON mydb.* TO 'user'@'192.168.1.%';

-- 6. 使用SSL/TLS加密连接
-- 在生产环境中使用加密连接
mysql --ssl-mode=REQUIRED -u user -p

-- 7. 定期更新数据库软件
-- 及时应用安全补丁

-- 8. 审计数据库访问
-- 记录重要的数据库操作

11. C#中使用SQL

作为C#开发者,你需要掌握在C#应用程序中使用SQL的技术。

11.1 使用ADO.NET

ADO.NET是.NET框架提供的数据访问技术,提供了直接操作数据库的能力。

csharp 复制代码
using System;
using System.Data.SqlClient;

class Program
{
    static void Main()
    {
        // 数据库连接字符串
        string connectionString = "Server=localhost;Database=mydb;User Id=sa;Password=yourpassword;";

        // 查询数据
        using (SqlConnection conn = new SqlConnection(connectionString))
        {
            conn.Open();
            
            string sql = "SELECT * FROM Students WHERE Age > @age";
            using (SqlCommand cmd = new SqlCommand(sql, conn))
            {
                cmd.Parameters.AddWithValue("@age", 18);
                
                using (SqlDataReader reader = cmd.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        Console.WriteLine($"ID: {reader["ID"]}, Name: {reader["Name"]}, Age: {reader["Age"]}");
                    }
                }
            }
        }

        // 插入数据
        using (SqlConnection conn = new SqlConnection(connectionString))
        {
            conn.Open();
            
            string sql = "INSERT INTO Students (ID, Name, Age, Email) VALUES (@id, @name, @age, @email)";
            using (SqlCommand cmd = new SqlCommand(sql, conn))
            {
                cmd.Parameters.AddWithValue("@id", 10);
                cmd.Parameters.AddWithValue("@name", "新学生");
                cmd.Parameters.AddWithValue("@age", 20);
                cmd.Parameters.AddWithValue("@email", "newstudent@example.com");
                
                int rowsAffected = cmd.ExecuteNonQuery();
                Console.WriteLine($"插入了 {rowsAffected} 行数据");
            }
        }

        // 更新数据
        using (SqlConnection conn = new SqlConnection(connectionString))
        {
            conn.Open();
            
            string sql = "UPDATE Students SET Age = @age WHERE ID = @id";
            using (SqlCommand cmd = new SqlCommand(sql, conn))
            {
                cmd.Parameters.AddWithValue("@age", 21);
                cmd.Parameters.AddWithValue("@id", 10);
                
                int rowsAffected = cmd.ExecuteNonQuery();
                Console.WriteLine($"更新了 {rowsAffected} 行数据");
            }
        }

        // 删除数据
        using (SqlConnection conn = new SqlConnection(connectionString))
        {
            conn.Open();
            
            string sql = "DELETE FROM Students WHERE ID = @id";
            using (SqlCommand cmd = new SqlCommand(sql, conn))
            {
                cmd.Parameters.AddWithValue("@id", 10);
                
                int rowsAffected = cmd.ExecuteNonQuery();
                Console.WriteLine($"删除了 {rowsAffected} 行数据");
            }
        }

        // 使用事务
        using (SqlConnection conn = new SqlConnection(connectionString))
        {
            conn.Open();
            SqlTransaction transaction = conn.BeginTransaction();
            
            try
            {
                // 执行多个操作
                using (SqlCommand cmd1 = new SqlCommand("INSERT INTO Students VALUES (11, '学生A', 20)", conn, transaction))
                {
                    cmd1.ExecuteNonQuery();
                }
                
                using (SqlCommand cmd2 = new SqlCommand("UPDATE Students SET Age = 21 WHERE ID = 1", conn, transaction))
                {
                    cmd2.ExecuteNonQuery();
                }
                
                // 提交事务
                transaction.Commit();
                Console.WriteLine("事务执行成功");
            }
            catch (Exception ex)
            {
                // 回滚事务
                transaction.Rollback();
                Console.WriteLine($"事务失败:{ex.Message}");
            }
        }

        // 调用存储过程
        using (SqlConnection conn = new SqlConnection(connectionString))
        {
            conn.Open();
            
            using (SqlCommand cmd = new SqlCommand("GetStudentsByAge", conn))
            {
                cmd.CommandType = System.Data.CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@minAge", 20);
                
                using (SqlDataReader reader = cmd.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        Console.WriteLine($"ID: {reader["ID"]}, Name: {reader["Name"]}");
                    }
                }
            }
        }
    }
}

11.2 使用Entity Framework

Entity Framework是微软提供的ORM(对象关系映射)框架,允许你使用.NET对象来操作数据库。

csharp 复制代码
using System;
using System.Linq;

// 1. 定义实体类
public class Student
{
    public int ID { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }
    public DateTime CreateTime { get; set; }
}

// 2. 定义DbContext
public class MyDbContext : Microsoft.EntityFrameworkCore.DbContext
{
    public Microsoft.EntityFrameworkCore.DbSet<Student> Students { get; set; }
    
    protected override void OnConfiguring(Microsoft.EntityFrameworkCore.DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("Server=localhost;Database=mydb;User Id=sa;Password=yourpassword;");
    }
}

class Program
{
    static void Main()
    {
        using (var context = new MyDbContext())
        {
            // 查询数据
            var students = context.Students
                .Where(s => s.Age > 18)
                .OrderBy(s => s.Name)
                .ToList();
            
            foreach (var student in students)
            {
                Console.WriteLine($"ID: {student.ID}, Name: {student.Name}, Age: {student.Age}");
            }

            // 查询单个记录
            var student = context.Students.Find(1);
            if (student != null)
            {
                Console.WriteLine($"找到学生:{student.Name}");
            }

            // 添加新记录
            var newStudent = new Student
            {
                ID = 20,
                Name = "新学生",
                Age = 20,
                Email = "newstudent@example.com",
                CreateTime = DateTime.Now
            };
            context.Students.Add(newStudent);
            context.SaveChanges();
            Console.WriteLine("添加了新学生");

            // 更新记录
            var updateStudent = context.Students.Find(20);
            if (updateStudent != null)
            {
                updateStudent.Age = 21;
                context.SaveChanges();
                Console.WriteLine("更新了学生信息");
            }

            // 删除记录
            var deleteStudent = context.Students.Find(20);
            if (deleteStudent != null)
            {
                context.Students.Remove(deleteStudent);
                context.SaveChanges();
                Console.WriteLine("删除了学生");
            }

            // 使用LINQ进行复杂查询
            var results = from s in context.Students
                          where s.Age >= 18 && s.Age <= 22
                          orderby s.Name
                          select new { s.Name, s.Age, s.Email };
            
            foreach (var result in results)
            {
                Console.WriteLine($"{result.Name}, {result.Age}, {result.Email}");
            }

            // 使用Include加载关联数据
            var studentsWithCourses = context.Students
                .Include(s => s.Courses)
                .ToList();

            // 使用事务
            using (var transaction = context.Database.BeginTransaction())
            {
                try
                {
                    var student1 = new Student { ID = 21, Name = "学生1", Age = 20 };
                    var student2 = new Student { ID = 22, Name = "学生2", Age = 21 };
                    
                    context.Students.Add(student1);
                    context.Students.Add(student2);
                    
                    context.SaveChanges();
                    transaction.Commit();
                    Console.WriteLine("事务执行成功");
                }
                catch (Exception ex)
                {
                    transaction.Rollback();
                    Console.WriteLine($"事务失败:{ex.Message}");
                }
            }

            // 使用原生SQL查询
            var studentsFromSql = context.Students
                .FromSqlRaw("SELECT * FROM Students WHERE Age > {0}", 18)
                .ToList();

            // 执行原生SQL命令
            context.Database.ExecuteSqlRaw("UPDATE Students SET Age = Age + 1 WHERE ID = {0}", 1);
        }
    }
}

11.3 Dapper

Dapper是一个轻量级的ORM框架,性能接近ADO.NET,使用简单。

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using Dapper;

class Program
{
    static void Main()
    {
        string connectionString = "Server=localhost;Database=mydb;User Id=sa;Password=yourpassword;";

        using (var conn = new SqlConnection(connectionString))
        {
            // 查询数据
            var students = conn.Query<Student>("SELECT * FROM Students WHERE Age > @Age", new { Age = 18 });
            
            foreach (var student in students)
            {
                Console.WriteLine($"ID: {student.ID}, Name: {student.Name}");
            }

            // 查询单个记录
            var student = conn.QuerySingleOrDefault<Student>("SELECT * FROM Students WHERE ID = @ID", new { ID = 1 });

            // 插入数据
            var newStudent = new Student { ID = 30, Name = "新学生", Age = 20 };
            var rowsAffected = conn.Execute("INSERT INTO Students (ID, Name, Age) VALUES (@ID, @Name, @Age)", newStudent);
            Console.WriteLine($"插入了 {rowsAffected} 行");

            // 更新数据
            rowsAffected = conn.Execute("UPDATE Students SET Age = @Age WHERE ID = @ID", new { Age = 21, ID = 30 });
            Console.WriteLine($"更新了 {rowsAffected} 行");

            // 删除数据
            rowsAffected = conn.Execute("DELETE FROM Students WHERE ID = @ID", new { ID = 30 });
            Console.WriteLine($"删除了 {rowsAffected} 行");

            // 执行存储过程
            var results = conn.Query<Student>("GetStudentsByAge", new { minAge = 20 }, commandType: System.Data.CommandType.StoredProcedure);
        }
    }
}

public class Student
{
    public int ID { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }
}

12. 学习建议

掌握SQL需要系统的学习和大量的实践。以下是一些学习建议:

  1. 基础优先: 先掌握基本的CRUD(创建、读取、更新、删除)操作,这是SQL的核心。理解SELECT、INSERT、UPDATE、DELETE的基本语法和用法

  2. 多练习: 通过实际项目练习SQL编写。可以设计一些练习场景,如学生管理系统、订单管理系统等,从简单到复杂逐步练习

  3. 理解原理: 深入了解索引、事务等底层原理。理解这些概念有助于写出更高效的SQL语句

  4. 学习优化: 学习SQL性能优化技巧。掌握EXPLAIN命令的使用,了解如何分析和优化查询性能

  5. 结合实际: 在C#项目中应用SQL知识。通过实际项目加深对SQL的理解和应用

  6. 循序渐进: 不要试图一次性掌握所有内容。从基本查询开始,逐步学习连接、子查询、存储过程等高级特性

  7. 多做实验: 在测试数据库中尝试各种SQL语句,观察结果,理解每个命令的作用

  8. 阅读源码: 学习优秀的开源项目中的SQL编写方式,学习最佳实践

  9. 关注安全: 始终关注SQL注入等安全问题,学习如何编写安全的SQL语句

  10. 持续学习: SQL技术不断发展,持续学习新的特性和最佳实践

13. 常见数据库

不同的数据库系统各有特点,了解它们有助于选择合适的数据库:

  • MySQL: 最流行的开源数据库,广泛用于Web应用。优点是免费、社区活跃、文档丰富;适合中小型项目

  • SQL Server: Microsoft开发的数据库,与C#和.NET框架集成良好。优点是强大的工具支持、良好的性能、企业级功能;适合Windows环境下的项目

  • PostgreSQL: 功能强大的开源数据库,支持复杂的数据类型和高级功能。优点是开源、功能丰富、扩展性强;适合需要复杂查询和高级功能的项目

  • SQLite: 轻量级的嵌入式数据库,无需服务器。优点是零配置、单文件、跨平台;适合移动应用、桌面应用和小型网站

  • Oracle: 企业级数据库系统,功能强大但成本较高。优点是稳定可靠、功能全面、安全性高;适合大型企业级应用

选择数据库时考虑因素:

  • 项目规模和需求
  • 预算限制
  • 团队技术栈
  • 社区支持和文档
  • 性能要求
  • 可扩展性需求

14. 学习资源

丰富的学习资源可以帮助你更快地掌握SQL:

在线教程和文档:

  • W3Schools SQL教程:适合初学者的入门教程
  • 菜鸟教程SQL:中文教程,内容全面
  • 各数据库官方文档:最权威的参考资料

练习平台:

  • LeetCode SQL练习:通过算法题练习SQL
  • SQL Fiddle:在线SQL练习环境
  • HackerRank SQL挑战:有趣的SQL练习题目

书籍推荐:

  • 《SQL必知必会》:入门经典书籍
  • 《高性能MySQL》:深入理解MySQL
  • 《SQL反模式》:学习如何避免常见错误

实战项目:

  • 设计并实现一个简单的学生管理系统
  • 创建一个电商订单系统
  • 开发一个博客系统

社区和论坛:

  • Stack Overflow:解决具体问题
  • 数据库相关论坛:交流学习经验
  • GitHub开源项目:学习优秀的SQL实践

重要提示

  1. 防止SQL注入: 在实际开发中,建议使用参数化查询来防止SQL注入攻击。始终对用户输入进行验证和清理

  2. 备份重要数据: 在执行任何可能修改或删除数据的操作前,务必备份重要数据

  3. 测试环境: 在生产环境执行任何SQL操作前,先在测试环境中验证

  4. 持续学习: SQL技术不断发展,保持学习新特性和最佳实践

  5. 安全第一: 始终考虑数据安全性,遵循最小权限原则

SQL是每个开发者都应该掌握的重要技能。通过系统学习和持续练习,你将能够高效地使用SQL来管理和操作数据,为你的应用程序提供强大的数据支持。祝你学习顺利!

相关推荐
陈皮糖..30 分钟前
27 届运维实习笔记|第三、四周:从流程熟练到故障排查,企业运维实战深化
运维·笔记·sql·nginx·ci/cd·云计算·jenkins
weixin_520649871 小时前
数据库函数
数据库
Bert.Cai1 小时前
MySQL LPAD()函数详解
数据库·mysql
OnlyEasyCode3 小时前
Navicat 任务自动备份指定数据库
数据库
if else3 小时前
Redis 哨兵集群部署方案
数据库·redis
yejqvow123 小时前
Pandas 高效实现组内跨行时间戳匹配与布尔标记
jvm·数据库·python
了不起的云计算V3 小时前
从DeepSeek V4适配看国产算力的三个拐点
数据库·人工智能
qq_189807033 小时前
html标签如何提升可访问性_aria-label与title区别【指南】
jvm·数据库·python
norq juox3 小时前
MySQL 导出数据
数据库·mysql·adb
qq_349317484 小时前
mysql如何设置定时自动备份脚本_编写shell脚本与cron任务
jvm·数据库·python