MySQL

mysql事务:begin、rollback、commit
MYSQL的事务处理主要有两种方法。
1、用begin,rollback,commit来实现
begin 开始一个事务
rollback 事务回滚
commit  事务确认

直接用set来改变mysql的自动提交模式
MYSQL默认是自动提交的,也就是你提交一个QUERY,它就直接执行!我们可以通过
set autocommit=0   禁止自动提交
set autocommit=1 开启自动提交
来实现事务的处理。
但注意当你用 set autocommit=0 的时候,你以后所有的SQL都将做为事务处理,直到你用commit确认或rollback结束,注意当你结束这个事务的同时也开启了个新的事务!按第一种方法只将当前的作为一个事务!
个人推荐使用第一种方法!
MYSQL中只有INNODB和BDB类型的数据表才能支持事务处理!其他的类型是不支持的!(切记!)



create table color1(
    nid int,
    name char(16) not null);

insert into color1 (nid,name) values (1,"red"),(2,"blue");  
#另一个mysql可以看到插入的结果;
begin;
insert into color1 (nid,name) values (3,"green"),(4,"yellow");
commit;
#未commit前,本client可以查到配置结果,但是其他client查不到结果;此时client因意外退出,mysql会自动回滚
#结果不符合预期,那么执行 rollback操作来回滚事物
mysql事务:begin、rollback、commit
mysql数据库备份与还原
=========================
#备份数据库
mysqldump -h127.0.0.1 -uroot -p123456 库 >lianjia.sql
mysqldump -h127.0.0.1 -uroot -p123456 lianjia >lianjia.sql
#备份数据库表
mysqldump -h127.0.0.1 -uroot -p123456 库 表 >color1.sql
mysqldump -h127.0.0.1 -uroot -p123456 lianjia color1 >color1.sql

#删除了部分数据
delete from color1 where name = "green";
drop table color1;
#导入数据
mysql -h127.0.0.1 -uroot -p123456 库 < color1.sql #导出时可以导出表,导入时直接选择库即可
mysql -h127.0.0.1 -uroot -p123456 lianjia < color1.sql
    #这一步相当于还原,而不是新增操作
    #删除表,也可以直接按上面的操作,直接导入
mysql数据库备份与还原

1.连接客户端

客户端连接(启动客户端)
    将mysql服务端制作成一个Windows服务
    1.CMD命令:net start mysql
    net stop mysql 
    2.或者通过任务管理器打开:任务管理器-服务-服务-找到mysql
    3.win启动:C:\mysql-5.7.16-winx64\mysql-5.7.16-winx64\mysql
    4.mysql.server start #linux启动
        /etc/init.d/mysql start 

    客户端连接(用户登录)
        C:\mysql-5.7.16-winx64\mysql-5.7.16-winx64\mysql -u root -p 

MySQL初始化
cd c:\mysql-5.7.16-winx64\bin
先创建一个data文件夹;
cmd进入bin文件夹后,
1.mysqld --initialize-insecure#默认初始化命令,初始化了一个root用户,没有密码
2.mysqld #启动服务器端

再开一个cmd作为用户端,进入bin文件夹
Mysql -u root -p 连接至客户端



便捷操作方式一:增加环境变量

便捷操作方式二:将mysql服务端制作成一个windows服务
C:\Windows\System32>c:\mysql-5.7.21-winx64\bin\mysqld --install

Net start mysql
Net stop mysql
或者通过任务管理器打开win服务,效果等同


mysql -u username -h 127.0.0.1 -p#登录时,指定服务器ip
连接客户端、制作windows服务

 2.数据库操作

1、显示数据库
show databases;#显示数据库

2、创建数据库
create database 数据库名称 default charset utf8 collate utf8_general_ci;#创建数据库
create database 数据库名称 default character set gbk collate gbk_chinese_ci;

3、使用数据库
use 数据库名称;  #使用数据库

4、用户管理
create user '用户名'@'IP地址' identified by '密码';       #创建用户
drop user '用户名'@'IP地址';                              #删除用户
rename user '用户名'@'IP地址'; to '新用户名'@'IP地址';    #修改用户
set password for '用户名'@'IP地址' = Password('新密码');  #修改密码
PS:用户权限相关数据保存在mysql数据库的user表中,所以也可以直接对其进行操作(不建议)

5、授权管理
show grants for '用户'@'IP地址'                  #查看权限
grant  权限 on 数据库.表 to   '用户'@'IP地址'      #授权
revoke 权限 on 数据库.表 from '用户'@'IP地址'      #取消权限

通配符%,all privileges除grant外的所有权限,select仅查权限,select,insert查和插入权限
创建用户时,指定用户登录地址使用通配符%时,需要加上双引号“”


用户名@IP地址         用户只能在改IP下才能访问
用户名@192.168.1.%   用户只能在改IP段下才能访问(通配符%表示任意)
用户名@%             用户可以再任意IP下访问(默认IP地址为%)
grant all privileges on db1.tb1 TO '用户名'@'IP'
grant select on db1.* TO '用户名'@'IP'
grant select,insert on *.* TO '用户名'@'IP'
revoke select on db1.tb1 from '用户名'@'IP'

忘记密码
1启动免授权服务端
mysqld --skip-grant-tables
2客户端
mysql -u root -p
3修改用户名密码
update mysql.user set authentication_string=password('666') where user='root';
flush privileges;
数据库操作

 3.数据表操作

