SQL

引入

如果有一段学生信息,那么你会产生哪些处理?

基本上你的操作都是围绕的增删改查来做的,新增学生记录,删除学生记录,修改学生记录,查询学生记录

那我们先来做一件事情好了,我们有一个文本文件(d://app/students.txt),其中有一些学生的信息,对应如下

姓名,年龄,性别,生日,邮箱,电话
张三,20,男,2003-05-12,zhangsan@example.com,123-456-7890
李四,22,女,2001-07-23,lisi@example.com,123-456-7891
王五,21,男,2002-02-17,wangwu@example.com,123-456-7892
赵六,23,女,2000-11-29,zhaoliu@example.com,123-456-7893
孙七,19,男,2004-08-08,sunqi@example.com,123-456-7894
周八,20,女,2003-01-30,zhouba@example.com,123-456-7895
郑九,21,男,2002-06-15,zhengjiu@example.com,123-456-7896
韩十,22,女,2001-09-10,hanshi@example.com,123-456-7897

如果是你一名Java开发工程师,接下来如何来读取这些信息呢?

首先我们要先明确每行信息,使用什么来保存,这里我们可以定义一个Student类,每个Student的对象就对应了一行内容

public class Student {
  private String name;
  private int age;
  private String gender;
  private String birthday;
  private String email;
  private String phone;
  public Student(){
  }
  public Student(String name, int age, String gender, String birthday, String email, String phone) {
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.birthday = birthday;
    this.email = email;
    this.phone = phone;
  }

  @Override
  public String toString() {
    return "Student{" +
      "name='" + name + '\'' +
      ", age=" + age +
      ", gender='" + gender + '\'' +
      ", birthday='" + birthday + '\'' +
      ", email='" + email + '\'' +
      ", phone='" + phone + '\'' +
      '}';
  }
}

那么我们这里的信息,就可以使用一个List来进行接收了

public class StudentLoader {
  public static void main(String[] args) {
    String filePath = "D:/app/students.txt"; // 文件路径
    // 读取文件中的内容为Student信息
    List<Student> students = loadStudentsFromFile(filePath);

    // 输出学生列表
    for (Student student : students) {
      System.out.println(student);
    }
  }

  /**
     * 从指定文件路径加载学生信息
     * @param filePath 文件路径
     * @return 学生信息列表
     */
  private static List<Student> loadStudentsFromFile(String filePath) {
    List<Student> students = new ArrayList<>(); // 创建一个空的学生列表
    try {
      BufferedReader br = new BufferedReader(new FileReader(filePath)); // 创建BufferedReader读取文件
      String line;
      boolean isFirstLine = true; // 标记第一行是否已经读取
      while ((line = br.readLine()) != null) {
        if (isFirstLine) {
          isFirstLine = false; // 跳过标题行
          continue;
        }
        // 解析单行文本为student对象
        Student lineStudent = parseLineToStudent(line);
        students.add(lineStudent); // 将解析后的学生添加到列表中
      }
    } catch (IOException e) {
      e.printStackTrace(); // 捕获并打印IO异常
    }
    return students; // 返回学生列表
  }

  /**
     * 将单行内容解析为Student对象
     * 单行内容格式如下:
     * 张三,20,男,2003-05-12,zhangsan@example.com,123-456-7890
     * @param line 单行文本
     * @return 解析后的Student对象
     */
  private static Student parseLineToStudent(String line) {
    String[] values = line.split(","); // 用逗号分隔字符串
    String name = values[0]; // 获取姓名
    int age = Integer.parseInt(values[1]); // 获取并解析年龄
    String gender = values[2]; // 获取性别
    String birthday = values[3]; // 获取生日
    String email = values[4]; // 获取邮箱
    String phone = values[5]; // 获取电话
    Student lineStudent = new Student(name, age, gender, birthday, email, phone); // 创建Student对象
    return lineStudent; // 返回Student对象
  }
}

在上述的学生列表中找到年龄≤20的学生列表,找到性别为男性的学生列表

// 找到年龄小于等于20岁的学生
List<Student> youngStudents = students.stream()
  .filter(student -> student.getAge() <= 20)
  .collect(Collectors.toList());
// 找到性别为男性的学生
List<Student> maleStudents = students.stream()
  .filter(student -> "男".equals(student.getGender()))
  .collect(Collectors.toList());

将孙七的性别为女,并重新保存修改后的文件

// 修改孙七的性别为女
for (Student student : students) {
  if ("孙七".equals(student.getName())) {
    student.setGender("女");
    break;
  }
}

// 将修改后的学生信息写回文件
saveStudentsToFile(filePath, students);
/**
     * 将学生信息保存到指定文件路径
     * @param filePath 文件路径
     * @param students 学生信息列表
     */
private static void saveStudentsToFile(String filePath, List<Student> students) {
  try {
    BufferedWriter bw = new BufferedWriter(new FileWriter(filePath));
    // 写入标题行
    bw.write("姓名,年龄,性别,生日,邮箱,电话");
    bw.newLine();
    // 写入每个学生的信息
    for (Student student : students) {
      bw.write(student.toCSVString());
      bw.newLine();
    }
    bw.close();
  } catch (IOException e) {
    e.printStackTrace(); // 捕获并打印IO异常
  }
}

其实在上面的查询过程,还不算很繁琐,到修改这里开始繁琐了,你要把所有的学生信息查询出来,然后再其中找到你要修改的记录if判断通过条件,并且在你找到之后对全部内容重新进行写出,其实这个过程中做了非常多的IO操作,这个是非常消耗系统资源的

同理,删除和新增也是如此

// 删除孙七的记录
students.removeIf(student -> "孙七".equals(student.getName()));

// 将修改后的学生信息写回文件
saveStudentsToFile(filePath, students);
// 创建刘七的记录
Student liuQi = new Student("刘七", 20, "男", "2003-07-07", "liuq@example.com", "123-456-7899");

// 在赵六和周八之间新增刘七的记录
for (int i = 0; i < students.size(); i++) {
  if ("赵六".equals(students.get(i).getName()) && i + 1 < students.size() && "周八".equals(students.get(i + 1).getName())) {
    students.add(i + 1, liuQi);
    break;
  }
}

// 将修改后的学生信息写回文件
saveStudentsToFile(filePath, students);

小结:

虽然在简单的应用中使用文本文件存储数据可能足够且实现简单,但在复杂和大规模的应用中,数据库系统提供的数据完整性、安全性、效率、便利性、管理性和扩展性是文本文件无法比拟的。因此,数据库是现代应用中数据管理的首选解决方案。

数据库

什么是数据库

数据库管理系统(DataBase Management System,DBMS)是一种操作和管理数据库的大型软件,用于建立、使用和维护数据库,它对数据库进行统一的管理和控制,以保证数据库的安全性和完整性。用户通过数据库管理系统访问数据库中的数据。

在现实中,数据、数据库管理系统 (DBMS) 一起被称为数据库系统,通常简称为数据库(DB)。

为什么使用数据库

1. 数据完整性和一致性

事务支持:数据库系统支持事务处理,可以确保一组操作要么全部成功,要么全部失败,保证数据的一致性和完整性。例如,在银行应用中,转账操作必须确保从一个账户扣款和向另一个账户存款这两个操作同时成功。

并发控制:数据库提供锁机制和并发控制,防止多个用户同时访问和修改数据时发生冲突,从而保证数据的一致性。

2. 数据安全

权限控制:数据库系统提供细粒度的权限控制,可以限制不同用户对数据的访问权限,确保数据的安全性。

备份与恢复:数据库系统提供数据备份和恢复功能,可以在数据丢失或损坏时恢复数据,保证数据的安全性和持久性。

3. 数据检索效率(最后阶段)

索引:数据库支持索引,可以大幅提高数据检索效率。对于大规模数据集,通过索引查找数据比逐行扫描文本文件要快得多。

查询优化:数据库系统有查询优化器,可以根据查询语句的不同自动选择最优的查询路径,提高数据访问效率。

4. 数据操作便利

结构化查询语言 (SQL):数据库系统支持SQL,SQL是一种强大的数据操作语言,可以方便地进行数据查询、插入、更新和删除等操作。

复杂查询:数据库可以支持复杂的查询操作,如多表连接、聚合函数、子查询等,而在文本文件中实现这些操作非常困难且效率低下。

5. 数据管理

数据模型:数据库系统支持关系型、文档型、图形等多种数据模型,可以根据应用需求选择最合适的数据模型。

数据约束:数据库支持数据约束,如主键、外键、唯一性约束等,可以保证数据的完整性和正确性。

存储与检索:数据库系统提供自动的数据存储与检索机制,不需要手动管理数据存储文件。

6. 扩展性

水平扩展:数据库系统支持集群和分布式存储,可以水平扩展处理大规模数据和高并发访问。

性能优化:数据库系统可以通过分区、索引、缓存等多种技术手段优化性能,满足大数据量和高并发的应用需求。

数据库分类

关系型数据库(SQL): 不仅可以存储数据,还可以存储数据与数据之间的关系。

非关系型数据库(NoSQL): 不仅仅只有SQL。对关系型数据库的补充,主要是用来做一些关系型数据库不擅长的事情。关系型数据库的数据,一般是存储在磁盘上,所以速度比较慢。非关系型数据库一般是存在内存中的,所以性能比较好。

常见数据库

关系型数据库

接下来,我们介绍几款常见的关系型数据库。

  • Oracle:甲骨文公司的一个产品,世界上使用最多的收费型数据库。性能较好。

又名Oracle RDBMS(Relational Database Management System),或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小微机环境。它是一种高效率的、可靠性好的、适应高吞吐量的数据库方案

使用公司:大商业公司,政府使用较多,但近几年由于一些原因,银行,政府机构等正在替换Oracle。

MySQL:最流行的关系型数据库,之前是开源的。

由瑞典MySQL AB公司开发,目前属于Oracle旗下产品。MySQL是最流行的关系型数据库管理系统之一。

目前分为两种版本:社区版,收费版。

注:2008年被Sun公司收购。2009年,Sun公司被Oracle收购。

使用公司:中小企业,追求快速发展。性能非常优秀。

  • MariaDB:MySQL原作者开发的,MySQL被Oracle收购后,考虑到Oracle可能会将其闭源,所以基于MySQL的一个分支,又开发了一个MariaDB。

MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。MariaDB名称来自Michael Widenius的女儿Maria的名字。

使用公司:中小企业,追求快速发展。MySQL作者的粉丝。MaxDB

  • Sql server:微软开发的。

SQL Server是由微软公司开发的一种关系型据库管理系统,它已广泛用于电子商务、银行、保险、电力等行业。SQLServer提供了对XML和Internet标准的支持,具有强大的、灵活的、基于Web的应用程序管理功能。而且界面友好、易于操作,深受广大用户的喜爱,但它只能在Windows平台上运行(2017 SQL Server后对Linux支持),并对操作系统的稳定性要求较高,因此很难处理日益增长的用户数量。

使用公司:中大型企业。国内用的不多。

  • DB2数据库

DB2数据库是由IBM公司研制的一种关系型数据库管理系统,主要应用于OS/2、Windows等平台下,具有较好的可伸缩性,可支持从大型计算机到单用户环境。

DB2支持标准的SQL,并且提供了高层次的数据利用性、完整性、安全性和可恢复性,以及从小规模到大规模应用程序的执行能力,适合于海量数据的存储,但相对于其他数据库管理系统而言,DB2的操作比较复杂。

使用公司:大公司,政府部门

  • PostgreSQL

是以加州大学伯克利分校计算机系开发的 Postgres 版本 4.2 为基础的对象关系型数据库管理系统(ORDBMS)。

因为许可证的灵活,任何人都可以以任何目的免费使用、修改和分发PostgreSQL。

使用公司:有自研能力的公司使用较多。例如国内的华为。

非关系型数据库

常见的非关系型数据库

  • Redis: 最常用的非关系型数据库,数据存在内存,速度快,吞吐量高。单机10w吞吐。

Remote Dictionary Server(Redis) 是一个由 Salvatore Sanfilippo 写的 key-value 存储系统,是跨平台的非关系型数据库。 Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的 API。它是一个基于内存实现的键值型非关系(NoSQL)数据库

Redis 遵守 BSD 协议,实现了免费开源,其最新版本是 6.20,常用版本包括 3.0 、4.0、5.0。自 Redis 诞生以来,它以其超高的性能、完美的文档和简洁易懂的源码广受好评,国内外很多大型互联网公司都在使用 Redis,比如腾讯、阿里、Twitter、Github 等等。

redisCli.put(“name”, “zhangsan”);

redisCli.get(“name”);

  • Memcached

基于内存的key-value数据库。出现自Redis之前,但有一些问题。

– 数据全部在内存中,断电后会挂掉,数据不能超过内存大小

– 支持的类型不丰富

所以目前公司使用它的较少,一些老项目可能仍然在使用,新项目无使用它的。

  • Mongodb:文档数据库。存储大量文档的。
  • Hbase:大数据领域使用的比较多的数据库,列式数据库。

小结

数据库有很多种,这无关孰优孰劣(没有谁好谁差之分),主要取决于企业希望如何使用数据.

企业要求: 钱少,能用。性能差不多就可以。 MySQL

政府要求: 钱无所谓,千万数据要安全。

关系型数据库和非关系型数据库, 区别很多.

而在关系型和非关系型数据中,最本质的区别是: 关系型数据库以数据和数据之间存在的关系维护数据, 而非关系型数据库是指存储数据的时候数据和数据之间没有什么特定关系.

在大多数时候, 非关系型数据库是在传统关系型数据库基础上(其实已经基本上完全不同), 在功能上简化, 在数据存储结构上大大改变, 在效率上提升. 通过减少用不到或很少用的功能, 在能力弱化的同时也带来产品性能的大幅度提高.

但是本质上讲, 他们都是用来存储数据的. 而对于我们Java后端开发来讲,我们在工作中基本上是以关系型数据库为主, 非关系型数据库为辅的用法

MySQL内部数据组织方式

在MySQL中, 我们对数据的组织逻辑上是按照库/表/数据 这种结构组织的.

数据库(Database): 表示一份完整的数据仓库, 在这个数据仓库中分为多张不同的表.

表(Table): 表示某种特定类型数据的的结构化清单, 里面包含多条数据.

数据(Data): 表中数据的基本单元.

SQL

SQL有如下优点

SQL语言不是某个特定的数据库提供的语言, 它是一种数据库标准语言(最初由美国国家标准局 ANSI于1986年完成第一版SQL标准的定义,即SQL-86)。这也就意味着每个关系型数据库都支持SQL语言.

SQL简单易学, 是由多个描述性很强的单词构成, 并且这些单词数量不多。

SQL尽管看上去很简单, 但是非常强有力; 灵活的使用SQL, 可以进行比较复杂的和高级的数据库操作。

后续使用的指令出现标点符号的地方均使用英文标点!每个SQL语句后面都要有分号

数据库管理工具

数据库管理工具是用于创建、管理、维护和优化数据库系统的软件应用程序。这些工具通常提供直观的用户界面,使数据库管理员和开发人员能够轻松地执行各种数据库任务。

MySQL Workbench、SQLyog、Navicat、DataGrip

SQL基本操作

数据库内部数据的组织逻辑上是按照库/表/数据 这种结构组织的,接下来对于SQL基本操作的介绍也是按照这个顺序进行,由外向内分别为

  1. 数据库(Database)
  2. 表(Table)
  3. 数据(Data)

登录数据库

MySQL是C/S架构的软件,所以我们的Server是一直启动着的,我们使用官方提供的客户端去连接Server,然后发送命令给server端执行,server端返回执行结果。

在配置好环境变量之后,可以通过命令来登录MySQL

mysql -u[用户名] -p
mysql -u[用户名] -p[密码]

前者是执行指令之后输入密码,后者是将密码直接在指令上明文输入。不建议大家直接将密码写在控制台上,这样子不安全。(其实没啥关系)

操作数据库

MySQL默认自带的库(mysql 、performance_schema 、information_schemasys),不要去删除它们。

注意 库名,表名,列名均不区分大小写(这个是MySQL的默认配置)。

查看数据库

-- 查看所有的数据库
show databases;

show databases like 'alpha%'; -- 以alpha开头 
show databases like '%beta';  -- 以beta结尾
show databases like '%gamma%' -- gamma出现在任何位置
-- 其中单引号''也可以替换为双引号"",但是注意使用英文

-- 查看创建数据库命令(指定的数据库使用什么SQL语句创建出来的)
show create database <数据库名称>;

创建数据库

-- 创建数据库(名称不区分大小写)
create database <数据库名称>;
-- 如果是复合词名称,使用下划线命名法

-- 创建数据库,指定字符集和默认校对规则
create database <数据库名称> character set <字符集> collate <校对规则>;
-- 比如创建一个数据库student_info,字符集是  utf8mb4  校对规则  utf8mb4_general_ci
create database student_info character set utf8mb4 collate utf8mb4_general_ci;

Navicat中可以使用图形化界面直接新建数据库

其实本质上是一样的,可以点击那里的SQL预览,本质上图形化页面是帮你执行了对应的SQL语句。

注意:关键词作为参数时使用“

上面最开始没有使用飘号(“)时,报错1064,存在SQL语法错误,使用后则可以正常执行,创建名称为show的数据库。

阿里编程规范

MySQL 的字符集(CHARACTER)和校对规则(COLLATION)是两个不同的概念。字符集是用来定义 MySQL 存储字符串的方式,校对规则也可以称为排序规则,是指在同一个字符集内字符之间的比较规则。字符集和校对规则是一对多的关系,每个字符集都有一个默认的校对规则。

推荐大家使用: utf8mb4 。

utf-8: 使用4个字节来代表一个字符。MySQL官方里面有一个utf8 ,但是它是三个字节的,有一些四个字节的表示不了。

utf8mb4才是当下真正的utf-8。

字符集,就是MySQL存储数据的方式。比如现在有一个zhansgan 的字符串,需要存储到MySQL中。因为文件系统中,只能存二进制的。所以需要把zhangsan进行编码,编码之后,才能写入到磁盘里面。

校对规则,用于比较和排序字符串字符集的一套规则。比如有一些排序规则区分大小写,有一些排序规则不区分大小写.

<span style=color:red;background:yellow><b>注意:</b></span>如果不设置字符集,有可能会使用了默认的latin1,导致存储中文报错。(尤其是使用Navicat之类的软件的时候)

-- 下面这两个不重要,指导能够查询所有字符集和校对规则即可
-- 查看所有的字符集
show character set;

-- 查看所有的校对规则
show collation where charset='utf8mb4';

删除数据库

-- 删除数据库
drop database <数据库名称>;

修改数据库

-- 修改指定库的  字符集和校对规则
alter database <数据库名称> character set <字符集> collate <校对规则>;

数据库中未提供改库名的操作,只提供修改字符集和校对规则。

选择数据库

-- 选择使用指定的数据库
use <数据库名称>;

一个MySQL系统中, 管理多个数据库。 我们只有进入对应的数据库中, 才能进一步操作数据库中的数据。

因为表都是在库里面的。所以需要进入库中,再对表进行各种操作。

操作表

在表格级别的所有操作,都必须要在数据库中,所以必须要先选择数据库。

-- 选择指定的数据库
use <数据库名称>;

查看表

-- 查看当前数据库所有表(新创建的数据库表为空)
show tables;

-- 查看表格结构(有哪些列,以及列的类型)
desc <表名>;
describe <表名>;

-- 查看表的创建语句。获取出来的语句,可以直接运行,(改改表名)
show create table <表名>;

创建表

-- 创建表格的写法。
create table <表名> ( <列名> <列类型> ,<列名> <列类型> ,...,<列名> <列类型> )

列名

列名一般采用下划线命名法!

比如create_time,add_time,product_id,category_id

列类型

数字(整数型、小数)

– 整数

– tinyint:1字节。

– int(integer): 4字节。

– bigint: 8字节。和long比较像。

– 小数

– float(M,D): 4字节。浮点型

– double(M,D): 8字节。浮点型

– decimal (M, D)  ,dec: 压缩的“严格”定点数M+2 个字节。定点型 。

浮点数相对于定点数的优点是在长度一定的情况下,浮点数能够表示更大的范围;缺点是会引起精度问题。

说明:M代表的是允许存储的最大位数,D代表是小数位数。也就是留给整数的是 M-D位。如果整数超了,直接报错,如果小数超了,会四舍五入。

float(5,2) 代表可以存储最大为 999.99 的数字,总共有5位数字,其中2位在小数点右侧。

– 如果整数超了。 —会报错。Out of range

– 如果小数超了。 — 四舍五入 100.6666 –》 100.67

存钱的时候: 如果之前是100.331 千万不能存储成后面100.33 这里建议使用decimal(6,3)

对一些精度要求比较高的数据(货币数据、科学数据),使用DECIMAL 。

或者使用字符串的形式。

在 MySQL 中,定点数以字符串形式存储,在对精度要求比较高的时候(如货币、科学数据),使用 DECIMAL 的类型比较好,另外两个浮点数进行减法和比较运算时也容易出问题,所以在使用浮点数时需要注意,并尽量避免做浮点数比较。

日期

– year:年(YYYY)。

– time: 时分秒(HH:MM:SS)。

– date: 年月日(YYYY-MM-DD)。

– datetime: 年月日时分秒。(YYYY-MM-DD HH:MM:SS)。

– timestamp: 年月日时分秒。(YYYY-MM-DD HH:MM:SS)。

timestamp:时区 信息不同 获取到的值不同

字符串

– char(M): 定长字符串,设置了长度,无论存储多少长度的字符串,都会占满M。

– varchar(M):变长字符串,会用1-2字节来存储长度。也就是 实际长度+1(2)。所有的列。最大长度65535字节。

– text:文本字符串,会用2字节来存储长度。最大长度65535字符,约64K。

– longtext:大文本字符串。会使用4字节存储长度。最大长度2^32,约4G。

注意:

– 如果你需要存储的字符串在2000以内,可以使用varchar()。如果超过,建议使用text。

比如我现在有一个电影 3.8G。需要存怎么办? 把电影,找个电脑存起来,存路径。 D:/data1/学习.mp4。数据库是存放数据的,但是不能存放这种大的视频。

但是一般使用varchar。不会写太多。 varchar(500) varchar(1000) 这个都是可以的。但是如果要写varchar(5000)。不建议使用varchar。

举例

现在有一个学生表,需要存储的信息包括,学生id,姓名,年龄,身高,体重,籍贯,身份证号,联系电话

-- 创建一个表,主要是三个问题:

-- 1.表名

-- 2.有哪些字段

-- 3.字段的类型及长度

create table student(

id int,

name varchar(200),

age int,

height double(10,2),

weight double(10,2),

address varchar(500),

id_card varchar(50),

phone varchar(20)

);

-- 建表语句的原则。应该留有一定扩展性。

-- varchar(2) varchar(5) varchar(10) varchar(50) varchar(200)

-- 写得多,并不一定会直接占用那么多。 varchar(5)。建表的时候留有扩展空间,防止后续数据量变大了之后,频繁去改表。

-- 所以在最开始创建表的时候,直接写大一点点。
-- 现在有一个图书信息表,需要存储的信息包括,序号,书名,作者,出版社,出版日期,定价,备注

create table book(

id int,

book_name varchar(200),

author varchar(100),

press varchar(200),

publication_date date,

price decimal(10,2),

comment varchar(1000)

);

-- 有一些字段,可以根据上下文进行推断,所以我们也可以做一点省略

-- 比如在书这张表里,出现了name,我们会直接认为name是书的名字,不会认为是作者或者其他人的名字,所以可以省略一点点。

-- book_name -> name

-- 可以根据上下文推断。

删除表

drop table <表名>;

修改表

知道有这个东西就可以了,但是在工作中,如果有这种需求,你不要去操作。在自己的数据库上练习一下无所谓。

表名

-- 修改表名
rename table <旧表名> to <新表名>;
alter table <旧表名> rename <新表名> ;

字符集和排序规则

-- 修改表字符集 排序规则
alter table <表名> character set <字符集> collate <校对规则>;

添加列

-- 添加列
alter table <表名> add column <列名> <类型>;

删除列

-- 删除列
alter table <表名> drop column <列名>;

修改列类型

-- 修改某列的类型
alter table <表名> modify column <列名> <类型>;

注意事项:修改表结构的时候可以使用图形化页面,但是要把SQL语句给它保存起来

操作数据❗❗❗

操作数据是今后大家最重要的内容

CRUD工程师 => CRUD对应的是增删改查

增加(Create)、读取(Read)、更新(Update)和删除(Delete)

查询数据

-- 查询数据
select [列名1],[列名2],...,[列名n] from <表名>;

如果要查询所有列的内容的话

-- 使用*来代表所有列
select * from <表名>;

新增数据

-- 方式1,先设定插入的列的顺序,然后按照顺序提供至
insert into <表名> ([列名1],[列名2],...,[列名n]) values ([值1],[值2],...,[值n]);

-- 方式2,不需要指定列名,但是所有列都需要提供值(不建议修改数据库表结构会导致已有的SQL语句执行出现问题)
insert into <表名> values ([值1],[值2],...,[值n]);

-- 方式3,使用set方式,指定列以及值
insert into <表名> set [列名]=[值],[列名]=[值],...,[列名]=[值];

修改数据

-- 更新数据
update <表名> set <列名>=[值],<列名>=[值],...,<列名>=[值];

删除数据

-- 删除数据
delete from <表名>;

补充说明

关于字符集和校对规则的说明

有四个层次的字符集和校对规则。

– 数据库服务 service

– 数据库 database

– 表 table

– 列 column

默认值向上继承,若创建后修改,不影响已有子内容的字符集。

举个例子:如果创建时数据库的字符集是utf8mb4,如果创建表的话没有指定字符集,则表的字符集会继承数据库的字符集,也是utf8mb4;此时如果修改数据库的字符集为utf8,那么这个数据库已经创建的表的字符集不会随之修改

这时候我们一般如何做,建议把表删了,重新创建

注释的说明

-- <注释>

-- 使用两个横线进行注释的时候。后面必须跟空格

# <注释>

/*
  <注释>
*/    

注意:

在数据库语句中如果我们需要注释某些内容, 一般有三种方式。

-- 注释符(要注意的是--之后要有一个空格再接着书写注释内容)

# 注释符 (之后不需要空格)

/* */ 注释符 (一般用于多行注释)

命名法

以Teacher Info这个复合词,采用不同的命名方式来写一下

类型命名场景
小驼峰命名teacherInfo变量名、方法名
大驼峰命名TeacherInfo接口名、类名、属性、命名空间
下划线命名teacher_info数据库(库、表、列)、文件名
连字符命名teacher-infoCSS、(SpringBoot)配置文件

SQL关键字(难点)

首先进行数据准备,可以将一下内容放到navicat中执行

CREATE TABLE `student_t`  (
	`id` int(11)  PRIMARY KEY  AUTO_INCREMENT,
	`name` varchar(255)   ,
	`class` varchar(255)  ,
	`chinese` float  ,
	`english` float  ,
	`math` float  
) ;
INSERT INTO student_t (id, name, class, chinese, english, math) VALUES (1, '武松', '一班', 70, 90, 60);
INSERT INTO student_t VALUES (2, '林冲', '一班', 70, 90, 90);
INSERT INTO student_t VALUES (3, '松江', '一班', 90, 90, 20);
INSERT INTO student_t VALUES (4, '贾琏', '二班', 60, 60, 60);
INSERT INTO student_t VALUES (5, '贾宝玉', '二班', 95, 80, 5); 
INSERT INTO student_t VALUES (6, '贾环', '二班', 25, 25, 5); 
INSERT INTO student_t VALUES (7, '曹操', '三班', 90, 90, 90); 
INSERT INTO student_t VALUES (8, '曹丕', '三班', 90, 80, 80); 
INSERT INTO student_t VALUES (9, '曹植', '三班', 98, 90, 80); 
INSERT INTO student_t VALUES (10, '刘备', '三班', 95, 90, 80); 
INSERT INTO student_t VALUES (11, '诸葛亮', '三班', 98, 95, 95); 
INSERT INTO student_t VALUES (12, '孙权', '三班', 80, 90, 80); 
INSERT INTO student_t (id) VALUES (13);

where => 条件

使用 WHERE 关键字并指定查询条件|表达式, 从数据表中获得满足条件的数据内容.

使用位置:查询语句(select),更新语句(update),删除语句(delete)。在update里,和delete必须要用。

-- 举例
-- 想找id为1的
select * from student_t where id=1;
-- chinese 大于90的
select * from student_t where chinese > 90;
-- 想找到id大于10的
select id, name from student_t where id > 10;

使用 WHERE 关键字并指定查询条件|表达式, 从数据表中获得满足条件的数据内容.

在构建Where的查询条件|表达式的过程中, 我们可能需要了解到一些重要的SQL运算符

在where语句的后面,我们可以用到算数运算符。也可以用到逻辑运算符。

在查询结果中,我们也可以使用算术运算符。

算术运算符

运算符作用
+
*
/
%取余
-- 算数运算符,不仅可以出现在where中,还可以出现在查询列中。

-- 语数外总分 小于180的
-- 语文-数学 分差大于30的
-- 加权平均,按语文0.5 英语0.1 数学0.4求加权平均分
-- 加权平均分,小于等于60的
-- 求每个人的平均分,语数外三科
-- 求每个人的平均分,只筛选出平均分小于60的
-- 找出id是奇数的
-- 找语文成绩是偶数的

-- eg:
-- 语数外总分 小于180的
	select * from student_t where (chinese + english + math) < 180; 
	
-- 语文和数学 分差大于30的
	select * from student_t where (chinese - math) > 30; 
	
-- 加权平均,按语文0.5 英语0.1 数学0.4求加权平均分
	select *, (chinese*0.5 + english*0.1 + math *0.4) from student_t; 
	
-- 加权平均分,小于等于60的
	select *, (chinese*0.5 + english*0.1 + math *0.4)  from student_t where (chinese*0.5 + english*0.1 + math *0.4) <= 60 ;  
	
-- 求每个人的平均分。语数外
    select *, (chinese + english + math) / 3 from student_t ;  
    
-- 求每个人的平均分,只筛选出平均分小于60的
    select *, (chinese + english + math) / 3 from student_t  where (chinese + english + math) /3  < 60; 

-- 找出id是奇数的
SELECT * FROM student_t WHERE id % 2 = 1;
-- 找语文成绩是偶数的
SELECT * FROM student_t WHERE chinese % 2 = 0;

比较和逻辑运算符

运算符作用运算符作用
=等于<=>等于(可比较null)
!=不等于<>不等于
<小于>大于
<=小于等于>=大于等于
between and在闭区间内like通配符匹配(%:通配, _占位)
is null是否为nullis not null是否不为null
in在列表内not in不在列表内
and&&
or\\

需要注意的:

  • =无法判断null。一般使用 is null来单独处理null
  • like中,%表示通配,_表示占位。 一个_代表一个字符。
-- 查询语数外总成绩大于 180 的同学信息;
select *,(chinese + english + math) from student_t where (chinese + english + math) > 180;

-- 查询数学成绩在[80,90]区间的同学姓名;
select * from student_t where math between 80 and 90;

select * from student_t where math >= 80 and math <=90;

-- 查询各科都及格的同学姓名;
select * from student_t where chinese >= 60 and math >= 60 and english >= 60;

-- 查询各科只要有一科及格的同学姓名;
select * from student_t where chinese >= 60 or math >= 60 or english >= 60;

select * from student_t where id=6;

-- 查询一班和二班的同学信息;
select * from student_t where class = '一班' or class = '二班';
select * from student_t where class in ("一班", "二班");

-- 查询姓贾的同学(只要姓贾就行)
SELECT * FROM `student` where name like '贾%';

-- 查询姓贾的同学,两个字的
SELECT * FROM `student` where name like '贾_';

-- 查询语文分数在 60 或90的同学
SELECT * FROM `student` where chinese in (60,90);

distinct => 去重

获取某个列的不重复值。或者是某些列的不重复值

SELECT DISTINCT <字段名> FROM <表名>;

使用DISTINCT对数据表中一个或多个字段重复的数据进行过滤,重复的数据只返回其一条数据给用户.

什么叫重复:就是多个列,全部相等,这时候就认为是重复的数据。

-- 返回所有的 class
select class from student_t;

-- 返回不重复的 class
select distinct class from student_t;


-- 返回所有去重后的英语成绩
-- 6条
select distinct english from student_t;

-- 返回两列 英语和数学去重后的结果。
-- 10条
select distinct english,math from student_t;

-- 13条
-- 90.90 重复了一条
-- 90,80 重复了两条
select english,math from student_t;

limit => 限制结果集

一般用来做,比如限制最大的返回数目。或者是做分页上面。

select * from student_t limit 10;

SELECT <查询内容|列等> FROM  <表名字> LIMIT 记录数目;
SELECT <查询内容|列等> FROM  <表名字> LIMIT 初始位置,记录数目;
SELECT <查询内容|列等> FROM  <表名字> LIMIT 记录数目 OFFSET 初始位置;

eg:
-- 限制数目 为number个
-- 限制最大返回number个,如果表中记录不足number个,只会返回表中的记录数。
select * from <表名> where condition limit [限制量];

-- 偏移量为offsetNumber 从0开始
select * from <表名> where condition limit [偏移量], [限制量];

-- 偏移量offsetNumber
select * from tableName where condition limit number offset offsetNumber;

使用LIMIT对数据表查询结果集大小进行限定.

LIMIT 记录数目: 从第一条开始, 限定记录数目

LIMIT 初始位置,记录数目: 从起始位置开始, 限定记录数目

LIMIT 记录数目 OFFSET 初始位置: 从起始位置开始, 限定记录数目

注意: 数据(默认下标从0开始)

-- 从第一条开始拿 
select * from student_t limit 5;

-- limit offsetNumer,number 偏移数目,需要限制的总数
-- 2,5 代表从第三个开始拿 总共拿回来5个
select * from student_t limit 2,5;

-- limit number offset offsetNumber
-- limit 5 offset 3 代表 从第四个开始拿,总共最大拿回来五个
select * from student_t limit 5 offset 3;

一般用它来做分页查询。

as => 别名

<内容> AS <别名>

AS 关键字用来为表和字段指定别名.

-- 可以为取出来的列名 取一个别名
select id,name as student_name,class from student_t;

-- 可以为一些计算的属性取别名
select (chinese + english + math) as total_score from student_t;

-- 也可以为表名取别名
select s.name from student_t as s;

select s.name from student_t s;

-- as 可以省略

as给字段名取别名其实是为了修改结果集中的列名 → 在后面JDBC内容中也会使用到

as给表名取别名在后面多表查询中会使用,主要是为了写SQL的时候偷懒

简而言之,主要场景是多表查询

order by => 排序

比如我们想根据id进行排序; 或者想根据年龄进行排序。

SELECT <查询内容|列等> FROM  <表名字> ORDER BY <字段名> [ASC|DESC];

ORDER BY对查询数据结果集进行排序.

不加排序模式: 升序排序.

ASC: 升序排序.

DESC: 降序排序.

注意: 如上查询, 当我们进行多字段排序的时候, 会先满足第一个列的排序要求, 如果第一列一致的话, 再按照第二列进行排序, 以此类推.

业务举例:按照价格排序,按照销量排序,按照时间排序(最近发布的产品)

group by => 分组聚合

按照某个、某些字段分组。

比如想看一个班级有多少学生。班级的最高分,最低分。

SELECT <查询内容|列等> FROM  <表名字> GROUP BY  <字段名...>

eg: 
select class from student_t group by class;
select class,chinese from student_t group by class, chinese;
select class, group_concat(name), group_concat(chinese) from student_t group by class;

-- 获取语文成绩大于90分的,按照班级分组
select class, group_concat(name) from student_t where chinese > 90 group by class;

-- 获取班级的平均分
select class, group_concat(name), avg(chinese) from student_t group by class;

-- 获取班级人数大于三个人的班级
select class, group_concat(name) from student_t group by class having count(*) > 3;

-- 获取班级平均语文成绩大于60分的
select class, group_concat(name), avg(chinese) from student_t group by class having avg(chinese) > 60;

GROUP_CONCAT()函数会把每个分组的字段值都拼接显示出来.

HAVING 可以让我们对分组后的各组数据过滤。(一般和分组+聚合函数配合使用)

having注意和where的区别

where主要用于对原始表进行过滤。having是对group by 后的结果进行过滤,一般配合聚合函数一起使用。

注意点:

  • group by的select列中,只能有两种,1.在group中出现的字段2.聚合函数聚合起来的东西
  • 多个字段分组查询时,会先按照第一个字段进行分组。如果第一个字段中有相同的值,MySQL 才会按照第二个字段进行分组。如果第一个字段中的数据都是唯一的,那么 MySQL 将不再对第二个字段进行分组.
  • 如果在select 字段中,可以看出group 字段,后方可以使用数字代替,从1开始
-- 会报错。如果有同学不报错,是因为有一个选项没开
-- select * from student_t group by class;
-- select id,class from student_t group by class;

-- 在select中出现的,只能有 group by 后的字段;或者是聚合函数聚合起来的东西
select class from student_t group by class;

-- 根据英语成绩进行分组
select english from student_t group by 1;

-- 根据英语成绩,数学成绩进行分组
-- 会先按照英语成绩分组,如果英语成绩相同,则按照数学成绩进行分组
select english,math from student_t group by english,math;

聚合函数

聚合函数一般用来计算列相关的指定值. 通常聚合一起使用

函数作用函数作用
COUNT计数SUM
AVG平均值MAX最大值
MIN最小值
SELECT <查询内容>|列等 , (聚合函数)|* FROM  <表名字> GROUP BY  <列等> HAVING (聚合函数)条件 |条件;

其中HAVING是用来做拼接聚合值条件

COUNT: 计数

select count(<列>) from tableName [where 条件];

-- eg:
select count(*) from student_t;
select count(name) from student_t;

-- 和分组一起使用。查看每个班级有多少人数

select class,count(*) from student_tgroup by class;

COUNT(*):表示表中总行数

COUNT(列): 计算非NULL的总行数。统计这个组,这一列非null的总行数。

SUM: 求和

SELECT <查询内容>|列等 , SUM<列> FROM  <表名字> GROUP BY <列等> HAVING SUM<表达式>|条件
  
-- eg:
select sum(chinese) from student_t;
select sum(chinese), sum(english), sum(math) from student_t;
  
-- 查看每个班级的语文总分
select class,sum(chinese),group_concat(chinese) from student_tgroup by class;

AVG: 平均值

SELECT <查询内容>|列等 , AVG<列> FROM  <表名字> GROUP BY  <列等> HAVING AVG<表达式>|条件

-- eg:
select avg(chinese) from student_t;
select avg(chinese), avg(english), avg(math) from student_t;
    
-- 按班级查看平均分
select class,avg(chinese), avg(english), avg(math) from student_tgroup by 1;

MAX: 最大值

SELECT <查询内容>|列等 , MAX(<列>) FROM  <表名字> GROUP BY <列等> HAVING MAX(<表达式>)|条件

-- eg: 
select max(chinese) from student_t;
select max(chinese), max(english), max(math) from student_t;

MIN: 最小值

SELECT <查询内容>|列等 , MIN(<列>) FROM  <表名字> GROUP BY  <列等> HAVING MIN(<表达式>)|条件

-- eg:
select min(chinese) from student_t;
select min(chinese), min(english), max(math) from student_t;


-- 查询每个同学的总成绩,平均成绩,并用别名表示;
-- ROUND(100.3465,2) 四舍五入
select name, (chinese + english + math) as total_score, ((chinese + english + math)/3) as avg_score from student_t;

select name, (chinese + math + english) as total_score , round((chinese+math+english) /3, 2) as avg_score from student_t;

-- 查询数学最大值,并用别名表示;
select max(math) as max_math_score from student_t;

-- 查询外语最小值,并用别名表示;
select min(english) as min_english_score from student_t;

-- 查询全体学生的语数外各科平均成绩,并用别名表示;
select avg(chinese),avg(math),avg(english) as avg_english from student_t;

增加上Having对聚合结果进行筛选

SELECT <查询内容>|列等 , (聚合函数)|* FROM  <表名字> GROUP BY  <列等> HAVING (聚合函数)条件 |条件;

-- eg:
select class, group_concat(name), count(*) from student_t group by class;
select class, group_concat(name), count(*) from student_t group by class having count(*) > 3;  
    

-- 获取语文成绩大于90分的,按照班级分组

-- 获取班级的平均分

-- 获取班级人数大于三个人的班级

-- 获取班级平均语文成绩大于60分的

-- 查询班级语文总分大于200的班级(可以显示一下语文总分)

-- 查询班级平均分,学生的限制:数学大于等于60,语文大于等于60的 

-- 查询班级情况,要求学生语文最大的大于等于90,语文最少分大于等于70

-- 查询班级,语文最小成绩大于等于60,数学也是

select class from student_t group by class having sum(chinese) > 200;

SQL执行顺序

 (5) SELECT <列名>, ...   
 (1) FROM <表名>, ...   
 (2) [WHERE ...]   
 (3) [GROUP BY ...]   
 (4) [HAVING ...]   
 (6) [ORDER BY ...];  
 (7) [Limit ...]

– 小括号中的数字代表执行顺序

– SQL语句的关键字是有顺序的,需要按照上面的顺序来写

– 要注意书写顺序。也要注意执行顺序。

数据完整性

主要是用来限制MySQL表中的数据,使数据符合规范,也称之为完整。

比如,你现在,写了一个用户表。希望用户表里面的name这一列,全部都应该有数据。 有时候,如果不加以限制,那会被插入一些无意义的值。

大家可以理解为这是一种约束,一种限制

这个主要是在设计表以及字段的过程中可以提前考虑一下

实体完整性

缺少了这个字段,实体就不完整。

列约束

MySQL可以对插入的数据进行特定的验证,只有满足条件才可以插入到数据表中,否则认为是非法插入

主键(primary key)

  • 一个表只能有一个主键
  • 主键具有唯一性
  • 主键字段的值不能为null
  • 声明字段时,用 primary key 标识
  • 主键可以由多个字段共同组成。此时需要在字段列表后声明的方法
create table test_primary_key(
    -- 代表这个 columnName是主键
	columnName columnType primary key,
	......
)

create table test_primary_key2(
	columnName columnType,
	......,
	primary key(columnName, columnName2)
)

auto_increment 自动增长约束

一些序号,没有必须手动生成,想让mysql自动生成。

  • 自动增长必须为索引(主键或unique)
  • 只能存在一个字段为自动增长。
  • 默认为1开始自动增长。
create table test_auto_increment(
	columnName columnType primary key auto_increment,
	......
)
create table stu(  
   id int PRIMARY KEY auto_increment,  
   name varchar(20),  
   age int,  
   class varchar(20)  
 ) auto_increment=1000 ;

在上表中,id是主键,是自增的。主键值从1000开始自增,通过auto_increment来指定,假如没有指定,从1开始自增

-- 我们可以使用一条命令去看  自动增长目前增长到哪
show create table table_name;

域完整性

域完整性是针对某一具体关系数据库的约束条件,它保证表中某些列不能输入无效的值。

比如这个人的姓名,不允许输入null这种值。

比如有一张学生表。 id name id_card。不希望这个id_card有任何的null输入值。

null约束

  • null不是数据类型,是列的一个属性。一个具体的值
  • 表示当前列是否可以为null,表示什么都没有
  • null, 允许为空。默认
  • not null, 不允许为空

null表示没有数据,但是注意null不是空字符串。

create table test_null(
	columnName columnType not null,
	columnName2 columnType2 null,
	......
)

-- 这代表null
insert into test_null(column1) values (null);

-- 这个不是null。这是一个普通字符串
insert into test_null(column1) values ("null");

唯一值约束

  • unique

表示值是唯一的,不重复的

create table teacher(
	id int PRIMARY KEY,
	name varchar(20) unique,
	age int not null
);

1. 插入的值不能重复

2. 可以插入null

3. null可以重复

unique和primary key的区别:

  1. 主键值不能为空(null),而unique可以为空(null)
  2. 相同点: 都不能插入重复的数据。

参照完整性

外键(了解)是关系数据库中一个非常重要的概念,用于建立表与表之间的关系。一个表中的外键指向另一个表中的某个字段,这个字段通常是另一个表中的主键。外键的作用是保障数据的完整性和一致性,它可以确保两个表之间的关系正确地维护,防止数据出现不一致或者不完整的情况。

外键。关系型数据库,不仅可以存储数据,还可以存储数据和数据之间的关系,具体的体现就是外键。

create table province(
	id int PRIMARY KEY,
	name varchar(20)
);

create table city(
	id int ,
	name varchar(20),
	province_id int,
	-- 声明外键
    -- CONSTRAINT 外键名称 foreign key(列) references 表名(列名)
    -- 在 province_id上建立一个外键,指向   province表的id字段
	CONSTRAINT fk_pid foreign key(province_id) REFERENCES province(id)
);

-- 外键的另外一种写法
-- foreign key(列) references 表名(列名)
-- foreign key(s_id) references school(id)
 
insert into province values(41, "河南省");
insert into province values(43, "湖南省");
insert into province values(42, "湖北省");

insert into city(id, name, province_id) values(1, "武汉", 42);
insert into city(id, name, province_id) values(13, "随州", 42);
insert into city(id, name, province_id) values(1, "长沙", 43);
insert into city(id, name, province_id) values(6, "岳阳", 43);

-- 城市表插入

-- 插入城市表的时候会去寻找有没有23对应的省份,如果有,插入
-- 如果没有,那么会报错
insert into city values (6,'哈尔滨',23);

-- 不能删除还有子行的数据
delete from province where id = 32;

外键的优缺点:

  • 优点:能够限制数据的增加、删除或者是修改操作,来保证数据的正确性。
  • 缺点:
  1. 在插入(修改)子行(城市表)的数据的时候,需要去父表(省份表)中找对应的数据
  2. 在删除(修改)父表(省份表)的数据的时候,需要去检查城市表中是否有对应的数据

总结:有了外键之后,影响了增加、删除、修改的性能

其他约束(属性)

default 默认值属性

当前字段的默认值。

create table test_default(
	columnName timestamp ,
	......
);

create table test_default(
	id int primary key,
    name varchar(200),
    -- 如果你插入数据的时候,没有指定,这时候我就用默认值
    country varchar(200) default "中国"
);


CREATE TABLE `user_info`  (
  id int, 
  name varchar(200)
  create_time timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  update_time timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
);

create table tab ( create_time timestamp default current_timestamp );

— 表示将当前时间的时间戳设为默认值。

current_date, current_time

ON UPDATE CURRENT_TIMESTAMP(0) COMMENT ‘更新时间’

表中,一般会有三个字段是固定的(也就是创表必须得有)。id create_time update_time 见阿里编程规范 9条。

comment 注释

-- 作用是什么?
-- 为了让代码更好理解。
-- SQL里面的注释,是为了让SQL的字段更好理解。如果你进入了一个公司,不懂这个表里面的字段的含义。
-- 可以运行一下这个命令,看一下有没有备注帮你更好的理解这个表。    show create table test_comment;

create table test_comment(
  id int primary key auto_increment,
  name varchar(255) comment "名字",
  status int comment "0表示未付款,1表示已付款,2"
);

-- 相当于是字段的备注信息
-- 可以使用 show create table test_comment;来查看备注

说明

这些也都可以在开发过程中随着开发的需求进行调整

CREATE TABLE `market_admin` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '管理员名称',
  `password` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '管理员密码',
  `last_login_ip` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '最近一次登录IP地址',
  `last_login_time` datetime DEFAULT NULL COMMENT '最近一次登录时间',
  `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '''' COMMENT '头像图片',
  `add_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除',
  `role_ids` varchar(127) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '[]' COMMENT '角色列表',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='管理员表';

多表设计

在关系型数据库中,多表设计是指将数据分散到多个表中,每个表存储不同的数据。这种设计方式可以提高数据存储的效率和灵活性,同时也可以更好地保障数据的完整性和一致性。

有关系的一些表才需要这种多表设计。

学生表和订单表。 其实没有关系,所以它们不需要多表设计。

用户表和用户详情表。 这个有关系,需要多表设计。

一对一

在关系型数据库中,多表设计中的一对一关系指的是两个表之间的关系,其中一个表的记录只能对应另一个表中的一条记录,反之亦然。这种关系,在任意一方添加关系即可。

  • 人和身份证号
  • 学号和学生
  • 用户和用户详情

所有的一一对应的表,在逻辑上,都可以合并为一个表。

为什么本来可以用一张表,却要拆成两张表?

主要的原因是效率。如果一个表的列太多,比如有300列。最终数据量太大的时候,效率会很差。但是如果将其拆分成两个表,最终频繁查询的这个表,将其列弄得少一点,会提升查询效率。

比如,淘宝。用户表,每天都要大量使用。 比如,单表300列,有3亿人都在用。

可以把这个表拆成一个小表+另一个大表。

10列。 290列。

一对多

在关系型数据库中,多表设计中的一对多关系指的是两个表之间的关系,其中一个表的记录可以对应另一个表中的多条记录,而另一个表中的每条记录只能对应一个表中的记录。这种关系,会在多的一方添加字段来表示关系。

一对多是指 存在表A和表B,表A中的一条数据,对应表B中的多条数据;而表B中的一条数据,对应表A中的一条数据。

  • 班级和学生
  • 省份和城市
  • 用户和订单
  • 品牌和商品

在”多”表中维护”一”表的主键

多对多

在关系型数据库中,多表设计中的多对多关系指的是两个表之间的关系,其中一个表的记录可以对应另一个表中的多条记录,而另一个表中的每条记录也可以对应另一个表中的多条记录。这种关系通常需要通过中间表来实现。

多对多其实是指存在表A和表B,表A中的一条数据,对应表B中的多条数据;而表B中的一条数据,对应表A中的多条数据。

互为一对多

  • 学生和课程
  • 订单和商品
  • 品牌和类目

数据库设计三大范式

数据库表设计的时候,应该遵循的规范。只有遵循了这些范式(规范),设计出来的表才是好的。前人总结出来的一些原则,被称之为范式。

第一范式

每一列应该保持原子性。在设计表格的时候,要遵守。

一定要留有一定空间,灵活空间。

原子性:表示表中的数据都是一个不可拆分的最小单元。

第一范式:是跟着业务走的。但是业务是变动的,所以我们在设计表的时候,应该考虑之后业务的变化,来尽量的让每一列保持原子性。

姓名这东西,是否需要分。

第二范式

记录的唯一性

唯一性是指每一条记录都有唯一的标识。主键。表中必须得有一个 主键。

create table test1(
	id int primary key auto_increment
)

第三范式

数据不要冗余。

在上表中,班主任名字重复存储了,冗余了

  • 缺点:
  1. 重复存储了,需要占用更多的磁盘空间
  2. 如果要去修改某个老师的名字,那么需要在多个地方进行修改,增加了数据的维护成本
  • 优点:
  1. 根据学生去查班主任的名字变得更简单了,查询效率变高了

多表查询

多表查询是指在关系型数据库中,从多个表中查询数据的操作。多表查询可以帮助我们获得更加丰富的数据,以满足各种不同的需求。

连接查询❗❗❗

先来做数据准备

-- 如果这个表存在 就删除
drop table if exists user;
create table user(
	id int primary key auto_increment,
	name varchar(255),
	password varchar(255)
);

drop table if exists user_detail;
create table user_detail(
	id int primary key auto_increment,
	user_id int,
	address varchar(255),
	pic varchar(255)
);

insert into user values (1, "猪八戒", "zhubajie");
insert into user values (2, "孙悟空", "sunwukong");
insert into user values (3, "白骨精", "baigujing");
insert into user values (4, "唐僧", "tangseng");
insert into user values (5, "沙僧", "shaseng");

select * from user;

insert into user_detail values(null, 1, "高老庄", "猪八戒.jpg");
insert into user_detail values(null, 2, "花果山", "孙悟空.jpg");
insert into user_detail values(null, 3, "白虎岭", "白骨精.jpg");
insert into user_detail values(null, 4, "东土大唐", "唐僧.jpg");

select * from user_detail;

交叉连接

交叉连接其实就是求多个表的笛卡尔积。

-- 交叉连接
select * from user cross join user_detail;

交叉连接的结果没有实际的意义。

但是内连接和外连接都是基于交叉连接的结果去筛选的。

比如表A有3条数据,表B中有4条数据,最终会有3*4=12条数据。这个被称为笛卡尔积

内连接

内连接(inner join)是一种SQL中的表连接操作,用于将两个或多个表中的数据进行合并匹配。内连接只返回两个表中具有相同值的行,也就是说,只有在连接列中存在匹配值的行才会被返回。

内连接的语法如下:

SELECT columns
FROM table1
INNER JOIN table2
ON table1.column = table2.column;

-- 其中,columns是要返回的列,table1和table2是要连接的表,column是要连接的列。ON子句指定了连接条件,它指定了table1和table2之间的匹配条件。

inner join

  • 显式
  -- 显式内连接
  select * from user inner join user_detail 
  on user.id = user_detail.user_id;
  
  -- 这个SQL语句将user和user_detail表连接起来,只返回两个表都有相同的id的行。
  • 隐式
  -- 隐式内连接。不建议这样写。
  select * from user,user_detail 
  where user.id =user_detail.user_id;

外连接

外连接(outer join)是一种SQL中的表连接操作,用于将两个或多个表中的数据进行合并匹配,与内连接不同的是,外连接会返回左表或右表中即使没有匹配的行也会被返回,这些没有匹配的行将被填充为NULL值。

外连接有左外连接(left outer join)、右外连接(right outer join)和全外连接(@)三种类型。

说明:但是MySQL不支持全外连接。

左右表中的数据都保留。

outer可以省略掉。

左外连接

左外连接返回左表中的所有行以及右表中与左表匹配的行,右表中没有匹配的行将被填充为NULL值。左外连接的语法如下:

SELECT columns
FROM table1
LEFT OUTER JOIN table2
ON table1.column = table2.column;

-- 左外连接,保留匹配的数据。还会保留左表的所有数据
-- 左外连接
select * from user left outer join user_detail on 
user.id = user_detail.user_id;

右外连接

右外连接返回右表中的所有行以及左表中与右表匹配的行,左表中没有匹配的行将被填充为NULL值。右外连接的语法如下:

SELECT columns
FROM table1
RIGHT JOIN table2
ON table1.column = table2.column;
-- 右外连接
select * from user right outer join user_detail on 
user.id = user_detail.user_id;

连接查询最重要的,是找到连接的条件。

子查询(了解)

在关系型数据库中,子查询是指在一个 SQL 语句中嵌套另一个 SQL 语句来实现查询的方式。子查询通常用于在查询结果中过滤、排序、分组或者统计数据,或者作为其他查询语句的一部分。

子查询可以嵌套多层,每一层都返回一个结果集供上一层使用。

子查询可以用于实现各种复杂的查询需求,例如,使用子查询查询最大值或最小值,使用子查询实现分组统计等等。但是,由于子查询会增加查询的复杂度和执行时间,因此在使用子查询时需要注意查询性能的影响。

一个SQL语句的结果可以作为另外一个SQL语句的条件。

-- 现在想找有哪些同学学了Java。


-- 首先先拿到Java的id
select id from tec_cource where name='Java'; -- 1
-- 然后再把这个id=1 放给第二个
select * from tec_sele_cource where cource_id=1; -- 1,3

select * from tec_stu where id in (1,3);

-- 看学生信息
select * from tec_stu where id in (
    -- 看哪些学生选了 Java
	select student_id from tec_sele_cource where cource_id=(
        -- 获取Java的id
		select id from tec_cource where name='Java'
	)
)

不建议大家用。效率差。因为每一层查询会生成临时表

联合查询(了解)

SQL支持把多个SQL语句的结果拼装起来。

-- 写了两个SQL。把两个SQL的结果拼接起来

select * from student_t where class = '一班'
union
select * from student_t where class = '二班';

-- union要求返回的列数目要一致 

-- 我们可以使用union关键字对SQL1和SQL2的结果去做并集,一般来说联合查询作用不大

select * from student_t where class in ('一班','二班');
-- 当上面这个SQL语句查询速度很慢的时候,可以考虑union联合查询来提高效率。

-- union all  会把sql的结果,直接拼接起来。
select * from student_t where class = '一班'  
union all  
select * from student_t where class = '二班';
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
下一篇