这篇文章主要梳理了 SQL 的基础用法,会涉及到以下方面内容:

SQL大小写的规范

数据库的类型以及适用场景

SELECT 的执行过程

WHERE 使用规范

MySQL 中常见函数

子查询分类

如何选择合适的 EXISTS 和 IN 子查询

了解 SQL

SQL 是我们用来最长和数据打交道的方式之一,如果按照功能划分可分为如下 4 个部分:

DDL,数据定义语言。定义数据库对象,数据表,数据列。也就是,对数据库和表结构进行增删改操作。

DML,数据操作语言。对数据表的增删改。

DCL,数据控制语言。定义访问权限和安全级别。

DQL,数据查询语言。用来查询数据。

平时在编写 SQL 时,可能发现许多 SQL 大小写不统一,虽然不会影响 SQL 的执行结果,但保持统一的书写规范,是提高效率的关键,通常遵循如下的原则:

表名,表别名,字段名,字段别名等用小写。

SQL 保留字,函数名,绑定变量等用大写。

数据表,字段名采用下划线命名。

目前排名较前的 DBMS:

关系型数据库:建立在关系模型上的数据库,在建表时,通常先设计 ER 图表示之间的关系。

键值型数据库:以 key-value 的形式存储数据,优点是查找速度快,缺点是无法向关系型数据库一样使用如 WHERE 等的过滤条件。常见场景是作为内容缓存。

文档型数据库,在保存时以文档作为处理信息的基本单位。

搜索引擎:针对全文检索而设计。核心原理是 “倒排索引”。

列式数据库:相对于如 MySQL 等行式存储的数据库,是以列将数据存在数据库中,由于列具有相同的数据类型,所以可以更好的压缩,从而减低系统的 I/O,适用于分布式文件系统,但功能相对有限。

图形数据库,利用图的数据结构存储实体之间的关系。比如社交网络中人与人的关系,数据模型为节点和边来实现。

认识 SELECT

SELECT 一般是在学习 SQL 接触的第一个关键字,基础的内容就是不提了,这里整理常用的规范:

起别名

SELECT name AS n FROM student

查询常数, 增加一列固定的常数列:

SELECT '学生信息' as student_info, name FROM student

去重重复行

SELECT DISTINCT age FROM student

需要注意的是 DISTINCT 是对后面的所有列进行去重, 下面这种情况就会对 age 和 name 的组合进行去重。

SELECT DISTINCT age,name FROM student

排序数据,ASC 代表升序,DESC 代表降序

如先按照 name 排序,name 相等的情况下按照 age 排序。

SELECT DISTINCT age FROM student ORDERY BY name,age DESC

限制返回的数量

SELECT DISTINCT age FROM student ORDERY BY name DESC LIMIT 5

SELECT 的执行顺序

了解了 SELECT 的执行顺序,才能更好地写出更有效率的 SQL。

对于 SELECT 顺序有两个原则:

关键字的顺序不能颠倒:

SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ...

SELECT 会按照如下顺序执行:

FROM > WHERE > GROUP BY > HAVING > SELECT的字段 > DISTINCT > ORDER BY > LIMIT

SELECT DISTINCT student_id, name, count(*) as num #顺序5

FROM student JOIN class ON student.class_id = class.class_id #顺序1

WHERE age > 18 #顺序2

GROUP BY student.class_id #顺序3

HAVING num > 2 #顺序4

ORDER BY num DESC #顺序6

LIMIT 2 #顺序7

在逐一分析下这个过程前,我们需要知道在上面的每一个步骤中都会产生一个虚拟表,然后将这个虚拟表作为下一个步骤中作为输入,但这一过程对我们来说是不可见的:

从 FROM 语句开始,对 student 和 class 表进行 CROSS JOIN 笛卡尔积运算,得到虚拟表 vt 1-1;

通过 ON 筛选,在 vt1-1 的基础上进行过滤然后得到表 vt 1-2;

