索引是一种数据结构,可以帮助我们快速的进行数据的查找。
索引的数据结构和具体存储引擎的实现有关,在 MySQL 中使用较多的索引有 Hash 索引,B+ 树索引等,而我们经常使用的 InnoDB 存储引擎的默认索引实现为:B+ 树索引。
如果表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页。如果使用非自增主键(如果身份证号或学号等),由于每次插入主键的值近似于随机,因此每次新纪录都要被插到现有索引页得中间某个位置, 频繁的移动、分页操作造成了大量的碎片,得到了不够紧凑的索引结构,后续不得不通过OPTIMIZE TABLE(optimize table)来重建表并优化填充页面。
首先要知道 Hash 索引和 B+ 树索引的底层实现原理:
hash 索引底层就是 hash 表,进行查找时,调用一次 hash 函数就可以获取到相应的键值,之后进行回表查询获得实际数据。B+ 树底层实现是多路平衡查找树。对于每一次的查询都是从根节点出发,查找到叶子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据。
那么可以看出他们有以下的不同:
因此,在大多数情况下,直接选择 B+ 树索引可以获得稳定且较好的查询速度。而不需要使用 hash 索引。
聚簇索引就是按照每张表的 主键 构造一棵B+树,同时叶子节点中存放的就是整张表的行记录数据。
在 InnoDB 中,只有主键索引是聚簇索引,如果没有主键,则挑选一个唯一键建立聚簇索引。如果没有唯一键,则MySQL自动为InnoDB表生成一个隐含字段来建立聚簇索引,这个字段长度为6个字节,类型为长整形。
当查询使用聚簇索引时,在对应的叶子节点,可以获取到整行数据,因此不用再次进行回表查询。
Hash索引
基于哈希表实现,只有精确匹配索引所有列的查询才有效,对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码(hash code),并且Hash索引将所有的哈希码存储在索引中,同时在索引表中保存指向每个数据行的指针。
B-Tree索引(MySQL使用B+Tree)
B-Tree能加快数据的访问速度,因为存储引擎不再需要进行全表扫描来获取数据,数据分布在各个节点之中。
B+Tree索引
是B-Tree的改进版本,同时也是数据库索引所采用的存储结构。数据都在叶子节点上,并且增加了顺序访问指针,每个叶子节点都指向相邻的叶子节点的地址。相比B-Tree来说,进行范围查找时只需要查找两个节点,进行遍历即可。而B-Tree需要获取所有节点,相比之下B+Tree效率更高。
B+tree性质:
索引的优点
索引的缺点
在根据主键索引搜索时,直接找到key所在的节点即可取出数据;根据辅助索引查找时,则需要先取出主键的值,再走一遍主索引。 因此,在设计表的时候,不建议使用过长的字段为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。
总结:InnoDB 主键索引使用的是聚簇索引,MyISAM 不管是主键索引,还是二级索引使用的都是非聚簇索引。
如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称 之为“覆盖索引”。
我们知道在InnoDB存储引 擎中,如果不是主键索引,叶子节点存储的是主键值。最终还是要“回表”,也就是要通过主键再查找一次,这样就 会比较慢。覆盖索引就是把要查询出的列和索引是对应的,不做回表操作!
不一定,这涉及到查询语句所要求的字段是否全部命中了索引,如果全部命中了索引,那么就不必再进行回表查询。
举个简单的例子,假设我们在员工表的年龄上建立了索引,那么当进行select age from employee where age < 20
的查询时,在索引的叶子节点上,已经包含了age信息,不会再次进行回表查询。
MySQL 可以使用多个字段同时建立一个索引,叫做联合索引。在联合索引中,如果想要命中索引,需要按照建立索引时的字段顺序挨个使用,否则无法命中索引。
具体原因为:
MySQL 使用索引时需要索引有序,假设现在建立了"name,age,school"的联合索引,那么索引的排序为:先按照name排序,如果 name 相同,则按照 age 排序,如果 age 的值也相等,则按照 school 进行排序。
当进行查询时,此时索引仅仅按照 name 严格有序,因此必须首先使用 name 字段进行等值查询,之后对于匹配到的列而言,其按照 age 字段严格有序,此时可以使用 age 字段用做索引查找,以此类推。因此在建立联合索引的时候应该注意索引列的顺序,一般情况下,将查询需求频繁或者字段选择性高的列放在前面。此外可以根据特例的查询或者表结构进行单独的调整。
MySQL 提供了 explain
命令来查看语句的执行计划,MySQL 在执行某个语句之前,会将该语句过一遍查询优化器,之后会拿到对语句的分析,也就是执行计划,其中包含了许多信息。可以通过其中和索引有关的信息来分析是否命中了索引,例如possilbe_key,key,key_len
等字段,分别说明了此语句可能会使用的索引,实际使用的索引以及使用的索引长度。
“执行计划”中需要知道的几个“关键字”
以上情况,MySQL无法使用索引。
主要原因:B+树只要遍历叶子节点就可以实现整棵树的遍历,而且在数据库中基于范围的查询是非常频繁的,而B树只能中序遍历所有节点,效率太低。
B-tree:
Hash:
二叉树: 树的高度不均匀,不能自平衡,查找效率跟数据有关(树的高度),并且IO代价高。
红黑树: 树的高度随着数据量增加而增加,IO代价高。
普通索引、唯一索引(主键索引、唯一索引)、联合索引、全文索引、空间索引
在建立了联合索引的前提条件下,数据库会一直从左向右的顺序依次查找,直到遇到了范围查询(>,<,between,like等)
当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制.
就像酒店的房间,如果大家随意进出,就会出现多人抢夺同一个房间的情况,而在房间上装上锁,申请到钥匙的人才可以入住并且将房间锁起来,其他人只有等他使用完毕才可以再次使用.
Mysql中锁的分类按照不同类型的划分可以分成不同的锁:
按照 锁的粒度 划分可以分成:
按照 使用的方式 划分可以分为:
按照 思想 的划分:
行级锁:
表级锁:
页级锁:
共享锁:
描述:
共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。
如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。
用法:
SELECT … LOCK IN SHARE MODE;
排他锁:
描述
:
用法
:
SELECT … FOR UPDATE;
用上面的例子来说就是用户的行为有两种,一种是来看房,多个用户一起看房是可以接受的. 一种是真正的入住一晚,在这期间,无论是想入住的还是想看房的都不可以.
乐观锁, 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
悲观锁,顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
悲观锁,先获取锁,再进行业务操作,一般就是利用类似 SELECT … FOR UPDATE 这样的语句,对数据加锁,避免其他事务意外修改数据。当数据库执行SELECT … FOR UPDATE时会获取被select中的数据行的行锁,select for update获取的行锁会在当前事务结束时自动释放,因此必须在事务中使用。
乐观锁,先进行业务操作,只在最后实际更新数据时进行检查数据是否被更新过。Java 并发包中的 AtomicFieldUpdater 类似,也是利用 CAS 机制,并不会对数据加锁,而是通过对比数据的时间戳或者版本号,来实现乐观锁需要的版本判断。
是指二个或者二个以上的进程在执行时候,因为争夺资源造成相互等待的现象,进程一直处于等待中,无法得到释放,这种状态就叫做死锁。
批量入库,存在则更新,不存在则插入,insert into tab(xx,xx) on duplicate key update xx=‘xx’
。
innodblockwait_timeout
来设置超时时间,一直等待直到超时select …for update
语句来获取必要的锁,即使这些行的更改语句是在之后才执行的select …lock in share mode
获取行的读锁后,如果当前事务在需要对该记录进行更新操作,则很有可能造成死锁innodb默认是使用设置死锁时间来让死锁超时的策略,默认innodblockwait_timeout
设置的时长是50s
设置innodbdeadlockdetect
设置为on可以主动检测死锁,在innodb中这个值默认就是on开启的状态
全局锁就是对整个数据库实例加锁,它的典型使用场景就是做全库逻辑备份,这个命令可以使用整个库处于只读状态,使用该命令之后,数据更新语句,数据定义语句,更新类事务的提交语句等操作都会被阻塞。
MySQL 支持多种存储引擎,比如InnoDB,MyISAM,Memory,Archive等等。在大多数的情况下,直接选择使用 InnoDB
引擎都是最合适的,InnoDB 也是 MySQL 的默认存储引擎。
InnoDB
MyISAM
总结:
可以分为服务层和存储引擎层两部分,其中:
服务层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖MySQL的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。
存储引擎层负责数据的存储和提取。 其架构模式是插件式的,支持InnoDB、MyISAM、Memory等多个存储引擎。现在最常用的存储引擎是InnoDB,它从MySQL5.5.5版本开始成为了默认的存储引擎。
Server层按顺序执行sql的步骤为:
1.客户端请求->
2.连接器(验证用户身份,给予权限) ->
3.查询缓存(存在缓存则直接返回,不存在则执行后续操作)->
4.分析器(对SQL进行词法分析和语法分析操作) ->
5.优化器(主要对执行的sql优化选择最优的执行方案方法) ->
6.执行器(执行时会先看用户是否有执行权限,有才去使用这个引擎提供的接口)->
7.去引擎层获取数据返回(如果开启查询缓存则会缓存查询结果)
简单概括:
SELECT DISTINCT< select_list >
FROM< left_table > < join_type >
JOIN < right_table > ON < join_condition >
WHERE< where_condition >
GROUP BY< group_by_list >
HAVING< having_condition >
ORDER BY< order_by_condition >
LIMIT < limit_number >
它的执行顺序你知道吗?这道题就给你一个回答。
FROM 连接
首先,对 SELECT 语句执行查询时,对FROM 关键字两边的表执行连接,会形成笛卡尔积,这时候会产生一个虚表VT1(virtual table)
ON 过滤
然后对 FROM 连接的结果进行 ON 筛选,创建 VT2,把符合记录的条件存在 VT2 中。
JOIN 连接
第三步,如果是 OUTER JOIN(left join、right join) ,那么这一步就将添加外部行,如果是 left join 就把 ON 过滤条件的左表添加进来,如果是 right join ,就把右表添加进来,从而生成新的虚拟表 VT3。
WHERE 过滤
第四步,是执行 WHERE 过滤器,对上一步生产的虚拟表引用 WHERE 筛选,生成虚拟表 VT4。
GROUP BY
根据 group by 字句中的列,会对 VT4 中的记录进行分组操作,产生虚拟机表 VT5。果应用了group by,那么后面的所有步骤都只能得到的 VT5 的列或者是聚合函数(count、sum、avg等)。
HAVING
紧跟着 GROUP BY 字句后面的是 HAVING,使用 HAVING 过滤,会把符合条件的放在 VT6
SELECT
第七步才会执行 SELECT 语句,将 VT6 中的结果按照 SELECT 进行刷选,生成 VT7
DISTINCT
在第八步中,会对 TV7 生成的记录进行去重操作,生成 VT8。事实上如果应用了 group by 子句那么 distinct 是多余的,原因同样在于,分组的时候是将列中唯一的值分成一组,同时只为每一组返回一行记录,那么所以的记录都将是不相同的。
ORDER BY
应用 order by 子句。按照 order_by_condition 排序 VT8,此时返回的一个游标,而不是虚拟表。sql 是基于集合的理论的,集合不会预先对他的行排序,它只是成员的逻辑集合,成员的顺序是无关紧要的。
SQL 语句执行的过程如下:
视图是虚拟的表,与包含数据的表不一样,视图只包含使用时动态检索数据的查询;不包含任何列或数据。使用视图可以简化复杂的 sql 操作,隐藏具体的细节,保护数据;视图创建后,可以使用与表相同的方式利用它们。
视图不能被索引,也不能有关联的触发器或默认值,如果视图本身内有order by 则对视图再次order by将被覆盖。
创建视图:
create view xxx as xxxx
对于某些视图比如未使用联结子查询分组聚集函数Distinct Union等,是可以对其更新的,对视图的更新将对基表进行更新;但是视图主要用于简化检索,保护数据,并不用于更新,而且大部分视图都不可以更新。
事务是一系列的数据库操作,他们要符合 ACID 特性,事务是数据库应用的基本单位。MySQL 事务主要用于处理操作量大,复杂度高的数据。
A=Atomicity
:原子性,就是要么全部成功,要么全部失败。不可能只执行一部分操作。C=Consistency
:一致性,系统(数据库)总是从一个一致性的状态转移到另一个一致性的状态,不会存在中间状态。I=Isolation
:隔离性,通常来说:一个事务在完全提交之前,对其他事务是不可见的.注意前面的通常来说加了红色,意味着有例外情况。D=Durability
:持久性,一旦事务提交,那么就永远是这样子了,哪怕系统崩溃也不会影响到这个事务的结果。而在 MySQL 中,恢复机制是通过回滚日志(undo log)实现的,所有事务进行的修改都会先记录到这个回滚日志中,然后在对数据库中的对应行进行写入。当事务已经被提交之后,就无法再次回滚了。
回滚日志作用:
ROLLBACK
时提供回滚相关的信息数据库并发事务会带来 脏读、幻读、丢弃更改、不可重复读 这四个常见问题,其中:
主键是数据库确保数据行在整张表唯一性的保障,即使业务上本张表没有主键,也建议添加一个自增长的 ID 列作为主键.设定了主键之后,在后续的删改查的时候可能更加快速以及确保操作数据范围安全。
推荐使用自增ID,不要使用 UUID。
因为在 InnoDB存储引擎中,主键索引是作为聚簇索引存在的,也就是说,主键索引的B+树叶子节点上存储了主键索引以及全部的数据(按照顺序),如果主键索引是自增ID,那么只需要不断向后排列即可,如果是UUID,由于到来的ID与原来的大小不确定,会造成非常多的数据插入,数据移动,然后导致产生很多的内存碎片,进而造成插入性能的下降.
总之,在数据量大一些的情况下,用自增主键性能会好一些。
图片来源于《高性能MySQL》: 其中默认后缀为使用自增ID,_uuid为使用 UUID为主键的测试,测试了插入 100w 行和 300w 行的性能。
null 值会占用更多的字节,且会在程序中造成很多与预期不符的情况。
密码散列,用户身份证号等固定长度的字符串应该使用 char
而不是 varchar
来存储,这样可以节省空间且提高检索效率。
char 是一个定长字段,假如申请了char(10)
的空间,那么无论实际存储多少内容。该字段都占用 10 个字符,而 varchar 是变长的,也就是说申请的只是最大长度,占用的空间为实际字符长度 +1,最后一个字符存储使用了多长的空间
在检索效率上来讲,char > varchar
,因此在使用中,如果确定某个字段的值的长度,可以使用 char,否则应该尽量使用 varchar。例如存储用户 MD5 加密后的密码,则应该使用 char。
varchar的10代表了申请的空间长度,也是可以存储的数据的最大长度,而int的10只是代表了展示的长度,不足10位以0填充.也就是说,varchar(10) 和int(10)所能存储的数字大小以及占用的空间都是相同的,只是在展示时按照长度展示.
有三种格式:statement、row和mixed.
超大的分页一般从两个方向上来解决.:
select * from table where age > 20 limit 1000000,10
这种查询其实也是有可以优化的余地的. 这条语句需要load1000000数据然后基本上全部丢弃,只取10条当然比较慢. 当时我们可以修改为select * from table where id in (select id from table where age > 20 limit 1000000,10)
.这样虽然也load了一百万的数据,但是由于索引覆盖,要查询的所有字段都在索引中,所以速度会很快. 同时如果ID连续的好,我们还可以select * from table where id > 1000000 limit 10
,效率也是不错的,优化的可能性有许多种,但是核心思想都一样,就是减少load的数据.在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有足够的理由。比如性能,事实上我们经常会为了性能而妥协数据库的设计。
left join
:左关联,主表在左边,右边为从表。如果左侧的主表中没有关联字段,会用null 填满right join
:右关联 主表在右边和letf join相反inner join
: 内关联只会显示主表和从表相关联的字段,不会出现null数据库约束用于保证数据库、表数据的完整性(正确性和一致性)。
可以通过定义约束\索引\触发器来保证数据的完整性。总体来讲,约束可以分为:
SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有细致地过滤用户输入的数据,致使非法数据侵入系统。
读写分离为了确保数据库产品的稳数据定性,很多数据库拥有双机热备功能。也就是,第一台数据库服务器,是对外提供增删改业务的生产服务器;第二台数据库服务器,主要进行读的操作。
排查过程:
show processlist
,查看session情况,确定是不是有消耗资源的sql在运行。处理:
主从复制分了五个步骤进行:
主从同步延迟的原因:
一个服务器开放N个链接给客户端来连接的,这样有会有大并发的更新操作, 但是从服务器的里面读取binlog的线程仅有一个,当某个SQL在从服务器上执行的时间稍长 或者由于某个SQL要进行锁表就会导致,主服务器的SQL大量积压,未被同步到从服务器里。这就导致了主从不一致, 也就是主从延迟。
主从同步延迟的解决办法:
sync_binlog=1,innodb_flush_log_at_trx_commit = 1
之类的设置等。分库分表方案:
常用的分库分表中间件:
分库分表可能遇到的问题:
count,order by,group by
以及聚合函数问题:分别在各个节点上得到结果后在应用程序端进行合并。执行效果上 :
count(*)
:包括了所有的列,相当于行数,在统计结果的时候, 不会忽略列值为NULLcount(1)
:包括了忽略所有列,用1代表代码行,在统计结果的时候, 不会忽略列值为NULLcount(列名)
:只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是只空字符串或者0,而是表示null)的计数, 即某个字段值为NULL时,不统计。执行效率上:
见文章:
若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
变长字段存储空间小,可以节省存储空间。
比如有一列是性别,几乎只有男、女、未知,这样的索引是无效的。
SQL注入攻击是一种常见的网络安全威胁,它利用恶意构造的SQL查询语句来绕过应用程序的输入验证,从而执行未经授权的操作或者获取敏感数据。网络物理隔绝并不能直接解决SQL注入攻击问题,因为SQL注入攻击是通过恶意构造的SQL查询语句进行的,与网络物理隔离无关。
要防止SQL注入攻击,需要在应用程序层面采取相应的防护措施。以下是一些常见的防护方法:
1. 使用参数化查询或预编译语句:使用参数化查询(或称为绑定变量)可以将用户输入与SQL查询语句分开,从而消除注入漏洞的可能性。
2. 输入验证和过滤:对用户输入进行验证和过滤,确保输入符合预期的格式和范围,避免非法字符进入SQL查询语句。
3. 最小特权原则:限制数据库用户的权限,确保其只能执行必要的操作,避免恶意用户利用注入漏洞获取敏感数据或执行危险操作。
4.安全编码实践:开发人员应该了解SQL注入攻击的原理和常见手段,并采用安全编码实践,如避免使用拼接字符串的方式构建SQL查询语句,使用框架提供的安全接口等。
5. 使用参数化查询或预编译语句:通过使用参数化查询或预编译语句,可将用户提供的输入与SQL语句分开处理,从而避免将用户输入作为SQL查询的一部分。
6. 输入验证和过滤:在接收用户输入之前,对其进行验证和过滤,确保只接受符合预期格式的输入,并拒绝包含有害字符的输入。可以使用正则表达式或白名单过滤器对输入进行检查。
7. 最小权限原则:数据库用户应被授予最小权限,即仅允许其执行必要的操作,以限制攻击者利用注入漏洞对数据库进行恶意操作的能力。
8. 错误处理和日志记录:在应用程序中实施严格的错误处理机制,并对异常情况进行适当的日志记录。这样可以帮助检测到潜在的SQL注入攻击,并提供调试和追踪攻击者的信息。
9. 定期更新和维护:及时应用数据库供应商发布的安全补丁和更新,以修复已知的漏洞,并确保数据库系统处于最新和最安全的状态。
数据库SQL操作是指使用结构化查询语言(SQL)对数据库进行增删改查等操作的技术。常用的数据库包括MySQL、SQL Server等。
MySQL是一种开源的关系型数据库管理系统,采用客户端-服务器模式。它由一个或多个服务器组成,每个服务器保存着一部分数据。应用程序通过客户端与服务器进行通信,并发送SQL语句来操作数据。
MySQL的体系构架主要包括以下几个组件:
SQL Server是由Microsoft开发的关系型数据库管理系统,也采用了客户端-服务器模式。它的体系构架类似于MySQL,但在一些细节上有所不同。
SQL Server的体系构架主要包括以下几个组件:
以上是MySQL和SQL Server两种常用数据库的运行机制和体系构架的简介。在实际应用中,根据需要选择适合的数据库,并掌握相应的SQL操作技术,可以高效地管理和操作数据。
要实现同一个班级里学号不能重复的功能,可以通过以下步骤来实现:
在学生表中添加一个唯一约束(unique constraint)或者创建一个唯一索引(unique index)来保证学号的唯一性。这将阻止插入或更新操作中出现重复的学号。
如果你使用的是SQL语言,可以使用以下语句来添加唯一约束:
ALTER TABLE 表名 ADD CONSTRAINT 约束名 UNIQUE (学号列名);
或者使用以下语句来创建唯一索引:
CREATE UNIQUE INDEX 索引名 ON 表名 (学号列名);
当你插入或更新学生数据时,数据库会自动检查学号是否已存在。如果学号重复,将会抛出错误并阻止操作。
以上方法可以有效地确保同一个班级里学号的唯一性。希望对你有帮助!如有任何其他问题,请随时提问。
CREATE TABLE 学生表 (
学号 INT PRIMARY KEY,
姓名 VARCHAR(50),
班级 VARCHAR(50),
..其他字段..
);
ALTER TABLE 学生表
ADD CONSTRAINT 唯一学号 UNIQUE (学号);
话不多说,直接上干货
1、查询部分列
select 列名 from 表名
例:员工表中查询员工编号、名字、邮箱
select employee_id,employee_name,employee_email from t_employee;
2、查询所有列
例:查询所有员工信息
select * from t_employee;
3、对列中的数据进行运算
例:查询员工编号、姓名、月薪、年薪
select employee_id,employee_name,employee_salary,employee_salary12;
注:算术运算符:+(加)、-(减)、(乘)、/(除);%为占位符而非模运算符
4、去重查询DISTINCT 列名
例:查询员工表中所有的经理ID
select distinct manager_id from t_employee;
5、排序查询
排序规则
ASC 升序
DESC 降序
select 列名 from 表名 order by 排序列 排序规则
依据单列排序:
例:查询员工编号、姓名、月薪;薪资按高到低排序
select employee_id,employee_name,employee_salary from t_employee order by employee_salary DESC;
依据多列排序:
例:
查询员工编号、姓名、月薪。按照薪资降序排列,薪资相同时候按照编号进行升序排列
select employee_id,employee_name,employee_salary from t_employee
order by employee_salary DESC,employee_id ASC;
6、条件查询where
select 列名 from 表名 where 条件
补充:where查询条件筛选符号条件的查询结果,为布尔表达式
例:查询员工为"rich_wang"的员工信息
select * from t_employee where employee_name = “rich_wang”
7、等值判断(=)
查询薪资是11000的员工信息(编号、名字、薪资)
select employee_id,employee_name,employee_salary
from t_employee
where employee_salary = 11000;
8、逻辑判断(and,or,not)
例:
查询薪资是11000且提成是0.3的员工信息(编号、名字、薪资)
select employee_id,employee_name,employee_salary
from t_employee
where employee_salary = 11000 and employee_pct = 0.3;
or和not同理
9、不等值判断(>,<,>=,<=,!=,<>)
例:
查询员工薪资在10000-20000之间的员工信息(编号、名字、薪资)
select employee_id,employee_name,employee_salary
from t_employee
where employee_salary>=10000 and employee_salary<=20000
其他几个同理
10、区间判断(between and)
例:查询员工薪资在10000-20000之间的员工信息
select employee_id,employee_name,employee_salary
from t_employee
where employee_salary between 10000 and 20000;
说明:区间判断中,小值在前,大值在后,否则得不到正确结果;
11、NULL值判断(IS NULL,IS NOT NULL)
列名 IS NULL
列名 IS NOT NULL
例:查询没有提成的员工信息(编号、名字、薪资、提成)
select employee_id,employee_name,employee_salary,employee_pct
from t_employee
where employee_pct IS NULL;
IS NOT NULL同理
注:不能是=NULL,要拿到NULL值需要IS NULL和IS NOT NULL来获取
12、枚举查询
IN (值1,值2,值3…)
例:
查询部门编号为70,80,90的员工信息(编号、名字、薪资、部门编号)
select e,manager_id,employee_name,employee_salary,employee_dpt
from t_employee
where employee_dpt in (70,80,90);
注:in的查询效率较低,推荐使用多条件拼接查询
13、模糊查询(LIKE、%)
LIKE_(单个任意字符):列名 LIKE ‘王_’;
%(任意长度字符)列名 LIKE ‘王%’;
注:模糊查询只能和LIKE关键字结合使用
例:
查询名字以’王’开头的员工信息(编号、名字、薪资、部门编号)
select employee_id,employee_name,employee_salary,employee_dpt
from t_employee
where employee_name LIKE ‘王%’;
例:
查询名字以"王"开头且长度为3的员工信息(编号、名字、薪资、部门编号)
select employee_id,employee_name,employee_salary,employee_pct
from t_employee
where employee_name LIKE ‘王___’;
注:一个"_"占一位
14、分支结构查询CASE END
CASE
WHEN 条件1 THEN 结果1
WHEN 条件2 THEN 结果2
WHEN 条件3 THEN 结果3
ELSE 除以上条件外的结果
…
END
说明:通过使用CASE END进行条件判断,每条数据对应生成一个值,类似Java中的switch case
例:
查询员工信息(编号、名字、薪资、薪资级别<根据级别条件生成>)
select employee_id、employee_name,employee_salary,
CASE
WHEN employee_salary >=10000 THEN ‘A’
WHEN employee_salary >=8000 AND employee_salary<10000 THEN ‘B’
WHEN employee_salary >=6000 AND employee_salary<8000 THEN ‘C’
WHEN employee_salary >=4000 AND employee_salary<6000 THEN ‘D’
ELSE
‘E’
END
AS “薪资级别”
from t_employee;
15、时间查询-时间函数
select 时间函数(参数列表)
注:执行时间函数查询,会自动生成一张虚表(一列一行)
相关时间函数:
SYSDATE() 当前系统时间(年月日时分秒)
CURDATE() 获取当前日期(年月日)
CURTIME() 获取当前时间(时分秒)
WEEK(DATE) 获取指定日期为一年中的第几周
YEAR(DATE) 获取指定日期的年份
HOUR(TIME) 获取指定时间的小时值
MINUTE(TIME) 获取指定时间分钟值
DATEDIFF(DATE1,DATE2) 获取DATE1和DATE2之间相差的天数
ADDDATE(DATE,N) 计算DATE加上N天后的日期
例:
获取系统当前时间
select SYSDATE()
获取当前日期
select CURDATE
获取指定日期中的年份
select YEAR(‘2022-07-03’)
指定日期之前的天数
select DATEDIFF(‘2021-07-03’,‘2022-07-03’)
其他时间函数同理
16、字符串查询-字符串函数
select 字符串函数(参数列表)
相关字符串函数:
CONCAT(str1,str2,…) 将多个字符串拼接
INSERT(str,pos,len,newStr) 将str字符串中指定的pos位置开始长度为len的内容替换为newStr
LOWER(str) 将指定字符串str转换为小写
UPPER(str) 将指定字符串str转换为大写
SUBSTRING(str,num,len) 将指定字符串str中从位置num开始截取len个长度的子串
例:
拼接字符串
select CONCAT(‘My’,‘S’,‘QL’);//MUSQL
字符串替换
select INSERT(‘学习什么数据库’,3,2,‘MYSQL’);//学习MYSQL数据库
其他几个同理
注:在mysql中字符串下标从1开始,而非像Java中从0开始
17、聚合函数(SUM、AVG、MAX、MIN、COUNT)
select 聚合函数(列名) from 表名
相关聚合函数
SUM() 对所有行中的单列结果求和
AVG() 平均值
MAX() 最大值
MIN() 最小值
COUNT() 求表总行数
例:
统计所有员工每月的薪资总和
select SUM(employee_salary) from t_employee
其他几个同理
注:聚合函数会自动忽略NULL值,不对NUL进行统计
18、分组查询GROUP BY
select 列名 from 表名 where 条件 GROUP BY 分组依据(列名)
注:分组依据,必须在where之后生效
例:
查询各部门的总人数
分析:
先按部门编号(employee_dpt)进行分组,再针对各部门人数进行统计(COUNT)
select employee_dpt AS ‘部门’,COUNT(employee_id)
from t_employee
GROUP BY employee_dpt
查询各部门的平均工资
select employee_dpt,AVG(employee_salary)
from t_employee
GROUP BY employee_dpt
查询各个部门、各个岗位的人数
分析:
先按部门编号进行分组,再按岗位进行分组,再对各个部门各个岗位进行COUNT
select employee_dpt,employee_job,COUNT(employee_id) AS ‘岗位人数’
from t_employee
GROUP BY employee_dpt,employee_job
注:分组查询中,select显示的列只能是分组依据列或者聚合函数;列,不能出现其他列!注意规避
19、分组过滤查询HAVING
select 列名 from 表名 where 条件 GROUP BY 分组列 HAVING 过滤规则
注:过滤规则定义对分组后的数据进行过滤
例:
统计部门编号70、80、90部门的最高工资
分析:
先按部门编号进行分组,对分组后的数据过滤出部门编号为70、80、90的部门,然后在MAX进行统计
select employee_dpt,MAX(employee_salary)
from t_employee
GROUP BY employee_dpt
HAVING employee_dpt in (70,80,90)
20、限定查询LIMIT
select 列名 from 表名 LIMIT 起始行,查询行数
关键字:
LIMIT offset_start,row_count 限定查询结果的起始行和总行数
限定查询一般用于分页的场景最多
例:
查询前5条数据
select * from t_employee LIMIT 0,5;
注:起始行是从0开始,代表第一行,第二个参数代表的是指定行开始查询几行
查询范围记录,表中第三条开始,查询10行
select * from t_employee LIMIT 3,10;
小结:
sql语句的编写顺序:
select列名from表名where条件GROUP BY分组依据HAVING过滤条件order by排序列 排序规则 LIMIT 起始行,总条数
sql语句的执行顺序:
from->where->GROUP BY->HAVING->select->order by->LIMIT
21、子查询(作为条件判断)
select 列名 from 表名 where 条件(子查询结果)
例:
查询工资大于员工rich_wang的员工信息
select * from t_employee where employee_salary > (
select employee_salary from t_employee where employee_name=“rich_wang”
);
注:将子查询的"一行一列"的结果作为外部查询的条件做二次查询,子查询结果得到一行一列才能作为外部查询的判断条件
21、子查询(作为枚举查询条件)
select 列名 from 表名 where 列名 in (子查询结果)
例:
查询与员工rich_wang在同一个部门的员工信息,其中rich_wang所属多个部门
select * from t_employee where employee_dpt in (
select employee_dpt from t_employee where employee_name = “rich_wang”
);
注:将多行一列的子查询结果作为外部查询枚举查询条件
22、子查询(ANY和ALL)
查询工资高于60部门的所有员工信息
高于部分:
select * from t_employee where employee_salary > ANY(
select employee_salary from t_employee where employee_dpt=60
);
高于所有:
select * from t_employee where employee_salary > ALL(
select employee_salary from t_employee where employee_dpt=60
);
注:当子查询结果形式为多行单列时可使用ANY或者ALL关键字
23、子查询(作为一张表)
select 列名 from 子查询结果集 where 条件
例:
查询员工表中工资排名前五的员工信息(编号、名字、薪资)
select employee_id,employee_name,employee_salary from
(select employee_id,employee_name,employee_salary
from t_employee
order by employee_salary DESC
) as temp
LIMIT 0,5
注:将多行多列的结果作为外部查询的一张表进行二次查询
24、合并查询UNION(应用场景较少)
select * from 表名1 UNION select * from 表2
select * from 表名1 UNION ALL select * from 表2
例:
合并2张表的结果(去除重复记录)
select * from t1 UNION select * from t2
注意:合并结果的两张表,列数必须相同!!!列的数据类型可以不同
合并2张表(保留重复记录)
select * from t1 UNION ALL select * from t2
25、表连接查询-内连接查询(INNER JOIN ON)
select 列名 from 表1 连接方式 表2 连接条件
通过员工表t_employee和岗位表t_job查询所有有部门的员工信息
sql标准写法:
select * from t_employee INNER JOIN t_job ON t_employee.job_id = t_job.job_id
mysql可这样写:
select * from t_employee,t_job where t_employee.job_id = t_job.job_id
注意:务必要指定连接条件,否则会造成笛卡尔积的结果
26、表连接查询-三表连接查询
例:
查询所有员工信息包含员工编号,姓名,部门名称,部门所在国家ID
涉及三张表:t_employee员工表,t_department部门表,t_location国家地区表
select * from t_employee e
INNER JOIN t_department d
ON e.employee_id = d.employee_id
INNER JOIN t_location l
ON d.location_id = l.location_id
说明:左外连接,是以左表作为主表,依次像右表匹配,匹配成功则返回结果,匹配不到则返回NULL填充
查询所有员工信息,以及对应的部门名称(没有部门的员工,也在查询结果中,部门以NULL填充)
涉及员工表和部门表
select e.employee_id,e.employee_name,e.employee_salary,d.department_name from t_employee e
LEFT JOIN t_department d
ON e.department_id = d.department_id
注:实际开发时根据需求选择主表
27、表连接查询-右外连接查询(RIGHT JOIN ON)
说明:右外连接,是以右表作为主表,依次像左表匹配,匹配成功则返回结果,匹配不到则返回NULL填充
查询所有部门信息,以及此部门中的所有员工信息(没有员工的部门,也在查询结果中,员工信息以NULL填充)
select e.employee_id,e.employee_name,e.employee_salary,d.department_name from t_employee e
RIGHT JOIN t_department d
ON e.department_id = d.department_id
elect * from 表2
例:
合并2张表的结果(去除重复记录)
select * from t1 UNION select * from t2
注意:合并结果的两张表,列数必须相同!!!列的数据类型可以不同
合并2张表(保留重复记录)
select * from t1 UNION ALL select * from t2
25、表连接查询-内连接查询(INNER JOIN ON)
select 列名 from 表1 连接方式 表2 连接条件
通过员工表t_employee和岗位表t_job查询所有有部门的员工信息
sql标准写法:
select * from t_employee INNER JOIN t_job ON t_employee.job_id = t_job.job_id
mysql可这样写:
select * from t_employee,t_job where t_employee.job_id = t_job.job_id
注意:务必要指定连接条件,否则会造成笛卡尔积的结果
26、表连接查询-三表连接查询
例:
查询所有员工信息包含员工编号,姓名,部门名称,部门所在国家ID
涉及三张表:t_employee员工表,t_department部门表,t_location国家地区表
select * from t_employee e
INNER JOIN t_department d
ON e.employee_id = d.employee_id
INNER JOIN t_location l
ON d.location_id = l.location_id
说明:左外连接,是以左表作为主表,依次像右表匹配,匹配成功则返回结果,匹配不到则返回NULL填充
查询所有员工信息,以及对应的部门名称(没有部门的员工,也在查询结果中,部门以NULL填充)
涉及员工表和部门表
select e.employee_id,e.employee_name,e.employee_salary,d.department_name from t_employee e
LEFT JOIN t_department d
ON e.department_id = d.department_id
注:实际开发时根据需求选择主表
27、表连接查询-右外连接查询(RIGHT JOIN ON)
说明:右外连接,是以右表作为主表,依次像左表匹配,匹配成功则返回结果,匹配不到则返回NULL填充
查询所有部门信息,以及此部门中的所有员工信息(没有员工的部门,也在查询结果中,员工信息以NULL填充)
select e.employee_id,e.employee_name,e.employee_salary,d.department_name from t_employee e
RIGHT JOIN t_department d
ON e.department_id = d.department_id
注:实际开发时根据需求选择主表