是否可空,null表示空,非字符串
默认值,创建列时可以指定默认值,当插入数据时如果未主动设置,则自动添加默认值
自增,如果为某列设置自增列,插入数据时无需设置此列,默认将自增(表中只能有一个自增列)
    注意:1、对于自增列,必须是索引(含主键)。2、对于自增可以设置步长和起始值
主键,一种特殊的唯一索引,不允许有空值,如果主键使用单个列,则它的值必须唯一,如果是多列,则其组合必须唯一。
外键,一个特殊的索引,只能是指定内容

create table 表名(
    列名  类型  是否可以为空,
    列名  类型  是否可以为空
)ENGINE=InnoDB DEFAULT CHARSET=utf8

create table 表名(                             
    列名 int not null auto_increment primary key,    
    列名 varchar(20) not null defalut 2,
    列名 int default 888)engine = innodb default charset=utf8;
#自增可以设置步长和起始值


creat table color(
    nid int not null primary key,
    name char(16) not null);
create table fruit(
    nid int not null primary key,
    smt char(32) null ,
    color_id int not null,
    constraint fk_cc foreign key (color_id) references color(nid));设置外键

    
    
Show tables;
Desc  表名;
drop table 表名;   删除表
delete from 表名;  清空表,自增不清除
truncate table 表名;清空表,自增清除,      貌似效率更高?


添加列:alter table 表名 add 列名 类型
删除列:alter table 表名 drop column 列名
修改列:alter table 表名 modify column 列名 类型;  -- 类型   #不修改列名,修改其他
        alter table 表名 change 原列名 新列名 类型; -- 列名,类型#包含修改列名
  
添加主键:alter table 表名 add primary key(列名);
删除主键:alter table 表名 drop primary key;
          alter table 表名  modify  列名 int, drop primary key;
  
添加外键:alter table 从表 add constraint 外键名称(形如:FK_从表_主表) foreign key 从表(外键字段) references 主表(主键字段);
删除外键:alter table 表名 drop foreign key 外键名称


  
修改默认值:alter table testalter_tbl alter i SET DEFAULT 1000;
删除默认值:alter table testalter_tbl alter i DROP DEFAULT;


MySQL的数据类型大致分为:数值、时间和字符串
数据类型:
    二进制数:bit
    整数:tinyint、int、bigint、 
    小数:decimal(准确)、float(不准确)、double(准确度比float高)
    字符串:char(定长、占空间大、查找快)
        varchar(不定长,省空间,查找慢)
        text、mediumtext、longtext
    枚举类型enum(创建时,设置enum值,多选一)
    集合类型set(创建时,设置set值,多选N)
    DATE:YYYY-MM-DD(1000-01-01/9999-12-31)
        TIME:HH:MM:SS('-838:59:59'/'838:59:59')
        YEAR:YYYY(1901/2155)
        DATETIME:YYYY-MM-DD HH:MM:SS(1000-01-01 00:00:00/9999-12-31 23:59:59    Y)
        TIMESTAMP:YYYYMMDD HHMMSS(1970-01-01 00:00:00/2037 年某时)
数据表操作

 4.表内容基本操作

增
insert into 表 (列名,列名...) values (值,值,值...)
insert into 表 (列名,列名...) values (值,值,值...),(值,值,值...)
insert into 表 (列名,列名...) select (列名,列名...) from#将另一张表的内容直接插入表中
删
delete from 表
delete from 表 where id=1 and name='alex'
改
update 表 set name = 'alex' where id>1
查
desc tb1;#查看表中的所有列名
select * from 表
select * from 表 where id > 1
select nid,name,gender as gg from 表 where id > 1

其他(与增删改查配合使用)
a、条件
    select * from 表 where id > 1 and name != 'alex' and num = 12;
    select * from 表 where id between 5 and 16;
    select * from 表 where id in (11,22,33)
    select * from 表 where id not in (11,22,33)
    select * from 表 where id in (select nid from 表)
 
b、通配符
    select * from 表 where name like 'ale%'  - ale开头的所有(多个字符串)
    select * from 表 where name like 'ale_'  - ale开头的所有(一个字符)
 
c、限制(分页)
    select * from 表 limit 5;            - 前5行
    select * from 表 limit 4,5;          - 从第4行开始的5行
    select * from 表 limit 5 offset 4    - 从第4行开始的5行  效果同上一条,推荐使用这个
 
d、排序
    select * from 表 order by 列 asc              - 根据 “列” 从小到大排列
    select * from 表 order by 列 desc             - 根据 “列” 从大到小排列
    select * from 表 order by 列1 desc,列2 asc    - 根据 “列1” 从大到小排列,如果相同则按列2从小到大排序
 
e、分组
    select num from 表 group by num
    select num,nid from 表 group by num,nid
    select num,nid from 表  where nid > 10 group by num,nid order nid desc
    select num,nid,count(*),sum(score),max(score),min(score) from 表 group by num,nid
    select num from 表 group by num having max(id) > 10#根据聚合函数的结果来筛选时,使用having,不能使用where
 
    特别的:group by 必须在where之后,order by之前
 