添加外部行。如使用左连接,右连接和全连接时,就会涉及到外部行,会在 vt1-2 的基础上增加外部行,得到 vt1-3。

如果超过两张表,就会重复上面的步骤。

在拿到最终的 vt1 的表数据后,会执行 WHERE 后面的过滤阶段,得到表 vt2.

接着到 GROUP 阶段,进行分组得到 vt3.

接着到 HAVING 阶段,对分组的数据进行过滤,得到 vt4.

后面进入 SELECT 阶段,提取需要的字段,得到 vt5-1,接着通过 DISTINCT 阶段,过滤到重复的行,得到 vt5-2.

然后对指定的字段进行排序,进入 ORDER BY 阶段,得到 vt6.

最后在 LIMIT 阶段,取出指定的行,对应 vt7,也就是最后的结果。

如果涉及到函数的计算比如 sum() 等,会在 GROUP BY分组后,HAVING 分组前,进行聚集函数的计算。

涉及到表达式计算,如 age * 10 等,会在 HAVING 阶段后,SELECT 阶段前进行计算。

通过这里,就可以总结出提高 SQL 效率的第一个方法:

使用 SELECT 时指定明确的列来代替 SELECT * . 从而减少网络的传输量。

使用 WHERE 进行过滤

使用 WHERE 筛选时,常有通过比较运算符,逻辑运算符,通配符三种方式。

对于比较运算符,常用的运算符如下表。

对于逻辑运算符来说,可以将多个比较运行符连接起来,进行多条件的筛选,常用的运算符如下:

需要注意的是,当 AND 和 OR 同时出现时,AND 的优先级更高会先被执行。当如果存在 () 的话,则括号的优先级最高。

使用通配符过滤:

like:(%)代表零个或多个字符,(_)只代表一个字符

函数

和编程语言中的定义的函数一样,SQL 同样定义了一些函数方便使用,比如求和,平均值,长度等。

常见的函数主要分为如下四类,分类的原则是根据定义列时的数据类型:

算术函数:

字符串函数

需要注意的是,在使用字符串比较日期时,要使用 DATE 函数比较。

日期函数

转换函数:

CAST 函数在转换数据类型时,不会四舍五入,如果原数值是小数,在转换到整数时会报错。

在转换时可以使用 DECIMAL(a,b) 函数来规定小数的精度,比如 DECIMAL(8,2) 表示精度为 8 位 - 小数加整数最多 8 位。小数后面最多为 2 位。

然后通过 SELECT CAST(123.123 AS DECIMAL(8,2)) 来转换。

聚集函数

通常情况下,我们会使用聚集函数来汇总表的数据,输入为一组数据,输出为单个值。

常用的聚集函数有 5 个:

其中 COUNT 函数需要额外注意,具体的内容可以参考这篇。

如何进行分组

在统计结果时,往往需要对数据按照一定条件进行分组,对应就是 GROUP BY 语句。

比如统计每个班级的学生人数:

SELECT class_id, COUNT(*) as student_count FROM student \

GROUP BY class_id;

GROUP BY 后也可接多个列名,进行分组,比如按照班级和性别分组:

SELECT class_id, sex, COUNT(*) as student_count FROM \

student GROUP BY class_id, sex;

HAVING 过滤和 WHERE 的区别

和 WHERE 一样,可以对分组后的数据进行筛选。区别在于 WHERE 适用于数据行,HAVING 用于分组。

而且 WHERE 支持的操作,HAVING 也同样支持。

比如可以筛选大于2人的班级:

SELECT class_id, COUNT(*) as student_count FROM student \

GROUP BY class_id \

HAVING student_count > 20;

子查询

在一些更为复杂的情况中,往往会进行嵌套的查询,比如在获取结果后,该结果作为输入,去获取另外一组结果。

在 SQL 中,查询可以分为关联子查询和非关联子查询。

假设有如下的表结构:

-- ----------------------------

DROP TABLE IF EXISTS `student`;

CREATE TABLE `student` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`name` varchar(20) NOT NULL DEFAULT '',

