mysql安装
mysql
1 | MySQL是一种关系数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性. 由于MYSQL是一款开放源码软件,因此可以大大降低总体拥有成本.现在我们来介绍下MYSQL的SQL原生态语句 |
linux mysql数据库安装
如何安装数据库
1 | 先下载mysql |
编译安装MySQL
1 | #cmake |
MySQL二进制安装
1 | 1.解压安装包 |
使用systemd管理MySQL
1 | 使用systemd管理MySQL |
给root用户设置密码
1 | ###给root用户设置密码 |
01.误删除root用户解决方法
方法一:
1 | 1.停止数据库 |
方法二:
1 | 1.停止数据库 |
02.客户端与服务器模型
1 | 1.mysql是一个典型的C/S服务结构 |
03.MySQL服务器构成
1 | 什么是实例 |
04.MySQL的结构
1 | 1.库 |
windows mysql数据库安装
直接解压
在本地设置MYSQL账号
创建my.ini文件
1 | [mysqld] |
将模式设置为严格模式
1 | set global sql_mode= "strict_trans_tables,only_full_group_by"; #修改为严格模式,只能查看到分组后的字段,配合聚合函数.其余操作会报错 |
1 | ps:修改sql_mode为严格模式,必须重启客户端才能生效 |
注意
将MYSQL设置为后台自动运行
数据库服务器中存放的是
库(文件加) 表(文件) 表里面是记录(一行数据)
破解mysql密码,跳过配置文件
1 关闭mysql
2 重新启动
3 输入命令
4 关闭MySQL,正常启动
账号的创建(了解知识点)
跟权限有关的几张表
user,db,table_priv,columns_priv
只创建账号
创建账号并且授权(只有root账号才能为其他账号授权grant)
删除账号
MYSQL原生态SQL语句
注释
commnt
SQL语句规范第一条:
CREATE DATABASE oldboy CHARSET utf8mb4;
1.关键字大写(非必须),字面量小写(必须)
2.库名字,只能是小写,不能有数字开头,不能是预留关键字
3.库名字必须和业务名有关,例如his_user;
4.必须加字符集.
表
CREATE TABLE
DROP TABLE
ALTER TABLE
CREATE TABLE t1 (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT ‘用户ID’,
sname VARCHAR(20) NOT NULL COMMENT ‘用户姓名’,
gender ENUM(‘f’,’m’,’u’) NOT NULL DEFAULT ‘u’ COMMENT ‘用户性别’,
telnum CHAR(11) NOT NULL UNIQUE COMMENT ‘手机号’,
tmdate DATETIME NOT NULL DEFAULT NOW() COMMENT ‘录入时间’
)ENGINE INNODB CHARSET utf8mb4;
DDL数据定义语言(创建) DCL数据控制语言 DML数据操作语言(增,删,改) DQL数据查询
库(create/drop/alter/show)
就是一个文件夹
创建规则
1 | 可以由字母、数字、下划线、@、#、$ |
增
1 | create database 数据库名字 charset 编码格式(不需要加引号); |
删除
1 | 删: |
改
1 | 改: |
查
查所有库
1 | show datebases; |
查已经创建的库
1 | show create database 创建的数据库名字; |
表的操作首先要选择库文件
进入库中
选择当前所在的库
1 | use db1; |
表(create/drop;truncate/alter/show;desc)
增:
不需要切换
1 | creat table db1.t1(id int,name char); |
需要切换use
1 | create table 表名(attr1 type,attr2 type......); |
删:(彻底)
1 | drop table 表名; |
删:( 注意:只是清空表内内容,表结构还在)
1 | truncate table 表名; |
清空表的时候应该用truncate,不会保留id字段
改:
1.添加属性(字段)
1 | alter table 表名 add 属性 类型; |
2.删除属性(字段)
1 | alter table 表名 drop 属性; |
3.改表属性的数据类型(字段)
1 | alter table 表名 modify addr 新类型; |
4.覆盖修改表中的某个属性(字段)
1 | alter table 表名 change 老属性 新属性 新属性的类型; |
5.改表的字符编码
1 | alter table 表名 charset utf8; |
6.修改表名
1 | rename table 表名 to 新表名; |
查:
1.查所有的表
1 | 1.show tables; |
2.查创建的表
1 | 2.show create table 表名; |
3.查表结构
1 | 3.desc 表名; |
拷贝
1 | create table copy_table select *from customer ; |
查看当前所在库
1 | select batabase; |
记录(insert/delete/update/select)
增:
1 | 添加多个记录,记录的类型必须和表属性一致 |
删:use
1 | delete from 表名 (where 条件);没有条件就全删 |
不use
改: 没有条件就全删
1 | update 表名 set addr=rec (where 条件); 没有条件就全删 |
查:
查所有
1 | 1.select * from 表名; |
查一个单个属性
1 | 2.select 表属性 from 表名; |
创建表的完整语法
数据类型
1 整形
默认为有符号,即数字前有正负号
可以设置create table t4(x tinyint unsigned);无符号类型
强调:
整型类型后面的宽度限制的根本不是存储宽度,限制的是显示宽度
create table t5(id int(1));
create table t6(id int(5));
2.浮点型
三种格式的精度不一样
float(255,30)
double(255,30)
decimal(65,30)
格式
1 | create table t8(x float(255,30)); |
3 日期类型
year
1 | ============year=================== |
date time datetime
1 | ============date,time,datetime=========== |
timestamp
1 | create table t_stamp(t TIMESTAMP); |
实际场景
1 | /* |
1 | year 1999 #年 |
实列
1 | create table student( |
timestamp与datetime的区别
4.字符类型
注意:宽度指限制的是字符个数
1 | char:定长 |
相同点宽度指的都是最大存储的字符个数,超过了都无法正常存储
1 | 如何选择 |
设置
注意点
5 枚举与集合类型
字段的值只能在给定范围中选择,如单选框,多选框 enum 单选 只能在给定的范围内选一个值,如性别 sex 男male/女female set 多选 在给定的范围内可以选择一个或一个以上的值(爱好1,爱好2,爱好3…)
先创建
枚举enum:多选一
集合set: 多选多
1 | /* |
concat(字符串相加)
1 | insert into s1 values(n,concat('egon',n), |
表的约束
not null与default
unique key唯一
创建方法
1 | 方法一: |
联合唯一
1 | 联合唯一 |
primary key(主键)
站在约束角度看primary key=not null unique不为空且唯一 ,另外还有索引优化
注意点
以后但凡建表,必须注意:
1、必须有且只有一个主键
2、通常是id字段被设置为主键
写法
1 | create table t5( |
foreign key
外键:用来在表与表之间建立关系的。限制关联表某一个字段的值必是来自于被关联表的一个字段的
注意
1、被关联的字段必须是一个key,通常是id字段
2、创建表时:必须先建立被关联的表,才能建立关联表
3、插入记录时:必须先往被关联的表插入记录,才能往关联表中插入记录
创建表2个相互关联的表
references是指向的意思
1 | 创建表时:必须先建立被关联的表,才能建立关联表 |
插入2个相互关联表的数据
1 | insert into dep(dname,info) values |
删除表
ps:删除时:应该先删除关联表emp中的记录,再删除被关联表对应的记录
找两张表的关系的窍门
1 | create table author( |
一对一:
无需参考窍门,左表的一条数据唯一对应右表的一条记录
1 | 一对一 ****** |
补充存储引擎
1 | # 补充存储引擎: |
单表查询
准备的表和数据
1 | CREATE TABLE emp( |
产生
1 | mysql> SELECT * FROM emp; |
练习单表查询
1 | -- 查询男女员工各有多少人 |
步骤
1 | 1.找到表:from |
单表查询的完整语法
语法级别关键字排序
1 | select distinct 字段1,字段2,字段3,... from 库名.表名 |
关键字的执行顺序
1 |
|
单表简单的查询语法
1 | select * from t1; |
where和between和in
1 | where字句中可以使用: |
group by 分组
什么是分组
1 | #3、group by分组 |
ONLY_FULL_GROUP_BY
1 | ONLY_FULL_GROUP_BY的语义就是确定select target list中的所有列的值都是明确语义,简单的说来,在ONLY_FULL_GROUP_BY模式下,target list中的值要么是来自于聚集函数的结果,要么是来自于group by list中的表达式的值。 |
聚合函数(和分组搭配使用)
用法
1 | SELECT COUNT(*) FROM employee; |
having 和where的区别
1 | #!!!执行优先级从高到低:where > group by > having |
is null
判断是否是空
1 | SELECT name,post_comment FROM employee |
order by 排序
asc升序,desc降序
1 | select * from emp order by age asc; # 默认asc升序-》从小到大 |
limit 限制显示的条件限制显示条目
1 | #6、limit 限制显示的条件 |
1 | # 分页显示 |
正则表达式
1 | #正则表达式 |
多表查询
创建表
1 | CREATE TABLE emp( |
产生结果
1 | mysql> select * from emp; |
笛卡尔积
条件(2张表有对应关系)
简单粗暴的对应关系
1 | select * from emp,dep; |
select * from emp,dep;
1 | mysql> select * from emp,dep; |
内连接: 只取两张表有对应关系的记录
1 | #2、内连接:只取两张表有对应关系的记录 |
select * from emp inner join dep on emp.dep_id = dep.id;
1 | +----+--------+----------+--------+----+-----------+ |
这时,我们就可以利用两张表中所有的字段进行查询了
1 | -- 查询李四所在的部门名称 |
左连接: 在内连接的基础上保留左表没有对应关系的记录
1 | select * from emp left join dep on emp.dep_id = dep.id; |
select * from emp left join dep on emp.dep_id = dep.id;
查询结果
1 | +----+--------+----------+--------+------+-----------+ |
右连接: 在内连接的基础上保留右表没有对应关系的记录
1 | select * from emp right join dep on emp.dep_id = dep.id; |
select * from emp right join dep on emp.dep_id = dep.id;
1 | mysql> SELECT * FROM emp RIGHT JOIN dep ON dep.id=emp.dep_id; |
全连接:在内连接的基础上保留左、右面表没有对应关系的的记录
1 | select * from emp left join dep on emp.dep_id = dep.id |
子查询
就是将一个查询语句的结果用括号括起来当作另外一个查询语句的条件去用
1 | select * from emp where dep_id in (select id from dep |
每个部门最新入职的员工
1 | # 每个部门最新入职的员工 |
Mysql–用户相关
MySQL用户操作
创建账号
1 | 此处的主机地址 不是服务器地址 而是表示 这个账户可以在那台电脑上登录 |
1 | 方法一: CREATE USER语句创建 |
1 | # 2、只创建账号 |
删除用户
1 | drop user "tom"@"192.168.15.90"; |
1 | 方法一:DROP USER语句删除 |
修改密码
(root用户修改自己密码:)
1 | 方法一: |
root修改其他用户密码:
1 | 方法一: |
普通用户修改自己密码:
1 | SET password=password(‘new_password’); |
丢失root用户密码:
1 | # vim /etc/my.cnf |
修改用户名:
1 | rename user '用户名'@'IP地址' to '新用户名'@'IP地址'; |
MySQL权限管理
1、跟权限有关的几张表
user->db->table_priv->columns_priv
user是全局帐号的一个操作权限,
db 是某一个库文件的操作权限,
table_oriv 是某一个表的操作权限,
columns_priv 是表字段的操作权限。
创建账号并且授权
–语法格式:
1 | grant 权限列表 on 库名.表名 to 用户名@'客户端主机' [identified by '密码' with option参数]; |
1 | -- 给该用户授予对数据库db1下的表tb1所有的操作权限 |
1 | # *.* ===> mysql.user |
View Code
with_option参数:
1 | GRANT OPTION: -- 授权选项 |
View Code
示例: 限制用户每小时的查询数量
1 | mysql> grant select on *.* to '用户名'@'IP地址' identified by '123456' with max_queries_per_hour 5; |
查看权限
1 | show grants for '用户'@'IP地址' |
回收权限
1 | --语法: |
示例:
1 | REVOKE DELETE ON *.* FROM 用户名@’%’; -- 回收部分权限 |
修改权限之后一点要刷新
1 | flush privileges; |
注解 flush privileges:
1 | /* |
视图
1 | 视图是什么? |
1.为什么使用视图
为了提高复杂SQL语句的复用性和表操作的安全性(例如:工资字段不想展示给所有能查看该查询结果的人),MySQL提供了视图特性。所谓视图,本质上是一种虚拟表,其内容与真实的表相似,包含一系列带有名称的列和行数据。但是,视图并不在数据库中以存储数据值的形式存在。行和列的数据来自定义视图的查询所引用基本表,并且在具体使用视图时动态生成。
视图有如下特点;
1. 视图的列可以来自不同的表,是表的抽象和逻辑意义上建立的新关系。
2. 视图是由基本表(实表)产生的表(虚表)。
3. 视图的建立和删除不影响基本表。
4. 对视图内容的更新(添加、删除和修改)直接影响基本表。
5. 当视图来自多个基本表时,不允许添加和删除数据。
2.创建视图
在创建视图时,首先要确保拥有CREATE VIEW的权限,并且同时确保对创建视图所引用的表也具有相应的权限。
2.1 创建视图的语法形式: 虽然视图可以被看成是一种虚拟表,但是其在物理上是不存在的,即数据库管理系统没有专门的位置为视图存储数据。根据视图的概念发现其数据来源于查询语句,因此创建视图的语法为:
CREATE VIEW view_name AS 查询语句
//说明:和创建表一样,视图名不能和表名、也不能和其他视图名重名。视图的功能实际就是封装了复杂的查询语句。
示例:
1 | use view_test; //选择一个自己创建的库 |
结果为:
//其实在创建视图时实际代码里写的是一个表查询语句,只不过把这个查询语句封装起来重新起了一个名字,方便可以重复使用。
//再者,安全性方面可以隐藏一些不希望看到的字段,比如这里的价格字段。
//注意:在SQL语句的命名规范中,视图一般以view_xxx或者v_xxx的样式来命名。视图的查询语句和表的查询语句相同。
2.2 创建各种视图: 由于视图的功能实际上时封装查询语句,那么是不是任何形式的查询语句都可以封装在视图中呢?
封装使用聚合函数(SUM、MIN、MAX、COUNT等)查询语句的视图: 示例:
首先准备需要用到的两张表及其初始化数据;
1 | CREATE TABLE t_group( |
2.2.3 封装了实现排序功能(ORDER BY)查询语句的视图:
示例:
1 | mysql> create view view_test3 as select name from t_student order by id desc; |
2.2.4 封装了实现表内连接查询语句的视图:
示例:(第二组学生的姓名)
1 | mysql> create view view_test5 as select s.name from t_student s,t_group g where s.group_id=g.id and g.id=2; |
2.2.6 封装了实现子查询相关查询语句的视图:
示例:
1 | mysql> create view view_test7 as select s.name from t_student s where s.id in(select id from t_group); |
2.2.7 封装了实现记录联合(UNION和UNION ALL)查询语句的视图:
1 | mysql> create view view_test8 as select id,name from t_student union all select id,name from t_group; |
3.查看视图
3.1 SHOW TABLES语句查看视图名: 执行SHOW TABLES 语句时不仅可以显示表的名字,同时也是显示出视图的名字。
示例:
1 | mysql> show tables; |
3.2 SHOW TABLE STATUS语句查看视图详细信息:
和SHOW TABLES语句一样,SHOW TABLE STATUS语句不仅会显示表的详细信息,同时也会显示视图的详细信息。
语法如下:
SHOW TABLE STATUS [FROM db_name] [LIKE ‘pattern’]
//参数db_name用来设置数据库,SHOW TABLES STATUS表示将显示所设置库的表和视图的详细信息。
//设置LIKE关键字,可以查看某一个具体表或视图的详细信息。例如: SHOW TABLE STATUS FROM zhaojd LIKE ‘t_group’ \G
3.3 SHOW CREATE VIEW语句查看视图定义信息:
语法为:
SHOW CREATE VIEW viewname;
3.4 DESCRIBE | DESC 语句查看视图定义信息:
语法为:
DESCRIBE | DESC viewname;
3.5 通过系统表查看视图信息: 当MySQL安装成功后,会自动创建系统数据库infomation_schema。在该数据库中存在一个包含视图信息的表格,可以通过查看表格views来查看所有视图的相关信息。
示例:
1 | mysql> use information_schema; |
4.删除视图
在删除视图时首先要确保拥有删除视图的权限。
语法为:
DROP VIEW view_name [,view_name] ……
//从语法可以看出,DROP VIEW一次可以删除多个视图
示例:
1 | mysql> drop view view_test1,view_test2; |
5.修改视图
5.1 CREATE OR REPLACE VIEW语句修改视图: 对于已经创建好的表,尤其是已经有大量数据的表,通过先删除,然后再按照新的表定义重新建表的方式来修改表,需要做很多额外的工作,例如数据的重载等。可是对于视图来说,由于是“虚表”,并没有存储数据,所以完全可以通过该方式来修改视图。
实现思路就是:先删除同名的视图,然后再根据新的需求创建新的视图即可。
1 | DROP VIEW view_name; |
但是如果每次修改视图,都是先删除视图,然后再次创建一个同名的视图,则显得非常麻烦。于是MySQL提供了更方便的实现替换的创建视图的语法,完整语法为:
CREATE OR REPLACE VIEW view_name as 查询语句;
5.2 ALTER语句修改视图:
语法为:
ALTER VIEW view_name as 查询语句;
6.利用视图操作基本表
再MySQL中可以通常视图检索基本表数据,这是视图最基本的应用,除此之后还可以通过视图修改基本表中的数据。
6.1检索(查询)数据: 通过视图查询数据,与通过表进行查询完全相同,只不过通过视图查询表更安全,更简单实用。只需要把表名换成视图名即可。
6.2利用视图操作基本表数据: 由于视图是“虚表”,所以对视图数据进行的更新操作,实际上是对其基本表数据进行的更新操作。在具体更新视图数据时,需要注意以下两点;
1. 对视图数据进行添加、删除直接影响基本表。
2. 视图来自于多个基本表时,不允许添加、删除数据。
视图中的添加数据操作、删除数据操作、更新数据操作的语法同表完全相同。只是将表名换成视图名即可。
Mysql–内部
触发器
什么是触发器?
触发器是数据库的一个程序,他是用来监听着数据表的某个行为,一旦数据表的这个行为发生了,马上执行相应的sql语句
触发器的语法结构:
create trigger 触发器的名称触发器事件 on 监听的表名 for each row 行为发生后执行的sql语句
注意:行为发生后执行的sql语句可以是一句SQL语句,或者用 BEGIN 和 END 包含的多条语句。
触发器事件两个点:
- 触发器事件发生的时间—–是在监听的表的行为 after before 常用的是after
- 触发器执行的内容:增删改
建立一个表
1 | CREATE TABLE cmd ( |
建立一个要被触发的表
1 | CREATE TABLE errlog ( |
建立触发器
1 | delimiter $$ #把;变换成$$,让下面的代码完整运行 |
插入数据
1 | INSERT INTO cmd ( |
删除触发器
1 | drop trigger tri_after_insert_cmd; |
我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然后执行,而存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储过程带有参数)来调用执行它。
触发器案例研究
一旦生成订单,对应的库存表要减去相应的数据
(1)建两张表 :一个商品goods表 一个订单order表
1 | -- 创建表 |
(2)创建触发器
1 | create trigger alter_goods_number after insert on orders for each row |
new和old的使用
1 | 在 INSERT 型触发器中,NEW 用来表示将要(BEFORE)或已经(AFTER)插入的新数据; |
关键词new 的使用
1 | create trigger alter_goods_number after insert on orders for each row |
关键词old 的使用:
1 | create trigger back_goods_number after delete on orders for each row |
更新 (update将之前下的订单撤销,再重新下订单)
1 | create trigger update_goods_number after update on orders for each row |
查看触发器
1 | SHOW TRIGGERS [FROM schema_name]; |
其中,schema_name 即 Schema 的名称,在 MySQL 中 Schema 和 Database 是一样的,也就是说,可以指定数据库名,这样就 不必先USE database_name了
删除触发器
和删除数据库、删除表格一样,删除触发器的语法如下:
1 | DROP TRIGGER [IF EXISTS][schema_name.]trigger_name |
触发器的执行顺序
我们建立的数据库一般都是 InnoDB 数据库,其上建立的表是事务性表,也就是事务安全的。这时,若SQL语句或触发器执行失败,MySQL 会回滚事务,有:
①如果 BEFORE 触发器执行失败,SQL 无法正确执行。
②SQL 执行失败时,AFTER 型触发器不会触发。
③AFTER 类型的触发器执行失败,SQL 会回滚。
一、存储过程
方案
存储过程(Stored Procedure)是
在大型数据库系统中,一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户
通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。而我们常用的操作数据库语言SQL语句在执行的时
候需要要先编译,然后执行,所以执行的效率没有存储过程高。
存储过程优点如下:
重复使用。存储过程可以重复使用,从而可以减少数据库开发人员的工作量。提高性能。存储过程在创建的时候在进行了编译,将来使用的时候不再重新翻译。一般的SQL语句每执行一次就需要编译一次,所以使用存储过程提高了效率。减少网络流量。存储过程位于服务器上,调用的时候只需要传递存储过程的名称以及参数就可以了,因此降低了网络传输的数据量。安全性。参数化的存储过程可以防止SQL注入式攻击,而且可以将Grant、Deny以及Revoke权限应用于存储过程。
存储过程简单语法:
1 | CREATE PROCEDURE 存储过程名称( |
二、参数介绍示例
无参存储过程
1 | # 无参 |
有参存储过程
1 | delimiter $$ |
只带IN(输入参数)的存储过程
表示该参数的值必须在调用存储过程时指定,在存储过程中修改该参数的值不能被返回,为默认值。
1 | DROP PROCEDURE IF EXISTS proc_person_findById; |
View Code
3、只带OUT(输出参数)的存储过程
该值可在存储过程内部被改变,并可返回。
1 | DELIMITER ! |
View Code
4、带IN(输入参数)和OUT(输出参数),调用时指定,并且可被改变和返回
1 | DELIMITER ! |
View Code
5、带INOUT(输入输出)参数的存储过程
1 | DELIMITER ! |
View Code
注意:关于输入输出参数
IN为输入, 定义参数时,可以不加,不加则默认为输入参数。OUT为输出,定义参数时,必须加上。INOUT为输入和输出,必须加上。表示该参数可以输入也可在处理后存放结果进行输出。
pymysql调用存储过程
cursor.callproc(‘p4’,(3,111))
调用pymysql里的callproc方法,只有在这端的连接上才能查询到那个变量
#set @_p4_0 = 3; set @_p4_1 = 111
print(cursor.fetchall())
cursor.execute(‘select @_p4_1;’)
print(cursor.fetchone())
事务
pymysql模块
连接msyql
1 | import pymysql |
user_info插入列表里的数据增
方法一
1 | for user in user_info: |
方法二
1 | sql='insert into t1 values(%s,%s);' |
删除一条信息
1 | cursor.execute('delete from t1 where id=3;') |
更改信息
1 | cursor.execute("update moneyTable set money = money - 50 where name = '小明'") |
查询语句
1 | user_name = input('请输入账号名:').strip() |
查询语句把结果拿到
1 | sql = 'select id from user where id>3;' |
控制指针,有2种方式(这样就可以实现重复读取信息)
1 | cursor.scroll(0, mode='absolute') # 绝对位置移动(从行首开始) |
sql注入问题
–注入
1 | 最终产生的sql select *from user where user = 'yy' -- ' and pwd = '987657890'; |
or注入
1 | "select *from user where user = 'axxax' or 1=1; |
只能避免 黑客 从你的客户端软件注入 sql
黑客模拟客户端发送真正请求
1 | 但是无法避免 中间人攻击(在你的客户端和服务器中间加一个中转服务器) |
总结:
python如何避免sql注入? 把你的slq(用户输入的)参数 放execute函数的arg参数中 让pymysql 自动帮你屏蔽注入攻击
索引
索引:
本质都是通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。
对于Innodb,主键毫无疑问是一个聚集索引。但是当一个表没有主键,或者没有一个索引,Innodb会如何处理呢。请看如下规则:
- 如果一个主键被定义了,那么这个主键就是作为聚集索引。
- 如果没有主键被定义,那么该表的第一个唯一非空索引被作为聚集索引。
如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键是一个6个字节的列,该列的值会随着数据的插入自增。
解释:什么叫索引项的排序方式和表中数据记录排序方式一致呢? 我们把一本字典看做是数据库的表,那么字典的拼音目录就是聚集索引,它按照A-Z排列。实际存储的字也是按A-Z排列的。这就是索引项的排序方式和表中数据记录排序方式一致。
二 索引语法
创建索引
1 | --创建表时 |
添加和删除索引
1 | ---添加索引 |
三 索引测试实验
1 | --创建表 |
table
1 | 索引遵循:最左前缀匹配原则 |
全部搞索引插入就会变慢
基本函数
1、数学函数
1 | ABS(x) 返回x的绝对值 |
2、聚合函数
常用于GROUP BY从句的SELECT查询中
1 | AVG(col)返回指定列的平均值 |
3、字符串函数
1 | ASCII(char)返回字符的ASCII码值 |
4、日期和时间函数
1 | CURDATE()或CURRENT_DATE() 返回当前的日期 |
5、格式化函数
1 | DATE_FORMAT(date,fmt) 依照字符串fmt格式化日期date值 |
示例:
1 | SELECT FORMAT(34234.34323432,3); |
学生选课系统表练习
设计表关系
创建表和插入数据
1 |
|
查询条件
1、查询所有的课程的名称以及对应的任课老师姓名
2、查询学生表中男女生各有多少人
3、查询物理成绩等于100的学生的姓名
4、查询平均成绩大于八十分的同学的姓名和平均成绩
5、查询所有学生的学号,姓名,选课数,总成绩
6、 查询姓李老师的个数
7、 查询没有报李平老师课的学生姓名
8、 查询物理课程比生物课程高的学生的学号
9、 查询没有同时选修物理课程和体育课程的学生姓名
10、查询挂科超过两门(包括两门)的学生姓名和班级
11 、查询选修了所有课程的学生姓名
12、查询李平老师教的课程的所有成绩记录
13、查询全部学生都选修了的课程号和课程名
14、查询每门课程被选修的次数
15、查询之选修了一门课程的学生姓名和学号
16、查询所有学生考出的成绩并按从高到低排序(成绩去重)
17、查询平均成绩大于85的学生姓名和平均成绩
18、查询生物成绩不及格的学生姓名和对应生物分数
19、查询在所有选修了李平老师课程的学生中,这些课程(李平老师的课程,不是所有课程)平均成绩最高的学生姓名
答案
1、
1 | SELECT cname,tname FROM course LEFT JOIN teacher on course.teacher_id=teacher.tid |
2、
1 | SELECT gender 性别 ,COUNT(gender) 人数 from student GROUP BY gender; |
3、
1 | SELECT sname from student where sid in (SELECT student_id from score LEFT JOIN course |
4、SELECT sname 姓名,平均成绩 from student RIGHT JOIN
(SELECT student_id,avg(num) 平均成绩
from score GROUP BY student_id
HAVING avg(num)>80) as A
on student.sid=A.student_id;
5、SELECT student_id 学号,sname 姓名,COUNT(course_id) 课程数,SUM(num) 总分 FROM score
LEFT JOIN student ON score.student_id=student.sid GROUP BY student_id;
7、SELECT * FROM student
WHERE sid not in
(SELECT student_id FROM score WHERE course_id in
(SELECT cid FROM course LEFT JOIN teacher ON teacher.tid=course.teacher_id
WHERE tname like “李平%”))
8、SELECT * FROM
(SELECT * FROM score LEFT JOIN course
ON score.course_id=course.cid WHERE cname=”生物”) as A
INNER JOIN
(SELECT * FROM score LEFT JOIN course
ON score.course_id=course.cid WHERE cname=”物理”) as B
ON A.student_id=B.student_id
WHERE a.num<b.num
9、SELECT sname from student
WHERE sid NOT IN (SELECT student_id FROM score LEFT JOIN course
ON score.course_id=course.cid
WHERE cname=’体育’ OR cname=’物理’
GROUP BY student_id
HAVING COUNT(course_id)>1) ;
10、SELECT sname,gender from student
WHERE sid in (SELECT student_id from score WHERE num<60
GROUP BY student_id
HAVING COUNT(student_id)>1);
11、SELECT sid 学号,sname 姓名 from student
WHERE sid in (SELECT student_id FROM score
GROUP BY student_id
HAVING COUNT(student_id)!= (SELECT COUNT(cid) FROM course))
12、SELECT * FROM score
WHERE course_id NOT IN (SELECT cid from teacher INNER JOIN course
on teacher.tid=course.teacher_id
WHERE teacher.tname LIKE “李平%”);
13、SELECT course_id from score
GROUP BY course_id
HAVING COUNT(course_id)=(SELECT COUNT(sid) FROM student)
14、– SELECT course_id,COUNT(course_id) from score GROUP BY course_id
SELECT cname,COUNT(course_id) FROM score LEFT JOIN course
ON score.course_id=course.cid GROUP BY course_id
15、SELECT sid,sname FROM student
WHERE sid in (SELECT student_id from score
GROUP BY student_id
HAVING COUNT(sid)=1);
16、SELECT DISTINCT num FROM score ORDER BY num DESC;
17、SELECT sname from student
WHERE sid in (SELECT student_id FROM score
GROUP BY student_id
HAVING AVG(num)>85)
注意:用这种方式我们无法查看对应的平均成绩,所以不得不使用连表:
SELECT sname,AVG(num) FROM student LEFT JOIN score
ON student.sid=score.student_id
GROUP BY student_id HAVING AVG(num)>85;
18、SELECT sname 姓名,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 cname=”生物”;
19、 SELECT student_id,avg(num),sname FROM score
LEFT JOIN student ON score.student_id=student.sid
WHERE course_id in (SELECT cid FROM course
LEFT JOIN teacher ON teacher.tid=course.teacher_id WHERE tname LIKE “李平%”)
GROUP BY student_id
ORDER BY AVG(num) DESC
LIMIT 1