一个前端小白,学习SQL语句

214 阅读16分钟

一个写 ToB 端的前端仔,天天跟数据打交道,头皮发麻。看着后端同事代码中写着长串的SQL语句,好生羡慕。

装B的东西不应该都要去掌握一番,加上正好最近在学习 node.js,学到了数据库模块,那么SQL语句是该好好学一波。

接口 api 返回的数据,不能全听后端同事在那里吹。有些东西,该后端处理数据,就后端处理,不要徒劳的增加前端工作量(玩笑且认真)。

认识SQL,准备工作

SQL: Structured Query Language,翻译为结构化查询语句,简称SQL

使用 SQL 编写出来的语句就是 SQL语句。该语句用于对数据库进行操作。

接下来认识一些SQL的基本知识。

SQL 语句的规范

  • 关键词使用大写。比如:CREATE、 TABLE等(当然小写也行)
  • 一条SQL语句以分号(;) 结束。
  • 当遇到自己设计的表名或者字段是关键词的时候,可以使用``包裹,这样做目的就是不会看成一个关键词。

SQL 语句的类型

SQL2.png

注:上面标志小红旗的,是常用的SQL类型。

表的约束

主键(primary key)

为了区分每条记录的唯一性,必须有一个字段永远不会重复,并且不能为空,那么这个字段被称为主键

  • 唯一性
  • 必须是 NOT NULL。如果没有手动设置,默认也会自动设置为 NOT NULL
  • 主键也可以是多个字段组合成唯一性,被称为联合主键
  • 建议:最好不要是业务字段表主键。
唯一性(unique)

主键具有唯一性。但是针对一些字段,也是具有唯一性的,尽管这些字段不是主键。

比如说:用户名(name)在一些系统中是不能重复的。

不能为空(not null)

禁止某些字段是不能为空,必填的。

默认值(default)

当某些字段没有被填充时,自动加入默认值。

自动增长(auto_increment)

针对一些id,或者排序的字段,需要每添加一条数据,在原来的基础上增加1。

外键(foreign key)

当针对多张表进行关联时,需要外键进行链接。

创建数据库和对应表

喜欢看 NBA,喜欢老詹,老詹目前在紫金湖人队,那就拿湖人球员的数据来进行分析,举例。