`age` int(3) NOT NULL,

`sex` varchar(10) NOT NULL DEFAULT '',

`class_id` int(11) NOT NULL COMMENT '班级ID',

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

-- ----------------------------

-- Records of Student

-- ----------------------------

INSERT INTO `student` VALUES ('1', '胡一', 13, '男', '1');

INSERT INTO `student` VALUES ('3', '王阿', 11, '女', '1');

INSERT INTO `student` VALUES ('5', '王琦', 12, '男', '1');

INSERT INTO `student` VALUES ('7', '刘伟', 11, '女', '1');

INSERT INTO `student` VALUES ('7', '王意识', 11, '女', '2');

-- ----------------------------

DROP TABLE IF EXISTS `student_activities`;

CREATE TABLE `student_activities` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`name` varchar(20) NOT NULL DEFAULT '',

`stu_id` int(11) NOT NULL COMMENT '班级ID',

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

INSERT INTO `student_activities` VALUES ('1', '博物馆', 1);

INSERT INTO `student_activities` VALUES ('3, '春游', 3);

非关联子查询

子查询从数据表中查询了数据结果,如果这个数据结果只执行一次,然后这个数据结果作为主查询的条件接着执行。

这里想要查询和胡一相同班级的同学名称:

SELECT name FROM student WHERE class_id = \

(SELECT class_id FROM student WHERE name='胡一')

这里先查到胡一的班级,只有一次查询,再根据该班级查找学生就是非关联子查询。

关联子查询

如果子查询需要执行多次,即采用循环的方式,先从外部查询开始,每次都传入子查询进行查询,然后再将结果反馈给外部

再举个例子, 比如查询比每个班级中比平均年龄大的学生姓名信息:

SELECT name FROM student as s1 WHERE age >

(SELECT AVG(age) FROM student as s2 where s1.class_id = s2.class_id)

这里根据每名同学的班级信息,查找出对应班级的平均年龄,然后做判断。子查询每次执行时,都需要根据外部的查询然后进行计算。这样的子查询就是关联子查询。

EXISTS 子查询

在关联子查询中,常会和 EXISTS 一起使用。用来判断条件是否满足,满足的话为 True,不满足为 False。

比如查询参加过学校活动的学生名称:

SELECT NAME FROM student as s where \

EXISTS(SELECT stu_id FROM student_activities as sa where sa.stu_id=s.id)

同样 NOT EXISTS 就是不存在的意思,满足为 FALSE , 不满足为 True.

比如查询没有参加过学校活动的学生名称:

SELECT NAME FROM student as s where \

NOT EXISTS(SELECT stu_id FROM student_activities as sa where sa.stu_id=s.id)

集合比较子查询

可以在子查询中,使用集合操作符,来比较结果。

还是上面查询参加学校活动的学生名字的子查询, 同样可以使用 IN:

SELECT name FROM student WHERE id IN (SELECT stu_id FROM student_activities)

EXISTS 和 IN 的区别

既然 EXISTS 和 IN 都能实现相同的功能,那么他们之间的区别是什么?

现在假设我们有表 A 和 表 B,其中 A,B 都有字段 cc,并对 cc 建立了 b+ 索引,其中 A 表 n 条记录,B 表 m 条索引。

将其模式抽象为:

SELECT * FROM A WHERE cc IN (SELECT cc FROM B)

SELECT * FROM A WHERE EXIST (SELECT cc FROM B WHERE B.cc=A.cc)

对于 EXISTS 来说,会先对外表进行逐条循环,每次拿到外表的结果后,带入子查询的内表中,去判断该值是否存在。

伪代码类似于下面:

for i in A

for j in B

if j.cc == i.cc:

return result

首先先看外表 A,每一条都需要遍历到,所以需要 n 次。内表 B,在查询时由于使用索引进而查询效率变成 log(m) B+ 的树高,而不是 m。

进而总效率:n * log(m)

所以对于 A 表的数量明显小于 B 时,推荐使用 EXISTS 查询。

再看 IN ,会先对内表 B 进行查询,然后用外表 A 进行判断,伪代码如下:

for i in B

for j in A

if j.cc == i.cc:

return result

由于需要首先将内表所有数据查出,所以需要的次数就是 m. 再看外表 A ,由于使用了 cc 索引,可将 n 简化至 log(n), 也就是 m * log(n).

所以对于 A 表的数据明显大于 B 表时,推荐使用 IN 查询。

总结一下对于 IN 和 EXISTS时,采用小表驱动大表的原则。

这里再扩展下 NOT EXISTS 和 NOT IN 的区别:

SELECT * FROM A WHERE cc NOT IN (SELECT cc FROM B)

SELECT * FROM A WHERE NOT EXIST (SELECT cc FROM B WHERE B.cc=A.cc)

对于 NOT EXITS 来说,和 EXISTS 一样,对于内表可以使用 cc 的索引。适用于 A 表小于 B 表的情况。

但对于 NOT IN 来说,和 IN 就有区别了,由于 cc 设置了索引 cc IN (1, 2, 3) 可以转换成 WHERE cc=1 OR cc=2 OR cc=3 , 是可以正常走 cc 索引的。但对于 NOT IN 也就是转化为 cc!=1 OR cc!=2 OR cc!=3 这时由于是不等号查询,是无法走索引的,进而全表扫描。

也就是说,在设置索引的情况下 NOT EXISTS 比 NOT IN 的效率高。

但对于没有索引的情况,IN 和 OR 是不同的:

一、操作不同

1、in:in是把父查询表和子查询表作hash连接。

2、or:or是对父查询表作loop循环,每次loop循环再对子查询表进行查询。

二、适用场景不同

1、in:in适合用于子查询表数据比父查询表数据多的情况。

2、or:or适合用于子查询表数据比父查询表数据少的情况。

三、效率不同

1、in:在没有索引的情况下,随着in后面的数据量越多,in的执行效率不会有太大的下降。

2、or:在没有索引的情况下,随着or后面的数据量越多,or的执行效率会有明显的下降。

总结

这篇文章中主要归纳了一些 SQL 的基础知识:

在使用 SELECT 查询时,通过显式指定列名,来减少 IO 的传输,从而提高效率。

并且需要注意 SELECT 的查询过程会从 FROM 后开始到 LIMIT 结束,理解了整体的流程,可以让我们更好的组织 SQL.

之后详细介绍了 WHERE 进行过滤的操作符和常用的函数,这里要注意在比较时间时要使用 DATE 函数,以及如何对数据进行分组和过滤。

最后着重介绍了子查询,IN 和 EXISTS 的适用场景。

以上就是快速学习MySQL基础知识的详细内容,更多关于MySQL基础知识的资料请关注脚本之家其它相关文章!

快速学习mysql_快速学习MySQL基础知识相关推荐

  1. MATLAB学习笔记2:MATLAB基础知识(下)

    阅读前请注意: 1. 该学习笔记是华中师范大学HelloWorld程序设计协会2021年寒假MATLAB培训的学习记录,是基于培训课堂内容的总结归纳.拓展阅读.博客内容由 @K2SO4钾 撰写.编辑, ...

  2. 学习光盘刻录必备基础知识

    学习光盘刻录必备基础知识 2011年04月11日 [b] 学习光盘刻录必备基础知识[/b] 日期:2009-10-10 11:04 1.什么是CD-R? CD-R就是光盘刻录片(CD Recordab ...

  3. JDBC 学习笔记(一)—— 基础知识 + 分页技术

    2019独角兽企业重金招聘Python工程师标准>>> 本文查阅方法:     1.查阅目录 -- 查阅本文目录,确定想要查阅的目录标题     2.快捷"查找" ...

  4. DL:深度学习(神经网络)的简介、基础知识(神经元/感知机、训练策略、预测原理)、算法分类、经典案例应用之详细攻略

    DL:深度学习(神经网络)的简介.基础知识(神经元/感知机.训练策略.预测原理).算法分类.经典案例应用之详细攻略 目录 深度学习(神经网络)的简介 1.深度学习浪潮兴起的三大因素 深度学习(神经网络 ...

  5. pwn学习总结(二) —— 基础知识(持续更新)

    pwn学习总结(二) -- 基础知识(持续更新) Canary PLT表&GOT表 格式化字符串漏洞 GCC编译参数 ASLR 危险函数 输入流 syscall条件 shellcode 其它 ...

  6. Android音视频学习系列(五) — 掌握音频基础知识并使用AudioTrack、OpenSL ES渲染PCM数据

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  7. 学习插画前期需要什么基础知识?插画师入门基础先学什么?

    学习插画前期需要什么基础知识?插画师入门基础先学什么?零基础到插画师需要学多久?想必这些问题都是初学者比较伤脑筋的问题,那么初学者学习插画前期需要什么基础知识呢?今天小编就在网络上收集整理了关于插画师 ...

  8. 【Python学习笔记】第一章基础知识:格式化输出,转义字符,变量类型转换,算术运算符,运算符优先级和赋值运算符,逻辑运算符,世界杯案例题目,条件判断if语句,猜拳游戏与三目运算符

    Python学习笔记之[第一章]基础知识 前言: 一.格式化输出 1.基本格式: 2.练习代码: 二.转义字符 1.基本格式: 2.练习代码: 3.输出结果: 三.输入 1.基本格式: 2.练习代码: ...

  9. 学习人工智能需要掌握哪些基础知识,需要具备哪些数学和编程技能?

    学习人工智能需要掌握以下基础知识: 数学基础:线性代数.概率论.微积分.优化等基本数学知识是人工智能领域的重要基础. 编程基础:熟悉至少一种编程语言(如Python.C++等),了解基本的数据结构和算 ...

最新文章

  1. mxnet保存模型,加载模型来预测新数据
  2. 大量POI点展示的一种解决方案
  3. 经典论文复现 | ICML 2017大热论文:Wasserstein GAN
  4. jax-rs jax-ws_JAX-RS Bean验证错误消息国际化
  5. 适用于Atom Feed的Spring MVC
  6. 实操来了!一文告诉你如何用 Streamlit 和 Heroku 开发 Web
  7. linux mysql 存储过程乱码,mysql存储过程中 乱码问题解决办法
  8. 来,一起来实现一个符合Promise/A+的Promose(1.0.1版本)
  9. 华为诺亚方舟预训练语言模型NEZHA、TinyBERT开源代码
  10. 概率校准Probability Calibration
  11. 过年回家送什么?这10个教小朋友编程玩具最有科技感
  12. php怎么循环显示图片,thinkphp 循环显示图片问题!!!~~~~
  13. python简单代码-Python简单进程锁代码实例
  14. Python 100 例
  15. ad19做直插封装 ipc_共享一下自己的PCB封装库(Altium Designer)
  16. SqlServer——Excel连接数据库相关知识
  17. vim 修改文件出现错误 “ E45: ‘readonly’ option is set (add to override)“
  18. WordPress调用自带的友情链接功能_只在首页显示
  19. 两个质数互质是_科学网—理解黎曼猜想(二)两个自然数互质的概率是多少? - 袁岚峰的博文...
  20. Flink(初识Flink,快速上手)

热门文章

  1. FileStream与StreamWriter区别
  2. 第一个算出地球周长的人
  3. SCU 2818 QQ音速
  4. OpenGL 法线贴图Normal Mapping
  5. python 利用for 循环简便生成list
  6. ftp服务器必须运行ftp服务器软件对吗,ftp是什么?FTP服务器搭建及选择
  7. 1454E - Number of Simple Paths
  8. (转)Oracle仓库管理系统(WMS)
  9. 助手论坛斑竹一些胡言乱语
  10. SELECT list is not in GROUP BY clause and contains nonaggregated column