数据库之结构化语言学习--SQL语句(1)

1. 数据库

1.1 概述

  1. 什么是数据库 ?
    数据的仓库,如:在ATM的示例中我们创建了一个 db 目录,称其为数据库

  2. 什么是 MySQL、Oracle、SQLite、Access、MS SQL Server等 ?
    他们均是一个软件,都有两个主要的功能:

a. 将数据保存到文件或内存
b. 接收特定的命令,然后对文件进行相应的操作

PS:如果有了以上软件,无须自己再去创建文件和文件夹,而是直接传递 命令 给上述软件,让其来进行文件操作,他们统称为数据库管理系统(DBMS,Database Management System)

  1. 什么是SQL ?
    上述提到MySQL等软件可以接受命令,并做出相应的操作,由于命令中可以包含删除文件、获取文件内容等众多操作,对于编写的命令就是是SQL语句。SQL是结构化查询语言(Structured Query Language)的缩写,SQL是一种专门用来与数据库通信的语言。

1.2 下载安装

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:
            点点点
  1. Window版本

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.3 数据库操作

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表中,所以也可以直接对其进行操作(不建议)
  1. 授权管理
show grants for '用户'@'IP地址'                  -- 查看权限
grant  权限 on 数据库.表 to   '用户'@'IP地址'      -- 授权
revoke 权限 on 数据库.表 from '用户'@'IP地址'      -- 取消权限
all privilegesgrant外的所有权限
select          仅查权限
select,insert   查和插入权限
...
usage                   无访问权限
alter                   使用alter table
alter routine           使用alter proceduredrop procedure
create                  使用create table
create routine          使用create procedure
create temporary tables 使用create temporary tables
create user             使用create userdrop userrename userrevoke  all privileges
create view             使用create view
delete                  使用delete
drop                    使用drop table
execute                 使用call和存储过程
file                    使用select into outfileload data infile
grant option            使用grantrevoke
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.4 数据表基本操作

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]

	小整数,数据类型用于保存一些范围的整数数值范围:
	有符号:
		-128127.
	无符号:
		0255

	特别的: MySQL中无布尔值,使用tinyint(1)构造。

int[(m)][unsigned][zerofill]

	整数,数据类型用于保存一些范围的整数数值范围:
		有符号:
			-21474836482147483647
		无符号:
			04294967295

	特别的:整数类型中的m仅用于显示,对存储范围无限制。例如: int(5),当插入数据2时,select 时数据显示为: 00002

bigint[(m)][unsigned][zerofill]
	大整数,数据类型用于保存一些范围的整数数值范围:
		有符号:
			-92233720368547758089223372036854775807
		无符号:
			018446744073709551615

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**161)个字符。

mediumtext
	A TEXT column with a maximum length of 16,777,215 (2**241) characters.

longtext
	A TEXT column with a maximum length of 4,294,967,295 or 4GB (2**321) 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-31TIME
	HH:MM:SS('-838:59:59'/'838:59:59'YEAR
	YYYY(1901/2155DATETIME

	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.5 表内容操作

1.增

insert into(列名,列名...) values (,,...)
insert into(列名,列名...) values (,,...),(,,...)
insert into(列名,列名...) select (列名,列名...) from

2.删

delete fromdelete fromwhere id=1 and name='alex'

3.改

updateset name = 'alex' where id>1

4.查

select * fromselect * fromwhere id > 1
select nid,name,gender as gg fromwhere id > 1

5.其他

a、条件
    select * fromwhere id > 1 and name != 'alex' and num = 12;
 
    select * fromwhere id between 5 and 16;
 
    select * fromwhere id in (11,22,33)
    select * fromwhere id not in (11,22,33)
    select * fromwhere id in (select nid from)
 
b、通配符
    select * fromwhere name like 'ale%'  - ale开头的所有(多个字符串)
    select * fromwhere name like 'ale_'  - ale开头的所有(一个字符)
 
c、限制
    select * fromlimit 5;            -5select * fromlimit 4,5;          - 从第4行开始的5select * fromlimit 5 offset 4    - 从第4行开始的5行
 
d、排序
    select * fromorder byasc              - 根据 “列” 从小到大排列
    select * fromorder bydesc             - 根据 “列” 从大到小排列
    select * fromorder by1 desc,2 asc    - 根据 “列1” 从大到小排列,如果相同则按列2从小到大排序
 
e、分组
    select num fromgroup by num
    select num,nid fromgroup by num,nid
    select num,nid fromwhere nid > 10 group by num,nid order nid desc
    select num,nid,count(*),sum(score),max(score),min(score) fromgroup by num,nid
 
    select num fromgroup 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

1.6 练习

表关系:
class:
数据库之结构化语言学习--SQL语句(1)_第1张图片
course:
数据库之结构化语言学习--SQL语句(1)_第2张图片
student:
数据库之结构化语言学习--SQL语句(1)_第3张图片
teacher:
数据库之结构化语言学习--SQL语句(1)_第4张图片
score:
数据库之结构化语言学习--SQL语句(1)_第5张图片
题目说明:
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

2. pymysql

2.1 安装

pip install pymysql

2.2 操作

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()

注意

  1. 获取最新自增ID
# 获取最新自增ID
new_id = cursor.lastrowid
  1. 游标操作
在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, '钢蛋')

2.3 fetch数据类型

关于默认获取的数据是元祖类型,如果想要或者字典类型的数据,即:

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()

2.4 SQL注入

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()

3. 其他

3.1 视图

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

SELECT
    *
FROM
    (
    SELECT
        course_id,
        student_id
    FROM
        score
    WHERE
        score.course_id = 2
) AS A
WHERE
    A.student_id IN(1, 2)
  1. 创建视图
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

数据库之结构化语言学习--SQL语句(1)_第6张图片

  1. 删除视图
--格式:DROP VIEW 视图名称

DROP VIEW v1
  1. 修改视图
ALTER VIEW
    v1 AS
SELECT
    *
FROM
    score
WHERE
    score.course_id = 2

数据库之结构化语言学习--SQL语句(1)_第7张图片
4. 使用视图
使用视图时,将其当作表进行操作即可,由于视图是虚拟表,所以无法使用其对真实表进行创建、更新和删除操作,仅能做查询用。

SELECT
    *
FROM
    v1
WHERE
    v1.num > 60

3.2 触发器

对某个表进行【增/删/改】操作的前后如果希望触发某个特定的行为时,可以使用触发器,触发器用于定制用户对表的行进行【增/删/改】前后的行为。

  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_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&&

3.3 存储过程

存储过程是一个SQL语句集合,当主动去调用存储过程时,其中内部的SQL语句会按照逻辑执行。

  1. 无参数存储过程
-- 创建存储过程:相当于创建一个函数
-- 修改SQL语句终止符
delimiter //
create procedure p1()
BEGIN
    select * from student;
END//
delimiter ;


-- 执行存储过程

call p1()
无参数存储过程

对于存储过程,可以接收参数,其参数有三类:

in 仅用于传入参数用
out 仅用于返回值用
inout 既可以传入又可以当作返回值

  1. 有参数存储过程
    实例1
-- 创建存储过程
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;   --查询

数据库之结构化语言学习--SQL语句(1)_第8张图片
实例3
有参数,out— null

-- 创建存储过程
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;   --查询

数据库之结构化语言学习--SQL语句(1)_第9张图片

  1. 删除存储过程
drop procedure proc_name;
  1. 执行存储过程
-- 无参数
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)
执行存储过程
  1. pymysql执行存储过程

先创建一个存储过程

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}]

3.4 函数

内置函数

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,则返回 NULLREPLACE(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)

在这里插入图片描述
2.实例2

3.5 事务

事务用于将某些操作的多个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 ;

支持事务的存储过程

3.6 索引

1.索引
索引,是数据库中专门用于帮助用户快速查询数据的一种数据结构。类似于字典中的目录,查找字典内容时可以根据目录查找到数据的存放位置,然后直接获取即可。对于索引,会保存在额外的文件中。

功能约束快速查找

实例
没有索引查询花费0.0554秒
数据库之结构化语言学习--SQL语句(1)_第10张图片

创建索引:create index sname on student(sname)
再次查询只要0.0018秒
在这里插入图片描述
2.相关命令
-查看表结构

desc 表名

数据库之结构化语言学习--SQL语句(1)_第11张图片
查看生成表的SQL

show create table 表名

数据库之结构化语言学习--SQL语句(1)_第12张图片
查看索引

 show index from  表名

在这里插入图片描述
查看执行时间

	set profiling = 1;
    SQL...
    show profiles;

数据库之结构化语言学习--SQL语句(1)_第13张图片

3.索引种类
普通索引:仅加速查询

  1. 创建表+索引
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)
)
创建表 + 索引
  1. 创建索引
create index index_name on table_name(column_name)
  1. 删除索引
drop index_name on table_name;

注意:
对于创建索引时如果是BLOB 和 TEXT 类型,必须指定length。

create index ix_extra on in1(extra(32));

唯一索引:加速查询 + 列值唯一(可以有null)

  1. 创建表 + 唯一索引
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)
)
  1. 创建唯一索引
create unique index 索引名 on 表名(列名)
  1. 创建唯一索引
drop unique index 索引名 on 表名

主键索引:加速查询 + 列值唯一 + 表中只有一个(不可以有null)

  1. 创建表 + 创建主键
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)
)
  1. 创建主键
alter table 表名 add primary key(列名);
  1. 删除主键
alter table 表名 drop primary key;
alter table 表名  modify  列名 int, drop primary key;

组合索引:多列值组成一个索引,
组合索引是将n个列组合成一个索引
其应用场景为:频繁的同时使用n列来进行查询,如:where n1 = ‘alex’ and n2 = 666

  1. 创建表
create table in3(
    nid int not null auto_increment primary key,
    name varchar(32) not null,
    email varchar(64) not null,
    extra text
)
  1. 创建组合索引
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优化
数据库之结构化语言学习--SQL语句(1)_第14张图片
详细说明

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 keyunique类型
                            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列中索引的位图,并且是冗余的。

4.其他

4.1 条件语句

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条件语句

4.2 循环语句

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

4.3 动态执行SQL语句

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

你可能感兴趣的:(数据库,SQL)