f、连表
    无对应关系则不显示
    select A.num, A.name, B.name  #没有where时,比如A.num有5组数据,B.name有3组数据,结果显示为3*5=15组数据
    from A,B
    Where A.nid = B.nid
 
    left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 
  right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
  inner join(等值连接) 只返回两个表中联结字段相等的行
    区别在于inner join:无对应关系则不显示;left join:无对应关系,则值为null
    无对应关系则不显示#
    select A.num, A.name, B.name                                   如果表1中的数据多,表2少,只显示都有的
    from A inner join B                                            无对应关系则不显示
    on A.nid = B.nid
 
    A表所有显示,如果B中无对应关系,则值为null
    select A.num, A.name, B.name                                   如果表1中的数据多,表2少,则显示为null
    from A left join B     #貌似后面不能跟where                    无对应关系,则值为null
    on A.nid = B.nid                                               left join = left outer join
 
    B表所有显示,如果B中无对应关系,则值为null
    select A.num, A.name, B.name
    from A right join B                                            无对应关系,则值为null
    on A.nid = B.nid
 
g、组合
    组合,自动处理重合
    select nickname
    from A
    union
    select name
    from B
 
    组合,不处理重合
    select nickname
    from A
    union all
    select name
    from B
表内容基本操作

示例及python操作MySQL示例

导出现有数据库数据:
mysqldump -u用户名 -p密码 数据库名称 >导出文件路径           # 结构+数据
mysqldump -u用户名 -p密码 -d 数据库名称 >导出文件路径       # 结构 
D:\>mysqldump -uroot -p test > 1.sql

导入现有数据库数据:
mysqldump -uroot -p密码  数据库名称 < 文件路径
例子:
C:\Windows\System32>mysqldump -u root -p test1 < d:\authdb.sql
导入导出数据库数据文件
Python操作MySQL
        https://pypi.python.org/pypi
                
        pip3 install pymysql
        # 下载本地
        # 解压到执行目录 
        # python2,默认无pip命令
        # python3,默认自带pip3命令 python3 -m pip install --upgrade pip
        
        yum install xxxx
        apt-get install ...
        brew install ...
        easyinstall …



import pymysql
conn = pymysql.connect(host = '127.0.0.1',port = 3306,user = 'root',passwd = '',db = 'test',charset = 'utf8')
cursor = conn.cursor() #创建游标
# abc=input('输入你要插入的')
# r = cursor.execute('insert into class(caption) values("%s")'%abc)#这样写会造成SQL注入,不使用这种方式
# r = cursor.execute('insert into class(caption) values(%s)',abc)#一定使用参数的这样形式
# cursor.execute('insert into student(gender,class_id,sname) values(%s,%s,%s)',('mail',1,'english'))#传入一组数据,使用executemany,可以包含多参数
# cursor.executemany('insert into class(caption) values(%s)',(('五年三班'),('范德萨班'),))#传入多组数据时,使用executemany,单组数据使用many会报错
# cursor.executemany('insert into student(gender,class_id,sname)values(%s,%s,%s)',(('女',1,'范德萨'),('女',2,'范德萨'),))
# conn.commit()#非增删改操作,即查操作时,可以不用commit
r = cursor.execute('select * from student')#execute的返回值是受影响的条数
result = cursor.fetchall()#实际上在execute时,已经把所有的结果都存在了内存中。而fetchall,one,many只是去内存中拿指定条数而已
# result = cursor.fetchone()
# result = cursor.fetchmany(3)
# cursor.scroll(0,mode='absolute')#指针移动到绝对位置 ‘0’
# cursor.scroll(2,mode = 'relative')#指针移动到相对位置,数值2,则指针往下走2个;可以为负

cursor.close()#关闭游标
conn.close()#关闭连接

# cursor = conn.cursor(cursor = pymysql.cursors.DictCursor)#配置游标,返回值为一个包含列名的字典
# nid = cursor.lastrowid#获取最新数据的自增id


SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编程时的疏忽,通过SQL语句,实现无帐号登录,甚至篡改数据库。
从理论上说,后台认证程序中会有如下的SQL语句:
String sql = "select * from user_table where username='userName' and password='password'";
当输入了上面的用户名和密码,上面的SQL语句变成:
SELECT * FROM user_table WHERE username='’or 1 = 1 -- and password='’后面加两个-,这意味着注释.
https://www.cnblogs.com/baizhanshi/p/6002898.html
Python操作MySQL
2、查询“生物”课程比“物理”课程成绩高的所有学生的学号;
思路:
    获取所有有生物课程的人(学号,成绩) - 临时表
    获取所有有物理课程的人(学号,成绩) - 临时表
    根据【学号】连接两个临时表:
        学号  物理成绩   生物成绩
 
    然后再进行筛选
 
        select A.student_id,sw,ty from
 
        (select student_id,num as sw from score left join course on score.course_id = course.cid where course.cname = '生物') as A
 
        left join
 
        (select student_id,num  as ty from score left join course on score.course_id = course.cid where course.cname = '体育') as B
 
        on A.student_id = B.student_id where sw > if(isnull(ty),0,ty);
 
3、查询平均成绩大于60分的同学的学号和平均成绩;
    思路:
        根据学生分组,使用avg获取平均值,通过having对avg进行筛选
 
        select student_id,avg(num) from score group by student_id having avg(num) > 60
 
4、查询所有同学的学号、姓名、选课数、总成绩;
 
    select score.student_id,sum(score.num),count(score.student_id),student.sname
    from
        score left join student on score.student_id = student.sid  
    group by score.student_id
 
5、查询姓“李”的老师的个数;
    select count(tid) from teacher where tname like '李%'
 
    select count(1) from (select tid from teacher where tname like '李%') as B
 
