前言
通过这篇文章你将收获: 批量插入数据的简单理解、DQL最基础的查询与匹配语句复习。
都是最基础的语法,但是网上整体总结的太少了,而且部分有错。应朋友的要求,将DQL常用命令整理了一份,命令太多了,还没整理完。每一条SQL语句都是在小编自己的数据库中跑过的,也会写出自己对于其中语句的一些看法。本人也是小菜鸟一枚,如果有错误还请留言,感谢各位大佬指正!
先给出总结的xmind导图(未完成):
(右击图片另存为可以保存)
DQL
DQL即Data Query Language数据库查询语言。关键字:SELECT ... FROM ... WHERE。即通过select 等字段从数据库中查询想要的信息,工作中涉及到MySQL的初期都是看表和查询,业务中对数据库最多的操作也是查询,所以DQL的编写很重要。
即使你是软件开发工程师,也要在保证DQL正确的基础上,使查询效率要足够高。即针对已有的表结构和信息,对功能正确的SQL进行适当的优化。
创建表
首先,我们简单地创建一个表tb_name,记录人的id,姓名,性别,年龄,地址;接下来的操作都将对这张表进行。
#创建表 DROP TABLE IF EXISTS tb_name ; CREATE TABLE tb_name ( id INT PRIMARY KEY COMMENT '主键', username VARCHAR(32) NOT NULL COMMENT '姓名', sex VARCHAR(20) NOT NULL COMMENT '性别', age INT NOT NULL COMMENT '年龄', addr VARCHAR(50) NOT NULL COMMENT '地址' , INDEX (username ,sex) );
创建存储过程
到了插入数据环节,因为太懒不想一条条插入,所以写个存储过程帮我们插入大量数据。
什么是存储过程
百度百科:存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象。在数据量特别庞大的情况下利用存储过程能达到倍速的效率提升。
MySQL存储过程有什么注意事项
1.首先,我们在写存储时,DELIMITER ##语句很关键,因为平时为了避免MySQL长事务的引起 (长事务是什么?危害?如何避免?如果忘了请点击链接) ,会默认使用自动提交,即:set autocommit=1;即每一条语句都会自动提交。平时SQL语句都是以';'结尾,表示一句SQL到此结束,然后就自动提交了。而存储过程需要一段语句一起提交,再不想将autocommit改为0时,就需要用到DELIMITER。
DELIMITER是界定符,DELIMITER ## 表示从现在开始,只有遇到“##”才是完整的SQL语句,当然符号可以自己随意定义。
2.MySQL的 declare一定要写在set前面,即在一个小段落里,要先把参数全部定义再开始使用。不明白逻辑,今天被这个坑了,调试了很久(小编的MySQL是5.5.15版本)。
3.MySQL的if语句一定要以end if结尾,大概格式为 if......then...elseif..then..else..then...end if;
4.当一次插入大量数据时,最好分批提交,每500次提交一次,语句为:
IF id%500=0 THEN COMMIT; END IF ;
为什么要分批提交呢,也是避免长事物的发生,怕生成较大的回滚段。长事物问题链接
企业中为什么很少用存储过程
1.不利于维护
假如存储过程出错了,或者业务逻辑变更了;很少定位到数据库中的存储过程中,即排查问题时难度更大,不利于快速迭代的互联网。
2.存储过程会占用空间
业务代码写在普通的程序中,然后统一地被仓库管理,如果是存储过程,直接存储在数据库中,会占用数据库的空间,且非常不利于整体代码的管理。
3.迁移性不强
理由同上,同样是业务代码,如果用存储过程写,迁移的时候需要将数据库的存储过程也进行复制迁移。
存储过程代码:
# MySQL存储过程笔记: # declare一定要写在set前面, # if......then...elseif...else.....end if #批量插入存储过程 DROP PROCEDURE IF EXISTS insert_people; DELIMITER ## CREATE PROCEDURE insert_people(IN start_id INT,IN num INT ) BEGIN DECLARE id INT; DECLARE sex VARCHAR(20) DEFAULT '男'; DECLARE addr VARCHAR(32) DEFAULT '北京'; DECLARE username VARCHAR(32); DECLARE age INT ; DECLARE username_string VARCHAR(50); DECLARE rand_num1 INT; DECLARE rand_num2 INT; DECLARE count_num INT DEFAULT num + 1; SET id = start_id; SET username_string ='张三不是丰好好学数据库您将受益无穷%_'; REPEAT SET rand_num1 = FLOOR(1+RAND()*19); SET rand_num2 = FLOOR(1+RAND()*19); SET username = CONCAT(SUBSTRING(username_string,rand_num1,1),MID(username_string,rand_num2,1)); IF id%3=0 THEN SET addr='广州';SET sex='未知'; ELSEIF id%2=0 THEN SET addr='北京';SET sex='女';ELSE SET addr='广州';SET sex='男'; END IF ; SET age = FLOOR(1+RAND()*99); INSERT IGNORE INTO tb_name VALUES(id,username,sex,age,addr); SET id = id + 1; IF id%500=0 THEN COMMIT; END IF ; UNTIL id = count_num END REPEAT; COMMIT; END ## DELIMITER ;
基础的查询
用*查所有数据
SELECT * FROM tb_name;
用全部字段查所有数据
SELECT id,username,sex,age,addr FROM tb_name;
查所有的年龄信息
select age FROM tb_name;
查所有的性别
SELECT sex FROM tb_name;
深入思考:select * 和select 字段平时该如何取舍?
从效率上考虑
如果是查询全部信息,功能和查询效率上两者没有差别
2. 如果只是查询部分信息,且该信息有索引的话,select字段效率更高
从可读性考虑
1.select字段能更清楚地告诉我们,这个表到底有什么字段,更方便进行处理;而select * 不行
从维护上考虑
1.如果表结构有更改,比如属性名修改了、属性新增或删除了,select *可以避免相应的维护;而select 字段需要程序员手动维护
从便捷上考虑
1.select * 只需要敲一个*,即可查询所有信息;而select 字段需要我们对着表复制粘贴,更加复杂麻烦
对结果去重
可以用distinct对结果集合去重,即重复的不再显示;相应的,查询性能会降低。
-- 显示全部年龄,重复的不再显示 SELECT DISTINCT age FROM tb_name; -- 显示全部性别,重复的不再显示 SELECT DISTINCT sex FROM tb_name;
where条件筛选
like通配符
1.直接使用like语句,等同于=
2.like与通配符一起使用,可以模糊匹配,即模糊查询
%(百分号)匹配零个或者多个任意字符
_(下划线)匹配一个任意字符
3.like与通配符和转义符使用,在通配符前加/ 后面加escape ’/‘;可以查询通配符相关的信息
#like通配符 SELECT * FROM tb_name WHERE username LIKE'张%'; SELECT * FROM tb_name WHERE username LIKE '%张%'; SELECT * FROM tb_name WHERE username LIKE'%张'; SELECT * FROM tb_name WHERE username LIKE '张_'; SELECT * FROM tb_name WHERE username LIKE '张_丰'; SELECT * FROM tb_name WHERE username LIKE '_三'; SELECT * FROM tb_name WHERE addr LIKE '广州'; SELECT * FROM tb_name WHERE addr NOT LIKE '广州'; SELECT * FROM tb_name WHERE username LIKE '%/%' ESCAPE '/'; SELECT * FROM tb_name WHERE username LIKE '%/_' ESCAPE '/';
比较运算符
MySQL的比较运算符包括:大于、小于、等于、不等于、大于等于、小于等于。
#比较运算符 SELECT * FROM tb_name WHERE sex = '男'; SELECT * FROM tb_name WHERE sex != '未知'; SELECT * FROM tb_name WHERE age > 9; SELECT * FROM tb_name WHERE age >= 9; SELECT * FROM tb_name WHERE age < 9; SELECT * FROM tb_name WHERE age <= 9;
逻辑运算符
逻辑运算符是用来拼接其他条件的,用and或者or来连接两个条件。and表示逻辑与,or表示逻辑或。
#逻辑运算符 SELECT * FROM tb_name WHERE age >= 3 AND age <= 9; SELECT * FROM tb_name WHERE age >= 80 OR age <= 18; SELECT * FROM tb_name WHERE sex = '男' AND age <= 9; SELECT * FROM tb_name WHERE sex = '男' OR age <= 9; SELECT * FROM tb_name WHERE sex ='男' OR sex ='女'; SELECT * FROM tb_name WHERE addr = '广州' OR addr='北京';
IN
IN字段指定多个值查询,格式为:field IN (value1,value2,value3,....);即在小括号里的都会匹配上。
#IN字段指定多个值查询 SELECT * FROM tb_name WHERE sex IN ('男','女'); SELECT * FROM tb_name WHERE addr IN ('广州','北京'); SELECT * FROM tb_name WHERE age IN (1,3,5,7,9);
BETWEEN AND
BETWEEN AND 区间查询,格式为:field BETWEEN value1 AND value2;MySQL的区间查询为两边都包含,以下语句等同于age >= 3 and age <=9 ;
#BETWEEN AND 区间查询 SELECT * FROM tb_name WHERE age BETWEEN 3 AND 9;
where优化建议
从数据库怎么存数据来思考,假设我们是优化器,会给执行器下达怎样的执行命令呢。可以把优化器看做一个记忆力超群,效率超高,但技能有限的孩子。
首先,我们可以把索引看成目录,通过索引,能清楚的知道每棵索引树上有多少行数据和包含什么字段信息。信息一定准确,行数是抽样调查得到的不精确值。
现在别人要让我们来查询时,我们会优先考虑能使用到的索引,通过查询字段是否在索引树上得知;然后查看字段是否为等值查询、范围查询,能清楚地知道对应每棵树上的数据行数。如果返回值也在树上的话,我们能很开心地快速返回。即覆盖索引(条件覆盖) 。
如果是非等值查询,如<> 或 != 或 is null 或 is not null 或 not in,则超出了我们优化器的能力范围,偶尔不优化。
即使有时候是in 和 or,优化器也可能会觉得任务过重而直接罢工选择不优化。如: in(1,2,3,4,5,6,7,8,9,10,11,12),得一个个去对应,优化器会觉得好麻烦,直接不干了。连续的值应尽量使用>= and <= 或者 between and,明确地给优化器说,你看,是一个范围,不难查。
优化器小屁孩经常罢工,我们就让他长点技能,即最左前缀匹配和索引条件下推(MySQL5.6引入) 。这样,他的效率又高起来了。
全部SQL代码:
#创建表 DROP TABLE IF EXISTS tb_name ; CREATE TABLE tb_name ( id INT PRIMARY KEY COMMENT '主键', username VARCHAR(32) NOT NULL COMMENT '姓名', sex VARCHAR(20) NOT NULL COMMENT '性别', age INT NOT NULL COMMENT '年龄', addr VARCHAR(50) NOT NULL COMMENT '地址' , INDEX (username ,sex) ); # MySQL存储过程笔记: # declare一定要写在set前面, # if......then...elseif...else.....end if #批量插入存储过程 DROP PROCEDURE IF EXISTS insert_people; DELIMITER ## CREATE PROCEDURE insert_people(IN start_id INT,IN num INT ) BEGIN DECLARE id INT; DECLARE sex VARCHAR(20) DEFAULT '男'; DECLARE addr VARCHAR(32) DEFAULT '北京'; DECLARE username VARCHAR(32); DECLARE age INT ; DECLARE username_string VARCHAR(50); DECLARE rand_num1 INT; DECLARE rand_num2 INT; DECLARE count_num INT DEFAULT num + 1; SET id = start_id; SET username_string ='张三不是丰好好学数据库您将受益无穷%_'; REPEAT SET rand_num1 = FLOOR(1+RAND()*19); SET rand_num2 = FLOOR(1+RAND()*19); SET username = CONCAT(SUBSTRING(username_string,rand_num1,1),MID(username_string,rand_num2,1)); IF id%3=0 THEN SET addr='广州';SET sex='未知'; ELSEIF id%2=0 THEN SET addr='北京';SET sex='女';ELSE SET addr='广州';SET sex='男'; END IF ; SET age = FLOOR(1+RAND()*99); INSERT IGNORE INTO tb_name VALUES(id,username,sex,age,addr); SET id = id + 1; IF id%500=0 THEN COMMIT; END IF ; UNTIL id = count_num END REPEAT; COMMIT; END ## DELIMITER ; #清空表 TRUNCATE tb_name ; #批量插入 CALL insert_people(1,2000); #插入特定 REPLACE INTO tb_name VALUES(201,'张三','男',99,'四川'),(202,'张三丰','男',99,'四川'); #####################################准备工作完成############################### SHOW INDEX FROM tb_name ; #查所有 SELECT * FROM tb_name; SELECT id,username,sex,age,addr FROM tb_name; SELECT age FROM tb_name; SELECT sex FROM tb_name; #去重 SELECT DISTINCT age FROM tb_name; SELECT DISTINCT sex FROM tb_name; ########################################where语句############################### #like通配符 SELECT * FROM tb_name WHERE username LIKE'张%'; SELECT * FROM tb_name WHERE username LIKE '%张%'; SELECT * FROM tb_name WHERE username LIKE'%张'; SELECT * FROM tb_name WHERE username LIKE '张_'; SELECT * FROM tb_name WHERE username LIKE '张_丰'; SELECT * FROM tb_name WHERE username LIKE '_三'; SELECT * FROM tb_name WHERE addr LIKE '广州'; SELECT * FROM tb_name WHERE addr NOT LIKE '广州'; SELECT * FROM tb_name WHERE username LIKE '%/%' ESCAPE '/'; SELECT * FROM tb_name WHERE username LIKE '%/_' ESCAPE '/'; #比较运算符 SELECT * FROM tb_name WHERE sex = '男'; SELECT * FROM tb_name WHERE sex != '未知'; SELECT * FROM tb_name WHERE age > 9; SELECT * FROM tb_name WHERE age >= 9; SELECT * FROM tb_name WHERE age < 9; SELECT * FROM tb_name WHERE age <= 9; #BETWEEN AND 区间查询 SELECT * FROM tb_name WHERE age BETWEEN 3 AND 9; #逻辑运算符 SELECT * FROM tb_name WHERE age >= 3 AND age <= 9; SELECT * FROM tb_name WHERE age >= 80 OR age <= 18; SELECT * FROM tb_name WHERE sex = '男' AND age <= 9; SELECT * FROM tb_name WHERE sex = '男' OR age <= 9; SELECT * FROM tb_name WHERE sex ='男' OR sex ='女'; SELECT * FROM tb_name WHERE addr = '广州' OR addr='北京'; #IN字段指定多个值查询 SELECT * FROM tb_name WHERE sex IN ('男','女'); SELECT * FROM tb_name WHERE addr IN ('广州','北京'); SELECT * FROM tb_name WHERE age IN (1,3,5,7,9);