计算机网络基础
1. 理解 Socket 技术的基础
Socket 是一种网络编程的工具,能够在网络设备之间传输数据。Java 的 Socket
和 ServerSocket
类用于建立 TCP 连接。
2. 使用 Socket 进行数据传输的简单示例
以下代码展示了如何使用 Java 的 Socket
和 ServerSocket
类在客户端和服务器端之间传输数据。
服务器端代码:
import java.io.*;
import java.net.*;
public class SimpleServer {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(8080)) {
System.out.println("Server is listening on port 8080");
Socket socket = serverSocket.accept();
System.out.println("Client connected");
// 从客户端读取数据
InputStream input = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String message = reader.readLine();
System.out.println("Received from client: " + message);
// 向客户端发送数据
OutputStream output = socket.getOutputStream();
PrintWriter writer = new PrintWriter(output, true);
writer.println("Hello, client!");
socket.close();
} catch (IOException ex) {
System.out.println("Server exception: " + ex.getMessage());
}
}
}
客户端代码:
import java.io.*;
import java.net.*;
public class SimpleClient {
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 8080)) {
// 向服务器发送数据
OutputStream output = socket.getOutputStream();
PrintWriter writer = new PrintWriter(output, true);
writer.println("Hello, server!");
// 从服务器读取数据
InputStream input = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String message = reader.readLine();
System.out.println("Received from server: " + message);
} catch (IOException ex) {
System.out.println("Client exception: " + ex.getMessage());
}
}
}
3. 使用 Socket 传输文件
Socket 也可以用于传输文件。以下代码示例展示了如何通过 Socket 传输文件:
服务器端代码(接收文件):
import java.io.*;
import java.net.*;
public class FileServer {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(8080)) {
System.out.println("Server is listening on port 8080");
Socket socket = serverSocket.accept();
System.out.println("Client connected");
// 接收文件
InputStream input = socket.getInputStream();
FileOutputStream fileOutput = new FileOutputStream("received_file.txt");
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1) {
fileOutput.write(buffer, 0, bytesRead);
}
fileOutput.close();
socket.close();
System.out.println("File received");
} catch (IOException ex) {
System.out.println("Server exception: " + ex.getMessage());
}
}
}
客户端代码(发送文件):
import java.io.*;
import java.net.*;
public class FileClient {
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 8080)) {
// 发送文件
File file = new File("send_file.txt");
FileInputStream fileInput = new FileInputStream(file);
OutputStream output = socket.getOutputStream();
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fileInput.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
fileInput.close();
System.out.println("File sent");
} catch (IOException ex) {
System.out.println("Client exception: " + ex.getMessage());
}
}
}
4. 使用浏览器访问 Socket 服务器
我们可以创建一个非常简单的 HTTP 服务器,来响应浏览器的请求。浏览器通过发送 HTTP 请求访问服务器,服务器响应 HTML 页面。
服务器端代码(响应浏览器请求):
import java.io.*;
import java.net.*;
public class HttpServer {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(8080)) {
System.out.println("HTTP Server is listening on port 8080");
while (true) {
Socket socket = serverSocket.accept();
System.out.println("New client connected");
// 读取浏览器请求
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while (!(line = reader.readLine()).isEmpty()) {
System.out.println(line); // 打印请求头
}
// 向浏览器响应 HTML
OutputStream output = socket.getOutputStream();
PrintWriter writer = new PrintWriter(output, true);
writer.println("HTTP/1.1 200 OK");
writer.println("Content-Type: text/html");
writer.println();
writer.println("<html><body><h1>Hello from Simple HTTP Server</h1></body></html>");
socket.close();
}
} catch (IOException ex) {
System.out.println("Server exception: " + ex.getMessage());
}
}
}
测试步骤:
- 运行服务器端程序
HttpServer
。 - 打开浏览器并访问
http://localhost:8080
。 - 浏览器会显示服务器返回的 HTML 内容。
总结
这些示例展示了 Socket 的基础应用、如何通过 Socket 传输数据和文件,以及如何使用浏览器访问一个简单的 Socket 服务器。
数据库基础
1. 什么是数据库
数据库是一种用于存储、管理和检索数据的结构化系统。它允许用户以高效和可靠的方式操作数据。常用的数据库系统管理数据的增删改查(CRUD操作,Create/Retrieve/Update/Delete)。
在 Java 中,我们通常使用 JDBC(Java Database Connectivity)与数据库进行交互。
2. 常见的数据库
以下是一些常见的数据库管理系统:
-
关系型数据库:
- MySQL:开源、常用的关系型数据库。
- PostgreSQL:功能丰富的开源数据库,支持复杂查询。
- Oracle:企业级商业数据库系统。
- SQL Server:由微软开发,广泛应用于企业。
-
非关系型数据库(NoSQL):
- MongoDB:面向文档的数据库,适合大规模数据存储。
- Cassandra:分布式的NoSQL数据库,用于处理大量数据。
3. 数据模型
数据模型用于描述数据的结构、关系和约束。常见的数据模型包括:
- 层次模型:数据以树的形式组织,每个记录有父子关系。
- 网状模型:数据通过图来表示,记录间可以有多对多的关系。
- 关系模型:数据通过表(关系)来表示,是目前最常用的模型。
在关系模型中,数据通过行和列存储。每个表中的一行代表一个记录,列表示字段或属性。关系数据库系统(RDBMS)使用 SQL(Structured Query Language)来操作数据。
4. 数据库的创建
我们以 MySQL 为例,展示如何通过 Java 使用 JDBC 创建数据库、表和插入数据。
连接到 MySQL 数据库并创建数据库:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class CreateDatabaseExample {
public static void main(String[] args) {
String jdbcURL = "jdbc:mysql://localhost:3306/";
String username = "root";
String password = "password"; // 你的MySQL密码
try (Connection connection = DriverManager.getConnection(jdbcURL, username, password)) {
Statement statement = connection.createStatement();
String sql = "CREATE DATABASE IF NOT EXISTS TestDB";
statement.executeUpdate(sql);
System.out.println("Database created successfully");
} catch (Exception e) {
e.printStackTrace();
}
}
}
创建表并插入数据:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class CreateTableExample {
public static void main(String[] args) {
String jdbcURL = "jdbc:mysql://localhost:3306/TestDB";
String username = "root";
String password = "password";
try (Connection connection = DriverManager.getConnection(jdbcURL, username, password)) {
Statement statement = connection.createStatement();
// 创建表
String createTableSQL = "CREATE TABLE IF NOT EXISTS Users (" +
"ID INT PRIMARY KEY AUTO_INCREMENT, " +
"Name VARCHAR(50), " +
"Email VARCHAR(50))";
statement.executeUpdate(createTableSQL);
System.out.println("Table created successfully");
// 插入数据
String insertSQL = "INSERT INTO Users (Name, Email) VALUES ('Alice', 'alice@example.com')";
statement.executeUpdate(insertSQL);
System.out.println("Data inserted successfully");
} catch (Exception e) {
e.printStackTrace();
}
}
}
5. 数据库的规范化
数据库规范化是一个设计过程,其目的是通过消除数据冗余、确保数据一致性来提高数据库的效率。规范化将数据库分为多个表,并通过外键(foreign key)来保持数据的关系。
- 第一范式(1NF):确保每个表中的字段是不可分的原子值。
- 第二范式(2NF):消除部分依赖,即非主键字段必须完全依赖主键。
- 第三范式(3NF):消除传递依赖,非主键字段不应该依赖其他非主键字段。
举例:规范化一个数据库
假设我们有一个未规范化的表 Orders
,它包括订单信息和客户信息:
OrderID | CustomerName | CustomerAddress | ProductName | Quantity |
---|---|---|---|---|
1 | Alice | 123 Street | Laptop | 1 |
2 | Bob | 456 Avenue | Phone | 2 |
为了规范化,我们可以将这个表拆分为两个表,一个存储订单信息,另一个存储客户信息。
-
Orders
表:OrderID CustomerID ProductName Quantity 1 101 Laptop 1 2 102 Phone 2 -
Customers
表:CustomerID CustomerName CustomerAddress 101 Alice 123 Street 102 Bob 456 Avenue
这样,客户的信息被单独存储,每次订单只需引用客户的 CustomerID
,避免了重复存储客户数据。
补充
BCNF(Boyce-Codd范式)是第三范式(3NF)的一个更严格的版本。它的目标是确保每个非平凡的函数依赖关系都符合范式要求,进一步消除数据冗余和异常情况。
BCNF 的定义
BCNF 要求:
- 表格中的每一个决定属性(determinant)必须是一个候选键。
候选键是表中的唯一标识一个记录的字段或字段组合。
如果一个表已经满足第三范式,但仍然存在非主属性依赖于非主键的情况,就需要将表进一步分解到 BCNF。
BCNF 与 3NF 的区别
BCNF 主要解决的是 3NF 不能解决的“非主属性依赖于候选键的部分”问题。简单来说:
- 3NF 解决非主键字段对主键的传递依赖问题。
- BCNF 解决主属性依赖于非主键的问题(候选键的依赖关系)。
BCNF 的例子
假设我们有一个表 StudentCourse
,记录学生选修课程的情况:
StudentID | CourseID | Instructor | InstructorOffice |
---|---|---|---|
1 | CS101 | Dr. Smith | Room 101 |
2 | CS101 | Dr. Smith | Room 101 |
3 | CS102 | Dr. Johnson | Room 202 |
- StudentID 是学生的唯一标识。
- CourseID 是课程的唯一标识。
- Instructor 是课程的授课老师,每个课程由一个固定的老师教授,并且每个老师都有一个固定的办公室。
在这个例子中,我们可以看到以下依赖关系:
StudentID
->CourseID
CourseID
->Instructor
和InstructorOffice
我们可以看到,CourseID
决定了 Instructor
和 InstructorOffice
,这意味着 Instructor
和 InstructorOffice
依赖于 CourseID
,而不是完全依赖于 StudentID
(主键)。
此时,虽然这个表可能已经符合 3NF,但它并不符合 BCNF,因为 CourseID
是一个决定属性,但它不是候选键(即它不能唯一标识一行)。
将表分解为 BCNF
要将这个表分解为符合 BCNF 的表格,我们需要把 Instructor
和 InstructorOffice
的依赖从原始表中移除。我们可以创建两个表:
-
StudentCourse
表:StudentID CourseID 1 CS101 2 CS101 3 CS102 -
CourseInstructor
表:CourseID Instructor InstructorOffice CS101 Dr. Smith Room 101 CS102 Dr. Johnson Room 202
现在,StudentCourse
表中每一行的数据依赖于候选键 StudentID
,而 CourseInstructor
表中,每一行的数据依赖于候选键 CourseID
,这就满足了 BCNF。
BCNF 的总结
- BCNF 是 3NF 的加强版本,它消除了 3NF 不能消除的某些依赖关系。
- BCNF 的要求是每一个决定属性必须是候选键。
- 当表中的非主键属性依赖于候选键的部分属性时,就需要将其分解到 BCNF。
通过将表分解为多个表,BCNF 进一步减少了数据冗余,并确保了数据一致性和完整性。
总结
- 数据库 是存储和管理数据的工具,常见的数据库有 MySQL、PostgreSQL、MongoDB 等。
- 数据模型 描述了数据的组织方式,关系模型是最常见的数据模型。
- 数据库创建 通过 SQL 和 JDBC 实现,Java 提供了强大的工具来与数据库进行交互。
- 数据库规范化 是设计数据库的关键步骤,可以消除冗余并提高数据的完整性。
SQL
1. SQL 简介
SQL(Structured Query Language,结构化查询语言)是一种用于管理关系型数据库的数据查询语言。它允许用户执行以下操作:
- 查询数据(SELECT)
- 插入数据(INSERT)
- 更新数据(UPDATE)
- 删除数据(DELETE)
- 创建表和修改表结构(CREATE、ALTER、DROP)
2. 创建数据库和表
2.1 创建数据库
CREATE DATABASE TestDB;
2.2 选择数据库
USE TestDB;
2.3 创建表
CREATE TABLE Users (
ID INT PRIMARY KEY AUTO_INCREMENT,
Name VARCHAR(50),
Email VARCHAR(50),
Age INT
);
3. 插入数据
INSERT INTO Users (Name, Email, Age)
VALUES ('Alice', 'alice@example.com', 25);
INSERT INTO Users (Name, Email, Age)
VALUES ('Bob', 'bob@example.com', 30);
4. 查询数据
4.1 查询所有列
SELECT * FROM Users;
4.2 查询指定列
SELECT Name, Email FROM Users;
4.3 添加条件(WHERE)
SELECT * FROM Users WHERE Age > 25;
4.4 使用 AND
和 OR
条件
SELECT * FROM Users WHERE Age > 25 AND Name = 'Bob';
4.5 使用 LIKE
模糊匹配
SELECT * FROM Users WHERE Name LIKE 'A%'; -- 查找名字以A开头的用户
4.6 排序查询结果(ORDER BY)
SELECT * FROM Users ORDER BY Age DESC;
5. 更新数据
UPDATE Users
SET Age = 26
WHERE Name = 'Alice';
6. 删除数据
6.1 删除特定数据
DELETE FROM Users WHERE Name = 'Bob';
6.2 删除所有数据
DELETE FROM Users; -- 仅删除数据,保留表结构
7. 表的修改
7.1 添加新列
ALTER TABLE Users ADD PhoneNumber VARCHAR(15);
7.2 修改列的数据类型
ALTER TABLE Users MODIFY Email VARCHAR(100);
7.3 删除列
ALTER TABLE Users DROP COLUMN PhoneNumber;
8. 连接查询(JOIN)
8.1 内连接(INNER JOIN)
SELECT Orders.OrderID, Users.Name, Orders.Product
FROM Orders
INNER JOIN Users ON Orders.UserID = Users.ID;
8.2 左连接(LEFT JOIN)
SELECT Users.Name, Orders.Product
FROM Users
LEFT JOIN Orders ON Users.ID = Orders.UserID;
9. 聚合函数
9.1 计数(COUNT)
SELECT COUNT(*) FROM Users;
9.2 最大值、最小值(MAX, MIN)
SELECT MAX(Age) FROM Users;
SELECT MIN(Age) FROM Users;
9.3 平均值(AVG)
SELECT AVG(Age) FROM Users;
9.4 求和(SUM)
SELECT SUM(Age) FROM Users;
10. 分组(GROUP BY)
SELECT Age, COUNT(*) FROM Users
GROUP BY Age;
11. 子查询
11.1 简单子查询
SELECT Name FROM Users
WHERE Age = (SELECT MAX(Age) FROM Users);
11.2 关联子查询
SELECT Name FROM Users u
WHERE EXISTS (SELECT 1 FROM Orders o WHERE o.UserID = u.ID);
12. 视图(View)
12.1 创建视图
CREATE VIEW UserOrders AS
SELECT Users.Name, Orders.Product
FROM Users
JOIN Orders ON Users.ID = Orders.UserID;
12.2 查询视图
SELECT * FROM UserOrders;
12.3 删除视图
DROP VIEW UserOrders;
13. 事务(Transactions)
事务确保一系列的 SQL 操作要么全部成功,要么全部失败。
13.1 开始事务
START TRANSACTION;
13.2 提交事务
COMMIT;
13.3 回滚事务
ROLLBACK;
14. 索引(Indexes)
索引用于加速数据检索。
14.1 创建索引
CREATE INDEX idx_name ON Users (Name);
14.2 删除索引
DROP INDEX idx_name ON Users;
15. 数据库的约束
- NOT NULL:确保字段不能为空。
- UNIQUE:确保字段中的所有值唯一。
- PRIMARY KEY:唯一标识表中的每条记录。
- FOREIGN KEY:用来链接两个表。
15.1 创建带约束的表
CREATE TABLE Orders (
OrderID INT PRIMARY KEY,
UserID INT,
Product VARCHAR(100),
FOREIGN KEY (UserID) REFERENCES Users(ID)
);
16. 正则化(Normalization)
数据库的正则化用于减少冗余数据并确保数据一致性。常见的正则化范式包括:
- 第一范式 (1NF):消除重复的列,确保每列原子化。
- 第二范式 (2NF):消除部分依赖,确保所有非主键字段依赖于主键。
- 第三范式 (3NF):消除传递依赖,确保非主键字段不依赖于其他非主键字段。
- BCNF:消除所有非主键依赖。
数据库查询语言(DQL)
在 SQL 中,根据操作类型的不同,可以将 SQL 分为不同的类别,如数据定义语言(DDL)、数据操纵语言(DML)、数据库查询语言(DQL)和数据库控制语言(DCL)。下面我们专注于 数据库查询语言(DQL) 和 数据库控制语言(DCL)。
1. 数据库查询语言(DQL)
DQL(Data Query Language,数据查询语言)主要用于从数据库中检索数据。它的核心命令是 SELECT
,允许用户执行复杂的查询操作,如过滤、排序、分组、聚合等。
核心语句:SELECT
SELECT
语句是 DQL 的基础,它允许从一个或多个表中检索数据。
基础用法
SELECT column1, column2, ...
FROM table_name;
column1, column2
:要检索的列。table_name
:数据所在的表名。
DQL 常见操作
-
查询所有数据
SELECT * FROM Users;
- 使用
*
可以检索表中的所有列。
- 使用
-
条件查询(WHERE)
SELECT Name, Email FROM Users WHERE Age > 25;
WHERE
子句用于过滤结果。
-
排序查询结果(ORDER BY)
SELECT Name, Age FROM Users ORDER BY Age DESC;
ORDER BY
用于按指定列排序,ASC
表示升序(默认),DESC
表示降序。
-
分组(GROUP BY)
SELECT Age, COUNT(*) FROM Users GROUP BY Age;
GROUP BY
用于对结果进行分组。
-
聚合函数
- COUNT:计数。
- SUM:求和。
- AVG:求平均值。
- MAX:求最大值。
- MIN:求最小值。
例子:
SELECT AVG(Age) FROM Users;
-
连接查询(JOIN)
SELECT Orders.OrderID, Users.Name, Orders.Product FROM Orders INNER JOIN Users ON Orders.UserID = Users.ID;
INNER JOIN
通过匹配两个表的字段来获取相关联的数据。
复杂查询示例
SELECT u.Name, u.Email, o.Product
FROM Users u
INNER JOIN Orders o ON u.ID = o.UserID
WHERE u.Age > 25
ORDER BY u.Name ASC;
- 该查询会检索用户
Users
和订单Orders
中的相关数据,按用户名升序排列。
2. 数据库控制语言(DCL)
DCL(Data Control Language,数据控制语言)用于控制数据库中的权限和访问,主要是对用户的权限进行授予和撤销,确保数据库的安全性。
核心语句:GRANT
和 REVOKE
GRANT
:用于向用户授予权限。REVOKE
:用于从用户撤销权限。
常见 DCL 操作
-
创建用户
在授予权限之前,通常需要先创建用户:CREATE USER 'username'@'localhost' IDENTIFIED BY 'password';
username
是新用户的用户名,password
是用户的密码。
-
授予权限(GRANT)
GRANT SELECT, INSERT ON TestDB.Users TO 'username'@'localhost';
- 该语句授予用户
username
对TestDB
数据库中的Users
表执行SELECT
(查询)和INSERT
(插入)的权限。
- 该语句授予用户
-
查看当前用户的权限
SHOW GRANTS FOR 'username'@'localhost';
- 该语句显示给定用户的权限列表。
-
撤销权限(REVOKE)
REVOKE SELECT ON TestDB.Users FROM 'username'@'localhost';
- 该语句撤销用户
username
对TestDB.Users
表的SELECT
权限。
- 该语句撤销用户
-
授予所有权限
GRANT ALL PRIVILEGES ON TestDB.* TO 'username'@'localhost';
ALL PRIVILEGES
表示授予所有权限,TestDB.*
表示该用户拥有对TestDB
数据库中所有表的完全访问权限。
-
撤销所有权限
REVOKE ALL PRIVILEGES ON TestDB.* FROM 'username'@'localhost';
- 该语句撤销用户对
TestDB
数据库中所有表的所有权限。
- 该语句撤销用户对
权限级别
权限可以在不同级别上授予:
- 数据库级别:对整个数据库授予权限。
GRANT SELECT ON TestDB.* TO 'username'@'localhost';
- 表级别:仅对指定表授予权限。
GRANT INSERT, UPDATE ON TestDB.Users TO 'username'@'localhost';
- 列级别:仅对指定列授予权限。
GRANT SELECT (Name, Email) ON TestDB.Users TO 'username'@'localhost';
- 特定操作:如授予某些高级权限,比如
GRANT OPTION
允许用户向其他用户授予权限。
3. 总结
DQL(数据库查询语言)
- 主要用于从数据库中检索数据,核心命令是
SELECT
。 - 可以通过
WHERE
、ORDER BY
、GROUP BY
、JOIN
等来实现复杂查询和数据分析。
DCL(数据库控制语言)
- 用于管理用户权限,确保数据库安全。
GRANT
用于授予权限,REVOKE
用于撤销权限。
通过熟练掌握 DQL 和 DCL,您可以轻松实现数据库的数据查询操作,并确保数据库访问的安全性。
视图、索引、触发器、事务
1. 视图(View)
概念:
视图是一个虚拟表,它基于 SQL 查询的结果集。视图并不存储实际数据,它只是一个查询的逻辑表示,帮助简化复杂查询、提高安全性和增强数据抽象。
使用场景:
- 简化查询:将复杂的 SQL 查询封装在视图中,简化数据访问。
- 数据安全:通过视图限制用户对敏感数据的访问。
- 提高可重用性:避免多次重复相同的复杂查询。
创建视图的语法:
CREATE VIEW view_name AS
SELECT column1, column2, ...
FROM table_name
WHERE condition;
视图的示例:
创建一个显示用户信息的视图:
CREATE VIEW UserDetails AS
SELECT Name, Email, Age
FROM Users
WHERE Age > 18;
现在你可以像查询表一样查询 UserDetails
视图:
SELECT * FROM UserDetails;
更新视图:
有些视图是可更新的,允许通过视图更新基础表的数据。更新视图的语法与更新表相同:
UPDATE UserDetails
SET Age = 30
WHERE Name = 'Alice';
注意:并不是所有视图都是可更新的,尤其是涉及到多表连接、聚合等操作的视图。
删除视图:
DROP VIEW UserDetails;
2. 索引(Index)
概念:
索引是数据库对象,用于加快查询操作的速度。索引类似于书本的目录,它使得数据库能够更快速地定位所需数据。
使用场景:
- 加速查询:大幅提高 SELECT 语句的执行速度。
- 强制唯一性:例如在主键或唯一约束上创建索引。
- 排序:索引还可以用于加速排序操作(ORDER BY)。
创建索引的语法:
CREATE INDEX index_name ON table_name (column1, column2, ...);
索引的示例:
为 Users
表的 Name
列创建索引:
CREATE INDEX idx_name ON Users (Name);
删除索引:
DROP INDEX idx_name ON Users;
索引的注意事项:
- 虽然索引能加速查询,但也会占用存储空间,并可能减慢插入、更新和删除操作。
- 不要在小表或者频繁更新的列上创建索引,因为索引维护的开销可能大于它带来的性能提升。
3. 触发器(Trigger)
概念:
触发器是一个自动执行的 SQL 语句,它在某些事件发生时触发。触发器通常与 INSERT
、UPDATE
或 DELETE
操作相关联,能够在这些操作之前或之后自动执行某些操作。
使用场景:
- 数据完整性:确保在插入或更新数据时满足特定业务规则。
- 自动日志记录:在数据更新时自动保存修改记录。
- 复杂验证:在数据修改前后执行复杂的业务逻辑。
创建触发器的语法:
CREATE TRIGGER trigger_name
AFTER | BEFORE INSERT | UPDATE | DELETE
ON table_name
FOR EACH ROW
BEGIN
-- 触发器逻辑
END;
触发器的示例:
在 Users
表中创建一个触发器,当插入新用户时,自动将他们的姓名转换为大写:
CREATE TRIGGER before_insert_user
BEFORE INSERT ON Users
FOR EACH ROW
BEGIN
SET NEW.Name = UPPER(NEW.Name);
END;
- BEFORE:在
INSERT
之前触发。 - NEW:表示新插入的数据。
删除触发器:
DROP TRIGGER trigger_name;
触发器的注意事项:
- 触发器是自动执行的,因此要谨慎使用,以免引起循环或性能问题。
- 过多的触发器会影响数据库的性能,尤其是高频率触发的操作。
4. 事务(Transaction)
概念:
事务是一组 SQL 操作的集合,这些操作要么全部成功,要么全部失败。事务的主要作用是保证数据库的完整性和一致性,尤其是在涉及多个表的复杂操作时。事务遵循 ACID 特性:
- A(原子性):事务是不可分割的操作单位,要么全部完成,要么全部不执行。
- C(一致性):事务执行前后,数据库处于一致的状态。
- I(隔离性):多个事务并发执行时,互不干扰。
- D(持久性):事务提交后,其结果永久保存,即使数据库系统崩溃。
使用场景:
- 多步操作:比如银行转账,涉及账户扣款和存款,两步操作必须要么都成功,要么都失败。
- 数据一致性要求:确保多个相关表的更新操作一致完成。
事务的语法:
-
开启事务:
START TRANSACTION;
-
提交事务:
COMMIT;
-
回滚事务(撤销未提交的更改):
ROLLBACK;
事务的示例:
银行转账示例,用户 A 转账给用户 B:
START TRANSACTION;
-- 从用户 A 的账户中扣除 100
UPDATE Accounts
SET Balance = Balance - 100
WHERE UserID = 'A';
-- 向用户 B 的账户中存入 100
UPDATE Accounts
SET Balance = Balance + 100
WHERE UserID = 'B';
-- 提交事务
COMMIT;
如果在操作过程中任何一步失败,则可以回滚事务,取消所有更改:
ROLLBACK;
自动提交和手动提交:
- 默认情况下,很多数据库系统(如 MySQL)在每个独立的 SQL 语句之后自动提交事务。这种模式称为 自动提交。
- 如果你想要手动控制事务,应该禁用自动提交:
SET AUTOCOMMIT = 0;
事务的注意事项:
- 在执行批量操作时,使用事务可以保证数据一致性。
- 过多的长时间事务会锁定资源,导致性能下降,因此要合理使用事务。
总结
- 视图:视图是 SQL 查询的逻辑表示,它帮助简化查询、提高安全性和数据抽象。
- 索引:索引用于加快查询速度,但需要权衡其在插入、更新和删除操作上的性能开销。
- 触发器:触发器是在特定事件发生时自动执行的 SQL 语句,适用于确保数据完整性和自动化操作。
- 事务:事务是一组 SQL 操作,确保数据库在操作失败时保持一致性,并遵循 ACID 原则。