6、查询没学过“叶平”老师课的同学的学号、姓名;
    思路:
        先查到“李平老师”老师教的所有课ID
        获取选过课的所有学生ID
        学生表中筛选
    select * from student where sid not in (
        select DISTINCT student_id from score where score.course_id in (
            select cid from course left join teacher on course.teacher_id = teacher.tid where tname = '李平老师'
        )
    )
 
7、查询学过“001”并且也学过编号“002”课程的同学的学号、姓名;
    思路:
        先查到既选择001又选择002课程的所有同学
        根据学生进行分组,如果学生数量等于2表示,两门均已选择
 
    select student_id,sname from
 
    (select student_id,course_id from score where course_id = 1 or course_id = 2) as B
      
    left join student on B.student_id = student.sid group by student_id HAVING count(student_id) > 1
 
 
8、查询学过“叶平”老师所教的所有课的同学的学号、姓名;
 
    同上,只不过将001和002变成 in (叶平老师的所有课)
 
9、查询课程编号“002”的成绩比课程编号“001”课程低的所有同学的学号、姓名;
    同第1题
 
 
10、查询有课程成绩小于60分的同学的学号、姓名;
         
    select sid,sname from student where sid in (
        select distinct student_id from score where num < 60
    )
 
11、查询没有学全所有课的同学的学号、姓名;
    思路:
        在分数表中根据学生进行分组,获取每一个学生选课数量
        如果数量 == 总课程数量,表示已经选择了所有课程
 
        select student_id,sname
        from score left join student on score.student_id = student.sid
        group by student_id HAVING count(course_id) = (select count(1) from course)
 
 
12、查询至少有一门课与学号为“001”的同学所学相同的同学的学号和姓名;
    思路:
        获取 001 同学选择的所有课程
        获取课程在其中的所有人以及所有课程
        根据学生筛选,获取所有学生信息
        再与学生表连接,获取姓名
 
        select student_id,sname, count(course_id)
        from score left join student on score.student_id = student.sid
        where student_id != 1 and course_id in (select course_id from score where student_id = 1) group by student_id
 
13、查询至少学过学号为“001”同学所有课的其他同学学号和姓名;
        先找到和001的学过的所有人
        然后个数 = 001所有学科     ==》 其他人可能选择的更多
 
        select student_id,sname, count(course_id)
        from score left join student on score.student_id = student.sid
        where student_id != 1 and course_id in (select course_id from score where student_id = 1) group by student_id having count(course_id) = (select count(course_id) from score where student_id = 1)
 
14、查询和“002”号的同学学习的课程完全相同的其他同学学号和姓名;
         
        个数相同
        002学过的也学过
 
        select student_id,sname from score left join student on score.student_id = student.sid where student_id in (
            select student_id from score  where student_id != 1 group by student_id HAVING count(course_id) = (select count(1) from score where student_id = 1)
        ) and course_id in (select course_id from score where student_id = 1) group by student_id HAVING count(course_id) = (select count(1) from score where student_id = 1)
 
 
15、删除学习“叶平”老师课的score表记录;
 
    delete from score where course_id in (
        select cid from course left join teacher on course.teacher_id = teacher.tid where teacher.name = '叶平'
    )
 
16、向SC表中插入一些记录,这些记录要求符合以下条件:①没有上过编号“002”课程的同学学号;②插入“002”号课程的平均成绩;
    思路:
        由于insert 支持 
                inset into tb1(xx,xx) select x1,x2 from tb2;
        所有,获取所有没上过002课的所有人,获取002的平均成绩
 
    insert into score(student_id, course_id, num) select sid,2,(select avg(num) from score where course_id = 2)
    from student where sid not in (
        select student_id from score where course_id = 2
    )
     
17、按平均成绩从低到高 显示所有学生的“语文”、“数学”、“英语”三门的课程成绩,按如下形式显示: 学生ID,语文,数学,英语,有效课程数,有效平均分;
    select sc.student_id,
        (select num from score left join course on score.course_id = course.cid where course.cname = "生物" and score.student_id=sc.student_id) as sy,
        (select num from score left join course on score.course_id = course.cid where course.cname = "物理" and score.student_id=sc.student_id) as wl,
        (select num from score left join course on score.course_id = course.cid where course.cname = "体育" and score.student_id=sc.student_id) as ty,
        count(sc.course_id),
        avg(sc.num)
    from score as sc
    group by student_id desc        
 
18、查询各科成绩最高和最低的分:以如下形式显示:课程ID,最高分,最低分;
     
    select course_id, max(num) as max_num, min(num) as min_num from score group by course_id;
 
19、按各科平均成绩从低到高和及格率的百分数从高到低顺序;
    思路:case when .. then
    select course_id, avg(num) as avgnum,sum(case when score.num > 60 then 1 else 0 END)/count(1)*100 as percent from score group by course_id order by avgnum asc,percent desc;
 
20、课程平均分从高到低显示(现实任课老师);
 
    select avg(if(isnull(score.num),0,score.num)),teacher.tname from course
    left join score on course.cid = score.course_id
    left join teacher on course.teacher_id = teacher.tid
 
    group by score.course_id
 
 
21、查询各科成绩前三名的记录:(不考虑成绩并列情况)
    select score.sid,score.course_id,score.num,T.first_num,T.second_num from score left join
    (
    select
        sid,
        (select num from score as s2 where s2.course_id = s1.course_id order by num desc limit 0,1) as first_num,
        (select num from score as s2 where s2.course_id = s1.course_id order by num desc limit 3,1) as second_num
    from
        score as s1
    ) as T
    on score.sid =T.sid
    where score.num <= T.first_num and score.num >= T.second_num
 