第一步:创建一个分析数据库(analyze_db

-- 创建数据库(analyze_db)
CREATE DATABASE analyze_db; -- 创建数据库,如果存在会保存
CREATE DATABASE IF NOT EXISTS analyze_db; -- 先判断该库是否存在,不存在再创建-- 查看所有数据库
SHOW DATABASES;
​
-- 使用数据库
USE analyze_db;
​
-- 删除数据库
DROP DATABASE analyze_db; -- 删除数据库,如果不存在会报错
DROP DATABASE IF EXISTS analyze_db; -- 先判断该库是否存在,存在就删除

第二步:创建一张湖人球员表(lakers_teams

在创建表执之前,先执行了 use analyze_db,确保创建的表就是在该库里面。

开始创建表之前,应该冷静分析一下,需要球员哪些信息,来供参考。

字段名称字段描述
id作为主键,表示数据的唯一性
name球员的名称
avg_time平均上场的时间(得到教练组的信任度)
avg_score场均得分(能力的体现)
avg_shooting_rate投篮命中率(效率的体现)
avg_three_shooting_rate三分投篮命中率(外线得分的体现)
position球队定位
backboard篮板
secondary_attack助攻

参考的数据还是挺多的,现在就来创建一张表。

-- 创建表(lakers_teams)
CREATE TABLE IF NOT EXISTS lakers_teams(
  id INT PRIMARY KEY AUTO_INCREMENT, -- id 主键 自动增加1
  name VARCHAR(50) UNIQUE NOT NULL, -- name 唯一性 不能为空
  avg_time DOUBLE DEFAULT 0, -- 场均时间 double 类型 默认值为0
  avg_score DOUBLE DEFAULT 0,
  avg_shooting_rate DOUBLE DEFAULT 0,
  avg_three_shooting_rate DOUBLE DEFAULT 0,
  position VARCHAR(50),
  backboard FLOAT DEFAULT 0,
  secondary_attack FLOAT DEFAULT 0
);
​
​
#################### 学习对表的其他SQL语句(最右边就是该SQL语句的功能关键词)  ####################
​
-- 删除表
DROP TABLE IF EXISTS lakers_teams; -- drop-- 表字段描述
DESC lakers_teams;  -- desc-- 修改表的名称(lakers_teams 改为 new_lakers_teams)
ALTER TABLE lakers_teams RENAME TO new_lakers_teams;  -- rename-- 添加新的字段(薪资:salary int类型)
ALTER TABLE lakers_teams ADD salary INT; -- add-- 修改字段名称(salary 改为 price)
ALTER TABLE lakers_teams CHANGE salary price INT; -- change-- 修改表中字段类型(price INT 改为 BIGINT 类型)
ALTER TABLE lakers_teams MODIFY price BIGINT; -- modify-- 删除表中某个字段(price)
ALTER TABLE lakers_teams DROP price; -- drop
​
####################  可以自己试一试   ####################

表的数据操作(增、删、改)

在上面已经创建好了库和表,那么现在就是向表中插入数据。

-- SQL语句添加几个球员的数据,其他的直接手动添加
INSERT INTO 
  lakers_teams (name, avg_time, avg_score, avg_shooting_rate, avg_three_shooting_rate, position, backboard, secondary_attack)
  VALUES ('詹姆斯', 36.1, 27.4, 0.49, 0.31, '前锋', 8.2, 6.7);
  
INSERT INTO 
  lakers_teams (name, avg_time, avg_score, avg_shooting_rate, avg_three_shooting_rate, position, backboard, secondary_attack)
  VALUES ('戴维斯', 33.4, 27.4, 0.59, 0.29, '中锋', 12.1, 2.6);
  
INSERT INTO 
  lakers_teams (name, avg_time, avg_score, avg_shooting_rate, avg_three_shooting_rate, position, backboard, secondary_attack)
  VALUES ('威斯布鲁克', 28.0, 14.6, 0.40, 0.28, '后卫', 6.1, 7.6);
  
  
####################   学习操作数据的其他SQL语句   ####################
​
-- 删除数据(删除表中所有的数据,谨慎使用)
DELETE FROM lakers_teams;
​
-- 删除表中数据(条件删除)
DELETE FROM lakers_teams WHERE id = 11;
​
-- 修改表中数据(所有数据,表中的position字段都会变成中锋)
UPDATE lakers_teams SET position = '中锋';
​
-- 修改表中数据(根据条件修改)
UPDATE lakers_teams SET position = '中锋' WHERE id = 11;
​
-- 拓展:当修改某一条数据时,使用最新的时间记录
ALTER TABLE lakers_teams 
       ADD updateTime TIMESTAMP 
       DEFAULT CURRENT_TIMESTAMP 
       ON UPDATE CURRENT_TIMESTAMP
-- lakers_teams 表添加 updateTime 类型为 timestamp ,默认值为 current_timestamp, 更新时也会重新设置 current_timestamp
​
####################  可以自己试一试   ####################

插入数据

插入数据的SQL语句的用法:

insert into 表名 (字段1,字段2,字段3,...)values (字段1的值,字段2的值,字段3的值,...)

录入数据完成(大部分数据手动录入的)

SQL3.png

表的数据查询(查)

万事都是从简开始,SQL语句也不例外。

-- 查询所有数据
SELECT * FROM lakers_teams;
​
-- 查询部分数据(id, name, avg_score)
SELECT id, name, avg_score FROM lakers_teams;
​
-- 查询部分数据(id, name, avg_score),对得分字段取别名
SELECT id,name, avg_score as score FROM lakers_teams;

where筛选条件

在简单的基础上,添加where筛选条件

-- 场均大于20分的球员
SELECT id, name, avg_score FROM lakers_teams WHERE avg_score > 20;
​
-- 场均篮板大于8个的球员
SELECT * FROM lakers_teams WHERE backboard > 8;
​
-- 场均助攻大于等于5个的球员
SELECT * FROM lakers_teams WHERE secondary_attack >= 5;
​
-- 场均大于10,但是小于20分的中坚力量 (&& 与 and 等价, between...and 也可以实现)
SELECT * FROM lakers_teams WHERE avg_score > 10 && avg_score <= 20;
SELECT * FROM lakers_teams WHERE avg_score > 10 and avg_score <= 20;
SELECT * FROM lakers_teams WHERE avg_score BETWEEN 10 AND 20;
​
-- 看看三分命中率高于40%或者低于20%的呢
SELECT * FROM lakers_teams WHERE avg_three_shooting_rate > 0.4 || avg_three_shooting_rate < 0.2;
SELECT * FROM lakers_teams WHERE avg_three_shooting_rate > 0.4 or avg_three_shooting_rate < 0.2;
​
-- 戴维斯伤了,看有不有其他的中锋球员可以接替,查看中锋球员个数
SELECT id, name FROM lakers_teams WHERE `position` = '中锋';

where 模糊查询

  • 使用 like 关键词
  • % 表示任意个字符
  • _ 表示一个任意字符
-- 查询后卫的情况(8位,这是两桌麻将的节奏呀)
SELECT * FROM lakers_teams WHERE `position` LIKE '%后卫%';

order by 数据排序

  • 使用公式 order by 字段名称 生序/降序
  • desc:降序; asc: 生序;
-- 得分降序排序
SELECT * FROM lakers_teams ORDER BY avg_score DESC; 

分页查询

  1. 使用公式1: limit 数据条数 offset 偏移量(推荐写法)
  2. 使用公式2:limit 偏移量, 数据条数(不推荐写法)
  3. 处理前端分页
// 前端传递参数
{current: 1, pageSize: 10}
​
// 后端处理参数
{
 limitNum: pageSize
 offsetNum: (current - 1) * pageSize
}
​
// SQL 实现
select * from lakers_teams limit limitNum offset offsetNum;
-- 查看所有球员的数据,看不过来,先五个的看吧,看看数据的均衡
SELECT * FROM lakers_teams LIMIT 5 OFFSET 0;  -- 前面五个
SELECT * FROM lakers_teams LIMIT 5 OFFSET 6;  -- 接下来的五个
SELECT * FROM lakers_teams LIMIT 11, 5 -- 最后的五个

聚合函数

先收集到一起,然后所有结果看成一组数据,进行操作

常见的聚合函数:

  • avg: 平均值
  • max: 最大值
  • min: 最小值
  • sum: 求和
  • count:计算个数(count的参数必须是必填的,不然如果没有值,就会不计算在内,那么就不准确)
-- 查询球员最好的投篮命中率
SELECT MAX(avg_shooting_rate) AS max_shooting_rate FROM lakers_teams;
​
-- 统计球员得分在10以上的球员个数(7个)
SELECT COUNT(*) FROM lakers_teams WHERE avg_score >= 10;
​
-- 统计三分投篮的平均命中率(0.3, 惨不忍睹)
SELECT AVG(avg_three_shooting_rate) as rate FROM lakers_teams;
​

group by 分组

聚合函数都是看成一组数据。

group by 进行分组,看成多组数据。

-- 统计各个位置的人数
SELECT `position`, COUNT(*) FROM lakers_teams GROUP BY `position`;

having 筛选条件

针对没有分组的 SQL 语句,筛选条件使用 where

但是针对 group by 组合后的 SQL 语句,筛选条件需要使用 having,而不能使用 where,使用 where 报错。

-- 统计各个位置的人数, 并且人数大于3。
SELECT `position`, COUNT(*) as count FROM lakers_teams GROUP BY `position` HAVING count >= 3;

为什么需要多表?

为什么需要多表?看看下面的举例,也许你就懂了。

创建一张表(collect_players),统计一些球员的基本数据和所在球队信息。

再创建表之前,还是要先分析分析(这里就不表格列举了)。

CREATE TABLE IF NOT EXISTS collect_players(
  id INT PRIMARY KEY AUTO_INCREMENT,  -- id 主键 自动增长
  name VARCHAR(50) UNIQUE NOT NULL, -- name 名称 不能为空,唯一性
  score DOUBLE DEFAULT 0, -- 场均得分
  shooting_rate DOUBLE DEFAULT 0, -- 投篮命中率
  three_shooting_rate DOUBLE DEFAULT 0, -- 三分投篮命中率
  salary DOUBLE, -- 薪资
  position VARCHAR(50), -- 角色定位
  team_name VARCHAR(100), -- 所在球队名称
  team_address VARCHAR(200), -- 所在球队地址
  team_note VARCHAR(300) -- 所在球队的备注
);

添加数据,这里可以使用SQL语句添加,自己动手试试。(我就直接数据库里面添加了)

SQL4.png

发现没有,这张表里面的数据,有很对是重复的。

就比如:

id 为 1 和 2,球队信息是一样的(三个字段重复)

id 为 4、5 、6时,球队信息也是一样的(三个字段重复)。

那么想象一下,这里的数据量不是很大。NBA中的球员这么多,每支球队,正规球员都有15人,还别说线下联赛人员。

如果表这样设计的话,就会存在大量的重复数据。并且一旦数据发生变动,又有大量的数据需要维护。

所以,针对不合理的表,就需要重新设计。

设计新表,重构原表

设计球队表(team_infos)。

CREATE TABLE IF NOT EXISTS team_infos(
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(100) UNIQUE NOT NULL,
  address VARCHAR(100),
  note TEXT
);

插入数据

SQL5.png 重构collect_players

-- 删除三个字段
ALTER TABLE collect_players DROP team_name;
ALTER TABLE collect_players DROP team_address;
ALTER TABLE collect_players DROP team_note;
​
-- 新增一个 team_id 字段,该字段是一个外键
ALTER TABLE collect_players ADD team_id INT; -- 新增team_id字段
ALTER TABLE collect_players ADD FOREIGN KEY (team_id) REFERENCES team_infos(id); -- 设置外键

重新录入数据

SQL6.png

悄悄的这里添加了一个自由人,目前失业的安东尼。

这样两张表就分开了,保存的数据也不重复,也好维护。

外键知识点

外键使用

使用格式:foreign key (字段名称) references 表名(字段名称)

  • foreign key (字段名称) 设置字段名称为外键
  • references 引用。表明外键的值,是引用表名(字段名称)的值,不能随便填写。

初始设计表的外键设置

在上面,是在表已经存在的时候设计的外键,那么在最初设计表的时候就设计外键呢?

CREATE TABLE IF NOT EXISTS collect_players(
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(30) NOT NULL,
  team_id INT, -- 外键
  FOREIGN KEY (team_id) REFERENCES team_infos(id)
);

外键属性

在上面设置了外键,当关联表的数据发生变化会怎么样?

UPDATE team_infos SET id = 1000;
-- 会发现,直接报错,因为 team_infos 中的id,被其他表的字段关联了;当id被更新时,其他的表的字段不知道是否要更新。

外键的属性值

SQL7.png 外键的默认值是 restrict。 当有值存在关联的时候,就不会让其修改。

所以,外键需要重新设置。

-- 查看外键信息(简单的来说,就是知道外键的名称)
SHOW CREATE TABLE collect_players;

查询的结果

CREATE TABLE `collect_players` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `score` double DEFAULT '0',
  `shooting_rate` double DEFAULT '0',
  `three_shooting_rate` double DEFAULT '0',
  `salary` double DEFAULT NULL,
  `position` varchar(50) DEFAULT NULL,
  `team_id` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`),
  KEY `team_id` (`team_id`),
  CONSTRAINT `collect_players_ibfk_1` FOREIGN KEY (`team_id`) REFERENCES `team_infos` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

从里面可以知道外键的名称为 collect_players_ibfk_1

如果存在多个外键,数字就依次内推。

  • collect_players_ibfk_1
  • collect_players_ibfk_2
-- 根据外键名字删除外键
ALTER TABLE collect_players DROP FOREIGN KEY collect_players_ibfk_1;
​
-- 重新创建一个新的外键
ALTER TABLE collect_players add FOREIGN KEY (team_id) REFERENCES team_infos(id) 
                         ON UPDATE CASCADE  
                         ON DELETE CASCADE;

这样就会实现一边更新,另外一边关联的数据也会更新。

表连接

上面设计了两张新表,减少数据量,维护更加的方便。但是也会有一个缺点,就是数据太分散了,查看数据时就看不全信息。

那么这时候,就需要把两张表连接在一起。

也许,SQL语句的 表连接 是一个很好的选择。

表连接分为四种:

  • 左连接(left [outer] join,常用)
  • 右连接(right [outer] join,不常用)
  • 内连接 ([cross/inner] join,常用)
  • 全连接(mysql不支持,需要使用union,不常用)

[]里面的是可以省略的

左连接

SQL8.png 使用格式: A LEFT JOIN B (展示A的全部数据)

-- 连接 collect_players 和 team_infos, 以collect_players为主
-- 这里分别为表取了别名
SELECT * FROM collect_players as cp LEFT JOIN team_infos as ti ON cp.team_id = ti.id; 
​
-- 去掉 collect_players 表中 team_id 中为null的数据
SELECT * FROM collect_players as cp LEFT JOIN team_infos as ti ON cp.team_id = ti.id WHERE team_id IS NOT NULL; 

右连接

SQL9.png 使用格式: A RIGHT JOIN B (展示B的全部数据)

-- 连接 collect_players 和 team_infos, 以team_infos为主
SELECT * FROM collect_players as cp RIGHT JOIN team_infos as ti ON cp.team_id = ti.id; 

内连接

使用格式: A JOIN B (展示A 与 B的交集数据)

A CROSS JOIN BA INTER JOIN B 也是同样的道理。

SELECT * FROM collect_players as cp JOIN team_infos as ti ON cp.team_id = ti.id; 

全连接

使用格式:(A LEFT JOIN B) union (A RIGHT JOIN B)

(SELECT * FROM collect_players as cp LEFT JOIN team_infos as ti ON cp.team_id = ti.id)
union
(SELECT * FROM collect_players as cp RIGHT JOIN team_infos as ti ON cp.team_id = ti.id); 

多表查询(多对多)

在上面球队球员的关系,就是一对多的关系。

但是在很多实际的情况下,数据间的联系是处于多对多的关系。

比如,NBA交易期阶段,被交易的球员和交易的球队就是一种多对多的关系。

  • 被交易的球员有想法去任何一个球队。
  • 交易的球队也有想法接受任何一个球员。

这就是一种多对多的关系。

上面对于不熟悉NBA规则的,例子不是很形象,但是下面这个例子一定很熟悉。

大学的时期选课(经典例子),这就是一种多对多的体现。

学生表(student):

idnamesex
1小明
2小红
3小张
4小李

课程表(course):

idnamescore
1数据结构4
2C++3
3算法导论2

学生肯定可以选择多门课,课程也同时可以被多个学生选择。

那么如何统计一个学生选了多少课呢?(错误的设计)

idnamesexselect_id
1小明[1,2]

设计的表存在数组形式,数组里面存放该学生选择的课。

这是错误的设计方案,因为当课程的信息变动的时候,数组的里面存放的信息是不好维护的。(只有通过代码强行维护),这是不合理的。

正确的设计就是创建一张中间表,来保存两者之间的对应关系。

实战演示

-- 创建学生表
CREATE TABLE IF NOT EXISTS students(
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(50) NOT NULL,
  sex VARCHAR(10)
);
-- 插入学生数据
INSERT INTO students (name, sex) VALUES ('小明', '男');
INSERT INTO students (name, sex) VALUES ('小红', '女');
INSERT INTO students (name, sex) VALUES ('小李', '男');
INSERT INTO students (name, sex) VALUES ('小张', '男');
​
​
-- 创建课程表
CREATE TABLE IF NOT EXISTS courses(
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(50) NOT NULL,
  score INT
);
-- 插入课程数据
INSERT INTO courses (name, score) VALUES ('数据结构',3);
INSERT INTO courses (name, score) VALUES ('C++', 2);
INSERT INTO courses (name, score) VALUES ('算法导论', 4);
​
​
-- 创建关系表
CREATE TABLE IF NOT EXISTS select_student_course(
  id INT PRIMARY KEY AUTO_INCREMENT,
  student_id INT NOT NULL,
  course_id INT NOT NULL,
  FOREIGN KEY (student_id) REFERENCES students(id) ON UPDATE CASCADE ON DELETE CASCADE,
  FOREIGN KEY (course_id) REFERENCES courses(id) ON UPDATE CASCADE ON DELETE CASCADE
);
-- 插入数据
INSERT INTO select_student_course (student_id, course_id) VALUES (1,3);
INSERT INTO select_student_course (student_id, course_id) VALUES (2,2);
INSERT INTO select_student_course (student_id, course_id) VALUES (3,1);
INSERT INTO select_student_course (student_id, course_id) VALUES (3,2);
INSERT INTO select_student_course (student_id, course_id) VALUES (2,1);

创建了三张表,分别插入了数据,接下来就可以通过关联表,来查看选课的情况。

-- 查看所有学生的选课情况
SELECT * FROM students LEFT JOIN select_student_course as ssc ON students.id = ssc.student_id
                       LEFT JOIN courses ON ssc.course_id = courses.id;

SQL11.png 可以清晰知道每个同学的选课情况。

但是也发现美中不足的地方,就是很多字段是重复的,那么这里就需要取一些别名,便于区分。

-- 简化后的所有学生选课情况
SELECT 
 students.id as id,
 students.name as name,
 sex,
 courses.name as courseName,
 score
FROM students LEFT JOIN select_student_course as ssc ON students.id = ssc.student_id
              LEFT JOIN courses ON ssc.course_id = courses.id;

sql12.png 是不是看着就挺清爽的。

-- 查询小红选的课程(也就是id为2)
SELECT 
students.id as id,
students.name as name,
sex,
courses.name as courseName,
score
FROM students LEFT JOIN select_student_course as ssc ON students.id = ssc.student_id
              LEFT JOIN courses ON ssc.course_id = courses.id
              WHERE students.id = 2;

SQL13.png

-- 查询C++被多少学生选择
SELECT 
students.id as id,
students.name as name,
sex,
courses.name as courseName,
score
FROM students LEFT JOIN select_student_course as ssc ON students.id = ssc.student_id
              LEFT JOIN courses ON ssc.course_id = courses.id
              WHERE courses.id = 2;

SQL14.png 当我们查询到的数据,通过一层对象直接返回,感觉数据表达的信息是非常的混乱。

比如,返回给前端的数据格式:

[
  // 这层对象中既包含了学生的信息,也包含了课程了信息(就感觉这层对象,没有层次感)
  {
    id: 2,
    name: '小红',
    sex: '女',
    courseId: 2,
    courseName: 'C++',
    score: 2
  }
]



[
      // 是否可以实现 课程信息单独使用一个字段来表示
  {
    id: 2,
    name: '小红',
    sex: '女',
    courseInfo: {
      id: 2,
      name: 'C++',
      score: 2
    }
  }
]

那么转化成上面这种形式,SQL语句改怎么实现呢?

SQL语句提供了JOSN_OBJECT来实现上面的效果

使用公式:JOSN_OBJECT(key1, value1, key2, value2, key3, value3,...)

SELECT 
students.id as id,
students.name as name,
sex,
JSON_OBJECT('id', courses.id, 'name', courses.name, 'score', courses.score) as courseInfo
FROM students LEFT JOIN select_student_course as ssc ON students.id = ssc.student_id
              LEFT JOIN courses ON ssc.course_id = courses.id;

SQL15.png 但是也发现不对劲,就比如小红,选择了两门课程,返回前端的数据,应该是这样的。

// 小红是选择了两门课程的,那么她的课程信息应该是一个数组
{
  id: 2,
  name: '小红',
  sex: '女'
  courseArr: [
    {id: 2, name: 'C++', score: 2},
    {id: 1, name: '数据结构', score: 3}
  ]
}

那么这种SQL语句应该如何实现?

SQL语句提供了JOSN_ARRAYAGG函数,来实现数组的形式,当然也需要配合 JOSN_OBJECT函数。

使用公式:JOSN_ARRAYAGG(object)

-- 要合并数据,肯定需要分组,所以根据students.id进行分组
-- 在结合JSON_ARRAYAGG 与 JSON_OBJECT 配合转化成数组
SELECT 
students.id as id,
students.name as name,
sex,
JSON_ARRAYAGG(JSON_OBJECT('id', courses.id, 'name', courses.name, 'score', courses.score)) as courseArr
FROM students LEFT JOIN select_student_course as ssc ON students.id = ssc.student_id
              LEFT JOIN courses ON ssc.course_id = courses.id
              GROUP BY students.id;

SQL16.png nice。

多表查询,基本上大致就这样了。

结语

终于写完了,感觉 SQL 的语法是真的不少。

本来想着使用NBA的数据,创建表,一条路线走完的。但是写到后面,感觉状态越来越差,甚至针对多表设计,已经想不出怎么构建了。最后就使用了通用的学生与课程的关系,进行演示,可能为了大家更好的理解,也更好的为我将来做回忆吧。(烂尾操作)。

但是对于自己而言,用额外的时间抽出了大量的精力来学习SQL语句,还是挺满足的(针对前端而言,使用SQL的语句基本为零)。掌握一个知识点,增添了自己的技术色彩。(copyer 加油)。

如果上面有误,请指教。(虽然写的挺烂的,但是针对知识点也许还是香的)。