【AI大数据工程师特训笔记】第05讲:关联查询

目录

[第一章 多表关联基础概念](#第一章 多表关联基础概念)

[1.1 什么是多表关联](#1.1 什么是多表关联)

[1.2 为什么需要多表关联](#1.2 为什么需要多表关联)

[1.3 银行业务案例:数据表设计](#1.3 银行业务案例:数据表设计)

[第二章 关联类型详解](#第二章 关联类型详解)

[2.1 INNER JOIN(内连接)](#2.1 INNER JOIN(内连接))

[2.1.1 概念性解释](#2.1.1 概念性解释)

[2.1.2 核心特点](#2.1.2 核心特点)

[2.1.3 基本语法](#2.1.3 基本语法)

[2.1.4 银行业务示例](#2.1.4 银行业务示例)

[2.2 LEFT JOIN(左外连接)](#2.2 LEFT JOIN(左外连接))

[2.2.1 概念性解释](#2.2.1 概念性解释)

[2.2.2 核心特点](#2.2.2 核心特点)

[2.2.3 基本语法](#2.2.3 基本语法)

[2.2.4 银行业务示例](#2.2.4 银行业务示例)

[2.3 RIGHT JOIN(右外连接)](#2.3 RIGHT JOIN(右外连接))

[2.3.1 概念性解释](#2.3.1 概念性解释)

[2.3.2 核心特点](#2.3.2 核心特点)

[2.3.3 示例(等价于上面的 LEFT JOIN)](#2.3.3 示例(等价于上面的 LEFT JOIN))

[2.4 FULL JOIN(全外连接)](#2.4 FULL JOIN(全外连接))

[2.4.1 概念性解释](#2.4.1 概念性解释)

[2.4.2 核心特点](#2.4.2 核心特点)

[2.4.3 基本语法](#2.4.3 基本语法)

[2.4.4 银行业务示例](#2.4.4 银行业务示例)

[2.5 交叉连接(CROSS JOIN)------ 扩展内容](#2.5 交叉连接(CROSS JOIN)—— 扩展内容)

[2.5.1 概念性解释](#2.5.1 概念性解释)

[2.5.2 语法](#2.5.2 语法)

[2.5.3 示例](#2.5.3 示例)

[2.6 自连接(Self JOIN)------ 扩展内容](#2.6 自连接(Self JOIN)—— 扩展内容)

[2.6.1 概念性解释](#2.6.1 概念性解释)

[2.6.2 示例](#2.6.2 示例)

[第三章 关联查询中的条件过滤](#第三章 关联查询中的条件过滤)

[3.1 ON 子句与 WHERE 子句的区别](#3.1 ON 子句与 WHERE 子句的区别)

[3.2 多条件关联](#3.2 多条件关联)

[第四章 EXISTS 与 NOT EXISTS 子查询](#第四章 EXISTS 与 NOT EXISTS 子查询)

[4.1 EXISTS 基本概念](#4.1 EXISTS 基本概念)

[4.2 NOT EXISTS 基本概念](#4.2 NOT EXISTS 基本概念)

[4.3 EXISTS 与 IN 的比较](#4.3 EXISTS 与 IN 的比较)

[4.4 性能优化建议](#4.4 性能优化建议)

[第五章 多表关联实战](#第五章 多表关联实战)

[5.1 三表关联(内连接)](#5.1 三表关联(内连接))

[5.2 多层 LEFT JOIN(保留左表全部)](#5.2 多层 LEFT JOIN(保留左表全部))

[5.3 使用聚合函数的关联](#5.3 使用聚合函数的关联)

[5.4 联合查询(UNION)------ 扩展内容](#5.4 联合查询(UNION)—— 扩展内容)


第一章 多表关联基础概念

1.1 什么是多表关联

多表关联是关系型数据库的核心功能。通过关联条件,我们可以将多个表中的数据"连接"起来,实现一次查询获取分散在不同表中的相关数据。

零基础解释:想象你在银行工作。客户信息存在一张表,账户信息存在另一张表。要查询"张三的存款余额",你就需要把这两张表按"客户编号"关联起来,才能得到完整的答案。

1.2 为什么需要多表关联

  • 避免数据冗余(遵循数据库设计规范):如果把客户信息和账户信息全塞在一张表里,一个客户有多个账户就会重复存储客户的姓名、地址等,浪费空间且容易出错。

  • 提高数据一致性:客户姓名只存一处,修改一次,所有关联账户自动生效。

  • 实现复杂业务查询:例如"找出所有本月交易额超过 10 万元的客户及其开户支行"。

1.3 银行业务案例:数据表设计

为了讲解关联查询,我们先创建一套银行业务的数据库对象。以下所有 SQL 均适用于 PostgreSQL(也兼容多数关系数据库)。

sql 复制代码
-- 创建银行业务的 schema(命名空间)
CREATE SCHEMA IF NOT EXISTS bank;

-- 1. 客户表 (customers)
CREATE TABLE bank.customers (
    customer_id   INTEGER PRIMARY KEY,   -- 客户编号
    full_name     VARCHAR(50) NOT NULL,  -- 客户姓名
    id_card       VARCHAR(18) UNIQUE,    -- 身份证号
    phone         VARCHAR(20),           -- 联系电话
    register_date DATE DEFAULT CURRENT_DATE
);

-- 2. 支行表 (branches)
CREATE TABLE bank.branches (
    branch_id     INTEGER PRIMARY KEY,   -- 支行编号
    branch_name   VARCHAR(50) NOT NULL,  -- 支行名称
    city          VARCHAR(30),           -- 所在城市
    manager_name  VARCHAR(50)            -- 行长姓名
);

-- 3. 账户表 (accounts)
CREATE TABLE bank.accounts (
    account_id    INTEGER PRIMARY KEY,   -- 账户号
    customer_id   INTEGER NOT NULL,      -- 所属客户编号
    branch_id     INTEGER NOT NULL,      -- 开户支行编号
    account_type  VARCHAR(20) DEFAULT '储蓄账户', -- 账户类型:储蓄/支票/信用卡
    balance       NUMERIC(12,2) DEFAULT 0.00,     -- 余额(单位:元)
    open_date     DATE DEFAULT CURRENT_DATE,
    status        VARCHAR(10) DEFAULT '正常',    -- 正常/冻结/销户
    FOREIGN KEY (customer_id) REFERENCES bank.customers(customer_id),
    FOREIGN KEY (branch_id)   REFERENCES bank.branches(branch_id)
);

-- 4. 交易记录表 (transactions)
CREATE TABLE bank.transactions (
    trans_id      INTEGER PRIMARY KEY,
    account_id    INTEGER NOT NULL,               -- 发生交易的账户
    trans_type    VARCHAR(10) NOT NULL,           -- 存入/取款/转账/消费
    amount        NUMERIC(12,2) NOT NULL,
    trans_time    TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    target_account INTEGER,                       -- 转账时的对方账户号
    FOREIGN KEY (account_id) REFERENCES bank.accounts(account_id)
);


-- 插入支行数据
INSERT INTO bank.branches VALUES (1, '总行营业部', '北京', '王行长');
INSERT INTO bank.branches VALUES (2, '浦东分行', '上海', '李行长');
INSERT INTO bank.branches VALUES (3, '深圳科技园支行', '深圳', '张行长');

-- 插入客户数据
INSERT INTO bank.customers VALUES (101, '张三', '110101199001011234', '13800001111', '2020-01-10');
INSERT INTO bank.customers VALUES (102, '李四', '310101199502023456', '13912345678', '2021-03-15');
INSERT INTO bank.customers VALUES (103, '王芳', '440301198803036789', '13687654321', '2019-11-20');
INSERT INTO bank.customers VALUES (104, '赵雷', '510101200012048888', '15900000000', '2023-05-01');

-- 插入账户数据
INSERT INTO bank.accounts VALUES (1001, 101, 1, '储蓄账户', 50000.00, '2020-01-15', '正常');
INSERT INTO bank.accounts VALUES (1002, 101, 1, '支票账户', 12000.00, '2020-02-01', '正常');
INSERT INTO bank.accounts VALUES (1003, 102, 2, '储蓄账户', 8000.00, '2021-03-20', '正常');
INSERT INTO bank.accounts VALUES (1004, 103, 3, '储蓄账户', 200000.00, '2019-12-01', '正常');
INSERT INTO bank.accounts VALUES (1005, 103, 3, '信用卡账户', 5000.00, '2020-06-01', '正常');
INSERT INTO bank.accounts VALUES (1006, 104, 2, '储蓄账户', 1000.00, '2023-05-10', '冻结');  -- 冻结账户
-- 注意:没有账户的客户?赵雷有账户(1006),但状态冻结。我们还可以插入一个无账户的客户测试外连接。
INSERT INTO bank.customers VALUES (105, '孙梅', '420106199810105555', '17711112222', '2022-08-08');

-- 插入交易记录
INSERT INTO bank.transactions VALUES (5001, 1001, '存入', 20000.00, '2025-01-10 09:30:00', NULL);
INSERT INTO bank.transactions VALUES (5002, 1001, '取款', 5000.00, '2025-01-15 14:20:00', NULL);
INSERT INTO bank.transactions VALUES (5003, 1003, '存入', 3000.00, '2025-02-01 11:00:00', NULL);
INSERT INTO bank.transactions VALUES (5004, 1004, '转账', 10000.00, '2025-02-10 16:00:00', 1001);
INSERT INTO bank.transactions VALUES (5005, 1002, '消费', 200.00, '2025-02-14 19:30:00', NULL);
INSERT INTO bank.transactions VALUES (5006, 1005, '消费', 1800.00, '2025-02-18 10:00:00', NULL);

说明:以上数据中,客户孙梅(customer_id=105)没有任何账户,用于演示外连接;赵雷账户被冻结;张三有两个账户(储蓄+支票)。

第二章 关联类型详解

2.1 INNER JOIN(内连接)

2.1.1 概念性解释

INNER JOIN 就像两个朋友圈的交集 。只返回两个表中都匹配的记录。如果某客户没有账户,或者某账户不属于任何客户,则不会出现在结果中。

银行业务场景:查询"所有已有账户的客户及其开户支行名称"。如果一个客户还没开户(比如刚注册),就不会出现。

2.1.2 核心特点

  • 只返回两个表中都存在的匹配记录

  • 类似于数学中的"交集"

  • 是最常用的一种关联方式

2.1.3 基本语法

sql 复制代码
SELECT 列1
      ,列2
      , ...
FROM 表1
INNER JOIN 表2 
        ON 表1.关联列 = 表2.关联列;

2.1.4 银行业务示例

sql 复制代码
-- 查询每个账户对应的客户姓名和账户余额(只显示有账户的客户)
SELECT a.account_id
      ,c.full_name  AS 客户姓名
      ,a.account_type
      ,a.balance
FROM bank.accounts a
INNER JOIN bank.customers c 
        ON a.customer_id = c.customer_id;

结果:孙梅(无账户)不会出现,赵雷的账户(状态冻结)仍会出现,因为他有账户记录。

2.2 LEFT JOIN(左外连接)

2.2.1 概念性解释

LEFT JOIN 以左表为主,完整展示左表的所有记录,右边没有匹配的列就用 NULL 填充。

银行业务场景:查询"所有客户及其账户信息"。即使客户尚未开立任何账户,我们也希望看到该客户的信息,而账户列显示为空。

2.2.2 核心特点

  • 返回左表的所有记录,不管右表是否有匹配

  • 右表无匹配时显示 NULL

  • 常用于"包含所有......并显示相关......"的场景

2.2.3 基本语法

sql 复制代码
SELECT columns
FROM 表1
LEFT JOIN 表2 
       ON 表1.关联列 = 表2.关联列;

2.2.4 银行业务示例

sql 复制代码
-- 查询所有客户及其账户号(包括没有账户的客户)
SELECT c.customer_id
      ,c.full_name
      ,a.account_id
      ,a.balance
FROM bank.customers c
LEFT JOIN bank.accounts a 
       ON c.customer_id = a.customer_id;

结果:孙梅(customer_id=105)会显示,但 account_id 和 balance 为 NULL。

2.3 RIGHT JOIN(右外连接)

2.3.1 概念性解释

RIGHT JOIN 以右表为主,与 LEFT JOIN 功能对称。实际工作中很少单独使用,因为可以通过调换表顺序用 LEFT JOIN 实现。

银行业务场景:查询"所有账户及其所属客户信息",如果以 accounts 为右表,效果等同于把 accounts 放左边做 LEFT JOIN。

2.3.2 核心特点

  • 返回右表的所有记录,左表无匹配时显示 NULL

  • 可用 LEFT JOIN 替代(交换表的顺序)

2.3.3 示例(等价于上面的 LEFT JOIN)

sql 复制代码
-- 查询所有账户及其客户姓名(右连接写法)
SELECT a.account_id
      ,c.full_name
FROM bank.customers c
RIGHT JOIN bank.accounts a 
        ON c.customer_id = a.customer_id;
-- 这条语句与 "accounts LEFT JOIN customers" 结果相同

2.4 FULL JOIN(全外连接)

2.4.1 概念性解释

FULL JOIN 返回两个表的所有记录,匹配的合并在一起,不匹配的部分用 NULL 填充。相当于 LEFT JOIN 和 RIGHT JOIN 的并集。

银行业务场景:查询"所有客户和所有账户的完全对应关系"。即使客户没有账户,或者账户没有绑定客户(理论上不会发生,因为外键约束),都会出现在结果中。

2.4.2 核心特点

  • 返回两个表的全部记录

  • 匹配的记录合并显示

  • 不匹配的部分用 NULL 填充

2.4.3 基本语法

sql 复制代码
SELECT columns
FROM 表1
FULL OUTER JOIN 表2 
             ON 表1.关联列 = 表2.关联列;

2.4.4 银行业务示例

sql 复制代码
-- 查询所有客户与所有账户的完整关系
SELECT c.full_name
      ,a.account_id
      ,a.balance
FROM bank.customers c
FULL OUTER JOIN bank.accounts a
             ON c.customer_id = a.customer_id;

结果:包含所有客户(即使无账户)和所有账户(即使无客户,但受外键约束,所有账户都有客户,所以此例中不会出现无客户的账户)。

2.5 交叉连接(CROSS JOIN)------ 扩展内容

2.5.1 概念性解释

CROSS JOIN 返回两个表的笛卡尔积:左表的每一行与右表的每一行组合。结果行数 = 左表行数 × 右表行数。

银行业务场景:极少直接使用,但可用于生成测试数据。例如,想为每个客户生成每个支行的虚拟推荐关系。

2.5.2 语法

sql 复制代码
SELECT *
FROM 表1
CROSS JOIN 表2;

2.5.3 示例

sql 复制代码
-- 列出所有客户和所有支行的组合(生成潜在业务关系)
SELECT c.full_name, b.branch_name
FROM bank.customers c
CROSS JOIN bank.branches b;

2.6 自连接(Self JOIN)------ 扩展内容

2.6.1 概念性解释

自连接是将一张表与自身进行关联。必须使用表别名来区分"两个不同的实例"。

银行业务场景:查询"同一个支行中,余额超过支行平均余额的账户"。或者更简单的:查询"每个员工的上司"(但我们的表没有员工,可以用客户表演示?不够贴切)。我们可以用账户表:查询"同一个客户名下,余额大于另一账户的账户对"。

2.6.2 示例

sql 复制代码
-- 查找同一个客户名下,余额比另一个账户高的账户对
SELECT a1.account_id AS 账户A
      ,a2.account_id AS 账户B
      ,a1.balance AS 余额A
      ,a2.balance AS 余额B
FROM bank.accounts a1
JOIN bank.accounts a2 
  ON a1.customer_id = a2.customer_id 
 AND a1.balance > a2.balance;

第三章 关联查询中的条件过滤

3.1 ON 子句与 WHERE 子句的区别

在关联查询中,ON 用于指定连接条件,WHERE 用于对连接后的结果集进行筛选。把条件放在 ON 中还是 WHERE 中,对于 INNER JOIN 效果相同,但对于 OUTER JOIN 则完全不同

(1)示例对比

sql 复制代码
-- 案例:左连接 + ON 条件(过滤条件在ON中)
SELECT c.full_name
      ,a.account_id
      ,a.balance
FROM bank.customers c
LEFT JOIN bank.accounts a 
       ON c.customer_id = a.customer_id 
      AND a.balance > 10000;

-- 案例:左连接 + WHERE 条件(过滤条件在WHERE中)
SELECT c.full_name
      ,a.account_id
      ,a.balance
FROM bank.customers c
LEFT JOIN bank.accounts a 
       ON c.customer_id = a.customer_id
WHERE a.balance > 10000;

结果差异

  • 第一种(ON):保留所有客户,只有那些余额>10000 的账户才会显示账户信息;余额≤10000 或没有账户的客户仍会显示(账户列为 NULL)。

  • 第二种(WHERE):因为 WHERE 条件要求 a.balance > 10000,不满足该条件的行(包括客户无账户时的 NULL)会被整体过滤,最终只显示"有账户且余额>10000"的客户,等同于内连接。

建议:如果希望左表全部保留,附加过滤条件应写在 ON 子句中;如果希望过滤整个结果,写在 WHERE 中。

3.2 多条件关联

可以在 ON 中使用 AND 连接多个条件。

sql 复制代码
-- 查询客户张三的储蓄账户信息
SELECT c.full_name
      ,a.account_id
      ,a.balance
FROM bank.customers c
JOIN bank.accounts a 
    ON c.customer_id = a.customer_id 
   AND a.account_type = '储蓄账户'
WHERE c.full_name = '张三';

第四章 EXISTS 与 NOT EXISTS 子查询

4.1 EXISTS 基本概念

EXISTS 是一个用于判断子查询 中是否存在至少一行数据 的逻辑运算符:当子查询能返回任何结果(哪怕只有一行)时,EXISTS 的条件就为"真";反之,如果子查询一行也查不到,条件就为"假"。它通常用在 WHERE 子句中,例如"查询所有有过交易记录的客户"------只需判断 EXISTS (SELECT 1 FROM accounts JOIN transactions ON ... WHERE accounts.customer_id = customers.customer_id),一旦存在匹配的交易记录,该客户就会被选中。因为 EXISTS 只关心"有没有数据",不在乎具体数据内容,所以子查询里写 SELECT 1SELECT * 效果一样,且执行效率通常较高。

银行业务场景:查询"发生过交易的客户"。

(1)语法

sql 复制代码
SELECT 列 FROM 表1
WHERE EXISTS (SELECT 1 FROM 表2 WHERE 连接条件);

(2)示例

sql 复制代码
-- 查找至少有一笔交易记录的客户
SELECT c.full_name
FROM bank.customers c
WHERE EXISTS (
    SELECT 1 
    FROM bank.accounts a
    JOIN bank.transactions t 
      ON a.account_id = t.account_id
    WHERE a.customer_id = c.customer_id
);

4.2 NOT EXISTS 基本概念

NOT EXISTSEXISTS 正好相反:当子查询没有返回任何行 时,条件为"真";只要子查询至少查出一行,条件就为"假"。因此,它常用于查找"不存在某种关联"的记录,例如"查询从未有过交易记录的客户"------写成 WHERE NOT EXISTS (SELECT 1 FROM accounts JOIN transactions ON ... WHERE accounts.customer_id = customers.customer_id),那些在子查询中找不到匹配交易行的客户就会被选中。同样,NOT EXISTS 只关心行是否存在,不关心数据内容,且能正确处理子查询中的 NULL 值,避免了 NOT IN 可能出现的空结果陷阱。

银行业务场景:查询"从未进行过任何交易的客户"。

(1)示例

sql 复制代码
-- 查找从未有过交易的客户(包括无账户的客户)
SELECT c.full_name
FROM bank.customers c
WHERE NOT EXISTS (
    SELECT 1
    FROM bank.accounts a
    JOIN bank.transactions t 
      ON a.account_id = t.account_id
    WHERE a.customer_id = c.customer_id
);

4.3 EXISTS 与 IN 的比较

特性 EXISTS / NOT EXISTS IN / NOT IN
NULL 安全 ✅ 安全,不受子查询中 NULL 影响 ❌ NOT IN 遇到 NULL 可能返回空集
性能(大数据量) 通常更快(可提前停止) 可能较慢(需去重、处理 NULL)
可读性 稍复杂 更直观

(1)NOT IN 的 NULL 陷阱示例

sql 复制代码
-- 错误示例:子查询中可能包含 NULL,导致结果为空
SELECT * 
FROM bank.customers
WHERE customer_id NOT IN (
        SELECT customer_id 
        FROM bank.accounts 
        WHERE 1=0
        ); -- 故意无数据,但假设 accounts 表有 NULL customer_id? 
-- 实际更常见:子查询某列有 NULL,NOT IN 会不返回任何行。
-- 解决办法:子查询中添加 IS NOT NULL 或使用 NOT EXISTS。

4.4 性能优化建议

  • 子查询中连接字段(如 customer_id)务必建立索引。

  • EXISTS 子查询中使用 SELECT 1(或任意常量),无需 SELECT *

  • 对于存在性检查,EXISTS 通常比 JOIN 更高效,因为它不需要去重。

第五章 多表关联实战

5.1 三表关联(内连接)

sql 复制代码
-- 查询每笔交易对应的客户姓名、开户支行、交易类型和金额
SELECT t.trans_id
      ,c.full_name AS 客户
      ,b.branch_name AS 支行
      ,t.trans_type
      ,t.amount
      ,t.trans_time
FROM bank.transactions t
INNER JOIN bank.accounts a 
        ON t.account_id = a.account_id
INNER JOIN bank.customers c 
        ON a.customer_id = c.customer_id
INNER JOIN bank.branches b 
        ON a.branch_id = b.branch_id
LIMIT 10;

5.2 多层 LEFT JOIN(保留左表全部)

sql 复制代码
-- 查询所有支行、支行下的账户、账户的交易记录(即使没有账户或没有交易也显示)
SELECT b.branch_name
      ,a.account_id
      ,a.balance
      ,t.trans_type
      ,t.amount
FROM bank.branches b
LEFT JOIN bank.accounts a 
       ON b.branch_id = a.branch_id
LEFT JOIN bank.transactions t 
       ON a.account_id = t.account_id
ORDER BY b.branch_name, a.account_id;

5.3 使用聚合函数的关联

sql 复制代码
-- 统计每个支行的客户数(去重)、账户总数、平均余额
SELECT b.branch_name
      ,COUNT(DISTINCT a.customer_id) AS 客户数量
      ,COUNT(a.account_id) AS 账户总数
      ,AVG(a.balance) AS 平均余额
FROM bank.branches b
LEFT JOIN bank.accounts a 
       ON b.branch_id = a.branch_id
GROUP BY b.branch_id, b.branch_name
ORDER BY 平均余额 DESC;

5.4 联合查询(UNION)------ 扩展内容

UNION 将两个查询的结果上下拼接 ,要求列数和类型一致。UNION ALL 包含重复行,UNION 去重。

银行业务场景:查询所有"账户余额高于 50000 的客户"和"有过取款交易的客户"合并名单。

sql 复制代码
-- 高余额客户
SELECT customer_id FROM bank.accounts WHERE balance > 50000
UNION
-- 有过取款交易的客户(通过账户关联)
SELECT a.customer_id 
FROM bank.transactions t
JOIN bank.accounts a ON t.account_id = a.account_id
WHERE t.trans_type = '取款';

总结

关联类型 记忆口诀 银行业务典型场景
INNER JOIN 只要两边都有 查询有账户的客户
LEFT JOIN 左表全保留,右边可能空 查询所有客户及其账户(包括无账户客户)
RIGHT JOIN 右表全保留,左边可能空 极少用,可被 LEFT JOIN 替代
FULL JOIN 两边全保留,互相填空 合并两个独立清单
CROSS JOIN 所有组合,风险大 生成测试数据
EXISTS "有"就行 发生过交易的客户
NOT EXISTS "没有"就行 从未交易的客户
相关推荐
倔强的石头_1 小时前
《Kingbase护城河》——跨平台环境下的数据库联调实战
数据库
lzhdim1 小时前
SQL 入门 17:MySQL 数据类型:从字符串到 JSON 的全面解析
数据库·sql·mysql·json
杨云龙UP2 小时前
Oracle RAC / ODA 生产环境指定 PDB 启动 SOP
linux·运维·数据库·oracle
kingwebo'sZone2 小时前
在Cent上安装Mysql 8.0的遇到的问题和解决办法
数据库·mysql·adb
幽络源小助理2 小时前
最新知识付费系统网站源码 PC+H5双端 附安装教程 – 幽络源源码网
大数据·数据库
小白考证进阶中2 小时前
Oracle OCP证书报考&考试全指南
数据库·oracle·oracle ocp·ocp认证·oracle认证·甲骨文认证·oracle ocp题库
Leon-Ning Liu3 小时前
【真实经验分享】 ORA-600 [qesmaGetTblSeg1]
数据库·oracle
与数据交流的路上3 小时前
MySQL 优化 -- 相关
数据库·mysql
Rooting++4 小时前
为什么mysql的表字段的collation会自动变
数据库·mysql