系统参数系列
show table status where name = "film": 显示某个表的属性
show status : 显示mysql状态
show processlist : 显示mysql连接状态
show index from user : 显示user表索引
show profiles : 显示查询执行时间状况,使用前先设置参数 set profiling=1;
show profile for query 1: 查看第一条sql的具体执行情况
PAGER cat > /dev/null : 将输出转到垃圾黑洞
TRUNCATE TABLE: 清空table所有数据并重置自增主键的值
SELECT * INTO OUTFILE 'out.txt' FROM user : 将user表的数据选择出来以后导入到out.txt文件.
Explain: 解析SQL语句的执行计划
EXPLAIN SELECT * FROM USER\G;
\G 表示输出EXPLAIN树
AUTOCOMMIT变量
AUTOCOMMIT变量表示是否自动提交事务,MySQL中的每一条单独的查询都是一个单独的事务
默认是AUTOCOMMIT=1时,如下是等价的
UPDATE user SET age = 10 WHERE user_id = 1;
与
BEGIN; UPDATE user SET age = 10 WHERE user_id = 1; COMMIT;
而如果SET AUTOCOMMIT=0,则我们执行的任何一条SQL都相当于在一个未提交的事务中,我们需要手动执行COMMIT或者说ROLLBACK.
JOIN 系列
MySQL中 cross join, join, inner join或者不加join都是等效的,如下
SELECT * FROM USER,BOOK;
SELECT * FROM USER CROSS JOIN BOOK;
SELECT * FROM USER JOIN BOOK;
SELECT * FROM USER INNER JOIN BOOK;
另外,有JOIN里的ON和不加JOIN的WHERE等效如下:
SELECT * FROM USER JOIN BOOK ON USER.USERID = BOOK.USERID;
SELECT * FROM USER,BOOK WHERE USER.USERID = BOOK.USERID;
SELECT * FROM USER,BOOK WHERE USING(USERID);
LEFT JOIN, RIGHT JOIN
SELECT * FROM USER LEFT JOIN BOOK ON USER.USERID = BOOK.USERID;
等效于
SELECT * FROM BOOK RIGHT JOIN USER ON BOOK.USERID = USER.USERID;
这两句的意思是将左表的每条记录与右表中的每条记录相连,如果右表中存在的则显示出来,不存在的用NULL表示,如下图
CREATE 系列
create database student character set = utf8 collate = utf8_general_ci; create table student ( stu_id int primary key not null auto_increment comment "主键id", stu_name varchar(5) comment "名称", stu_gender enum("男", "女") comment "性别", stu_dob date comment "出生年月", stu_natplace varchar(5) comment "籍贯", stu_depa varchar(5) comment "系", stu_intro blob comment "简介", stu_photo blob comment "照片", index stu_inx using btree (stu_name, stu_depa) ) engine = myisam, character set utf8, collate utf8_general_ci, comment = "学生信息表"; create table score ( stu_id int not null comment "学生id", cs_id int not null comment "课程id", score float not null comment "成绩", index stu_course_inx using btree (stu_id, cs_id) ) engine = myisam, character set utf8, collate utf8_general_ci, comment = "成绩表"; create table course ( cs_id int primary key not null auto_increment comment "主键id", cs_no char(8) not null unique comment "课程号", cs_credit float not null comment "学分", cs_hours tinyint not null comment "学时", cs_avg_grade float comment "平均分", cs_total_grade float comment "总分", index cs_no_inx using btree (cs_no) ) engine = myisam, character set utf8, collate utf8_general_ci, comment = "课程表";
建表是,key和index是同义词,都是索引的的意思
LIMIT的使用
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。
UNION: 合并两个结果集并过滤掉重复内容
UNION ALL: 合并两个结果集,结果集可能有重复
使用UNION要注意的是两个结果集的SELECT项必须是相同的
(select actor_id from actor) union (select actor_id from film_actor) (select actor_id from actor) union all (select actor_id from film_actor)
ALTER系列
修改表名: ALTER TABLE USER RENAME TO USERTEMP;
添加列: ALTER TALBE USER ADD COLUMN AGE INT;
删除列: ALTER TABLE USER DROP COLUMN AGE;
修改列: ALTER TABLE USER CHANGE AGE BIRTH INT;
修改列属性: ALTER TABLE USER MODIFY AGE TINYINT;
添加主键: ALTER TABLE USER ADD PRIMARY KEY(USERID);
添加唯一索引: ALTER TABLE USER ADD UNIQUE (USERID);
添加单列索引或联合索引(BTree索引): ALTER TABLE USER ADD INDEX index_name (column1, column2, column3);
添加全文索引:ALTER TABLE USER ADD FULLTEXT (column) ; #仅限于MyISAM
删除索引: ALTER TABLE USER DROP INDEX index_name;
添加外键: ALTER TABLE USER ADD CONSTRAINT (CONSTRAINT_NAME) FOREIGN KEY (BOOKID) REFERENCES BOOK(BOOKID) ON UPDATE/DELETE RESTRICT/CASCADE;
此处注意 ON UPDATE/DELETE RESTRICT/CASCADE;
如果是 update restrict,则如果user表存在有bookid = n时,book表的这个bookid = n的行,不能更新bookid
例如,user表存在 userid = 1, bookid = 2的行,就不能执行 update book set bookid = 3 where bookid = 2;
如果是 update cascade,那么user表如果存在bookid = n, 当book表的bookid = n 这行被修改为bookid = x时,user表里的bookid = n 也会变为bookid = x
例如,user表存在userid = 1, bookid = 2, 执行 update book set bookid = 3 where bookid = 2, 则user表里的 bookid = 2 都会变成 bookid = 3;
如果是 delete restrict,那么如果user表里存在userid = 1, bookid = 2的行,就不能删除 book 表里bookid = 2的行
如果是 delete cascade,那么如果 user 表里存在 userid = 1, bookid = 2的行, 此时如果删除book表里 bookid = 2 的行,则user表里所有bookid=2的行也会被删除
删除外键: ALTER TABLE USER DROP FOREIGN KEY BOOKID;
临时表
当前数据库连接有效,数据库连接结束时自动drop
当临时表名和已存在表名一样时,已存在表会被隐藏,直到临时表被drop
临时表不能被重复打开,也即是说一条SQL查询不能出现两次临时表
1.创建
(1)CREATE TEMPORARY TABLE test_temp (id INT NOT NULL, age int);
(2)CREATE TEMPORARY TABLE test_tem SELECT * FROM user;
2.使用
SELECT * FROM test_temp;
3.删除
DROP TABLE test_temp;
日期函数
1.获取时间: curtime() 输出:23:05:05
2.获取日期: curdate() 输出:2012-10-09
3.获取日期和时间: now(), sysdate() 输出 2012-10-09 23:05:05
4.日期比较(不包括时间)函数: datediff("2010-10-10 20:10:11", now()) = 0 表示当前日期(不包括时间)等于 "2010-10-10"
datediff的参数可以使datetime也可以是date,如果参数1小于参数2返回负数,相等返回0,否则返回整数,数值为天数的差值
5.时间比较,直接用大于小于号
6.返回日期的年份: SELECT YEAR('98-10-11'), 返回 1998
7.UNIX_TIMESTAMP('2010-10-10 20:10:00'): 将日期时间转换成时间戳
对于有联合索引(a, b)的表,如果执行以下查询
SELECT a FROM tb1 WHERE b BETWEEN 2 AND 3
即使b的范围很小,也需要进行全表扫描,因为没有用到前缀索引a,而Oracle则可以松散索引扫描达到高效查询
使用LIMIT来提高MIN()的效率
1. SELECT MIN(actor_id) FROM sakila.actor WHERE first_name = "PENELOPE"; 2. SELECT actor_id FROM sakila.actor WHERE first_name = "PENELOPE" LIMIT 1;
此处由于first_name没有索引,所以会对actor表进行全表扫描,第一句SQL取出所有记录.而由于actor_id是主键,它的索引是按顺序排列的,使用limit取到的第一条记录肯定就是MIN的值,所以它的效率高,但是这样失去了SQL可以表达出来的查询意义,这是效率和原则之间的权衡.
COUNT(expr): 统计所有expr不为null的行的行数,注意一下expr为假,并不代表为NULL,只有当expr确实返回NULL时,才是NULL
COUNT(col): 统计col列的非NULL结果数
COUNT(*): 统计结果集行数
*对于MyISAM来说,COUNT(*)且没有任何WHERE条件是非常快的,因为它可以直接从存储引擎获得一个表的行数
可以利用这点来优化COUNT查询,如下
SELECT COUNT(*) FROM world.city WHERE id >5;
这个语句需要扫描表中4000+行数据,而如果改成下面这种
SELECT (SELECT COUNT(*) FROM world.city) - COUNT(*) FROM world.city WHERE id <= 5;
这个语句仅仅扫描6条记录就可以得出结果,这是对于MyISAM而言,其他数据库引擎则不是这样
CASE ... WHEN ... 与 IF
统计不同颜色的商品数量
SELECT SUM(IF(color='blue', 1, 0)) AS blue, SUM(IF(color='red', 1, 0)) AS red FROM items; SELECT SUM(CASE WHEN color='blue' THEN 1 ELSE 0 END) AS blue, SUM(CASE WHEN color='red' THEN 1 ELSE 0 END) AS red FROM items;
也可以用COUNT()实现
SELECT COUNT(color='blue' OR NULL) as blue, COUNT(color='red' OR NULL) as red FROM items
GROUP BY 效率优化
应该尽量使GOURP BY的COL拥有索引,这样可以降低临时表和filesort的使用
select actor.first_name, actor.last_name, count(*) from film_actor inner join actor USING(actor_id) group by actor.first_name, actor.last_name;
explain分析
1 | SIMPLE | actor | ALL | PRIMARY | 200 | Using temporary; Using filesort | |||
1 | SIMPLE | film_actor | ref | PRIMARY | PRIMARY | 2 | sakila.actor.actor_id | 13 | Using index |
改为
select actor.first_name, actor.last_name, count(*) from film_actor inner join actor USING(actor_id) group by actor.actor_id;
explain分析
1 | SIMPLE | actor | index | PRIMARY,idx_actor_last_name | PRIMARY | 2 | 200 | ||
1 | SIMPLE | film_actor | ref | PRIMARY | PRIMARY | 2 | sakila.actor.actor_id | 13 | Using index |
可见这两个查询的效率差异是很大的,前者使用了临时表和filesort做全表扫描,而后者只是做了一次索引覆盖扫描
此外,GROUP BY默认会按照GROUP BY的列排序,如果不需要这个排序,可以ORDER BY NULL,或者使用DESC和ASC指定排序顺序
GROUP_CONCAT 与 GROUP BY 的配合
我们知道GROUP_BY(COL)会将COL列相同的值得行合在一起,这样就会损失其他列的数据
如果要获取所有其他列的数据,则可以使用GROUP_CONCAT, 它可以将丢失的这些列值使用 ", " 拼接起来
例如
数据库数据
直接使用group by,会损失一些password
使用 group_concat 拼接 password
LIMIT的优化
考察下面的SQL语句,观察一下film表的index
film | 0 | PRIMARY | 1 | film_id | A | 1000 | BTREE | |||||
film | 1 | idx_title | 1 | title | A | 1000 | BTREE | |||||
film | 1 | idx_fk_language_id | 1 | language_id | A | 2 | BTREE | |||||
film | 1 | idx_fk_original_language_id | 1 | original_language_id | A | 2 | YES | BTREE |
select film_id, description from film order by title limit 50, 5
EXPLAIN结果
1 | SIMPLE | film | ALL | 1000 | Using filesort |
可知idx_title为辅助索引,由于innodb的辅助索引只记下了主键索引而没有其他内容,所以当我们同时查询film_id和desciption的时候,就无法使用title索引进行排序了,所以改成如下写法,做一个"延迟关联"
select film_id, description from film inner join ( select film_id from film order by title limit 50, 5 ) as tb using (film_id);
EXPLAIN
1 | PRIMARY | <derived2> | ALL | 55 | |||||
1 | PRIMARY | film | eq_ref | PRIMARY | PRIMARY | 2 | tb.film_id | 1 | |
2 | DERIVED | film | index | idx_title | 767 | 1000 | Using index |
这种写法先产生子查询衍生表,查询的时候使用了title索引做索引覆盖扫描,然后再用外表与衍生表做联接,此时使用了主键索引,这种效率可以提高很多,对比如下
44 | 0.00152075 | select film_id, description from film order by title limit 50, 5 |
48 | 0.00040575 | select film_id, description from film inner join ( select film_id from film order by title limit 50, 5 ) as tb using (film_id) |
使用UNION与变量来做短路查询
select greatest(@found := -1, actor_id) as id, 'actor' from actor where actor_id = 1 UNION ALL select actor_id, 'film_actor' from film_actor where actor_id = 1 and @found is null UNION ALL select 1, 'reset' from DUAL where (@found := null) is not null
这个查询的效果是先查询actor表,当actor表查询得到记录时,@found变量会被赋值-1.这样第二个查询因为@found is null就不会被执行,最后再将@found重置为null
FOR UPDATE 的替代方案
如果使用SELECT ... FOR UPDATE会对记录加上行锁,在事务的后续操作过程中,可能会造成很多堵塞
一个替代方案就是不用FOR UPDATE,而改用一个标志位来表示记录正在被处理,例如
SET AUTOCOMMIT=1; COMMIT; UPDATE unsent_emails SET status='claimed', owner=CONNECTION_ID() WHERE owner=0 AND status='unsent' LIMIT 10; SET AUTOCOMMIT=0; SELECT id FROM unsent_emails WHERE owner=CONNECTION_ID() AND status='claimed';
那么当前连接线程就可以在后续造作中没有顾虑的修改这些记录了,而不需要加锁,不会造成阻塞.
LENGTH(), CHAR_LEGNTH(): 这两个函数用来计算字符串的长度,其中LENGTH()统计的字节数,CHAR_LEGNTH()是字符数
例如 @name := '中国北京' @name以UTF8编码
LENGTH(@name) 是 12
CHAR_LENGTH(@name) 是 4
substring_index()的使用
substring_index(str, delimiter, count) 可以从左开始计算delimiter在str中出现的次数,当出现次数达到count次时,如果count为正数,返回delimiter左边的字符串,如果count为负数,则返回delimiter右边的字符串,例子: