什么是数据库 ?
数据的仓库,如:在ATM的示例中我们创建了一个 db 目录,称其为数据库
什么是 MySQL、Oracle、SQLite、Access、MS SQL Server等 ?
他们均是一个软件,都有两个主要的功能:
a. 将数据保存到文件或内存
b. 接收特定的命令,然后对文件进行相应的操作
PS:如果有了以上软件,无须自己再去创建文件和文件夹,而是直接传递 命令 给上述软件,让其来进行文件操作,他们统称为数据库管理系统(DBMS,Database Management System)
MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下公司。MySQL 最流行的关系型数据库管理系统,在 WEB 应用方面MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。
想要使用MySQL来存储并操作数据,则需要做几件事情:
a. 安装MySQL服务端
b. 安装MySQL客户端
c. 【客户端】连接【服务端】
d. 【客户端】发送命令给【服务端MySQL】服务的接受命令并执行相应操作(增删改查等)
下载
http://dev.mysql.com/downloads/mysql/
安装
windows:
点点点
Linux:
yum install mysql-server
Mac:
点点点
a. 下载
http://dev.mysql.com/downloads/mysql/
b. 解压
如果想要让MySQL安装在指定目录,那么就将解压后的文件夹移动到指定目录,如:C:\mysql-5.7.16-winx64
c. 初始化
MySQL解压后的 bin 目录下有一大堆的可执行文件,执行如下命令初始化数据:
cd c:\mysql-5.7.16-winx64\bin
mysqld --initialize-insecure
d. 启动MySQL服务
进入可执行文件目录
cd c:\mysql-5.7.16-winx64\bin
启动MySQL服务
mysqld
e. 启动MySQL客户端并连接MySQL服务
由于初始化时使用的【mysqld --initialize-insecure】命令,其默认未给root账户设置密码
# 进入可执行文件目录
cd c:\mysql-5.7.16-winx64\bin
# 连接MySQL服务器
mysql -u root -p
# 提示请输入密码,直接回车
另:
a. 添加环境变量
后续操作会更简单
# 启动MySQL服务,在终端输入
mysqld
# 连接MySQL服务,在终端输入:
mysql -u root -p
b. 将MySQL服务制作成windows服务
上一步解决了一些问题,但不够彻底,因为在执行【mysqd】启动MySQL服务器时,当前终端会被hang住,那么做一下设置即可解决此问题:
# 制作MySQL的Windows服务,在终端执行此命令:
"c:\mysql-5.7.16-winx64\bin\mysqld" --install
# 移除MySQL的Windows服务,在终端执行此命令:
"c:\mysql-5.7.16-winx64\bin\mysqld" --remove
c. 注册成服务之后,以后再启动和关闭MySQL服务时,仅需执行如下命令
# 启动MySQL服务
net start mysql
# 关闭MySQL服务
net stop mysql
2 . Linux版本
a. 安装:
yum install mysql-server
b. 服务端启动
mysql.server start
c. 客户端连接
连接:
mysql -h host -u user -p
常见错误:
ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/tmp/mysql.sock’ (2), it means that the MySQL server daemon (Unix) or service (Windows) is not running.
退出:
QUIT 或者 Control+D
1.显示数据库
SHOW DATABASES;
默认数据库:
mysql - 用户权限相关数据
test - 用于用户测试数据
information_schema - MySQL本身架构相关数据
2. 创建数据库
# utf-8
CREATE DATABASE 数据库名称 DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
# gbk
CREATE DATABASE 数据库名称 DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci;
3.使用数据库
USE db_name;
#显示当前使用的数据库中所有表:
SHOW TABLES;
4.用户管理
#创建用户
create user '用户名'@'IP地址' identified by '密码';
#删除用户
drop user '用户名'@'IP地址';
#修改用户
rename user '用户名'@'IP地址' to '新用户名'@'IP地址';
#修改密码
set password for '用户名'@'IP地址' = Password('新密码')
#PS:用户权限相关数据保存在mysql数据库的user表中,所以也可以直接对其进行操作(不建议)
show grants for '用户'@'IP地址' -- 查看权限
grant 权限 on 数据库.表 to '用户'@'IP地址' -- 授权
revoke 权限 on 数据库.表 from '用户'@'IP地址' -- 取消权限
all privileges 除grant外的所有权限
select 仅查权限
select,insert 查和插入权限
...
usage 无访问权限
alter 使用alter table
alter routine 使用alter procedure和drop procedure
create 使用create table
create routine 使用create procedure
create temporary tables 使用create temporary tables
create user 使用create user、drop user、rename user和revoke all privileges
create view 使用create view
delete 使用delete
drop 使用drop table
execute 使用call和存储过程
file 使用select into outfile 和 load data infile
grant option 使用grant 和 revoke
index 使用index
insert 使用insert
lock tables 使用lock table
process 使用show full processlist
select 使用select
show databases 使用show databases
show view 使用show view
update 使用update
reload 使用flush
shutdown 使用mysqladmin shutdown(关闭MySQL)
super 使用change master、kill、logs、purge、master和set global。还允许mysqladmin调试登陆
replication client 服务器位置的访问
replication slave 由复制从属使用
6.忘记密码处理
# 启动免授权服务端
mysqld --skip-grant-tables
# 客户端
mysql -u root -p
# 修改用户名密码
update mysql.user set authentication_string=password('666') where user='root';
flush privileges;
1.创建表
create table 表名(
列名 类型 是否可以为空,
列名 类型 是否可以为空
)ENGINE=InnoDB DEFAULT CHARSET=utf8
是否可空
null表示空,非字符串
not null - 不可空
null - 可空
默认值
创建列时可以指定默认值,当插入数据时如果未主动设置,则自动添加默认值
create table tb1(
nid int not null defalut 2,
num int not null
)
自增
如果为某列设置自增列,插入数据时无需设置此列,默认将自增(表中只能有一个自增列)
create table tb1(
nid int not null auto_increment primary key,
num int null
)
或
create table tb1(
nid int not null auto_increment,
num int null,
index(nid)
)
注意:
1、对于自增列,必须是索引(含主键)。
2、对于自增可以设置步长和起始值
show session variables like 'auto_inc%';
set session auto_increment_increment=2;
set session auto_increment_offset=10;
show global variables like 'auto_inc%';
set global auto_increment_increment=2;
set global auto_increment_offset=10;
主键
主键,一种特殊的唯一索引,不允许有空值,如果主键使用单个列,则它的值必须唯一,如果是多列,则其组合必须唯一。
create table tb1(
nid int not null auto_increment primary key,
num int null
)
或
create table tb1(
nid int not null,
num int not null,
primary key(nid,num)
)
外键
外键与主键相对,作用就是通过主外键的之间关系使对张表中的数据更好的关联。
外键特点:
从表外键的值是对主表主键的引用。
从表外键类型,必须与主表主键类型一致。
作用:保持数据一致性,完整性,主要目的是控制存储在外键表中的数据。 使两张表形成关联,外键只能引用外表中的列的值或使用空值
注意:
**阻止执行**
从表插入新行,其外键值不是主表的主键值便阻止插入;
从表修改外键值,新值不是主表的主键值便阻止修改;
主表删除行,其主键值在从表里存在便阻止删除(要想删除,必须先删除从表的相关行);
主表修改主键值,旧值在从表里存在便阻止修改(要想修改,必须先删除从表的相关行)。
**级联执行**
主表删除行,连带从表的相关行一起删除;
主表修改主键值,连带从表相关行的外键值一起修改。两种方法提供给用户选择。无论选取哪种方法,从表里都不会有多余行。从另一个角度理解,用拒绝同一事物在从表中的标志与主表不一致来实现与主表中的标志一致。
例如:
#部门表:
CREATE TABLE dept(
id INT PRIMARY KEY AUTO_INCREMENT,
dname VARCHAR(20) UNIQUE NOT NULL,
dcode INT UNIQUE NOT NULL);
#员工表:
CREATE TABLE emp(
id INT PRIMARY KEY AUTO_INCREMENT,
ename VARCHAR(20) NOT NULL,
ecode INT UNIQUE NOT NULL,
did INT,
CONSTRAINT dept_emp FOREIGN KEY emp(did) REFERENCES dept(id));
#插入数据:
INSERT INTO dept VALUES(NULL,'技术部',100);
INSERT INTO dept VALUES(NULL,'财务部',200);
INSERT INTO dept VALUES(NULL,'人事部',300);
INSERT INTO emp VALUES(NULL,'张三',123,1);
INSERT INTO emp VALUES(NULL,'李四',124,2);
INSERT INTO emp VALUES(NULL,'王五',125,1);
INSERT INTO emp VALUES(NULL,'田七',126,1);
2.删除表
drop table 表名
3.清空表
delete from 表名
truncate table 表名
Truncate Table 表名和不带WHERE的Delete 表名功能相同,都是删除表中的所有行,但是TRUNCATE TABLE比DELETE快,而且TRUNCATE TABLE使用的系统和事务日志资源少。
对于由 FOREIGN KEY 约束引用的表,不能使用 TRUNCATE TABLE,而应使用不带 Where 子句的 Delete 语句。由于 TRUNCATE TABLE 不记录在日志中,所以它不能激活触发器。
4.修改表
添加列: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;
5.基本数据类型
bit[(M)]
二进制位(101001),m表示二进制位的长度(1-64),默认m=1
tinyint[(m)] [unsigned] [zerofill]
小整数,数据类型用于保存一些范围的整数数值范围:
有符号:
-128 ~ 127.
无符号:
0 ~ 255
特别的: MySQL中无布尔值,使用tinyint(1)构造。
int[(m)][unsigned][zerofill]
整数,数据类型用于保存一些范围的整数数值范围:
有符号:
-2147483648 ~ 2147483647
无符号:
0 ~ 4294967295
特别的:整数类型中的m仅用于显示,对存储范围无限制。例如: int(5),当插入数据2时,select 时数据显示为: 00002
bigint[(m)][unsigned][zerofill]
大整数,数据类型用于保存一些范围的整数数值范围:
有符号:
-9223372036854775808 ~ 9223372036854775807
无符号:
0 ~ 18446744073709551615
decimal[(m[,d])] [unsigned] [zerofill]
准确的小数值,m是数字总个数(负号不算),d是小数点后个数。 m最大值为65,d最大值为30。
特别的:对于精确数值计算时需要用此类型
decaimal能够存储精确值的原因在于其内部按照字符串存储。
FLOAT[(M,D)] [UNSIGNED] [ZEROFILL]
单精度浮点数(非准确小数值),m是数字总个数,d是小数点后个数。
无符号:
-3.402823466E+38 to -1.175494351E-38,
0
1.175494351E-38 to 3.402823466E+38
有符号:
0
1.175494351E-38 to 3.402823466E+38
**** 数值越大,越不准确 ****
DOUBLE[(M,D)] [UNSIGNED] [ZEROFILL]
双精度浮点数(非准确小数值),m是数字总个数,d是小数点后个数。
无符号:
-1.7976931348623157E+308 to -2.2250738585072014E-308
0
2.2250738585072014E-308 to 1.7976931348623157E+308
有符号:
0
2.2250738585072014E-308 to 1.7976931348623157E+308
**** 数值越大,越不准确 ****
char (m)
char数据类型用于表示固定长度的字符串,可以包含最多达255个字符。其中m代表字符串的长度。
PS: 即使数据小于m长度,也会占用m长度
varchar(m)
varchars数据类型用于变长的字符串,可以包含最多达255个字符。其中m代表该数据类型所允许保存的字符串的最大长度,只要长度小于该最大值的字符串都可以被保存在该数据类型中。
注:虽然varchar使用起来较为灵活,但是从整个系统的性能角度来说,char数据类型的处理速度更快,有时甚至可以超出varchar处理速度的50%。因此,用户在设计数据库时应当综合考虑各方面的因素,以求达到最佳的平衡
text
text数据类型用于保存变长的大字符串,可以组多到65535 (2**16 − 1)个字符。
mediumtext
A TEXT column with a maximum length of 16,777,215 (2**24 − 1) characters.
longtext
A TEXT column with a maximum length of 4,294,967,295 or 4GB (2**32 − 1) characters.
enum
枚举类型,
An ENUM column can have a maximum of 65,535 distinct elements. (The practical limit is less than 3000.)
示例:
CREATE TABLE shirts (
name VARCHAR(40),
size ENUM('x-small', 'small', 'medium', 'large', 'x-large')
);
INSERT INTO shirts (name, size) VALUES ('dress shirt','large'), ('t-shirt','medium'),('polo shirt','small');
set
集合类型
A SET column can have a maximum of 64 distinct members.
示例:
CREATE TABLE myset (col SET('a', 'b', 'c', 'd'));
INSERT INTO myset (col) VALUES ('a,d'), ('d,a'), ('a,d,a'), ('a,d,d'), ('d,a,d');
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 年某时)
1.增
insert into 表 (列名,列名...) values (值,值,值...)
insert into 表 (列名,列名...) values (值,值,值...),(值,值,值...)
insert into 表 (列名,列名...) select (列名,列名...) from 表
2.删
delete from 表
delete from 表 where id=1 and name='alex'
3.改
update 表 set name = 'alex' where id>1
4.查
select * from 表
select * from 表 where id > 1
select nid,name,gender as gg from 表 where id > 1
5.其他
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
特别的:group by 必须在where之后,order by之前
f、连表
无对应关系则不显示
select A.num, A.name, B.name
from A,B
Where A.nid = B.nid
无对应关系则不显示
select A.num, A.name, B.name
from A inner join B
on A.nid = B.nid
A表所有显示,如果B中无对应关系,则值为null
select A.num, A.name, B.name
from A left join B
on A.nid = B.nid
B表所有显示,如果B中无对应关系,则值为null
select A.num, A.name, B.name
from A right join B
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
表关系:
class:
course:
student:
teacher:
score:
题目说明:
https://www.cnblogs.com/wupeiqi/articles/5748496.html
1、自行创建测试数据
导出现有数据库数据:
mysqldump -u用户名 -p密码 数据库名称 >导出文件路径 # 结构+数据
mysqldump -u用户名 -p密码 -d 数据库名称 >导出文件路径 # 结构
导入现有数据库数据:
mysqldump -uroot -p密码 数据库名称 < 文件路径
创建相关sql:
/*
Navicat Premium Data Transfer
Source Server : localhost
Source Server Type : MySQL
Source Server Version : 50624
Source Host : localhost
Source Database : sqlexam
Target Server Type : MySQL
Target Server Version : 50624
File Encoding : utf-8
Date: 10/21/2016 06:46:46 AM
*/
SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for `class`
-- ----------------------------
DROP TABLE IF EXISTS `class`;
CREATE TABLE `class` (
`cid` int(11) NOT NULL AUTO_INCREMENT,
`caption` varchar(32) NOT NULL,
PRIMARY KEY (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of `class`
-- ----------------------------
BEGIN;
INSERT INTO `class` VALUES ('1', '三年二班'), ('2', '三年三班'), ('3', '一年二班'), ('4', '二年九班');
COMMIT;
-- ----------------------------
-- Table structure for `course`
-- ----------------------------
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
`cid` int(11) NOT NULL AUTO_INCREMENT,
`cname` varchar(32) NOT NULL,
`teacher_id` int(11) NOT NULL,
PRIMARY KEY (`cid`),
KEY `fk_course_teacher` (`teacher_id`),
CONSTRAINT `fk_course_teacher` FOREIGN KEY (`teacher_id`) REFERENCES `teacher` (`tid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of `course`
-- ----------------------------
BEGIN;
INSERT INTO `course` VALUES ('1', '生物', '1'), ('2', '物理', '2'), ('3', '体育', '3'), ('4', '美术', '2');
COMMIT;
-- ----------------------------
-- Table structure for `score`
-- ----------------------------
DROP TABLE IF EXISTS `score`;
CREATE TABLE `score` (
`sid` int(11) NOT NULL AUTO_INCREMENT,
`student_id` int(11) NOT NULL,
`course_id` int(11) NOT NULL,
`num` int(11) NOT NULL,
PRIMARY KEY (`sid`),
KEY `fk_score_student` (`student_id`),
KEY `fk_score_course` (`course_id`),
CONSTRAINT `fk_score_course` FOREIGN KEY (`course_id`) REFERENCES `course` (`cid`),
CONSTRAINT `fk_score_student` FOREIGN KEY (`student_id`) REFERENCES `student` (`sid`)
) ENGINE=InnoDB AUTO_INCREMENT=53 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of `score`
-- ----------------------------
BEGIN;
INSERT INTO `score` VALUES ('1', '1', '1', '10'), ('2', '1', '2', '9'), ('5', '1', '4', '66'), ('6', '2', '1', '8'), ('8', '2', '3', '68'), ('9', '2', '4', '99'), ('10', '3', '1', '77'), ('11', '3', '2', '66'), ('12', '3', '3', '87'), ('13', '3', '4', '99'), ('14', '4', '1', '79'), ('15', '4', '2', '11'), ('16', '4', '3', '67'), ('17', '4', '4', '100'), ('18', '5', '1', '79'), ('19', '5', '2', '11'), ('20', '5', '3', '67'), ('21', '5', '4', '100'), ('22', '6', '1', '9'), ('23', '6', '2', '100'), ('24', '6', '3', '67'), ('25', '6', '4', '100'), ('26', '7', '1', '9'), ('27', '7', '2', '100'), ('28', '7', '3', '67'), ('29', '7', '4', '88'), ('30', '8', '1', '9'), ('31', '8', '2', '100'), ('32', '8', '3', '67'), ('33', '8', '4', '88'), ('34', '9', '1', '91'), ('35', '9', '2', '88'), ('36', '9', '3', '67'), ('37', '9', '4', '22'), ('38', '10', '1', '90'), ('39', '10', '2', '77'), ('40', '10', '3', '43'), ('41', '10', '4', '87'), ('42', '11', '1', '90'), ('43', '11', '2', '77'), ('44', '11', '3', '43'), ('45', '11', '4', '87'), ('46', '12', '1', '90'), ('47', '12', '2', '77'), ('48', '12', '3', '43'), ('49', '12', '4', '87'), ('52', '13', '3', '87');
COMMIT;
-- ----------------------------
-- Table structure for `student`
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`sid` int(11) NOT NULL AUTO_INCREMENT,
`gender` char(1) NOT NULL,
`class_id` int(11) NOT NULL,
`sname` varchar(32) NOT NULL,
PRIMARY KEY (`sid`),
KEY `fk_class` (`class_id`),
CONSTRAINT `fk_class` FOREIGN KEY (`class_id`) REFERENCES `class` (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of `student`
-- ----------------------------
BEGIN;
INSERT INTO `student` VALUES ('1', '男', '1', '理解'), ('2', '女', '1', '钢蛋'), ('3', '男', '1', '张三'), ('4', '男', '1', '张一'), ('5', '女', '1', '张二'), ('6', '男', '1', '张四'), ('7', '女', '2', '铁锤'), ('8', '男', '2', '李三'), ('9', '男', '2', '李一'), ('10', '女', '2', '李二'), ('11', '男', '2', '李四'), ('12', '女', '3', '如花'), ('13', '男', '3', '刘三'), ('14', '男', '3', '刘一'), ('15', '女', '3', '刘二'), ('16', '男', '3', '刘四');
COMMIT;
-- ----------------------------
-- Table structure for `teacher`
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`tid` int(11) NOT NULL AUTO_INCREMENT,
`tname` varchar(32) NOT NULL,
PRIMARY KEY (`tid`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of `teacher`
-- ----------------------------
BEGIN;
INSERT INTO `teacher` VALUES ('1', '张磊老师'), ('2', '李平老师'), ('3', '刘海燕老师'), ('4', '朱云海老师'), ('5', '李杰老师');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
表结构和数据
2、查询“生物”课程比“物理”课程成绩高的所有学生的学号;
3、查询平均成绩大于60分的同学的学号和平均成绩;
思路1:
根据学生分组,使用avg获取平均值,通过having对avg进行筛选
SELECT
score.student_id,
AVG(score.num)
FROM
score
GROUP BY
score.student_id
HAVING
AVG(score.num) > 60
思路2:
根据学生分组,计算学生的总成绩,然计算平均分
SELECT
score.student_id,
AVG(score.num)
FROM
score
GROUP BY
score.student_id
HAVING
(
SUM(score.num) / COUNT(score.num)
) > 60
另:如果需要展示学生名字
思路:先联合student和score,然后按条件查询
SELECT
student_id,
AVG(num),
sname
FROM
score
LEFT JOIN student ON score.student_id = student.sid
GROUP BY
student_id
HAVING
AVG(num) > 60
SELECT
student_id,
AVG(num),
sname
FROM
score,
student
WHERE
score.student_id = student.sid
GROUP BY
student_id
HAVING
AVG(score.num) > 60
4、查询所有同学的学号、姓名、选课数、总成绩;
SELECT
sid,
sname,
COUNT(scid),
SUM(num)
FROM
(
SELECT
student.sid,
student.sname,
score.sid AS scid,
score.num
FROM
score
LEFT JOIN student ON score.student_id = student.sid
) AS A
GROUP BY
sid
5、查询姓“李”的老师的个数;
SELECT
COUNT(*)
FROM
teacher
WHERE
tname LIKE "李%"
6、查询没学过“叶平”老师课的同学的学号、姓名;
SELECT DISTINCT
(student.sid),
student.sname
FROM
(
SELECT
*
FROM
score
WHERE
score.course_id NOT IN(
SELECT
cid
FROM
course,
teacher
WHERE
teacher.tname = "叶平" AND teacher.tid = course.teacher_id
)
) AS A
LEFT JOIN student ON A.student_id = student.sid
7、查询学过“001”并且也学过编号“002”课程的同学的学号、姓名;
思路:
先查到既选择001又选择002课程的所有同学
根据学生进行分组,如果学生数量等于2表示,两门均已选择
SELECT
B.student_id,
student.sname
FROM
(
SELECT
*
FROM
(
SELECT
*
FROM
score
WHERE
course_id IN(1, 2)
) AS A
GROUP BY
A.student_id
HAVING
COUNT(*) = 2
) AS B,
student
WHERE
student.sid = B.student_id
8、查询学过“叶平”老师所教的所有课的同学的学号、姓名;
SELECT
B.student_id,
student.sname
FROM
(
SELECT
A.student_id
FROM
(
SELECT
*
FROM
`score`
WHERE
score.course_id IN(
SELECT
course.cid
FROM
course
WHERE
course.teacher_id IN(
SELECT
teacher.tid
FROM
teacher
WHERE
teacher.tname = "李平老师"
)
)
) AS A
GROUP BY
A.student_id
HAVING
COUNT(*) = 2
) AS B
LEFT JOIN student ON B.student_id = student.sid
9、查询课程编号“002”的成绩比课程编号“001”课程低的所有同学的学号、姓名;
SELECT
student.sid,
student.sname,
T.c1,
T.num1,
T.c2,
T.num2
FROM
(
SELECT
s1.student_id,
s1.course_id AS c1,
s1.num AS num1,
s2.course_id AS c2,
s2.num AS num2
FROM
(
SELECT
*
FROM
score
WHERE
score.course_id = 2
) AS s1,
(
SELECT
*
FROM
score
WHERE
score.course_id = 1
) AS s2
WHERE
s1.student_id = s2.student_id AND s1.num < s2.num
) AS T
LEFT JOIN student ON T.student_id = student.sid
10、查询有课程成绩小于60分的同学的学号、姓名;
SELECT
sid,
sname
FROM
student
WHERE
sid IN(
SELECT
student_id
FROM
score
WHERE
score.num < 60
)
SELECT
student.sid,
student.sname
FROM
(
SELECT DISTINCT
student_id
FROM
score
WHERE
score.num < 60
) AS A
LEFT JOIN student ON A.student_id = student.sid
11、查询没有学全所有课的同学的学号、姓名;
思路:
在分数表中根据学生进行分组,获取每一个学生选课数量
如果数量 == 总课程数量,表示已经选择了所有课程
SELECT
sid,
sname
FROM
student
WHERE
sid IN(
SELECT
score.student_id
FROM
score
GROUP BY
score.student_id
HAVING
COUNT(*) !=(
SELECT
COUNT(*)
FROM
`course`
WHERE
1
)
)
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.sid,
student.sname
FROM
student
WHERE
student.sid IN(
SELECT DISTINCT
score.student_id
FROM
score
WHERE
score.student_id != 1 AND score.course_id IN(
SELECT
score.course_id
FROM
score
WHERE
score.student_id = 1
)
)
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的学过的所有人
SELECT DISTINCT
(student.sid),
sname
FROM
(
SELECT
student_id
FROM
score
WHERE
score.course_id IN(
SELECT
score.course_id
FROM
score
WHERE
score.student_id = 1
) AND score.student_id != 1
) AS A
LEFT JOIN student ON A.student_id = student.sid
14、查询和“002”号的同学学习的课程完全相同的其他同学学号和姓名;
SELECT
student.sid,
student.sname
FROM
student
WHERE
student.sid IN(
SELECT
score.student_id
FROM
score
GROUP BY
score.student_id
HAVING
score.student_id != 2 AND COUNT(*) >=(
SELECT
COUNT(*)
FROM
score
WHERE
score.student_id = 2
)
)
SELECT
score.student_id,
student.sname,
COUNT(*)
FROM
score
LEFT JOIN student ON score.student_id = student.sid
GROUP BY
score.student_id
HAVING
score.student_id != 2 AND COUNT(*) >=(
SELECT
COUNT(*)
FROM
score
WHERE
score.student_id = 2
)
15、删除学习“叶平”老师课的SC表记录;
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 sw, (
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 sc.student_id ORDER by AVG(sc.num) asc
18、查询各科成绩最高和最低的分:以如下形式显示:课程ID,最高分,最低分;
SELECT
score.course_id,
MAX(score.num) AS max_num,
MIN(score.num) AS min_num
FROM
score
GROUP BY
score.course_id
19、按各科平均成绩从低到高和及格率的百分数从高到低顺序;
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
A.course_id,
A.avgnum,
teacher.tname
FROM
(
SELECT
score.course_id,
AVG(
IF(ISNULL(score.num),
0,
score.num)
) AS avgnum
FROM
score
GROUP BY
score.course_id
ORDER BY
avgnum
DESC
) AS A
LEFT JOIN course ON A.course_id = course.cid
LEFT JOIN teacher ON course.teacher_id = teacher.tid
21、查询各科成绩前三名的记录:(不考虑成绩并列情况)
自连接:
SELECT
sc.course_id,
sc.num
FROM
score AS sc
WHERE
(
SELECT
COUNT(*)
FROM
score
WHERE
sc.course_id = score.course_id AND sc.num < score.num
) < 2
ORDER BY
sc.course_id ASC,
sc.num
DESC
22、查询每门课程被选修的学生数;
select course_id, count(1) from score group by course_id;
23、查询出只选修了一门课程的全部学生的学号和姓名;
SELECT
score.student_id,
student.sname
FROM
score
LEFT JOIN student ON score.student_id = student.sid
GROUP BY
score.student_id
HAVING
COUNT(score.student_id) = 1
24、查询男生、女生的人数;
SELECT
*
FROM
(
SELECT
COUNT(*) AS man
FROM
student
WHERE
student.gender = "男"
) AS nan,
(
SELECT
COUNT(*) AS feman
FROM
student
WHERE
student.gender = "女"
) AS nv
25、查询姓“张”的学生名单;
select * from student where student.sname like "张%"
26、查询同名同姓学生名单,并统计同名人数;
select *,count(*) from student GROUP by student.sname
27、查询每门课程的平均成绩,结果按平均成绩升序排列,平均成绩相同时,按课程号降序排列;
select score.course_id, AVG(if(isnull(num), 0 ,num)) as avgnum from score GROUP by score.course_id ORDER by avgnum ASC,score.course_id desc
28、查询平均成绩大于80的所有学生的学号、姓名和平均成绩;
select score.student_id, student.sname, avg(if(isnull(num),0,num)) as avgnum from score left JOIN student on score.student_id=student.sid GROUP by score.student_id HAVING avgnum>80
29、查询课程名称为“生物”,且分数低于60的学生姓名和分数;
select student.sname,score.num from score left JOIN student on score.student_id=student.sid left JOIN course on course.cid=score.course_id where course.cname="生物" and score.num<60
30、查询课程编号为003且课程成绩在80分以上的学生的学号和姓名;
select student.sid, student.sname,score.num from score LEFT JOIN student on student.sid=score.student_id where score.course_id=3 and score.num>80
31、求选了课程的学生人数
select COUNT(*) from (SELECT * from score GROUP BY score.student_id) as A
select count(distinct student_id) from score
32、查询选修“叶平”老师所授课程的学生中,成绩最高的学生姓名及其成绩;
SELECT
student.sid,
student.sname,
MAX(score.num)
FROM
score
LEFT JOIN course ON score.course_id = course.cid
LEFT JOIN teacher ON teacher.tid = course.teacher_id
LEFT JOIN student ON student.sid = score.student_id
WHERE
teacher.tname = "叶平"
33、查询各个课程及相应的选修人数;
SELECT
score.course_id,
course.cname,
COUNT(*)
FROM
score
LEFT JOIN course ON score.course_id = course.cid
GROUP BY
score.course_id
34、查询不同课程但成绩相同的学生的学号、课程号、学生成绩;
SELECT DISTINCT
s1.student_id,
s1.course_id,
s1.num,
s2.course_id,
s2.num
FROM
score AS s1,
score AS s2
WHERE
s1.course_id != s2.course_id AND s1.num = s2.num
35、查询每门课程成绩最好的前两名;
36、检索至少选修两门课程的学生学号;
select score.student_id,COUNT(*) from score GROUP by score.student_id HAVING count(*)>=2
37、查询全部学生都选修的课程的课程号和课程名;
select score.course_id,count(*) from score group by score.course_id HAVING count(*)=(select count(*) from student)
38、查询没学过“叶平”老师讲授的任一门课程的学生姓名;
SELECT
score.student_id
FROM
score
GROUP BY
score.student_id
HAVING
SUM(
CASE WHEN score.course_id IN(
SELECT
course.cid
FROM
course,
teacher
WHERE
course.teacher_id = teacher.tid AND teacher.tname = "叶平"
) THEN 1 ELSE 0
END
) = 0
39、查询两门以上不及格课程的同学的学号及其平均成绩;
SELECT
student_id,
COUNT(1)
FROM
score
WHERE
num < 60
GROUP BY
student_id
HAVING
COUNT(1) > 2
40、检索“004”课程分数小于60,按分数降序排列的同学学号;
SELECT
score.student_id,
score.num
FROM
score
WHERE
score.course_id = 4 AND score.num < 60
ORDER BY
num
DESC
41、删除“002”同学的“001”课程的成绩;
delete from score where course_id = 1 and student_id = 2
pip install pymysql
import pymysql
#创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='test')
#创建游标
cursor = conn.cursor()
#SQL普通执行
#执行SQL1
# result = cursor.execute("select * from student where sid>1")
# print(result)
#SQL拼接执行:严禁使用,会出现SQL注入漏洞
table = "student"
sql = "select * from %s"%(table)
result = cursor.execute(sql)
#SQL参数传递执行:必须使用这种
#执行SQL2,元组传递参数
# db = "student"
# sql = "select * from %s"%db
# result = cursor.execute("select * from student where sid>%s", (1,))
# print(result)
#执行SQL3,元组传递多个参数
# result = cursor.execute("select * from score where sid>%s and course_id=%s",(3,4))
# print(result)
#执行SQL4,列表传参
# result = cursor.execute("select * from score where sid>%s and course_id=%s",[3, 4])
# print(result)
#执行SQL5,字典传参
# result = cursor.execute("select * from score where sid>%(ID)s and course_id=%(courseID)s",{"ID":3,"courseID":4})
# print(result)
# 执行SQL6,多参数
# result = cursor.executemany("select * from score where course_id=%(courseID)s and num=%(num)s",[{"courseID":1,"num":60},{"courseID":3,"num":67}])
# print(result)
#从内存中取出,类似指针
#获取第一行数据
row_1 = cursor.fetchone()
print(row_1) #(1, '三年二班')
#获取前n行数据
row_2 = cursor.fetchmany(3)
print(row_2) #((1, '三年二班'), (2, '三年三班'), (3, '一年二班'))
# 获取所有数据
row_3 = cursor.fetchall()
print(row_3) #((1, '三年二班'), (2, '三年三班'), (3, '一年二班'), (4, '二年九班'))
# 对于数据的更改(新增,更新,删除),需要提交,不然无法保存新建或者修改的数据
conn.commit()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
注意
# 获取最新自增ID
new_id = cursor.lastrowid
在fetch数据时按照顺序进行,可以使用cursor.scroll(num,mode)来移动游标位置
cursor.scroll(1,mode='relative') # 相对当前位置移动
cursor.scroll(2,mode='absolute') # 相对绝对位置移动
row_1 = cursor.fetchone()
print(row_1) #(1, '男', 1, '理解')
cursor.scroll(0,mode='absolute') # 相对绝对位置移动
row_1 = cursor.fetchone()
print(row_1) #(1, '男', 1, '理解')
cursor.scroll(-1,mode='relative') # 相对绝对位置移动:-1,0,1
row_1 = cursor.fetchone()
print(row_1) #(2, '女', 1, '钢蛋')
关于默认获取的数据是元祖类型,如果想要或者字典类型的数据,即:
import pymysql
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='test')
# 游标设置为字典类型
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
r = cursor.execute("select * from student where sid<5")
result = cursor.fetchall()
print(result)
'''
[{
'sid': 1,
'gender': '男',
'class_id': 1,
'sname': '理解'
}, {
'sid': 2,
'gender': '女',
'class_id': 1,
'sname': '钢蛋'
}, {
'sid': 3,
'gender': '男',
'class_id': 1,
'sname': '张三'
}, {
'sid': 4,
'gender': '男',
'class_id': 1,
'sname': '张一'
}
]
'''
conn.commit()
cursor.close()
conn.close()
import pymysql
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='test')
# 带参数执行:安全
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
r = cursor.execute("select * from user where uname=%s and password=%s", ("admin","password"))
result1 = cursor.fetchall()
print(result1) # [{'uid': 8, 'uname': 'admin', 'password': 'password'}]
# 字符串拼接
sql = 'select * from user where uname="%s" and password="%s"'
# #1.用户名和密码都正确
# sql = sql % ("admin","password")
# cursor.execute(sql)
# result2 = cursor.fetchall()
# print(result2) # [{'uid': 8, 'uname': 'admin', 'password': 'password'}]
#2.SQL注入
sql = sql % ('admin" or 1=1 -- ',"password") # "#"和" -- "都表示注释,那么后面的语句则无法执行
#sql = sql % ('admin1" or 1=1 -- ',"password")
print(sql) # select * from user where uname="admin" or 1=1 #" and password="password"
cursor.execute(sql)
result3 = cursor.fetchall()
print(result3)
# [{'uid': 6, 'uname': 'shitou', 'password': '123456'}, {'uid': 7, 'uname': 'frankshi', 'password': '98765'}, {'uid': 8, 'uname': 'admin', 'password': 'password'}]
conn.commit()
cursor.close()
conn.close()
视图是一个虚拟表(非真实存在),其本质是【根据SQL语句获取动态的数据集,并为其命名】,用户使用时只需使用【名称】即可获取结果集,并可以将其当作表来使用。
SELECT
*
FROM
(
SELECT
course_id,
student_id
FROM
score
WHERE
score.course_id = 2
) AS A
WHERE
A.student_id IN(1, 2)
CREATE VIEW V2 AS SELECT
score.sid,
score.student_id,
student.sname,
score.course_id,
score.num
FROM
score
LEFT JOIN student ON score.student_id = student.sid
--格式:CREATE VIEW 视图名称 AS SQL语句
CREATE VIEW v1 AS SELECT
*
FROM
score
WHERE
score.course_id = 1
--格式:DROP VIEW 视图名称
DROP VIEW v1
ALTER VIEW
v1 AS
SELECT
*
FROM
score
WHERE
score.course_id = 2
4. 使用视图
使用视图时,将其当作表进行操作即可,由于视图是虚拟表,所以无法使用其对真实表进行创建、更新和删除操作,仅能做查询用。
SELECT
*
FROM
v1
WHERE
v1.num > 60
对某个表进行【增/删/改】操作的前后如果希望触发某个特定的行为时,可以使用触发器,触发器用于定制用户对表的行进行【增/删/改】前后的行为。
# 插入前
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_after_delete_tb1 AFTER DELETE ON tb1 FOR EACH ROW
BEGIN
...
END
# 更新前
CREATE TRIGGER tri_before_update_tb1 BEFORE UPDATE ON tb1 FOR EACH ROW
BEGIN
...
END
# 更新后
CREATE TRIGGER tri_after_update_tb1 AFTER UPDATE ON tb1 FOR EACH ROW
BEGIN
...
END
实例1:
在student表中插入数据之前,在class中插入一条数据
CREATE TRIGGER tri_before_insert_tb01 BEFORE INSERT ON student FOR EACH ROW
BEGIN
INSERT INTO class(caption) values("89"); --固定
END&& --注意结束符
改善:为了使插入的数据可变
CREATE TRIGGER tri_before_insert_tb4 BEFORE INSERT ON
student FOR EACH ROW
BEGIN
IF NEW.sname == "frankshi" THEN
INSERT INTO class(caption)
VALUES(NEW.sname); END&&
存储过程是一个SQL语句集合,当主动去调用存储过程时,其中内部的SQL语句会按照逻辑执行。
-- 创建存储过程:相当于创建一个函数
-- 修改SQL语句终止符
delimiter //
create procedure p1()
BEGIN
select * from student;
END//
delimiter ;
-- 执行存储过程
call p1()
无参数存储过程
对于存储过程,可以接收参数,其参数有三类:
in 仅用于传入参数用
out 仅用于返回值用
inout 既可以传入又可以当作返回值
-- 创建存储过程
delimiter &&
create procedure p1(
in r1 int
)
BEGIN
select * from score where score.course_id=r1;
end &&
delimiter ;
-- 执行存储过程
call p1(2)
实例2
有参数
-- 创建存储过程
delimiter \\
create procedure p4(
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;
set @t2 = 0;
CALL p4 (1, 2 ,@t1, @t2); -- 调用
SELECT @t1,@t2; --查询
-- 创建存储过程
delimiter \\
create procedure p3(
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 = r1 + i1 + i2 + temp1 + temp2;
set i3 = i3 + 100;
end\\
delimiter ;
-- 执行存储过程
--创建变量
set @t1 =4;
set @t2 = 0;
CALL p3 (1, 2 ,@t1, @t2); -- 调用
SELECT @t1,@t2; --查询
drop procedure proc_name;
-- 无参数
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)
执行存储过程
先创建一个存储过程
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 &&
执行存储过程
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import pymysql
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='test')
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)
#[{'@_p1_0': 1, '@_p1_1': 22, '@_p1_2': 103, '@_p1_3': 24}]
内置函数
CHAR_LENGTH(str)
返回值为字符串str 的长度,长度的单位为字符。一个多字节字符算作一个单字符。
对于一个包含五个二字节字符集, LENGTH()返回值为 10, 而CHAR_LENGTH()的返回值为5。
CONCAT(str1,str2,...)
字符串拼接
如有任何一个参数为NULL ,则返回值为 NULL。
CONCAT_WS(separator,str1,str2,...)
字符串拼接(自定义连接符)
CONCAT_WS()不会忽略任何空字符串。 (然而会忽略所有的 NULL)。
CONV(N,from_base,to_base)
进制转换
例如:
SELECT CONV('a',16,2); 表示将 a 由16进制转换为2进制字符串表示
FORMAT(X,D)
将数字X 的格式写为'#,###,###.##',以四舍五入的方式保留小数点后 D 位, 并将结果以字符串的形式返回。若 D 为 0, 则返回结果不带有小数点,或不含小数部分。
例如:
SELECT FORMAT(12332.1,4); 结果为: '12,332.1000'
INSERT(str,pos,len,newstr)
在str的指定位置插入字符串
pos:要替换位置其实位置
len:替换的长度
newstr:新字符串
特别的:
如果pos超过原字符串长度,则返回原字符串
如果len超过原字符串长度,则由新字符串完全替换
INSTR(str,substr)
返回字符串 str 中子字符串的第一个出现位置。
LEFT(str,len)
返回字符串str 从开始的len位置的子序列字符。
LOWER(str)
变小写
UPPER(str)
变大写
LTRIM(str)
返回字符串 str ,其引导空格字符被删除。
RTRIM(str)
返回字符串 str ,结尾空格字符被删去。
SUBSTRING(str,pos,len)
获取字符串子序列
LOCATE(substr,str,pos)
获取子序列索引位置
REPEAT(str,count)
返回一个由重复的字符串str 组成的字符串,字符串str的数目等于count 。
若 count <= 0,则返回一个空字符串。
若str 或 count 为 NULL,则返回 NULL 。
REPLACE(str,from_str,to_str)
返回字符串str 以及所有被字符串to_str替代的字符串from_str 。
REVERSE(str)
返回字符串 str ,顺序和字符顺序相反。
RIGHT(str,len)
从字符串str 开始,返回从后边开始len个字符组成的子序列
SPACE(N)
返回一个由N空格组成的字符串。
SUBSTRING(str,pos) , SUBSTRING(str FROM pos) SUBSTRING(str,pos,len) , SUBSTRING(str FROM pos FOR len)
不带有len 参数的格式从字符串str返回一个子字符串,起始于位置 pos。带有len参数的格式从字符串str返回一个长度同len字符相同的子字符串,起始于位置 pos。 使用 FROM的格式为标准 SQL 语法。也可能对pos使用一个负值。假若这样,则子字符串的位置起始于字符串结尾的pos 字符,而不是字符串的开头位置。在以下格式的函数中可以对pos 使用一个负值。
mysql> SELECT SUBSTRING('Quadratically',5);
-> 'ratically'
mysql> SELECT SUBSTRING('foobarbar' FROM 4);
-> 'barbar'
mysql> SELECT SUBSTRING('Quadratically',5,6);
-> 'ratica'
mysql> SELECT SUBSTRING('Sakila', -3);
-> 'ila'
mysql> SELECT SUBSTRING('Sakila', -5, 3);
-> 'aki'
mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2);
-> 'ki'
TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str) TRIM(remstr FROM] str)
返回字符串 str , 其中所有remstr 前缀和/或后缀都已被删除。若分类符BOTH、LEADIN或TRAILING中没有一个是给定的,则假设为BOTH 。 remstr 为可选项,在未指定情况下,可删除空格。
mysql> SELECT TRIM(' bar ');
-> 'bar'
mysql> SELECT TRIM(LEADING 'x' FROM 'xxxbarxxx');
-> 'barxxx'
mysql> SELECT TRIM(BOTH 'x' FROM 'xxxbarxxx');
-> 'bar'
mysql> SELECT TRIM(TRAILING 'xyz' FROM 'barxxyz');
-> 'barx'
部分内置函数
自定义函数
delimiter \\
create function f1(
i1 int,
i2 int)
returns int
BEGIN
declare num int;
set num = i1 + i2;
return(num);
END \\
delimiter ;
删除函数
drop function func_name;
执行函数
1.实例1
SELECT f1(1,2)
事务用于将某些操作的多个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 ;
支持事务的存储过程
1.索引
索引,是数据库中专门用于帮助用户快速查询数据的一种数据结构。类似于字典中的目录,查找字典内容时可以根据目录查找到数据的存放位置,然后直接获取即可。对于索引,会保存在额外的文件中。
功能:约束,快速查找
创建索引:create index sname on student(sname)
再次查询只要0.0018秒
2.相关命令
-查看表结构
desc 表名
show create table 表名
show index from 表名
set profiling = 1;
SQL...
show profiles;
3.索引种类
普通索引:仅加速查询
create table in1(
nid int not null auto_increment primary key,
name varchar(32) not null,
email varchar(64) not null,
extra text,
index ix_name (name)
)
创建表 + 索引
create index index_name on table_name(column_name)
drop index_name on table_name;
注意:
对于创建索引时如果是BLOB 和 TEXT 类型,必须指定length。
create index ix_extra on in1(extra(32));
唯一索引:加速查询 + 列值唯一(可以有null)
create table in1(
nid int not null auto_increment primary key,
name varchar(32) not null,
email varchar(64) not null,
extra text,
unique ix_name (name)
)
create unique index 索引名 on 表名(列名)
drop unique index 索引名 on 表名
主键索引:加速查询 + 列值唯一 + 表中只有一个(不可以有null)
create table in1(
nid int not null auto_increment primary key,
name varchar(32) not null,
email varchar(64) not null,
extra text,
index ix_name (name)
)
OR
create table in1(
nid int not null auto_increment,
name varchar(32) not null,
email varchar(64) not null,
extra text,
primary key(ni1),
index ix_name (name)
)
alter table 表名 add primary key(列名);
alter table 表名 drop primary key;
alter table 表名 modify 列名 int, drop primary key;
组合索引:多列值组成一个索引,
组合索引是将n个列组合成一个索引
其应用场景为:频繁的同时使用n列来进行查询,如:where n1 = ‘alex’ and n2 = 666
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个条件时,组合索引的性能好于多个单一索引合并。
4.正确使用索引
数据库表中添加索引后确实会让查询速度起飞,但前提必须是正确的使用索引来查询,如果以错误的方式使用,则即使建立索引也会不奏效。
即使建立索引,索引也不会生效:
- like '%xx'
select * from tb1 where name like '%cn';
- 使用函数
select * from tb1 where reverse(name) = 'wupeiqi';
- or
select * from tb1 where nid = 1 or email = '[email protected]';
特别的:当or条件中有未建立索引的列才失效,以下会走索引
select * from tb1 where nid = 1 or name = 'seven';
select * from tb1 where nid = 1 or email = '[email protected]' and name = 'alex'
- 类型不一致
如果列是字符串类型,传入条件是必须用引号引起来,不然...
select * from tb1 where name = 999;
- !=
select * from tb1 where name != 'alex'
特别的:如果是主键,则还是会走索引
select * from tb1 where nid != 123
- >
select * from tb1 where name > 'alex'
特别的:如果是主键或索引是整数类型,则还是会走索引
select * from tb1 where nid > 123
select * from tb1 where num > 123
- order by
select email from tb1 order by name desc;
当根据索引排序时候,选择的映射如果不是索引,则不走索引
特别的:如果对主键排序,则还是走索引:
select * from tb1 order by nid desc;
- 组合索引最左前缀
如果组合索引为:(name,email)
name and email -- 使用索引
name -- 使用索引
email -- 不使用索引
5.其他注意事项
- 避免使用select *
- count(1)或count(列) 代替 count(*)
- 创建表时尽量时 char 代替 varchar
- 表的字段顺序固定长度的字段优先
- 组合索引代替多个单列索引(经常使用多个条件查询时)
- 尽量使用短索引
- 使用连接(JOIN)来代替子查询(Sub-Queries)
- 连表时注意条件类型需一致
- 索引散列值(重复少)不适合建索引,例:性别不适合
6.limit分页
下一页
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;
7.执行计划
explain + 查询SQL - 用于显示SQL执行信息参数,根据参考信息可以进行SQL优化
详细说明
id
查询顺序标识
如:explain select * from course where course.teacher_id in (select tid from teacher where tname="叶平");
+------+-------------+---------+------+-------------------+------+---------+------+------+-------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+-------------------+------+---------+------+------+-------------------------------------------------+
| 1 | PRIMARY | course | ALL | fk_course_teacher | NULL | NULL | NULL | 4 | |
| 1 | PRIMARY | teacher | ALL | PRIMARY | NULL | NULL | NULL | 5 | Using where; Using join buffer (flat, BNL join) |
+------+-------------+---------+------+-------------------+------+---------+------+------+-------------------------------------------------+
2 rows in set (0.021 sec)
特别的:如果使用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 = '[email protected]'
select * from tb1 where email = '[email protected]' 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列中索引的位图,并且是冗余的。
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条件语句
1.while循环
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循环
2.repeat循环
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循环
3.loop
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