22、查询每门课程被选修的学生数;
     
    select course_id, count(1) from score group by course_id;
 
23、查询出只选修了一门课程的全部学生的学号和姓名;
    select student.sid, student.sname, count(1) from score
 
    left join student on score.student_id  = student.sid
 
     group by course_id having count(1) = 1
 
 
24、查询男生、女生的人数;
    select * from
    (select count(1) as man from student where gender='') as A ,
    (select count(1) as feman from student where gender='') as B
 
25、查询姓“张”的学生名单;
    select sname from student where sname like '张%';
 
26、查询同名同姓学生名单,并统计同名人数;
 
    select sname,count(1) as count from student group by sname;
 
27、查询每门课程的平均成绩,结果按平均成绩升序排列,平均成绩相同时,按课程号降序排列;
    select course_id,avg(if(isnull(num), 0 ,num)) as avg from score group by course_id order by avg     asc,course_id desc;
 
28、查询平均成绩大于85的所有学生的学号、姓名和平均成绩;
 
    select student_id,sname, avg(if(isnull(num), 0 ,num)) from score left join student on score.student_id = student.sid group by student_id;
 
29、查询课程名称为“数学”,且分数低于60的学生姓名和分数;
 
    select student.sname,score.num from score
    left join course on score.course_id = course.cid
    left join student on score.student_id = student.sid
    where score.num < 60 and course.cname = '生物'
 
30、查询课程编号为003且课程成绩在80分以上的学生的学号和姓名;
    select * from score where score.student_id = 3 and score.num > 80
 
31、求选了课程的学生人数
 
    select count(distinct student_id) from score
 
    select count(c) from (
        select count(student_id) as c from score group by student_id) as A
 
32、查询选修“杨艳”老师所授课程的学生中,成绩最高的学生姓名及其成绩;
     
    select sname,num from score
    left join student on score.student_id = student.sid
    where score.course_id in (select course.cid from course left join teacher on course.teacher_id = teacher.tid where tname='张磊老师') order by num desc limit 1;
 
33、查询各个课程及相应的选修人数;
    select course.cname,count(1) from score
    left join course on score.course_id = course.cid
    group by course_id;
 
 
34、查询不同课程但成绩相同的学生的学号、课程号、学生成绩;
    select DISTINCT s1.course_id,s2.course_id,s1.num,s2.num from score as s1, score as s2 where s1.num = s2.num and s1.course_id != s2.course_id;
 
35、查询每门课程成绩最好的前两名;
 
    select score.sid,score.course_id,score.num,T.first_num,T.second_num from score left join
    (
    select
        sid,
        (select num from score as s2 where s2.course_id = s1.course_id order by num desc limit 0,1) as first_num,
        (select num from score as s2 where s2.course_id = s1.course_id order by num desc limit 1,1) as second_num
    from
        score as s1
    ) as T
    on score.sid =T.sid
    where score.num <= T.first_num and score.num >= T.second_num
 
36、检索至少选修两门课程的学生学号;
    select student_id from score group by student_id having count(student_id) > 1
 
37、查询全部学生都选修的课程的课程号和课程名;
    select course_id,count(1) from score group by course_id having count(1) = (select count(1) from student);
 
38、查询没学过“叶平”老师讲授的任一门课程的学生姓名;
    select student_id,student.sname from score
    left join student on score.student_id = student.sid
    where score.course_id not in (
        select cid from course left join teacher on course.teacher_id = teacher.tid where tname = '张磊老师'
    )
    group by student_id
 
39、查询两门以上不及格课程的同学的学号及其平均成绩;
 
    select student_id,count(1) from score where num < 60 group by student_id having count(1) > 2
 
40、检索“004”课程分数小于60,按分数降序排列的同学学号;
    select student_id from score where num< 60 and course_id = 4 order by num desc;
 
41、删除“002”同学的“001”课程的成绩;
    delete from score where course_id = 1 and student_id = 2
示例

5.表内容进阶操作

视图
视图是一个虚拟表(非真实存在),其本质是【根据SQL语句获取动态的数据集,并为其命名】,用户使用时只需使用【名称】即可获取结果集,并可以将其当作表来使用。

1、创建视图
--格式:CREATE VIEW 视图名称 AS  SQL语句
CREATE VIEW v1 AS 
SELET nid,  name FROM A WHERE nid > 4

2、删除视图
--格式:DROP VIEW 视图名称
DROP VIEW v1

3、修改视图
-- 格式:ALTER VIEW 视图名称 AS SQL语句
ALTER VIEW v1 AS
SELET A.nid, B. NAME FROM A
LEFT JOIN B ON A.id = B.nid
LEFT JOIN C ON A.id = C.nid
WHERE  A.id > 2 AND C.nid < 5

4、使用视图
使用视图时,将其当作表进行操作即可,由于视图是虚拟表,所以无法使用其对真实表进行创建、更新和删除操作,仅能做查询用。
show tables;同时查看到列表和视图
视图
触发器
触发器:对某个表进行【增/删/改】操作的前后如果希望触发某个特定的行为时,可以使用触发器,触发器用于定制用户对表的行进行【增/删/改】前后的行为。
1、创建基本语法
# 插入前
CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW
BEGIN
    ...
END
# 插入后
CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW
BEGIN
    ...
