Mosh MySQL教程 笔记
本文使用教程中配套的数据库
- SELECT 语句
SELECT关键字后面可以加上需要选取的表中的列,或跟表达式,或者*号选取所有列,AS关键字是取别名。
SELECT
last_name,
first_name,
points,
points + 10 AS 'discount factor'
FROM customers
- WHERE 语句
WHERE语句可以指定SELECT选取列时的条件,筛选points大于10的用户,可以这样写
SELECT * FROM customers
WHERE points > 10
WHERE语句的条件可以是逻辑运算表达式,算术表达式,或者多个逻辑表达式的,比如>, <, =, AND, OR等
SELECT * FROM customers
WHERE points > 10 AND state = 'VA'
- 逻辑运算符
逻辑运算符包括 AND,OR,NOT。AND表示并且的关系,需要满足AND左右的条件才算判定通过。OR表示或者,满足OR左右的任意条件之一就算判定通过。NOT表示反面,比如以WHERE语句中的例子来讲,筛选的是points > 10并且state='VA'的用户,我要筛选points不大于10且state也不是VA的用户,就可以使用NOT
SELECT * FROM customers
WHERE NOT (points > 10 AND state = 'VA')
优先级AND > OR 和 NOT,可根据需求混合使用
- OR运算符
当有多个条件需要满足时,比如想在customers表里筛选住在VA,FL,GA的人,可以使用OR运算符
SELECT * FROM customer
WHERE state = 'VA' OR state='FL' OR state='GA'
或者使用IN运算符
SELECT * FROM customer
WHERE state IN ('VA', 'FL', 'GA')
- BETWEEN运算符
正如BETWEEN运算符的字面意思,就是要找到满足一个条件区间的数据(区间包括临界值,是闭合区间),比如我想找points大于10 且小于100的用户,除了使用points > 10 AND points < 100
之外,可以使用BETWEEN来表示,如
SELECT * FROM customer
WHERE points BETWEEN 10 AND 100
- LIKE
LINK运算符是按照一定的字符串匹配模式筛选数据,常用的匹配模式有
a. 百分号,百分号表示任意个字符,%y,就表示最后一个字符是y的所有字符串。%y%,表示中间有y字符的字符串
b. 下划线,一个下划线表示一个任意字符,多个下划线就表示对应数量的字符,_y,表示第二个字符是y的字符串,第一个是任意字符的,且只有两个字符构成的字符串
需要满足多个字符串匹配模式或满足多个字符串匹配模式之一的,可以使用AND或者OR来实现,如
SELECT * FROM customer
WHERE last_name LIKE '%field%' OR last_name LIKE '%ted%'
- REGEXP 正则
使用REGEXP是比LIKE更强大的模式匹配,在REGEXP中,
匹配某个字符或者字符串,直接写上需要匹配的字符或者字符串即可
SELECT * FROM customer
WHERE last_name REGEXP 'field'
^ 符号表示字符串开头,即字符串必须以^符号后面的字符串开头
SELECT * FROM customer
WHERE last_name REGEXP '^field'
%符号表示字符串结尾,即字符串必须以^符号前面的字符串结尾
SELECT * FROM customer
WHERE last_name REGEXP 'field%'
|符号表示多个匹配模式,有或运算符的意思,但是,多个匹配模式中,必须有一个匹配上才算满足条件
SELECT * FROM customer
WHERE last_name REGEXP '^field|eg|ei'
上例表示查询以field开头,或者包含eg或者ei的last_name的用户信息
[]符号表示可选字符,匹配上方括号里的字符和方括号外的字符的任意组合即满足筛选条件。如例子中,字符串中有ee或te或ce的字符串即满足要求
SELECT * FROM customer
WHERE last_name REGEXP '[etc]e'
当方括号内字符串较多且为连续字符时,可以使用-表示范围
SELECT * FROM customer
WHERE last_name REGEXP '[a-k]e'
- IS NULL ,IS NOT NULL判断
IS NULL 用于判断数据是空,IS NOT NULL用于判断数据不为空。比如想查询电话号码时NULL的用户
SELECT * FROM customer
WHERE phone IS NULL
- ORDER BY
给SELECT选出来的数据按照指定字段(可以是多个字段)的顺序做排序,可以按照DESC(降序)和AES(升序,默认顺序)排序
SELECT * FROM customer
WHERE phone IS NULL
ORDER BY first_name DESC, last_name
- LIMIT
LIMIT用于限制数据的返回数量,比如
SELECT * FROM customer
WHERE phone IS NULL
LIMIT 3
只会返回前3条phone为NULL的数据
LIMIT还有个偏移量参数,告诉LIMIT从第几条数据开始取,如LIMIT 6, 3
表示从第6条数据为起始,往后取3条,也就是取出第7,8,9条数据
- JOIN(内连接)
有时候一张表的数据并不能满足需求,JOIN可以通过两张表都有的列来连接两张表的数据获取更复杂的结果,ON表示连接两张表数据的筛选条件,比如连接customer表和order表,并筛选出两表customer_id相同的数据:
SELECT * FROM orders
JOIN customers
ON orders.customer_id = customers.customer_id
如果不需要SELECT所有数据,选择某几列的话,需要注意两张表的列重名问题,如果有重名,需要指定表名,列名,此时可以使用别名,给数据表起另一个名字,简化语句,通常在JOIN的时候,也都会使用别名来避免类似问题,如:
SELECT c.customer_id, c.first_name FROM orders o
JOIN customers c
ON o.customer_id = c.customer_id
如果JOIN时,使用的列名两张表时相同的,则可以使用USING简写,如:
SELECT c.customer_id, c.first_name FROM orders o
JOIN customers c
USING(customer_id)
跨数据库连表查询的话,只需要在表名前加上数据库名作为前缀
数据库名.表名
多表连接的话,需要多个JOIN语句,比如:
SELECT c.customer_id, c.first_name FROM orders o
JOIN customers c
USING(customer_id)
JOIN order_status os
ON o.status = os.order_status_id
多个连接条件的话,需要使用AND运算符,比如:
SELECT * FROM order_items oi
JOIN order_item_notes oin
ON oi.order_id = oin.order_id
AND oi.product_id = oin.product_id
- 自连接
同一张表可以跟自己JOIN,以便获取到所需的数据结构 - LEFT JOIN 和 RIGHT JOIN
JOIN只会筛选出符合ON对应的条件的,字段不为空的数据,但有时候,也需要获取数据为空的记录做对比,此时就要用到外链接,内外连接的区别,可以理解成交集和并集的关系。
LEFT JOIN会返回LEFT JOIN左边的表(customers)的全部内容,不管满不满足条件,若左表字段内容在右表中不存在,则为NULL,比如
SELECT
c.customer_id,
c.first_name,
o.order_id
FROM customers c
LEFT JOIN orders o
ON c.customer_id = o.customer_id
在上述语句的执行结果中,可能customers表里的某些用户是没有订单ID的。
RIGHT JOIN就是右表(orders)的信息会全部返回,不管条件满不满足,若右表中内容在左表中不存在,则为NULL(但右表都是有订单的,有订单的,肯定有用户,不会有NULL的数据),比如
SELECT
c.customer_id,
c.first_name,
o.order_id
FROM customers c
RIGHT JOIN orders o
ON c.customer_id = o.customer_id
- 交叉连接
交叉连接会将左表的每一条数据与右表的所有数据组合一次,类似笛卡尔积的效果,一个可能用到交叉连接的情况是,3套衣服,每套衣服有3种颜色,使用交叉连接,就能得到所有的衣服规格,一共9种
SELECT * FROM customers
CROSS JOIN products
- UNION
UNION可以合并两次或者多次查询的结果,需要注意的是,使用UNION时,筛选的列数量必须相同,不能前一个SELECT选了两列,后一个SELECT只选了一列
SELECT first_name
FROM customers
UNION
SELECT name
FROM orders
- INSERT
INSERT用于插入数据,可插入单行或多行,例如:
INSERT INTO customers
VALUES(
DEFAULT,
'John',
'Smith'
'1990-01-01',
NULL,
'address',
'city',
'CA',
DEFAULT
)
其中DEFAULT是使用表的默认值,如果不想写默认值,可以只提供没有默认值的列插入的,有默认值的,MYSQL会自动使用默认值,例如:
INSERT INTO customers (
first_name,
last_name,
birth_date,
address,
city,
state
)
VALUES(
'John',
'Smith'
'1990-01-01',
'address',
'city',
'CA',
)
多行的插入:只需提供多个需要插入的行,用英文逗号分隔即可:
INSERT INTO shippers (name)
VALUES ('Shipper1'),
('Shipper2'),
('Shipper3'),
相关联的多表的插入:碰到A表和B表数据有关联的时候,在A表插入数据后,也需要在B表插入数据,这时候会碰到如何获取A表中新插入数据的ID问题,可以使用MYSQL内建方法LAST_INSERT_ID()获取上一次插入的ID,例如:
INSERT INTO orders(customer_id, order_date, status)
VALUES (1, '2019-01-02', 1);
INSERT INTO order_items
VALUES (LAST_INSERT_ID(), 1, 1, 2,95),
(LAST_INSERT_ID(), 1, 1, 2,95),
- UPDATE
UPDATE语句可以更新记录,可更新一条或者多条,如:
UPDATE invoices
SET
payment_total = invoice_total * 0.5
WHERE client_id = 3
- DELETE
DELETE用于删除表中记录,在不提供WHERE语句的情况下,会删除所有记录,但不会重置ID的起始值。
DELETE FROM invoices
WHERE invoice_id = 1
- 聚合函数
常用聚合函数包括SUM,AVG,MIN,MAX,COUNT,都是字面意思的聚合函数,名字就表明了功能,使用时指明需要统计的列即可,如SUM(payment)
,需要注意的是,聚合函数会跳过内容为空的记录,不会计算空行 - GROUP BY
以SUM函数举例,可以计算某列的总和,但如果需要对每个用户统计与当前用户相关的某列的值的总和时,就需要使用GROUP BY对数据按用户进行分组,再对每组数据求和,以invoices表举例:
SELECT
client_id,
SUM(invoice_total) AS total_sales
FROM invoices
GROUP BY client_id
除了按照用户这个单一条件对数据分组,也可以按多个条件对数据分组,满足多个条件相同的数据分为一组,如:
SELECT
state,
city,
SUM(invoice_total) AS total_sales
FROM invoices
JOIN clients USING(client_id)
GROUP BY state, city
- HAVING
有时在统计时,需要对分组后的数据添加条件,这个时候就不能用WHERE了,只能使用HAVING,HAVING使用在GROUP BY之后
SELECT
client_id,
SUM(invoice_total) AS total_sales
COUNT(*) AS number_of_invoices
FROM invoices
GROUP BY client_id
HAVING totals_sales > 500
通常来说,一个带有分组数据并要排序的数据,SQL语句书写模版是这样的:
SELECT *
FROM xxxx表
WHERE xxxx条件
GROUP BY
HAVING xxx条件
ORDER BY xxxx排序条件
- ROLLUP
ROLLUP 会对查询结果中的所有可以求和的列求和,并显示在最后一行,仅在MYSQL中有效
SELECT
state,
city,
SUM(invoice_total) AS total_sales
FROM invoices i
JOIN clients c USING(client_id)
GROUP BY state, city WITH ROLLUP
- 子查询
先看一个例子:
SELECT *
FROM products
WHERE unit_pirce > (
SELECT unit_price
FROM products
WHERE product_id = 3
)
上述语句是在查找单价大于product_id = 3的商品单价的商品有哪些。在执行时,会首先执行括号内的语句,也就是我们说的子查询语句,然后带着子查询的结果,执行外层查询。
- IN运算符
IN运算符可以判断指定的列的值是否在某个范围内,例子中的查询就是找出在order_items表里的产品信息(语句中的DISTINCT是去重的意思,子查询中查询到的product_id可能会有重复的)
SELECT * FROM products
WHERE product_id IN (
SELECT DISTINCT product_id
FROM order_items
)
- ALL关键字
ALL关键字跟IN用法类似,但不同的是,ALL的判定条件是ALL里面的所有值都满足,才能通过判定,例如:
SELECT *
FROM invoices
WHERE invoice_total > ALL(
SELECT invoice_total
FROM invoices
WHERE client_id = 3
)
上述查询就是在寻找满足大于所有client_id = 3的用户的invoice_total的记录,为了方便理解,可以将上述查询写成
SELECT *
FROM invoices
WHERE invoice_total > ALL(100,300,500)
就是找出所有invoice_total大于100且大与300且大于500的记录
- ANY关键字
ANY关键字与IN类似,都是满足任意一个条件就判定为符合条件的数据,两种方式效果一致,例如:
SELECT *
FROM clients
WHERE client_id IN (
SELECT client_id
FROM invoices
GROUP BY client_id
HAVING COUNT(*) >= 2
)
SELECT *
FROM clients
WHERE client_id = ANY (
SELECT client_id
FROM invoices
GROUP BY client_id
HAVING COUNT(*) >= 2
)
- 相关子查询
相关子查询,就是子查询与外部查询使用了同一张表,这会使得查询速度和资源消耗的增加,但功能很强大。示例中的语句是筛选出每个高于自己所在office的平均工资的员工
SELECT *
FROM employees e
WHERE salary > (
SELECT AVG(salary)
FROM empolyees
WHERE office_id = e.office_id
)
- EXISTS关键字
EXISTS与IN相似,但不同的是,EXISTS不会查询出一组满足条件的结果集,只是给主查询返回当前数据是否满足条件,这在数据量很大的时候,就比IN效率高很多。
SELECT *
FROM clients c
WHERE EXIST (
SELECT client_id
FROM invoices
WHERE client_id = c.client_id
)
SELECT *
FROM clients c
WHERE IN (
SELECT client_id
FROM invoices
WHERE client_id = c.client_id
)
- 子查询应用与SELECT,FROM
子查询不仅仅可以用在WHERE中,也可以用在SELECT,FROM中
SELECT子查询
SELECT
invoice_id,
invoice_total,
(SELECT AVG(invoice_total) FROM invoices AS invoice_average,
invoice_total - (SELECT invoice_average) AS difference
FROM invoices
FROM子查询
SELECT *
FROM (
SELECT
invoice_id,
invoice_total,
(SELECT AVG(invoice_total) FROM invoices AS invoice_average,
invoice_total - (SELECT invoice_average) AS difference
FROM invoices
) AS sales_summary
WHERE total_sales IS NOT NULL
- 创建视图View
View相当于创建了一个查询语句模板,常用的查询语句都可以做成视图,以免写过多的重复语句。创建视图使用CREATE VIEW xxxx AS ....
,xxxx表示VIEW的名称,取一个有意义的名称即可,....表示查询语句,例如:
CREATE VIEW sales_by_client AS
SELECT
c.client_id,
c.name,
SUM(invoice_total) AS total_sales
FROM clients c
JOIN invoices i USING(client_id)
GROUP BY client_id, name
之后,就可以把seales_by_client
当成一张表来使用,表的内容就是VIEW里写的查询的结果
- 更改或删除视图
更新视图可以删除旧视图,再创建新视图 ,或者使用repalace字段,替换视图内容,更推荐repalace的方式
CREATE OR REPALACE VIEW sales_by_client AS
SELECT
c.client_id,
c.name,
SUM(invoice_total) AS total_sales
FROM clients c
JOIN invoices i USING(client_id)
GROUP BY client_id, name
- 可更新表数据的视图
如果视图中的查询使用了如下关键字,则不能更新VIEW内的数据:
DISTINCT
AVG,MIN,MAX,SUM等聚合函数
GROUP BY, HAVING
UNION
否则,就可以使用DELETE,UPDATE,INSERT去更新VIEW内的数据 - WITH OPTION CHECK
在可更新的视图中,如果不想让INSERT或者DELETE等语句让VIEW内的行消失或增加,可以在查询的最后一行,加上WITH OPTION CHECK
,当查询试图去影响VIEW内的行数时,就会报错
- 创建数据库
CREATE DATABASE IF NOT EXIST your_db_name;
- 删除数据库
DROP DATABSE IF EXIST your_db_name;
- 创建表
-- 可以在创建表之前,删除相同的表,如果存在的话,但,慎用
-- DROP TABLE IF EXIST your_table_name;
CREATE TABLE IF NOT EXIST your_table_name
(
customer_id INT PRIMARY KEY AUTO_INCREMENT,
first_name VARCHAR(50) NOT NULL DEFAULT '',
points INT NOT NULL DEFAULT 0 COMMENT 'points of customer',
email VARCHAR(255) NOT NULL UNIQUE
);
- 修改表,可以对表字段增删改
ALTER TABLE customers
ADD last_name VARCHAR(50) NOT NULL AFTER first_name,
ADD city VARCHAR(50) NOT NULL,
DROP points,
MODIFY first_name VARCHAR(50) DEFAULT ''
;
- 创建触发器
触发器可以在满足一定条件时,自动启动,并执行相关语句。使用CREATE TRIGGER
创建触发器
DELIMITER $$
CREATE TRIGGER payments_after_insert --在给触发器命名时,遵照触发条件表,执行时机,触发条件,例如这里在payments表插入数据之后,触发触发器
AFTER INSERT ON payments -- 指明在payments表插入数据后执行,切记不要在触发器中修改payments表,不然触发器会一直被触发,除了AFTER,还有BEFORE,条件检测除了INSERT,还有UPDATE等常用语句
FOR EACH ROW
BEGIN -- 触发器执行的内容
UPDATE invoices
SET payment_total = payment_total + NEW.amount -- NEW表示新增或删除后的新数据,OLD表示新增或删除前的旧数据
WHERE invoice_id = NEW.invoice_id
END $$
DELIMITER ;
- 查看、删除触发器
查看触发器,使用SHOW TRIGGERS
删除触发器,使用DROP TRIGGER IF EXISTS your_trigger_name
- EVENT,事件
EVENT类似于定时任务,MYSQL会有一个后台程序一直查询有没有需要执行的EVENT,但有时,为了节约系统资源,可能会关闭EVENT,可以使用如下命令查看MYSQL中的EVENT开关是否打开:
SHOW VARIABLES LIKE 'event%`
如果没有打开,使用SET命令打开EVENT
SET GLOBAL event_scheduler = ON
创建EVENT使用CREATE EVENT
命令
DELIMITER $$
CREATE EVENT yearly_delete_stale_audit_rows
ON SCHEDULE
EVERY 1 YEAR STARTS '2019-01-01` ENDS '2029-01-01
DO BEGIN
DELETE FROM payments_audit
WHERE action_data < NOW() - INTERVAL 1 YEAR
END $$
DELEMITER ;
只需要在某一时间执行一次的任务,使用AT,定时重复执行的,使用EVERY,定时的时间可以是小时,天,年,或一个其他的时间区间
查询EVENT使用SHOW EVENTS
或按EVENT名字查找SHOW EVENT LIKE 'xxxx'
删除EVENT使用DROP EVENT IF EXISTS your_event_name
修改EVENT使用ALTER EVENT
,用法与CREATE EVENT
一致
暂停EVENT使用ALTER EVENT your_event_name DISABLE
重启EVENT使用ALTER EVENT your_event_name ENABLE
- 创建事务
对于事务,简单的解释,就是执行的内容要么全部成功,要么全部回滚,当没发生过,以保持数据的一致,避免出现一个执行过程需要3步,但只有前两步成功,最后一步失败,导致的数据不完整。
创建事务语句:
START TRANSACTION;
your query
COMMIT;
顺便说一下,DELETE,INSERT,UPDATE,这3个在执行过程中,MYSQL会自动添加一个Transaction来包裹这些语句,所以DELETE,INSERT,UPDATE也是事务的执行。
- 4种隔离级别处理事务的并发问题(文字高能,需要耐心)
在事务执行时,会碰到并发的问题,MYSQL使用隔离级别来应对不同的并发情况。
隔离级别的调整使用SET TRANSACTION ISOLATION LEVEL xxxxx LEVEL
来设置
接下来按隔离级别由低到高来讲解。
a. 读取未提交READ UNCOMMITTED
字面意思,就是读取到未提交到表内的数据。读取未提交是最低级别的隔离,并发效率最高,但也最不安全。有可能出现的情况是,A,B两个SESSION,A需要读取某表中的points数据,B要修改表中的points数据,假设points当前是100,B先执行对points的修改,修改points为500,但在修改并为执行完成,记录到表内的瞬间,A开始了对points的读取,此时A读取到的是500,如果points大于300的用户购买商品打骨折,那么A读取到500后返回给调用者,可能就会对用户的购买进行打折,但B在执行修改points过程中,碰到了意想不到的错误,导致修改失败,或者被人为取消了对points的修改,那么实际上用户的points是100,但却对用户的购买打折了,这就是数据的脏读,将隔离级别提高一个等级,就能避免这样的问题
b. 读取已提交READ COMMITTED
读取已提交的隔离界别下,就不会出现脏读的情况,因为查询只会读取提交到表内的数据,但也有另外的问题。如果一个查询中对同一个字段读取了两次,就可能出现两个不同的值,也就是不一致读的问题。还是刚才的例子,A,B两个SESSION,A需要读取某表中的points数据,B要修改表中的points数据,假设points当前是100,B先执行对points的修改,修改points为500,不同的是,A读取points会读取两次,就有可能出现A第一次读的时候,B还没有提交,读取到的points是100,当A再次读取points时,B已经提交了修改,此时A读到的就是500,就会出现两次读取同一个字段,读出来的数据不一致,要解决这个问题,需要再提升一个隔离级别
c. 可重复读REPEATABLE READ
在可重复读级别下,将不会出现多次读取不一致的情况,对一个字段的读取,将完全独立,类似读取了一份拷贝,不受任何修改的影响。现在可以换个例子了,但还是两个SESSION,A和B,这次A读取points大于100的用户,并送给他们每人1w刀!B修改某一个用户的points,在A读取points大于100的用户的同时,B将一个points小于100的用户的points改为大于100,并在A读取完之前就修改完成,但A并不能读取到B修改的这个用户的最新points,最后A读取的points大于100的用户,也不会包括B修改的这个用户,这就带来另一个问题,幻读,本该在条件范围内的数据,却没有被读取到。
d. 序列化SERIALIZABLE
序列化级别,就像串行一样执行查询,完全避免了并发问题,但效率也最低。
e. 死锁
死锁是两个Transaction互相需要等待对方的条件而造成的一直等待,直到天荒地老的情况。通常发生这种情况时,MYSQL会自动退出Transaction。为了尽可能避免(无法完全避免)死锁,除了祈祷外,就是尽量保证在修改数据时,保持一致的修改顺序,或者尽量减少查询执行的时间。
- 创建、查看索引
索引可以加快某些列数据查询的速度,但也会增加数据库的资源消耗,所以,仅在需要加索引的查询中,创建索引,不需要在建表的时候立刻创建索引,按需创建即可。
创建索引使用语句CREATE INDEX YOUR_INDEX_NAME ON YOUR_TABLE_NAME (column names)
,通常index的命名以idx打头,然后下划线,最后些列名。例如:
CREATE INDEX idx_state ON customers (state)
索引名称可以看出,是对customers表里的state加了索引。
使用SHOW INDEX IN YOUR_TABKE_NAME
语句可以查询指定表的索引,可以看到,在创建表的时候,主键本身就是索引,称为聚集索引,每张表只有一个聚集索引
- 前缀索引
对于字符串类型的列名,可以指定仅为字符串的前N个字符创建索引,而非整个字符串,这样可以节省资源,同时达到索引的效果,这样的索引就是前缀索引。
CREATE INDEX idx_lastname ON customers (last_name(20))
语句中的20,就是指定last_name的前20个字符作为索引,20个字符这个数字是试出来的最少的唯一区分出不同字符的前缀数量,可以使用COUNT
函数,一直替换LEFT函数的第二个参数,直到查询出的结果的数量满足你的要求:
SELECT COUNT(DISTINCT LEFT(last_name, 1)) FROM customers
- 全文索引
全文索引是对于在文章或者博客中需要做快速查询,或做任何对大量内容的快速查找使用,创建全文索引使用CREATE FULLTEXT INDEX
CREATE FULLTEXT INDEX idx_title_body ON posts (title, body);
查询全文索引时,需要使用MATCH
方法指定查询的字段名,AGAINST
指定查询的内容
SELECT *
FROM posts
WHERE MATCH(title, body) AGAINST('content you want to search');
全文索引支持两种模式,自然语言模式(默认模式)和布尔模式,自然语言模式就是类似使用谷歌搜索一样,布尔模式,是可以排出某些指定的内容来搜索。
自然语言模式:
SELECT *
FROM posts
WHERE MATCH(title, body) AGAINST('latest news');
布尔模式:
SELECT *
FROM posts
WHERE MATCH(title, body) AGAINST('latest -news' IN BOOLEAN MODE);
布尔模式下,对于需要去掉的内容前加负号即可,上述布尔模式的查询就是搜索包括latest,但不包括news的内容
- 复合索引
在使用过程中,单一列的索引可能并不能满足要求,通常查询或排序时,大多数情况也是多列查询,所以,为了提升效率,大部分情况,创建的都是复合索引,即对多个列创建索引,例如:
CREATE INDEX idx_lastname_state ON customers (last_name, state);
创建符合索引时,需要注意几点:
a. 将使用频率最高的列放在索引列的前面
b. 将基数高的列放在索引列的前面
c. 将能尽量缩小搜索范围的列放在索引列的前面
- 复合索引的列顺序
总的来说,创建索引时字段的先后顺序,需要根据数据的条数,实际搜索的速度实测来定,使用EXPLAIN
关键字查看查询语句的执行信息,比如:
EXPLAIN SELECT * FROM table
- 索引覆盖
索引覆盖,就是指尽量多的能让索引在更多的查询中发挥作用,索引的字段能覆盖大部分的查询 - 维护索引
重复索引:创建索引时,一定要先查询索引,MYSQL不会阻止创建多个同样的索引,所以为了节省资源,不要创建两个完全一样的索引
多余索引:创建索引时,如果创建的是复合索引,那就不用为这些字段单独创建索引,复合索引包括了复合索引内单个字段的索引 - 当索引无效时
当索引并不能显著提高查询效率时,可以考虑使用union或其他关键字拆分带索引的查询,减少 扫描的行数 - 使用索引排序
在创建索引时,MYSQL会对索引的数据排好序,因此对索引数据使用Order By
的执行效率比较高,如果对没有索引的数据进行排序,MYSQL将会使用外部排序算法对数据排序,这将会很耗资源,除非不得已,尽量不要对未索引的数据排序。查看资源消耗的参考值可以使用SHOW STATUS LIKE 'last_query_cost'
,参考值越高,越消耗资源
- 存储过程
coming soon