-
InnoDB 引擎:InnoDB 引擎提供了对数据库 acid 事务的支持,并且还提供了行级锁和外键的约束,它的设计的目标就是处理大数据容量的数据库系统。MySQL 运行的时候,InnoDB 会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎是不支持全文搜索,同时启动也比较的慢,它是不会保存表的行数的,所以当进行 select count(*) from table 指令的时候,需要进行扫描全表。由于锁的粒度小,写操作是不会锁定全表的,所以在并发度较高的场景下使用会提升效率的。MyIASM 引擎:MySQL 的默认引擎,但不提供事务的支持,也不支持行级锁和外键。因此当执行插入和更新语句时,即执行写操作的时候需要锁定这个表,所以会导致效率会降低。不过和 InnoDB 不同的是,MyIASM 引擎是保存了表的行数,于是当进行 select count(*) from table 语句时,可以直接的读取已经保存的值而不需要进行扫描全表。所以,如果表的读操作远远多于写操作时,并且不需要事务的支持的,可以将 MyIASM 作为数据库引擎的首选。
-
在MySQL中,大多数索引(如 PRIMARY KEY,UNIQUE,INDEX和FULLTEXT)都是在BTREE中存储,但使用memory引擎可以选择BTREE索引或者HASH索引,两种不同类型的索引各自有其不同的使用范围。B树索引具有范围查找和前缀查找的能力,对于有N节点的B树,检索一条记录的复杂度为O(LogN)。相当于二分查找。哈希索引只能做等于查找,但是无论多大的Hash表,查找复杂度都是O(1)。显然,如果值的差异性大,并且以等值查找(=、 <、>、in)为主,Hash索引是更高效的选择,它有O(1)的查找复杂度。如果值的差异性相对较差,并且以范围查找为主,B树是更好的选择,它支持范围查找。一、HASH索引利用哈希函数,计算存储地址,检索时不需要像Btree那样,从根节点开始遍历,逐级查找。Hash 索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以 Hash 索引的查询效率要远高于 B-Tree 索引。可能很多人又有疑问了,既然 Hash 索引的效率要比 B-Tree 高很多,为什么大家不都用 Hash 索引而还要使用 B-Tree 索引呢?任何事物都是有两面性的,Hash 索引也一样,虽然 Hash 索引效率高,但是 Hash 索引本身由于其特殊性也带来了很多限制和弊端,主要有以下这些。 (1)Hash 索引仅仅能满足”=”,”IN”和”<=>”查询,不能使用范围查询(查询范围时 慢)。由于 Hash 索引比较的是进行 Hash 运算之后的 Hash 值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的 Hash 算法处理之后的 Hash 值的大小关系,并不能保证和Hash运算前完全一样。 (2)Hash 索引无法被用来避免数据的排序操作。由于 Hash 索引中存放的是经过 Hash 计算之后的 Hash 值,而且Hash值的大小关系并不一定和 Hash 运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算; (3)Hash 索引不能利用部分索引键查询。对于组合索引,Hash 索引在计算 Hash 值的时候是组合索引键合并后再一起计算 Hash 值,而不是单独计算 Hash 值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash 索引也无法被利用。(4)Hash 索引在任何时候都不能避免表扫描。前面已经知道,Hash 索引是将索引键通过 Hash 运算之后,将 Hash运算结果的 Hash 值和所对应的行指针信息存放于一个 Hash 表中,由于不同索引键存在相同 Hash 值,所以即使取满足某个 Hash 键值的数据的记录条数,也无法从 Hash 索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较,并得到相应的结果。(5)Hash 索引遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高。对于选择性比较低的索引键,如果创建 Hash 索引,那么将会存在大量记录指针信息存于同一个 Hash 值相关联。这样要定位某一条记录时就会非常麻烦,会浪费多次表数据的访问,而造成整体性能低下。二、B+树b+树的查找过程如图所示,如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。b+树性质1.索引字段要尽量的小:通过上面的分析,我们知道IO次数取决于b+数的高度h,假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则有h=㏒(m+1)N,当数据量N一定的情况下,m越大,h越小;而m = 磁盘块的大小 / 数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低。这就是为什么每个数据项,即索引字段要尽量的小,比如int占4字节,要比bigint8字节少一半。这也是为什么b+树要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高。当数据项等于1时将会退化成线性表。2.索引的最左匹配特性(即从左往右匹配):当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性。
-
前言MySQL 的 innodb 引擎之所以使用 B+tree 来存储索引,就是想尽量减少数据查询时磁盘 IO 次数。树的高度直接影响了查询的性能。一般树的高度在 3~4 层较为适宜。数据库分表的目的也是为了控制树的高度。那么如何获取树的高度呢?下面使用一个示例来说明如何获取树的高度。示例数据准备建表语句如下:CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) CHARACTER SET latin1 DEFAULT NULL, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `name` (`name`), KEY `age` (`age`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8表中插入100万条数据。数据如下:mysql> select * from user limit 2\G *************************** 1. row *************************** id: 110000 name: ab age: 100 *************************** 2. row *************************** id: 110001 name: ab age: 100 2 rows in set (0.00 sec)通过查询相关数据表获取树的高度以 MySQL5.6 版本为例说明如何获取树的高度。首先获取 page_nomysql> SELECT b.name, a.name, index_id, type, a.space, a.PAGE_NO FROM information_schema.INNODB_SYS_INDEXES a, information_schema.INNODB_SYS_TABLES b WHERE a.table_id = b.table_id AND a.space <> 0 and b.name='test/user'; +-----------+---------+----------+------+-------+---------+ | name | name | index_id | type | space | PAGE_NO | +-----------+---------+----------+------+-------+---------+ | test/user | PRIMARY | 22 | 3 | 6 | 3 | | test/user | name | 23 | 0 | 6 | 4 | | test/user | age | 24 | 0 | 6 | 5 | +-----------+---------+----------+------+-------+---------+ 3 rows in set (0.00 sec)再读取页的大小mysql> show global variables like 'innodb_page_size'; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | innodb_page_size | 16384 | +------------------+-------+ 1 row in set (0.00 sec)最后读取索引树的高度$ hexdump -s 49216 -n 10 ./user.ibd000c040 0200 0000 0000 0000 1600000c04a可以发现 PAGE_LEVEL 为 0200,表示这棵二级索引树的高度为 3。后面的 1600 是索引的 index_id 值。十六进制的 16 转换为十进制数字是 22。这个 22 正好就是上面主键的 index_id。上面 hexdump 命令中 49216 是怎么算出来的?公式是 page_no * innodb_page_size + 64。3*16384+64=49216我们在用这个方式查看下其他两个索引的高度。$ hexdump -s 65600 -n 10 ./user.ibd 0010040 0100 0000 0000 0000 1700 001004a $ hexdump -s 81984 -n 10 ./user.ibd 0014040 0200 0000 0000 0000 1800 001404a可见,name 索引的高度是 2,age 索引的高度是 3。根据索引的结构估算如果你没有数据库服务器的权限。自己也可以根据数据库索引结构进行估算树的高度。根据 B+Tree 结构,非叶子节点存储的是索引数据,叶子节点存储的是每行的所有数据。非叶子节点每个索引项的大小是,数据大小+指针大小。假设指针大小为 8 个字节。每页不会被占满,预留1/5的空隙。下面我们估算下 name 和 age 两个索引的高度。name 索引高度估算非叶子节点每页存放的索引项数量。每页大小是 16k。name 的值为 ab。占2个字节。每项数据大小是 2+8=10字节。每页能存放的索引项数量是 16384 * 0.8 / 10 = 1310 个。叶子节点每页存放的索引数量。每页大小是 16k。每项数据大小是 4+2+8=14 个字节。没页能存放的索引数量是 16384 * 0.8 / 14 = 936 个。两层能存放 1310*936=1226160 个数据记录。可见120万条记录以下,树的高度为2。age 索引高度估算非叶子节点每页存放的索引项数量。每页大小是 16k。age 的类型为 int。占4个字节。每项数据大小是 4+8=12字节。每页能存放的索引项数量是 16384 * 0.8 / 12 = 1092 个。叶子节点每页存放的索引数量。每页大小是 16k。每项数据大小是 4+4+8=16 个字节。没页能存放的索引数量是 16384 * 0.8 / 16 = 819 个。两层能存放 1092*819=894348 个数据记录。可见90万条记录以下,树的高度为2。100万条为 3 层。
-
在使用 MySQL SELECT语句时,可以使用 WHERE 子句来指定查询条件,从 FROM 子句的中间结果中选取适当的数据行,达到数据过滤的效果。语法格式如下:WHERE <查询条件> {<判定运算1>,<判定运算2>,…}其中,判定运算其结果取值为 TRUE、FALSE 和 UNKNOWN。判定运算的语法分类如下:<表达式1>{=|<|<=|>|>=|<=>|<>|!=}<表达式2><表达式1>[NOT]LIKE<表达式2><表达式1>[NOT][REGEXP|RLIKE]<表达式2><表达式1>[NOT]BETWEEN<表达式2>AND<表达式3><表达式1>IS[NOT]NULL单一条件的查询语句【实例 1】在表 tb_students_info 中查询身高为 170cm 的学生的姓名,输入的 SQL 语句和行结果如下所示。mysql> use test_db Database changed mysql> SELECT name,height -> FROM tb_students_info -> WHERE height=170; +-------+--------+ | name | height | +-------+--------+ | Susan | 170 | +-------+--------+ 1 row in set (0.17 sec)该语句采用了简单的相等过滤,查询一个指定列 height 的具体值 170。【实例 2】查询年龄小于 22 的学生的姓名,输入的 SQL 语句和执行结果如下所示。mysql> SELECT name,age -> FROM tb_students_info -> WHERE age<22; +------+------+ | name | age | +------+------+ | John | 21 | +------+------+ 1 row in set (0.05 sec)可以看到,查询结果中所有记录的 age 字段的值均小于 22 岁,而大于或等于 22 岁的记录没有被返回。多条件的查询语句使用 SELECT 查询时,可以增加查询的限制条件,这样可以使查询的结果更加精确。MySQL 在 WHERE 子句中使用 AND 操作符限定只有满足所有查询条件的记录才会被返回。可以使用 AND 连接两个甚至多个查询条件,多个条件表达式之间用 AND 分开。【实例 3】在 tb_students_info 表中查询 age 大于 21,并且 height 大于等于 175 的学生的信息,输入的 SQL 语句和执行结果如下所示。mysql> SELECT * FROM tb_students_info -> WHERE age>21 AND height>=175; +----+--------+---------+------+------+--------+------------+ | id | name | dept_id | age | sex | height | login_date | +----+--------+---------+------+------+--------+------------+ | 3 | Henry | 2 | 23 | M | 185 | 2015-05-31 | | 5 | Jim | 1 | 24 | M | 175 | 2016-01-15 | | 9 | Thomas | 3 | 22 | M | 178 | 2016-06-07 | +----+--------+---------+------+------+--------+------------+ 3 rows in set (0.06 sec)注意:上例的 WHERE 子句中只包含一个 AND 语句,把两个过滤条件组合在一起,实际上可以添加多个 AND 过滤条件,增加条件的同时增加一个 AND 关键字。使用 LIKE 的模糊查询字符串匹配的语法格式如下:<表达式1> [NOT] LIKE <表达式2>字符串匹配是一种模式匹配,使用运算符 LIKE 设置过滤条件,过滤条件使用通配符进行匹配运算,而不是判断是否相等进行比较。相互间进行匹配运算的对象可以是 CHAR、VARCHAR、TEXT、DATETIME 等数据类型。运算返回的结果是 TRUE 或 FALSE。利用通配符可以在不完全确定比较值的情形下创建一个比较特定数据的搜索模式,并置于关键字 LIKE 之后。可以在搜索模式的任意位置使用通配符,并且可以使用多个通配符。MySQL 支持的通配符有以下两种:1) 百分号(%)百分号是 MySQL 中常用的一种通配符,在过滤条件中,百分号可以表示任何字符串,并且该字符串可以出现任意次。使用百分号通配符要注意以下几点:MySQL 默认是不区分大小写的,若要区分大小写,则需要更换字符集的校对规则。百分号不匹配空值。百分号可以代表搜索模式中给定位置的 0 个、1 个或多个字符。尾空格可能会干扰通配符的匹配,一般可以在搜索模式的最后附加一个百分号。2) 下划线(_)下划线通配符和百分号通配符的用途一样,下画线只匹配单个字符,而不是多个字符,也不是 0 个字符。注意:不要过度使用通配符,对通配符检索的处理一般会比其他检索方式花费更长的时间。【实例 4】在 tb_students_info 表中,查找所有以“T”字母开头的学生姓名,输入的 SQL 的语句和执行结果如下所示。mysql> SELECT name FROM tb_students_info -> WHERE name LIKE 'T%'; +--------+ | name | +--------+ | Thomas | | Tom | +--------+ 2 rows in set (0.12 sec)注意:在搜索匹配时,通配符“%”可以放在不同位置。【实例 5】在 tb_students_info 表中,查找所有包含“e”字母的学生姓名,输入的 SQL 的语句和执行结果如下所示。mysql> SELECT name FROM tb_students_info -> WHERE name LIKE '%e%'; +-------+ | name | +-------+ | Green | | Henry | | Jane | +-------+ 3 rows in set (0.00 sec)由执行结果可以看出,该语句查询字符串中包含字母 e 的学生的姓名,只要名字中有字母 e,其前面或后面无论有多少个字符,都满足查询的条件。【实例 6】在 tb_students_info 表中,查找所有以字母“y”结尾,且“y”前面只有 4 个字母的学生的姓名,输入的 SQL 语句和执行结果如下所示。mysql> SELECT name FROM tb_students_info -> WHERE name LIKE '____y'; +-------+ | name | +-------+ | Henry | +-------+ 1 row in set (0.00 sec)日期字段作为条件的查询语句以日期字段作为条件,可以使用比较运算符设置查询条件,也可以使用 BETWEEN AND 运算符查询某个范围内的值。BETWEEN AND 用来查询某个范围内的值,该操作符需要两个参数,即范围的开始值和结束值,若字段值满足指定的范围查询条件,则这些记录被返回。【实例 7】在表 tb_students_info 中查询注册日期在 2016-01-01 之前的学生的信息,输入的 SQL 语句和执行结果如下所示。mysql> SELECT * FROM tb_students_info -> WHERE login_date<'2016-01-01'; +----+-------+---------+------+------+--------+------------+ | id | name | dept_id | age | sex | height | login_date | +----+-------+---------+------+------+--------+------------+ | 1 | Dany | 1 | 25 | F | 160 | 2015-09-10 | | 3 | Henry | 2 | 23 | M | 185 | 2015-05-31 | | 6 | John | 2 | 21 | M | 172 | 2015-11-11 | | 8 | Susan | 4 | 23 | F | 170 | 2015-10-01 | +----+-------+---------+------+------+--------+------------+ 4 rows in set (0.04 sec)【实例 8】在表 tb_students_info 中查询注册日期在 2015-10-01 和 2016-05-01 之间的学生的信息,输入的 SQL 语句和执行结果如下所示。mysql> SELECT * FROM tb_students_info -> WHERE login_date -> BETWEEN '2015-10-01' -> AND '2016-05-01'; +----+-------+---------+------+------+--------+------------+ | id | name | dept_id | age | sex | height | login_date | +----+-------+---------+------+------+--------+------------+ | 5 | Jim | 1 | 24 | M | 175 | 2016-01-15 | | 6 | John | 2 | 21 | M | 172 | 2015-11-11 | | 7 | Lily | 6 | 22 | F | 165 | 2016-02-26 | | 8 | Susan | 4 | 23 | F | 170 | 2015-10-01 | +----+-------+---------+------+------+--------+------------+ 4 rows in set (0.02 sec)
-
在 MySQL SELECT 语句中,ORDER BY 子句主要用来将结果集中的数据按照一定的顺序进行排序。其语法格式为:ORDER BY {语法说明如下。1) 列名指定用于排序的列。可以指定多个列,列名之间用逗号分隔。2) 表达式指定用于排序的表达式。3) 位置指定用于排序的列在 SELECT 语句结果集中的位置,通常是一个正整数。4) ASC|DESC关键字 ASC 表示按升序分组,关键字 DESC 表示按降序分组,其中 ASC 为默认值。这两个关键字必须位于对应的列名、表达式、列的位置之后。使用 ORDER BY 子句应该注意以下几个方面:ORDER BY 子句中可以包含子查询。当排序的值中存在空值时,ORDER BY 子句会将该空值作为最小值来对待。当在 ORDER BY 子句中指定多个列进行排序时,MySQL 会按照列的顺序从左到右依次进行排序。查询的数据并没有以一种特定的顺序显示,如果没有对它们进行排序,则将根据插入到数据表中的顺序显示。使用 ORDER BY 子句对指定的列数据进行排序。【实例 1】查询 tb_students_info 表的 height 字段值,并对其进行排序,输入的 SQL 语句和执行结果如下所示。mysql> SELECT * FROM tb_students_info ORDER BY height; +----+--------+---------+------+------+--------+------------+ | id | name | dept_id | age | sex | height | login_date | +----+--------+---------+------+------+--------+------------+ | 2 | Green | 3 | 23 | F | 158 | 2016-10-22 | | 1 | Dany | 1 | 25 | F | 160 | 2015-09-10 | | 4 | Jane | 1 | 22 | F | 162 | 2016-12-20 | | 7 | Lily | 6 | 22 | F | 165 | 2016-02-26 | | 10 | Tom | 4 | 23 | M | 165 | 2016-08-05 | | 8 | Susan | 4 | 23 | F | 170 | 2015-10-01 | | 6 | John | 2 | 21 | M | 172 | 2015-11-11 | | 5 | Jim | 1 | 24 | M | 175 | 2016-01-15 | | 9 | Thomas | 3 | 22 | M | 178 | 2016-06-07 | | 3 | Henry | 2 | 23 | M | 185 | 2015-05-31 | +----+--------+---------+------+------+--------+------------+ 10 rows in set (0.08 sec)该语句通过指定 ORDER BY 子句,MySQL 对查询的 height 列的数据按数值的大小进行了升序排序。有时需要根据多列进行排序。对多列数据进行排序要将需要排序的列之间用逗号隔开。【实例 2】查询 tb_students_info 表中的 name 和 height 字段,先按 height 排序,再按 name 排序,输入的 SQL 语句和执行结果如下所示。mysql> SELECT name,height -> FROM tb_students_info -> ORDER BY height,name; +--------+--------+ | name | height | +--------+--------+ | Green | 158 | | Dany | 160 | | Jane | 162 | | Lily | 165 | | Tom | 165 | | Susan | 170 | | John | 172 | | Jim | 175 | | Thomas | 178 | | Henry | 185 | +--------+--------+ 10 rows in set (0.09 sec)注意:在对多列进行排序时,首行排序的第一列必须有相同的列值,才会对第二列进行排序。如果第一列数据中所有的值都是唯一的,将不再对第二列进行排序。默认情况下,查询数据按字母升序进行排序(A~Z),但数据的排序并不仅限于此,还可以使用 ORDER BY 对查询结果进行降序排序(Z~A),这可以通过关键字 DESC 实现。可以对多列进行不同的顺序排序。【实例 3】查询 tb_students_info 表,先按 height 降序排序,再按 name 升序排序,输入的 SQL 语句和执行过程如下所示。mysql> SELECT name,height FROM tb_student_info ORDER BY height DESC,name ASC; +--------+--------+ | name | height | +--------+--------+ | Henry | 185 | | Thomas | 178 | | Jim | 175 | | John | 172 | | Susan | 170 | | Lily | 165 | | Tom | 165 | | Jane | 162 | | Dany | 160 | | Green | 158 | +--------+--------+ 10 rows in set (0.00 sec)注意:DESC 关键字只对前面的列进行降序排列,在这里只对 height 排序,而并没有对 name 进行排序,因此,height 按降序排序,而 name 仍按升序排序,如果要对多列进行降序排序,必须要在每一列的后面加 DESC 关键字。
-
在 MySQL SELECT 语句中,ORDER BY 子句主要用来将结果集中的数据按照一定的顺序进行排序。其语法格式为:ORDER BY {<列名> | <表达式> | <位置>} [ASC|DESC]语法说明如下。1) 列名指定用于排序的列。可以指定多个列,列名之间用逗号分隔。2) 表达式指定用于排序的表达式。3) 位置指定用于排序的列在 SELECT 语句结果集中的位置,通常是一个正整数。4) ASC|DESC关键字 ASC 表示按升序分组,关键字 DESC 表示按降序分组,其中 ASC 为默认值。这两个关键字必须位于对应的列名、表达式、列的位置之后。使用 ORDER BY 子句应该注意以下几个方面:ORDER BY 子句中可以包含子查询。当排序的值中存在空值时,ORDER BY 子句会将该空值作为最小值来对待。当在 ORDER BY 子句中指定多个列进行排序时,MySQL 会按照列的顺序从左到右依次进行排序。查询的数据并没有以一种特定的顺序显示,如果没有对它们进行排序,则将根据插入到数据表中的顺序显示。使用 ORDER BY 子句对指定的列数据进行排序。【实例 1】查询 tb_students_info 表的 height 字段值,并对其进行排序,输入的 SQL 语句和执行结果如下所示。mysql> SELECT * FROM tb_students_info ORDER BY height; +----+--------+---------+------+------+--------+------------+ | id | name | dept_id | age | sex | height | login_date | +----+--------+---------+------+------+--------+------------+ | 2 | Green | 3 | 23 | F | 158 | 2016-10-22 | | 1 | Dany | 1 | 25 | F | 160 | 2015-09-10 | | 4 | Jane | 1 | 22 | F | 162 | 2016-12-20 | | 7 | Lily | 6 | 22 | F | 165 | 2016-02-26 | | 10 | Tom | 4 | 23 | M | 165 | 2016-08-05 | | 8 | Susan | 4 | 23 | F | 170 | 2015-10-01 | | 6 | John | 2 | 21 | M | 172 | 2015-11-11 | | 5 | Jim | 1 | 24 | M | 175 | 2016-01-15 | | 9 | Thomas | 3 | 22 | M | 178 | 2016-06-07 | | 3 | Henry | 2 | 23 | M | 185 | 2015-05-31 | +----+--------+---------+------+------+--------+------------+ 10 rows in set (0.08 sec)该语句通过指定 ORDER BY 子句,MySQL 对查询的 height 列的数据按数值的大小进行了升序排序。有时需要根据多列进行排序。对多列数据进行排序要将需要排序的列之间用逗号隔开。【实例 2】查询 tb_students_info 表中的 name 和 height 字段,先按 height 排序,再按 name 排序,输入的 SQL 语句和执行结果如下所示。mysql> SELECT name,height -> FROM tb_students_info -> ORDER BY height,name; +--------+--------+ | name | height | +--------+--------+ | Green | 158 | | Dany | 160 | | Jane | 162 | | Lily | 165 | | Tom | 165 | | Susan | 170 | | John | 172 | | Jim | 175 | | Thomas | 178 | | Henry | 185 | +--------+--------+ 10 rows in set (0.09 sec)注意:在对多列进行排序时,首行排序的第一列必须有相同的列值,才会对第二列进行排序。如果第一列数据中所有的值都是唯一的,将不再对第二列进行排序。默认情况下,查询数据按字母升序进行排序(A~Z),但数据的排序并不仅限于此,还可以使用 ORDER BY 对查询结果进行降序排序(Z~A),这可以通过关键字 DESC 实现。可以对多列进行不同的顺序排序。【实例 3】查询 tb_students_info 表,先按 height 降序排序,再按 name 升序排序,输入的 SQL 语句和执行过程如下所示。mysql> SELECT name,height FROM tb_student_info ORDER BY height DESC,name ASC; +--------+--------+ | name | height | +--------+--------+ | Henry | 185 | | Thomas | 178 | | Jim | 175 | | John | 172 | | Susan | 170 | | Lily | 165 | | Tom | 165 | | Jane | 162 | | Dany | 160 | | Green | 158 | +--------+--------+ 10 rows in set (0.00 sec)注意:DESC 关键字只对前面的列进行降序排列,在这里只对 height 排序,而并没有对 name 进行排序,因此,height 按降序排序,而 name 仍按升序排序,如果要对多列进行降序排序,必须要在每一列的后面加 DESC 关键字。
-
今日份温暖小句 : 都说90斤的女生漂亮,所以你要吃到180斤,这样你就是就是双倍漂亮。 下面才是正文:往下看1. 多表连接类型1. 笛卡尔积(交叉连接) 在MySQL中可以为CROSS JOIN或者省略CROSS即JOIN,或者使用',' 如:SELECT * FROM table1 CROSS JOIN table2 SELECT * FROM table1 JOIN table2 SELECT * FROM table1,table2由于其返回的结果为被连接的两个数据表的乘积,因此当有WHERE, ON或USING条件的时候一般不建议使用,因为当数据表项目太多的时候,会非常慢。一般使用LEFT [OUTER] JOIN或者RIGHT [OUTER] JOIN 2. 内连接INNER JOIN 在MySQL中把INNER JOIN叫做等值连接,即需要指定等值连接条件在MySQL中CROSS和INNER JOIN被划分在一起。 join_table: table_reference [INNER | CROSS] JOIN table_factor [join_condition]3. MySQL中的外连接,分为左外连接和右连接,即除了返回符合连接条件的结果之外,还要返回左表(左连接)或者右表(右连接)中不符合连接条件的结果,相对应的使用NULL对应。例子:user表:id | name———1 | libk2 | zyfon3 | daodaouser_action表:user_id | action—————1 | jump1 | kick1 | jump2 | run4 | swimsql:select id, name, action from user as u left join user_action a on u.id = a.user_idresult:id | name | action——————————–1 | libk | jump ①1 | libk | kick ②1 | libk | jump ③2 | zyfon | run ④3 | daodao | null ⑤分析:注意到user_action中还有一个user_id=4, action=swim的纪录,但是没有在结果中出现,而user表中的id=3, name=daodao的用户在user_action中没有相应的纪录,但是却出现在了结果集中因为现在是left join,所有的工作以left为准.结果1,2,3,4都是既在左表又在右表的纪录,5是只在左表,不在右表的纪录工作原理:从左表读出一条,选出所有与on匹配的右表纪录(n条)进行连接,形成n条纪录(包括重复的行,如:结果1和结果3),如果右边没有与on条件匹配的表,那连接的字段都是null.然后继续读下一条。引申:我们可以用右表没有on匹配则显示null的规律, 来找出所有在左表,不在右表的纪录, 注意用来判断的那列必须声明为not null的。如:sql:select id, name, action from user as u left join user_action a on u.id = a.user_id where a.user_id is NULL注意: 1.列值为null应该用is null 而不能用=NULL 2.这里a.user_id 列必须声明为 NOT NULL 的.上面sql的result:id | name | action————————–3 | daodao | NULL——————————————————————————–一般用法:a. LEFT [OUTER] JOIN:除了返回符合连接条件的结果之外,还需要显示左表中不符合连接条件的数据列,相对应使用NULL对应SELECT column_name FROM table1 LEFT [OUTER] JOIN table2 ON table1.column=table2.column b. RIGHT [OUTER] JOIN:RIGHT与LEFT JOIN相似不同的仅仅是除了显示符合连接条件的结果之外,还需要显示右表中不符合连接条件的数据列,相应使用NULL对应SELECT column_name FROM table1 RIGHT [OUTER] JOIN table2 ON table1.column=table2.columnTips:1. on a.c1 = b.c1 等同于 using(c1)2. INNER JOIN 和 , (逗号) 在语义上是等同的3. 当 MySQL 在从一个表中检索信息时,你可以提示它选择了哪一个索引。如果 EXPLAIN 显示 MySQL 使用了可能的索引列表中错误的索引,这个特性将是很有用的。通过指定 USE INDEX (key_list),你可以告诉 MySQL 使用可能的索引中最合适的一个索引在表中查找记录行。可选的二选一句法 IGNORE INDEX (key_list) 可被用于告诉 MySQL 不使用特定的索引。如:mysql> SELECT * FROM table1 USE INDEX (key1,key2) -> WHERE key1=1 AND key2=2 AND key3=3; mysql> SELECT * FROM table1 IGNORE INDEX (key3) -> WHERE key1=1 AND key2=2 AND key3=3;2. 表连接的约束条件 添加显示条件WHERE, ON, USING1. WHERE子句SELECT * FROM table1,table2 WHERE table1.id=table2.id;2. ONSELECT * FROM table1 LEFT JOIN table2 ON table1.id=table2.id; SELECT * FROM table1 LEFT JOIN table2 ON table1.id=table2.id LEFT JOIN table3 ON table2.id=table3.id;3. USING子句,如果连接的两个表连接条件的两个列具有相同的名字的话可以使用USING 例如:SELECT FROM LEFT JOIN USING () 连接多于两个表的情况举例SELECT artists.Artist, cds.title, genres.genre FROM cds LEFT JOIN genres N cds.genreID = genres.genreID LEFT JOIN artists ON cds.artistID = artists.artistID; 或者 mysql>SELECT artists.Artist, cds.title, genres.genre FROM cds LEFT JOIN genres ON cds.genreID = genres.genreID LEFT JOIN artists -> ON cds.artistID = artists.artistID WHERE (genres.genre = 'Pop');另外需要注意的地方 在MySQL中涉及到多表查询的时候,需要根据查询的情况,想好使用哪种连接方式效率更高。 1. 交叉连接(笛卡尔积)或者内连接 [INNER | CROSS] JOIN 2. 左外连接LEFT [OUTER] JOIN或者右外连接RIGHT [OUTER] JOIN 注意指定连接条件WHERE, ON,USING.3. MySQL如何优化LEFT JOIN和RIGHT JOIN在MySQL中,A LEFT JOIN B join_condition执行过程如下:1)· 根据表A和A依赖的所有表设置表B。2)· 根据LEFT JOIN条件中使用的所有表(除了B)设置表A。3)· LEFT JOIN条件用于确定如何从表B搜索行。(换句话说,不使用WHERE子句中的任何条件)。4)· 可以对所有标准联接进行优化,只是只有从它所依赖的所有表读取的表例外。如果出现循环依赖关系,MySQL提示出现一个错误。5)· 进行所有标准WHERE优化。6)· 如果A中有一行匹配WHERE子句,但B中没有一行匹配ON条件,则生成另一个B行,其中所有列设置为NULL。7)· 如果使用LEFT JOIN找出在某些表中不存在的行,并且进行了下面的测试:WHERE部分的col_name IS NULL,其中col_name是一个声明为 NOT NULL的列,MySQL找到匹配LEFT JOIN条件的一个行后停止(为具体的关键字组合)搜索其它行。RIGHT JOIN的执行类似LEFT JOIN,只是表的角色反过来。联接优化器计算表应联接的顺序。LEFT JOIN和STRAIGHT_JOIN强制的表读顺序可以帮助联接优化器更快地工作,因为检查的表交换更少。请注意这说明如果执行下面类型的查询,MySQL进行全扫描b,因为LEFT JOIN强制它在d之前读取: SELECT * FROM a,b LEFT JOIN c ON (c.key=a.key) LEFT JOIN d ON (d.key=a.key) WHERE b.key=d.key;在这种情况下修复时用a的相反顺序,b列于FROM子句中:SELECT * FROM b,a LEFT JOIN c ON (c.key=a.key) LEFT JOIN d ON (d.key=a.key) WHERE b.key=d.key;MySQL可以进行下面的LEFT JOIN优化:如果对于产生的NULL行,WHERE条件总为假,LEFT JOIN变为普通联接。例如,在下面的查询中如果t2.column1为NULL,WHERE 子句将为false:SELECT * FROM t1 LEFT JOIN t2 ON (column1) WHERE t2.column2=5;因此,可以安全地将查询转换为普通联接:SELECT * FROM t1, t2 WHERE t2.column2=5 AND t1.column1=t2.column1;这样可以更快,因为如果可以使查询更佳,MySQL可以在表t1之前使用表t2。为了强制使用表顺序,使用STRAIGHT_JOIN。文章很长,学习本来就是积累的过程,加油!!!
-
来碗鸡汤,继续学习: “ 我们各自努力,最高处见。"一、mysql常用连接INNER JOIN(内连接,或等值连接):获取两个表中字段匹配关系的记录。LEFT JOIN(左连接):获取左表所有记录,即使右表没有对应匹配的记录。RIGHT JOIN(右连接): 与 LEFT JOIN 相反,用于获取右表所有记录,即使左表没有对应匹配的记录。mysql> select * from name_address; +----------+------+----+ | address | name | id | +----------+------+----+ | 西北一路 | 张三 | 1 | | 西北二路 | 李四 | 2 | | 西北三路 | 王五 | 3 | +----------+------+----+ 3 rows in set mysql> select * from name_age; +-----+--------+----+ | age | name | id | +-----+--------+----+ | 18 | 张三 | 1 | | 20 | 王五 | 2 | | 21 | 路人甲 | 3 | +-----+--------+----+ 3 rows in set1、INNER JOININNER JOIN与一般的连表查询一致,即使用逗号隔开的查询方式。mysql> SELECT a.`name`,a.age,b.address FROM name_age a INNER JOIN name_address b WHERE(on) a.`name`=b.`name`; +------+-----+----------+ | name | age | address | +------+-----+----------+ | 张三 | 18 | 西北一路 | | 王五 | 20 | 西北三路 | +------+-----+----------+ 2 rows in set2、LEFT JOIN以左边的数据表为准mysql> SELECT a.`name`,a.age,b.address FROM name_age a left JOIN name_address b on a.`name`=b.`name`; +--------+-----+----------+ | name | age | address | +--------+-----+----------+ | 张三 | 18 | 西北一路 | | 王五 | 20 | 西北三路 | | 路人甲 | 21 | NULL | +--------+-----+----------+ 3 rows in set3、RIGHT JOIN与LEFT JOIN相反,即以右边的数据为准mysql> SELECT b.`name`,a.age,b.address FROM name_age a right JOIN name_address b on a.`name`=b.`name`; +------+------+----------+ | name | age | address | +------+------+----------+ | 张三 | 18 | 西北一路 | | 王五 | 20 | 西北三路 | | 李四 | NULL | 西北二路 | +------+------+----------+ 3 rows in set
-
子查询指一个查询语句嵌套在另一个查询语句内部的查询,这个特性从 MySQL 4.1 开始引入,在 SELECT 子句中先计算子查询,子查询结果作为外层另一个查询的过滤条件,查询可以基于一个表或者多个表。子查询中常用的操作符有 ANY(SOME)、ALL、IN 和 EXISTS。子查询可以添加到 SELECT、UPDATE 和 DELETE 语句中,而且可以进行多层嵌套。子查询也可以使用比较运算符,如“<”、“<=”、“>”、“>=”、“!=”等。子查询中常用的运算符1) IN子查询结合关键字 IN 所使用的子查询主要用于判断一个给定值是否存在于子查询的结果集中。其语法格式为:<表达式> [NOT] IN <子查询>语法说明如下。<表达式>:用于指定表达式。当表达式与子查询返回的结果集中的某个值相等时,返回 TRUE,否则返回 FALSE;若使用关键字 NOT,则返回的值正好相反。<子查询>:用于指定子查询。这里的子查询只能返回一列数据。对于比较复杂的查询要求,可以使用 SELECT 语句实现子查询的多层嵌套。2) 比较运算符子查询比较运算符所使用的子查询主要用于对表达式的值和子查询返回的值进行比较运算。其语法格式为:<表达式> {= | < | > | >= | <= | <=> | < > | != }{ ALL | SOME | ANY} <子查询>语法说明如下。<子查询>:用于指定子查询。<表达式>:用于指定要进行比较的表达式。ALL、SOME 和 ANY:可选项。用于指定对比较运算的限制。其中,关键字 ALL 用于指定表达式需要与子查询结果集中的每个值都进行比较,当表达式与每个值都满足比较关系时,会返回 TRUE,否则返回 FALSE;关键字 SOME 和 ANY 是同义词,表示表达式只要与子查询结果集中的某个值满足比较关系,就返回 TRUE,否则返回 FALSE。3) EXIST子查询关键字 EXIST 所使用的子查询主要用于判断子查询的结果集是否为空。其语法格式为:EXIST <子查询>若子查询的结果集不为空,则返回 TRUE;否则返回 FALSE。子查询的应用【实例 1】在 tb_departments 表中查询 dept_type 为 A 的学院 ID,并根据学院 ID 查询该学院学生的名字,输入的 SQL 语句和执行结果如下所示。mysql> SELECT name FROM tb_students_info -> WHERE dept_id IN -> (SELECT dept_id -> FROM tb_departments -> WHERE dept_type= 'A' ); +-------+ | name | +-------+ | Dany | | Henry | | Jane | | Jim | | John | +-------+ 5 rows in set (0.01 sec)上述查询过程可以分步执行,首先内层子查询查出 tb_departments 表中符合条件的学院 ID,单独执行内查询,查询结果如下所示。mysql> SELECT dept_id -> FROM tb_departments -> WHERE dept_type='A'; +---------+ | dept_id | +---------+ | 1 | | 2 | +---------+ 2 rows in set (0.00 sec)可以看到,符合条件的 dept_id 列的值有两个:1 和 2。然后执行外层查询,在 tb_students_info 表中查询 dept_id 等于 1 或 2 的学生的名字。嵌套子查询语句还可以写为如下形式,可以实现相同的效果。mysql> SELECT name FROM tb_students_info -> WHERE dept_id IN(1,2); +-------+ | name | +-------+ | Dany | | Henry | | Jane | | Jim | | John | +-------+ 5 rows in set (0.03 sec)上例说明在处理 SELECT 语句时,MySQL 实际上执行了两个操作过程,即先执行内层子查询,再执行外层查询,内层子查询的结果作为外部查询的比较条件。【实例 2】与前一个例子类似,但是在 SELECT 语句中使用 NOT IN 关键字,输入的 SQL 语句和执行结果如下所示。mysql> SELECT name FROM tb_students_info -> WHERE dept_id NOT IN -> (SELECT dept_id -> FROM tb_departments -> WHERE dept_type='A'); +--------+ | name | +--------+ | Green | | Lily | | Susan | | Thomas | | Tom | +--------+ 5 rows in set (0.04 sec)提示:子查询的功能也可以通过连接查询完成,但是子查询使得 MySQL 代码更容易阅读和编写。【实例 3】在 tb_departments 表中查询 dept_name 等于“Computer”的学院 id,然后在 tb_students_info 表中查询所有该学院的学生的姓名,输入的 SQL 语句和执行过程如下所示。 mysql> SELECT name FROM tb_students_info -> WHERE dept_id = -> (SELECT dept_id -> FROM tb_departments -> WHERE dept_name='Computer'); +------+ | name | +------+ | Dany | | Jane | | Jim | +------+ 3 rows in set (0.00 sec)【实例 4】在 tb_departments 表中查询 dept_name 不等于“Computer”的学院 id,然后在 tb_students_info 表中查询所有该学院的学生的姓名,输入的 SQL 语句和执行过程如下所示。mysql> SELECT name FROM tb_students_info -> WHERE dept_id <> -> (SELECT dept_id -> FROM tb_departments -> WHERE dept_name='Computer'); +--------+ | name | +--------+ | Green | | Henry | | John | | Lily | | Susan | | Thomas | | Tom | +--------+ 7 rows in set (0.00 sec)【实例 5】查询 tb_departments 表中是否存在 dept_id=1 的供应商,如果存在,就查询 tb_students_info 表中的记录,输入的 SQL 语句和执行结果如下所示。mysql> SELECT * FROM tb_students_info -> WHERE EXISTS -> (SELECT dept_name -> FROM tb_departments -> WHERE dept_id=1); +----+--------+---------+------+------+--------+------------+ | id | name | dept_id | age | sex | height | login_date | +----+--------+---------+------+------+--------+------------+ | 1 | Dany | 1 | 25 | F | 160 | 2015-09-10 | | 2 | Green | 3 | 23 | F | 158 | 2016-10-22 | | 3 | Henry | 2 | 23 | M | 185 | 2015-05-31 | | 4 | Jane | 1 | 22 | F | 162 | 2016-12-20 | | 5 | Jim | 1 | 24 | M | 175 | 2016-01-15 | | 6 | John | 2 | 21 | M | 172 | 2015-11-11 | | 7 | Lily | 6 | 22 | F | 165 | 2016-02-26 | | 8 | Susan | 4 | 23 | F | 170 | 2015-10-01 | | 9 | Thomas | 3 | 22 | M | 178 | 2016-06-07 | | 10 | Tom | 4 | 23 | M | 165 | 2016-08-05 | +----+--------+---------+------+------+--------+------------+ 10 rows in set (0.00 sec)由结果可以看到,内层查询结果表明 tb_departments 表中存在 dept_id=1 的记录,因此 EXSTS 表达式返回 TRUE,外层查询语句接收 TRUE 之后对表 tb_students_info 进行查询,返回所有的记录。EXISTS 关键字可以和条件表达式一起使用。【实例 6】查询 tb_departments 表中是否存在 dept_id=7 的供应商,如果存在,就查询 tb_students_info 表中的记录,输入的 SQL 语句和执行结果如下所示。mysql> SELECT * FROM tb_students_info -> WHERE EXISTS -> (SELECT dept_name -> FROM tb_departments -> WHERE dept_id=7); Empty set (0.00 sec)
-
limit用法在我们使用查询语句的时候,经常要返回前几条或者中间某几行数据,这个时候怎么办呢?不用担心,mysql已经为我们提供了这样一个功能。SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset LIMIT 子句可以被用于强制 SELECT 语句返回指定的记录数。LIMIT 接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。初始记录行的便宜量是(0而不是1): 为了与 PostgreSQL 兼容,MySQL 也支持句法: LIMIT # OFFSET #。mysql> SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:mysql> SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.如果只给定一个参数,它表示返回最大的记录行数目:mysql> SELECT * FROM table LIMIT 5; //检索前 5 个记录行换句话说,LIMIT n 等价于 LIMIT 0,n。换句话说,LIMIT n 等价于Mysql的分页查询语句的性能分析MySql分页sql语句,如果和MSSQL的TOP语法相比,那么MySQL的LIMIT语法要显得优雅了许多。使用它来分页是再自然不过的事情了。最基本的分页方式:SELECT ... FROM ... WHERE ... ORDER BY ... LIMIT ...在中小数据量的情况下,这样的SQL足够用了,唯一需要注意的问题就是确保使用了索引:举例来说,如果实际SQL类似下面语句,那么在category_id, id两列上建立复合索引比较好:SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset SELECT * FROM articles WHERE category_id = 123 ORDER BY id LIMIT 50, 10子查询的分页方式:随着数据量的增加,页数会越来越多,查看后几页的SQL就可能类似:SELECT * FROM articles WHERE category_id = 123 ORDER BY id LIMIT 10000, 10一言以蔽之,就是越往后分页。limit语句的偏移量就会越大,熟读就会明显变慢。此时,我们可以通过子查询的方式来提高分页效率,大致如下:SELECT * FROM articles WHERE id >= (SELECT id FROM articles WHERE category_id = 123 ORDER BY id LIMIT 10000, 1) LIMIT 10JOIN分页方式SELECT * FROM `content` AS t1 JOIN (SELECT id FROM `content` ORDER BY id desc LIMIT ".($page-1)*$pagesize.", 1) AS t2 WHERE t1.id <= t2.id ORDER BY t1.id desc LIMIT $pagesize;经过我的测试,join分页和子查询分页的效率基本在一个等级上,消耗的时间也基本一致。 explain SQL语句id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY <derived2> system NULL NULL NULL NULL 1 1 PRIMARY t1 range PRIMARY PRIMARY 4 NULL 6264 Using where 2 DERIVED content index NULL PRIMARY 4 NULL 27085 Using index为什么会这样呢?因为子查询是在索引上完成的,而普通的查询时在数据文件上完成的,通常来说,索引文件要比数据文件小得多,所以操作起来也会更有效率。实际可以利用类似策略模式的方式去处理分页,比如判断如果是一百页以内,就使用最基本的分页方式,大于一百页,则使用子查询的分页方式。
-
关于mysql 8.0.20在鲲鹏上目前部署资源缺少的问题,这里提供了资源下载地址以及部署教程下载资源:wget https://obs-mirror-ftp4.obs.cn-north-4.myhuaweicloud.com/database/mysql-8.0.20.tar.gz--------------------------------------------用户创建:如果已经创建了mysql 用户,次操作可省略 root groupadd -r mysql && useradd -r -g mysql -s /sbin/nologin -M mysql-----------------------------------------------解压安装:tar -zxvf mysql-8.0.20.tar.gzrpm -ivh mysql-community-common-8.0.20-1.el7.aarch64.rpmrpm -ivh mysql-community-libs-8.0.20-1.el7.aarch64.rpmrpm -ivh mysql-community-libs-compat-8.0.20-1.el7.aarch64.rpmrpm -ivh mysql-community-client-8.0.20-1.el7.aarch64.rpm rpm -ivh mysql-community-server-8.0.20-1.el7.aarch64.rpm服务启动systemctl start mysqld.servicemysqld --initialize --user=mysqlsystemctl enable mysqld.service-------------------------------------------过程可能出现问题:Updating / installing... 1:mysql-community-server-8.0.20-1.e################################# [100%][/usr/lib/tmpfiles.d/mysql.conf:23] Line references path below legacy directory /var/run/, updating /var/run/mysqld → /run/mysqld; please update the tmpfiles.d/ drop-in file accordingly.解决:cd /usr/lib/tmpfiles.d/cp mysql.conf mysql.conf.bakvi mysql.confd /var/run/mysqld 0755 mysql mysql - 修改为d /run/mysqld 0755 mysql mysql -------------------------------------------------
-
视图是数据库系统中一种非常有用的数据库对象。MySQL 5.0 之后的版本添加了对视图的支持。认识视图视图是一个虚拟表,其内容由查询定义。同真实表一样,视图包含一系列带有名称的列和行数据,但视图并不是数据库真实存储的数据表。视图是从一个、多个表或者视图中导出的表,包含一系列带有名称的数据列和若干条数据行。视图并不同于数据表,它们的区别在于以下几点:视图不是数据库中真实的表,而是一张虚拟表,其结构和数据是建立在对数据中真实表的查询基础上的。存储在数据库中的查询操作 SQL 语句定义了视图的内容,列数据和行数据来自于视图查询所引用的实际表,引用视图时动态生成这些数据。视图没有实际的物理记录,不是以数据集的形式存储在数据库中的,它所对应的数据实际上是存储在视图所引用的真实表中的。视图是数据的窗口,而表是内容。表是实际数据的存放单位,而视图只是以不同的显示方式展示数据,其数据来源还是实际表。视图是查看数据表的一种方法,可以查询数据表中某些字段构成的数据,只是一些 SQL 语句的集合。从安全的角度来看,视图的数据安全性更高,使用视图的用户不接触数据表,不知道表结构。视图的建立和删除只影响视图本身,不影响对应的基本表。创建视图(基本语法): create view 视图名 as select 字段名 from 表名; Mysql视图的定义在from关键字后面不能包含子查询 修改了视图,对基表数据有影响 修改了基表,对视图也有影响 修改视图 基本语法是 alter view 视图名 as select 语句 alter view 视图名 as select 视图; 显示视图创建情况 show create view 视图名;查看视图 Show tables; Show table status [ from db_name ] [ like ‘pattern’ ]; SELECT * FROM information_schema.views where table_name = 'my_view';删除视图 drop view 视图名[,视图名…];重命名视图 Rename table 视图名 to 新视图名;;视图与表在本质上虽然不相同,但视图经过定义以后,结构形式和表一样,可以进行查询、修改、更新和删除等操作。同时,视图具有如下优点:1) 定制用户数据,聚焦特定的数据在实际的应用过程中,不同的用户可能对不同的数据有不同的要求。例如,当数据库同时存在时,如学生基本信息表、课程表和教师信息表等多种表同时存在时,可以根据需求让不同的用户使用各自的数据。学生查看修改自己基本信息的视图,安排课程人员查看修改课程表和教师信息的视图,教师查看学生信息和课程信息表的视图。2) 简化数据操作在使用查询时,很多时候要使用聚合函数,同时还要显示其他字段的信息,可能还需要关联到其他表,语句可能会很长,如果这个动作频繁发生的话,可以创建视图来简化操作。3) 提高基表数据的安全性视图是虚拟的,物理上是不存在的。可以只授予用户视图的权限,而不具体指定使用表的权限,来保护基础数据的安全。4) 共享所需数据通过使用视图,每个用户不必都定义和存储自己所需的数据,可以共享数据库中的数据,同样的数据只需要存储一次。5) 更改数据格式通过使用视图,可以重新格式化检索出的数据,并组织输出到其他应用程序中。6) 重用 SQL 语句视图提供的是对查询操作的封装,本身不包含数据,所呈现的数据是根据视图定义从基础表中检索出来的,如果基础表的数据新增或删除,视图呈现的也是更新后的数据。视图定义后,编写完所需的查询,可以方便地重用该视图。注意:要区别视图和数据表的本质,即视图是基于真实表的一张虚拟的表,其数据来源均建立在真实表的基础上。使用视图的时候,还应该注意以下几点:创建视图需要足够的访问权限。创建视图的数目没有限制。视图可以嵌套,即从其他视图中检索数据的查询来创建视图。视图不能索引,也不能有关联的触发器、默认值或规则。视图可以和表一起使用。视图不包含数据,所以每次使用视图时,都必须执行查询中所需的任何一个检索操作。如果用多个连接和过滤条件创建了复杂的视图或嵌套了视图,可能会发现系统运行性能下降得十分严重。因此,在部署大量视图应用时,应该进行系统测试。ORDER BY 子句可以用在视图中,但若该视图检索数据的 SELECT 语句中也含有 ORDER BY 子句,则该视图中的 ORDER BY 子句将被覆盖。
-
一、前言隔壁小王问我,如何使用 Mysql 实现类似于 ElasticSearch 的全文检索功能,并且对检索关键词跑分?我当时脑子里立马产生了疑问?为啥不直接用es呢?简单好用还贼快。但是听他说,数据量不多,客户给的时间非常有限,根本没时间去搭建es,所以还是看一下 Mysql 的全文检索功能吧!MySQL 从 5.7.6 版本开始,MySQL就内置了ngram全文解析器,用来支持中文、日文、韩文分词。在 MySQL 5.7.6 版本之前,全文索引只支持英文全文索引,不支持中文全文索引,需要利用分词器把中文段落预处理拆分成单词,然后存入数据库。本篇文章测试的时候,采用的 Mysql 5.7.6 ,InnoDB数据库引擎。mysql全文检索二、全文解析器ngramngram就是一段文字里面连续的n个字的序列。ngram全文解析器能够对文本进行分词,每个单词是连续的n个字的序列。例如,用ngram全文解析器对“你好世界”进行分词:n=1: '你', '好', '世', '界' n=2: '你好', '好世', '世界' n=3: '你好世', '好世界' n=4: '你好世界'MySQL 中使用全局变量 ngram_token_size 来配置 ngram 中 n 的大小,它的取值范围是1到10,默认值是 2。通常ngram_token_size设置为要查询的单词的最小字数。如果需要搜索单字,就要把ngram_token_size设置为 1。在默认值是 2 的情况下,搜索单字是得不到任何结果的。因为中文单词最少是两个汉字,推荐使用默认值 2。咱们看一下Mysql默认的ngram_token_size大小show variables like 'ngram_token_size'ngram_token_size 变量的两种设置方式:1、启动mysqld命令时指定mysqld --ngram_token_size=22、修改mysql配置文件[mysqld] ngram_token_size=2三、全文索引以某文书数据为例,新建数据表 t_wenshu ,并且针对文书内容字段创建全文索引,导入10w条测试数据。1、建表时创建全文索引CREATE TABLE `t_wenshu` ( `province` varchar(255) DEFAULT NULL, `caseclass` varchar(255) DEFAULT NULL, `casenumber` varchar(255) DEFAULT NULL, `caseid` varchar(255) DEFAULT NULL, `types` varchar(255) DEFAULT NULL, `title` varchar(255) DEFAULT NULL, `content` longtext, `updatetime` varchar(255) DEFAULT NULL, FULLTEXT KEY `content` (`content`) WITH PARSER `ngram` ) ENGINE=InnoDB DEFAULT CHARSET=utf8;2、通过 alter table 方式ALTER TABLE t_wenshu ADD FULLTEXT INDEX content_index (content) WITH PARSER ngram;3、通过 create index 方式CREATE FULLTEXT INDEX content_index ON t_wenshu (content) WITH PARSER ngram;四、检索模式自然语言检索(IN NATURAL LANGUAGE MODE)自然语言模式是 MySQL 默认的全文检索模式。自然语言模式不能使用操作符,不能指定关键词必须出现或者必须不能出现等复杂查询。布尔检索(IN BOOLEAN MODE)剔除一半匹配行以上都有的词,例如,每行都有this这个词的话,那用this去查时,会找不到任何结果,这在记录条数特别多时很有用,原因是数据库认为把所有行都找出来是没有意义的,这时,this几乎被当作是stopword(中断词);布尔检索模式可以使用操作符,可以支持指定关键词必须出现或者必须不能出现或者关键词的权重高还是低等复杂查询。● IN BOOLEAN MODE的特色: ·不剔除50%以上符合的row。 ·不自动以相关性反向排序。 ·可以对没有FULLTEXT index的字段进行搜寻,但会非常慢。 ·限制最长与最短的字符串。 ·套用Stopwords。 ● 搜索语法规则: + 一定要有(不含有该关键词的数据条均被忽略)。 - 不可以有(排除指定关键词,含有该关键词的均被忽略)。 > 提高该条匹配数据的权重值。 < 降低该条匹配数据的权重值。 ~ 将其相关性由正转负,表示拥有该字会降低相关性(但不像-将之排除),只是排在较后面权重值降低。 * 万用字,不像其他语法放在前面,这个要接在字符串后面。 " " 用双引号将一段句子包起来表示要完全相符,不可拆字。 查询扩展检索注释:(WITH QUERY EXPANSION)由于查询扩展可能带来许多非相关性的查询,谨慎使用!五、检索查询1)查询 content 中包含“盗窃罪”的记录,查询语句如下select caseid,content, MATCH ( content) AGAINST ('盗窃罪') as score from t_wenshu where MATCH ( content) AGAINST ('盗窃罪' IN NATURAL LANGUAGE MODE)2)查询 content 中包含“寻衅滋事”的记录,查询语句如下select caseid,content, MATCH ( content) AGAINST ('寻衅滋事') as score from t_wenshu where MATCH ( content) AGAINST ('寻衅滋事' IN NATURAL LANGUAGE MODE) ;3)单个汉字,查询 content 中包含“我”的记录,查询语句如下 select caseid,content, MATCH ( content) AGAINST ('我') as score from t_wenshu where MATCH ( content) AGAINST ('我' IN NATURAL LANGUAGE MODE) ;4)查询字段 content 中包含 “危险驾驶”和“寻衅滋事”的语句如下:select caseid,content, MATCH (content) AGAINST ('+危险驾驶 +寻衅滋事') as score from t_wenshu where MATCH (content) AGAINST ('+危险驾驶 +寻衅滋事' IN BOOLEAN MODE); 5)查询字段 content 中包含 “危险驾驶”,但不包含“寻衅滋事”的语句如下:select caseid,content, MATCH (content) AGAINST ('+危险驾驶 -寻衅滋事') as score from t_wenshu where MATCH (content) AGAINST ('+危险驾驶 -寻衅滋事' IN BOOLEAN MODE);6)查询字段 conent 中包含“危险驾驶”或者“寻衅滋事”的语句如下:select caseid,content, MATCH (content) AGAINST ('危险驾驶 寻衅滋事') as score from t_wenshu where MATCH (content) AGAINST ('危险驾驶 寻衅滋事' IN BOOLEAN MODE);六、总结1)使用 Mysql 全文索引之前,搞清楚各版本支持情况;2)全文索引比 like + % 快 N 倍,但是可能存在精度问题;3)如果需要全文索引的是大量数据,建议先添加数据,再创建索引;4)对于中文,可以使用 MySQL 5.7.6 之后的版本,或者 Sphinx、Lucene 等第三方的插件;5)MATCH()函数使用的字段名,必须要与创建全文索引时指定的字段名一致,且只能是同一个表的字段不能跨表;
-
一、概述本章节介绍使用游标来批量进行表操作,包括批量添加索引、批量添加字段等。如果对存储过程、变量定义、预处理还不是很熟悉先阅读我前面写过的关于这三个概念的文章,只有先了解了这三个概念才能更好的理解这篇文章。理解MySQL变量和条件:https://bbs.huaweicloud.com/forum/thread-82694-1-1.html理解Mysql prepare预处理语句:https://bbs.huaweicloud.com/forum/thread-82700-1-1.html理解MySQL存储过程和函数:https://bbs.huaweicloud.com/forum/thread-82702-1-1.html二、正文1、声明光标DECLARE cursor_name CURSOR FOR select_statement这个语句声明一个光标。也可以在子程序中定义多个光标,但是一个块中的每一个光标必须有唯一的名字。注意:SELECT语句不能有INTO子句。2、打开光标DECLARE cursor_name CURSOR FOR select_statement这个语句打开先前声明的光标。3、前进光标FETCH cursor_name INTO var_name [, var_name] ...4、关闭光标CLOSE cursor_name 5、批量添加索引共享一个批量添加索引的游标,当一个库中有上百张表结构一样但是名称不一样的表,这个时候批量操作就变得简单了。#删除创建存储过程 DROP PROCEDURE IF EXISTS FountTable; DELIMITER $$ CREATE PROCEDURE FountTable() BEGIN DECLARE TableName varchar(64); #声明游标 DECLARE cur_FountTable CURSOR FOR SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA='front' AND TABLE_NAME LIKE 'student%'; DECLARE EXIT HANDLER FOR not found CLOSE cur_FountTable; #打开游标 OPEN cur_FountTable; REPEAT FETCH cur_FountTable INTO TableName; #定义预处理 SET @SQLSTR1 = CONCAT('create index Flag on ','`',TableName,'`',' (Flag); '); SET @SQLSTR2 = CONCAT('create index State on ','`',TableName,'`',' (State); '); SET @SQLSTR3 = CONCAT('create index upload on ','`',TableName,'`',' (upload); '); SET @SQLSTR4 = CONCAT('create index ccFlag on ','`',TableName,'`',' (lockFlag); '); SET @SQLSTR5 = CONCAT('create index comes on ','`',TableName,'`',' (comes); '); ###SET @SQLSTR=CONCAT(@SQLSTR1,@SQLSTR2,@SQLSTR3,@SQLSTR4,@SQLSTR5 ); PREPARE STMT1 FROM @SQLSTR1; PREPARE STMT2 FROM @SQLSTR2; PREPARE STMT3 FROM @SQLSTR3; PREPARE STMT4 FROM @SQLSTR4; PREPARE STMT5 FROM @SQLSTR5; EXECUTE STMT1; EXECUTE STMT2; EXECUTE STMT3; EXECUTE STMT4; EXECUTE STMT5; DEALLOCATE PREPARE STMT1; DEALLOCATE PREPARE STMT2; DEALLOCATE PREPARE STMT3; DEALLOCATE PREPARE STMT4; DEALLOCATE PREPARE STMT5; # SELECT @SQLSTR; UNTIL 0 END REPEAT; #关闭游标 CLOSE cur_FountTable; END $$ DELIMITER ; CALL FountTable();这里有几个细节:在声明游标的时候记得修改自己需要查询的条件在预处理这里也需要改成对应的字段在定义条件变量的时候这里我使用的是EXIT就是遇到错误就中断,当然也可以使用CONTINUE 。注意:由于mysql在存储过程当中无法将查询出来的变量名直接作为表名来用,所以这里要用到动态拼接SQL的方法,但是通常的SET CONCAT的方法并不管用,所以这里就使用了PREPARE来进行预编译。总结批量处理虽然有时候能提高工作的效率,但是带来的潜在危险也是挺大了,所以在执行之前必须要非常有把握你执行的语句对数据的影响,否则在生成环境就非常危险了。
-
一、概述一提到存储过程可能就会引出另一个话题就是存储过程的优缺点,这里也不做讨论,一般别人问我我就这样回答你觉得它好你就用它。因为mysql中存储过程和函数的语法非常接近所以就放在一起,主要区别就是函数必须有返回值(return),并且函数的参数只有IN类型而存储过程有IN、OUT、INOUT这三种类型。二、语法 创建存储过程和函数语法CREATE PROCEDURE sp_name ([proc_parameter[,...]]) [characteristic ...] routine_body CREATE FUNCTION sp_name ([func_parameter[,...]]) RETURNS type [characteristic ...] routine_body proc_parameter: [ IN | OUT | INOUT ] param_name type func_parameter: param_name type type: Any valid MySQL data type characteristic: LANGUAGE SQL | [NOT] DETERMINISTIC | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { DEFINER | INVOKER } | COMMENT 'string' routine_body: Valid SQL procedure statement or statements语法来自官方自带的参考手册,characteristic语法块是需要注意的地方,先用一个例子来介绍。例子:#创建数据库 DROP DATABASE IF EXISTS Dpro; CREATE DATABASE Dpro CHARACTER SET utf8 ; USE Dpro; #创建部门表 DROP TABLE IF EXISTS Employee; CREATE TABLE Employee (id INT NOT NULL PRIMARY KEY COMMENT '主键', name VARCHAR(20) NOT NULL COMMENT '人名', depid INT NOT NULL COMMENT '部门id' ); #插入测试数据 INSERT INTO Employee(id,name,depid) VALUES(1,'陈',100),(2,'王',101),(3,'张',101),(4,'李',102),(5,'郭',103); #创建存储过程 DROP PROCEDURE IF EXISTS Pro_Employee; DELIMITER $$ CREATE PROCEDURE Pro_Employee(IN pdepid VARCHAR(20),OUT pcount INT ) READS SQL DATA SQL SECURITY INVOKER BEGIN SELECT COUNT(id) INTO pcount FROM Employee WHERE depid=pdepid; END$$ DELIMITER ; #执行存储过程 CALL Pro_Employee(101,@pcount); SELECT @pcount;语法解释:在创建存储过程的时候一般都会用DELIMITER$$.....END$$ DELIMITER ;放在开头和结束,目的就是避免mysql把存储过程内部的";"解释成结束符号,最后通过“DELIMITER ;”来告知存储过程结束。主要解释characteristic部分:LANGUAGE SQL:用来说明语句部分是SQL语句,未来可能会支持其它类型的语句。[NOT] DETERMINISTIC:如果程序或线程总是对同样的输入参数产生同样的结果,则被认为它是“确定的”,否则就是“非确定”的。如果既没有给定DETERMINISTIC也没有给定NOT DETERMINISTIC,默认的就是NOT DETERMINISTIC(非确定的)CONTAINS SQL:表示子程序不包含读或写数据的语句。NO SQL:表示子程序不包含SQL语句。READS SQL DATA:表示子程序包含读数据的语句,但不包含写数据的语句。MODIFIES SQL DATA:表示子程序包含写数据的语句。SQL SECURITY DEFINER:表示执行存储过程中的程序是由创建该存储过程的用户的权限来执行。SQL SECURITY INVOKER:表示执行存储过程中的程序是由调用该存储过程的用户的权限来执行。(例如上面的存储过程我写的是由调用该存储过程的用户的权限来执行,当前存储过程是用来查询Employee表,如果我当前执行存储过程的用户没有查询Employee表的权限那么就会返回权限不足的错误,如果换成DEFINER如果存储过程是由ROOT用户创建那么任何一个用户登入调用存储过程都可以执行,因为执行存储过程的权限变成了root)COMMENT 'string':备注,和创建表的字段备注一样。注意:在编写存储过程和函数时建议明确指定上面characteristic部分的状态,特别是存在复制的环境中,如果创建函数不明确指定这些状态会报错,从一个非复制环境将带函数的数据库迁移到复制环境的机器上如果没有明确指定DETERMINISTIC, NO SQL, or READS SQL DATA该三个状态也会报错。报错示例Error Code: 1418. This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)这个报错就是上面注意部分说的问题。原来是因为在主从复制的两台MySQL服务器中开启了二进制日志选项log-bin,slave会从master复制数据,而一些操作,比如function所得的结果在master和slave上可能不同,所以存在潜在的安全隐患。因此,在默认情况下回阻止function的创建。解决办法有两种:1.将log_bin_trust_function_creators参数设置为ON,这样一来开启了log-bin的MySQL Server便可以随意创建function。这里存在潜在的数据安全问题,除非明确的知道创建的function在master和slave上的行为完全一致。设置该参数可以用动态的方式或者指定该参数来启动数据库服务器或者修改配置文件后重启服务器。需注意的是,动态设置的方式会在服务器重启后失效。mysql> show variables like 'log_bin_trust_function_creators'; mysql> set global log_bin_trust_function_creators=1;另外如果是在master上创建函数,想通过主从复制的方式将函数复制到slave上则也需在开启了log-bin的slave中设置上述变量的值为ON(变量的设置不会从master复制到slave上,这点需要注意),否则主从复制会报错。2.明确指明函数的类型 1 )、DETERMINISTIC 不确定的 2 )、NO SQL 没有SQl语句,当然也不会修改数据 3 )、READS SQL DATA 只是读取数据,当然也不会修改数据比如:CREATE DEFINER=`username`@`%` READS SQL DATA FUNCTION `fn_getitemclock`(i_itemid bigint,i_clock int,i_pos int) RETURNS int(11)...这样一来相当于明确的告知MySQL服务器这个函数不会修改数据,因此可以在开启了log-bin的服务器上安全的创建并被复制到开启了log-bin的slave上。修改存储过程函数语法ALTER {PROCEDURE | FUNCTION} sp_name [characteristic ...] characteristic: { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { DEFINER | INVOKER } | COMMENT 'string'删除存储过程函数语法DROP {PROCEDURE | FUNCTION} [IF EXISTS] sp_name查看存储过程和函数1.查看存储过程状态SHOW {PROCEDURE | FUNCTION} STATUS [LIKE 'pattern'] show procedure status like 'Pro_Employee' \G2.查看存储过程和函数的创建语法SHOW CREATE {PROCEDURE | FUNCTION} sp_name SHOW CREATE PROCEDURE Pro_Employee \G;3.查看存储过程和函数详细信息SELECT * FROM information_schema.ROUTINES WHERE ROUTINE_NAME='Pro_Employee' \G;总结 存储过程和函数语法不难理解,但是往往存储过程中不单单只包含这种简单的查询语法,还会嵌套循环语句、变量、报错处理、事务等,下一篇文章会单独讲变量,将变量的知识加入到存储过程,包括变量的声明和报错处理,欢迎关注。
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签