END
# 删除前
CREATE TRIGGER tri_before_delete_tb1 BEFORE DELETE ON tb1 FOR EACH ROW
BEGIN
    ...
END
# 删除后 ...
# 更新前
CREATE TRIGGER tri_before_update_tb1 BEFORE UPDATE ON tb1 FOR EACH ROW
BEGIN
    ...
END
# 更新后 ...




示例:
插入前触发器
delimiter //              --修改终止符为\\
CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW
BEGIN

IF NEW. NAME == 'alex' THEN
    INSERT INTO tb2 (NAME)
VALUES
    ('aa')
END
END//
delimiter ;               --把终止符复原为;

插入前触发器
delimiter //
CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW
BEGIN
    IF NEW. num = 666 THEN
        INSERT INTO tb2 (NAME)
        VALUES
            ('666'),
            ('666') ;
    ELSEIF NEW. num = 555 THEN
        INSERT INTO tb2 (NAME)
        VALUES
            ('555'),
            ('555') ;
    END IF;
END//
delimiter ;
特别的:NEW表示即将插入的数据行,OLD表示即将删除的数据行。



2、删除触发器
DROP TRIGGER tri_after_insert_tb1;

3、使用触发器
触发器无法由用户直接调用,而是由于对表的【增/删/改】操作被动引发的。
触发器
 存储过程
存储过程是一个SQL语句集合,当主动去调用存储过程时,其中内部的SQL语句会按照逻辑执行。

1、创建存储过程

-- 创建存储过程(无参数存储过程)
delimiter //
create procedure p1()
BEGIN
    select * from t1;
END//
delimiter ;

-- 执行存储过程
call p1()





对于存储过程,可以接收参数,其参数有三类:
in          仅用于传入参数用
out        仅用于返回值用
inout     既可以传入又可以当作返回值

-- 创建存储过程(有参数存储过程)
delimiter \\
create procedure p1(
    in i1 int,
    in i2 int,
    inout i3 int,
    out r1 int
)
BEGIN
    DECLARE temp1 int;
    DECLARE temp2 int default 0;
    
    set temp1 = 1;
    set r1 = i1 + i2 + temp1 + temp2;   
    set i3 = i3 + 100;

end\\
delimiter ;

-- 执行存储过程
set @t1 =4;   #mysql的变量默认在前面要加@
set @t2 = 0;
CALL p1 (1, 2 ,@t1, @t2);
SELECT @t1,@t2;

以下有更多示例
http://www.cnblogs.com/wupeiqi/articles/5713323.html






2、删除存储过程
drop procedure proc_name;






3、执行存储过程
-- 无参数
call proc_name()

-- 有参数,全in
call proc_name(1,2)

-- 有参数,有in,out,inout
set @t1=0;
set @t2=3;
call proc_name(1,2,@t1,@t2)




pymysql执行存储过程
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import pymysql

conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 执行存储过程
cursor.callproc('p1', args=(1, 22, 3, 4))
# 获取执行完存储的参数
cursor.execute("select @_p1_0,@_p1_1,@_p1_2,@_p1_3")
result = cursor.fetchall()

conn.commit()
cursor.close()
conn.close()


print(result)
存储过程
 内置函数、自定义函数
MySQL中提供了许多内置函数,例如:
详情见
http://www.cnblogs.com/wupeiqi/articles/5713323.html

1、自定义函数
delimiter \\
create function f1(
    i1 int,
    i2 int)
returns int
BEGIN
    declare num int;
    set num = i1 + i2;
    return(num);
END \\
delimiter ;

2、删除函数
drop function func_name;

3、执行函数
# 获取返回值
declare @i VARCHAR(32);
select UPPER('alex') into @i;
SELECT @i;
# 在查询中使用
select f1(11,nid) ,name from tb2;
内置函数、自定义函数
 事务
事务用于将某些操作的多个SQL作为原子性操作,一旦有某一个出现错误,即可回滚到原来的状态,从而保证数据库数据完整性。

支持事务的存储过程
delimiter \\
create PROCEDURE p1(
    OUT p_return_code tinyint
)
BEGIN 
  DECLARE exit handler for sqlexception 
  BEGIN 
    -- ERROR 
    set p_return_code = 1; 
    rollback; 
  END; 
 
  DECLARE exit handler for sqlwarning 
  BEGIN 
    -- WARNING 
    set p_return_code = 2; 
    rollback; 
  END; 
 
  START TRANSACTION; 
    DELETE from tb1;
    insert into tb2(name)values('seven');
  COMMIT; 
 
  -- SUCCESS 
  set p_return_code = 0; 
 
  END\\
delimiter ;


使用该存储过程
set @i =0;
call p1(@i);
select @i;
事务
 索引
索引,是数据库中专门用于帮助用户快速查询数据的一种数据结构。类似于字典中的目录,查找字典内容时可以根据目录查找到数据的存放位置,然后直接获取即可。

普通索引(index):仅加速查询
唯一索引(unique):加速查询 + 列值唯一(可以有null)
主键索引(primary):加速查询 + 列值唯一 + 表中只有一个(不可以有null)
组合索引(index):多列值组成一个索引,
              专门用于组合搜索,其效率大于索引合并
全文索引:对文本的内容进行分词,进行搜索 
索引说明
创建时,至少都有2种方法:1.在创建表时,直接注明索引列,索引类型。2.配置已经存在的表的列为索引,指定索引类型

建立索引
方法1
create table in1(
    nid int not null auto_increment primary key,#指定为主键索引,方式1
    name varchar(32) not null,
    email varchar(64) not null,
    extra text,
    index ix_name (name)      #指定name列为普通索引
    unique ix_name (sid)      #指定sid列为唯一索引
    primary key(nid)          #指定为主键索引,方式2(注意,一张表只能有一个主键,这里只是为了说明创建主键的方式而已)
)
方法2
create index index_name on table_name(column_name) #指定name列为普通索引
create unique index 索引名 on 表名(列名)           #指定sid列为唯一索引
alter table 表名 add primary key(列名);            #创建主键索引(和上面的表有区别,也就是说明主键索引其实并没有再其他表上配置数据?)

删除索引
drop index_name on table_name;                              #删除普通索引
drop unique index 索引名 on 表名                            #删除唯一索引
alter table 表名 drop primary key;                          #删除主键索引,方式1
alter table 表名  modify  列名 int, drop primary key;       #删除主键索引,方式2

查看索引
show index from table_name;



注意:对于创建索引时如果是BLOB 和 TEXT 类型,必须指定length。
create index ix_extra on in1(extra(32));



组合索引
create table in3(
    nid int not null auto_increment primary key,
    name varchar(32) not null,
    email varchar(64) not null,
    extra text
)

create index ix_name_email on in3(name,email);#创建组合索引

如上创建组合索引之后,查询:
name and email  -- 使用索引
name            -- 使用索引
email           -- 不使用索引
注意:对于同时搜索n个条件时,组合索引的性能好于多个单一索引合并。
示例(创建、删除索引)
- 查看表结构
    desc 表名
- 查看生成表的SQL
    show create table 表名
- 查看索引
    show index from  表名
- 查看执行时间
    set profiling = 1;
    SQL...
    show profiles;
相关命令
有很多条件都会导致索引无效,要注意。建议还是另外找资料看看
注意:类型不一致可能会导致索引失效:如果查找列是字符串类型,传入条件是必须用引号引起来
当根据索引排序时候,选择的映射如果不是索引,则不走索引
- 避免使用select *
- count(1)或count(列) 代替 count(*)
- 创建表时尽量时 char 代替 varchar
- 表的字段顺序固定长度的字段优先
- 组合索引代替多个单列索引(经常使用多个条件查询时)
- 尽量使用短索引
- 使用连接(JOIN)来代替子查询(Sub-Queries)
- 连表时注意条件类型需一致
- 索引散列值(重复少)不适合建索引,例:性别不适合
正确使用索引
无论是否有索引,limit分页是一个值得关注的问题
每页显示10条:
当前 118 120, 125

倒序:
            大      小
   970  7 6  6 5  54  43  32
19 98     
下一页:

    select 
        * 
    from 
        tb1 
    where 
        nid < (select nid from (select nid from tb1 where nid < 当前页最小值 order by nid desc limit 每页数据 *【页码-当前页】) A order by A.nid asc limit 1)  
    order by 
        nid desc 
    limit 10;



    select 
        * 
    from 
        tb1 
    where 
        nid < (select nid from (select nid from tb1 where nid < 970  order by nid desc limit 40) A order by A.nid asc limit 1)  
    order by 
        nid desc 
    limit 10;


上一页:

    select 
        * 
    from 
        tb1 
    where 
        nid < (select nid from (select nid from tb1 where nid > 当前页最大值 order by nid asc limit 每页数据 *【当前页-页码】) A order by A.nid asc limit 1)  
    order by 
        nid desc 
    limit 10;


    select 
        * 
    from 
        tb1 
    where 
        nid < (select nid from (select nid from tb1 where nid > 980 order by nid asc limit 20) A order by A.nid desc limit 1)  
    order by 
        nid desc 
    limit 10;
limit分页
explain + 查询SQL - 用于显示SQL执行信息参数,根据参考信息可以进行SQL优化


查询顺序标识
    如:mysql> explain select * from (select nid,name from tb1 where nid < 10) as B;
    +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
    | id | select_type | table      | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
    +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
    |  1 | PRIMARY     | <derived2> | ALL   | NULL          | NULL    | NULL    | NULL |    9 | NULL        |
    |  2 | DERIVED     | tb1        | range | PRIMARY       | PRIMARY | 8       | NULL |    9 | Using where |
    +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
特别的:如果使用union连接气值可能为null



select_type
    查询类型
        SIMPLE          简单查询
        PRIMARY         最外层查询
        SUBQUERY        映射为子查询
        DERIVED         子查询
        UNION           联合
        UNION RESULT    使用联合的结果
        ...
table
    正在访问的表名


type
    查询时的访问方式,性能:all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const
        ALL             全表扫描,对于数据表从头到尾找一遍
                        select * from tb1;
                        特别的:如果有limit限制,则找到之后就不在继续向下扫描
                               select * from tb1 where email = 'seven@live.com'
                               select * from tb1 where email = 'seven@live.com' limit 1;
                               虽然上述两个语句都会进行全表扫描,第二句使用了limit,则找到一个后就不再继续扫描。

        INDEX           全索引扫描,对索引从头到尾找一遍
                        select nid from tb1;

        RANGE          对索引列进行范围查找
                        select *  from tb1 where name < 'alex';
                        PS:
                            between and
                            in
                            >   >=  <   <=  操作
                            注意:!= 和 > 符号


        INDEX_MERGE     合并索引,使用多个单列索引搜索
                        select *  from tb1 where name = 'alex' or nid in (11,22,33);

        REF             根据索引查找一个或多个值
                        select *  from tb1 where name = 'seven';

        EQ_REF          连接时使用primary key 或 unique类型
                        select tb2.nid,tb1.name from tb2 left join tb1 on tb2.nid = tb1.nid;



        CONST           常量
                        表最多有一个匹配行,因为仅有一行,在这行的列值可被优化器剩余部分认为是常数,const表很快,因为它们只读取一次。
                        select nid from tb1 where nid = 2 ;

        SYSTEM          系统
                        表仅有一行(=系统表)。这是const联接类型的一个特例。
                        select * from (select nid from tb1 where nid = 1) as A;
possible_keys
    可能使用的索引

key
    真实使用的

key_len
    MySQL中使用索引字节长度

rows
    mysql估计为了找到所需的行而要读取的行数 ------ 只是预估值

extra
    该列包含MySQL解决查询的详细信息
    “Using index”
        此值表示mysql将使用覆盖索引,以避免访问表。不要把覆盖索引和index访问类型弄混了。
    “Using where”
        这意味着mysql服务器将在存储引擎检索行后再进行过滤,许多where条件里涉及索引中的列,当(并且如果)它读取索引时,就能被存储引擎检验,因此不是所有带where子句的查询都会显示“Using where”。有时“Using where”的出现就是一个暗示:查询可受益于不同的索引。
    “Using temporary”
        这意味着mysql在对查询结果排序时会使用一个临时表。
    “Using filesort”
        这意味着mysql会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。mysql有两种文件排序算法,这两种排序方式都可以在内存或者磁盘上完成,explain不会告诉你mysql将使用哪一种文件排序,也不会告诉你排序会在内存里还是磁盘上完成。
    “Range checked for each record(index map: N)”
        这个意味着没有好用的索引,新的索引将在联接的每一行上重新估算,N是显示在possible_keys列中索引的位图,并且是冗余的。
 
执行计划
a、配置MySQL自动记录慢日志
slow_query_log = OFF                    是否开启慢日志记录
long_query_time = 2                     时间限制,超过此时间,则记录
slow_query_log_file = /usr/slow.log     日志文件
log_queries_not_using_indexes = OFF     为使用索引的搜索是否记录




b、查看MySQL慢日志

mysqldumpslow -s at -a  /usr/local/var/mysql/MacBook-Pro-3-slow.log

"""
--verbose    版本
--debug      调试
--help       帮助
 
-v           版本
-d           调试模式
-s ORDER     排序方式
             what to sort by (al, at, ar, c, l, r, t), 'at' is default
              al: average lock time
              ar: average rows sent
              at: average query time
               c: count
               l: lock time
               r: rows sent
               t: query time
-r           反转顺序,默认文件倒序拍。reverse the sort order (largest last instead of first)
-t NUM       显示前N条just show the top n queries
-a           不要将SQL中数字转换成N,字符串转换成S。don't abstract all numbers to N and strings to 'S'
-n NUM       abstract numbers with at least n digits within names
-g PATTERN   正则匹配;grep: only consider stmts that include this string
-h HOSTNAME  mysql机器名或者IP;hostname of db server for *-slow.log filename (can be wildcard),
             default is '*', i.e. match all
-i NAME      name of server instance (if using mysql.server startup script)
-l           总时间中不减去锁定时间;don't subtract lock time from total time
"""
慢日志查询

6.其他

delimiter \\
CREATE PROCEDURE proc_if ()
BEGIN
    
    declare i int default 0;
    if i = 1 THEN
        SELECT 1;
    ELSEIF i = 2 THEN
        SELECT 2;
    ELSE
        SELECT 7;
    END IF;

END\\
delimiter ;
if条件语句
delimiter \\
CREATE PROCEDURE proc_while ()
BEGIN

    DECLARE num INT ;
    SET num = 0 ;
    WHILE num < 10 DO
        SELECT
            num ;
        SET num = num + 1 ;
    END WHILE ;

END\\
delimiter ;
while循环
delimiter \\
CREATE PROCEDURE proc_repeat ()
BEGIN

    DECLARE i INT ;
    SET i = 0 ;
    repeat
        select i;
        set i = i + 1;
        until i >= 5
    end repeat;

END\\
delimiter ;
repeat循环
BEGIN
    
    declare i int default 0;
    loop_label: loop
        
        set i=i+1;
        if i<8 then
            iterate loop_label;
        end if;
        if i>=10 then
            leave loop_label;
        end if;
        select i;
    end loop loop_label;

END
loop

 

delimiter \\
DROP PROCEDURE IF EXISTS proc_sql \\
CREATE PROCEDURE proc_sql ()
BEGIN
    declare p1 int;
    set p1 = 11;
    set @p1 = p1;

    PREPARE prod FROM 'select * from tb2 where nid > ?';
    EXECUTE prod USING @p1;
    DEALLOCATE prepare prod; 

END\\
delimiter ;
动态执行SQL语句

 

参考:

基础1: http://www.cnblogs.com/wupeiqi/articles/5713315.html
基础2: http://www.cnblogs.com/wupeiqi/articles/5713323.html
练习题:http://www.cnblogs.com/wupeiqi/articles/5729934.html
练习题答案: http://www.cnblogs.com/wupeiqi/articles/5748496.html
索引知识补充:http://www.cnblogs.com/wupeiqi/articles/5716963.html

posted @ 2019-01-01 10:01  雲淡風輕333  阅读(117)  评论(0编辑  收藏  举报