MySQL基础知识

Source
版权声明:------------------------------这个世界上只有一种成功,就是按照自己想要的方式度过一生--------------------------- https://blog.csdn.net/qq_44238142/article/details/88411770

Pain is inevitable.Suffering is optional.

写在前面

本文主要介绍了关于mysql数据库的部分基础知识,仅供初学者参考,很小一部分内容摘自网上前辈,已添加原链接供大家浏览。文章较长,都是个人学习总结,由于时间仓促,部分内容略显空洞,但还是希望给屏幕前的你带来帮助(^_−)☆。

什么是数据库

数据库,简而言之可视为电子化的文件柜——存储电子文件的处所,用户可以对文件中的数据进行新增、截取、更新、删除等操作。

MySQL 是最流行的关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System:关系数据库管理系统)应用软件之一。

本文主要介绍MySQL数据库的一些基本知识。

登录数据库

  • 登陆数据库:
    mysql -uroot -p密码
  • 服务:系统后台进程
    • 启动服务:net start mysql
    • 停止服务: net stop mysql

SQL——结构化查询语言

结构化查询语言(Structured Query Language)简称SQL,是一种特殊目的的编程语言,是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统;同时也是数据库脚本文件的扩展名。

通过SQL语言可以操作所有的关系型数据库,但是每种数据库之间都会有一定的差异,我们称之为“方言”。

SQL分类

  • DDL(Data Definition Language):数据定义语言

    用来定义数据库对象:库、表、列等

  • DML(Data Manipulation Language):数据操作语言

    用来定义数据库记录(增删改)

  • DCL(Data Control Language):数据控制语言

    用来定义访问权限和安全级别

  • DQL(Data Query Language):数据查询语言

    用来查询记录(数据)

返回顶部

DDL

注意SQL语言标签不区分大小写,但是数据库对象名最好前后保持一致。

  1. 操作数据库

    • 创建 CREATE DATABASE 数据库名;
    • 删除 DROP DATABASE 数据库名;
    • 修改 ALTER DATABASE 数据库名 CHARACTER SET 要修改的字符集名称;
    • 查询
      • 查询所有数据库名称 SHOW DATABASES;
      • 查询创建数据库语句SHOW CREATE DATABASE 数据库名称;
  2. 操作表

    • 创建表

    先来看一些常见的列类型:

    列类型 中文解释
    int 整型
    double 浮点型
    char 固定长度字符串类型
    varchar 可变长度字符串类型
    text 字符串类型
    bolb 字节类型
    date 日期类型 yyyy-MM-dd
    datetime 日期时间类型 yyyy-MM-dd hh:mm:ss
    time 时间类型
    timestamp 时间戳类型 默认为系统当前时间

    有时我们会在列类型字段后面加上括号使其完整,比如double(5,2)表示最多5位,其中必须有2位小数,即最大值为999.99;char(2)表示限定两个字符。

    在创建数据库之前,你应该想清楚数据库结构:你需要什么数据库表,各数据库表中有什么样的列。

    下面我们来举例说明怎样建立一个表,比如我们要用一张表存储所有的学生信息:

    CREATE TABLE students(
        sid		int,
    	sname	varchar(20),
        sage	int,
        ssex	char(1)
    );
    

    这样就创建了一个简单的学生表,每个学生都有id,姓名,年龄,性别。
    varchar 类型是可变长度的字符串类型,很适合于name列,因为列值是变长的。这些列的长度不必都相同,而且不必是20。你可以挑选从1到65535的任何长度,从中选择一个最合理的值。如果后面你觉得长度不够用,还可以通过SQL语句ALTER TABLE来修改。

  • 删除表 DROP TABLE 表名;

  • 修改表

  1. 添加列

    比如我们要给上面的students增加一个 班级 列:

    ALTER TABLE students ADD (class varchar(10));

  2. 修改列类型

    修改学生表中的性别列为char(2):

    ALTER TABLE students MODIFY ssex CHAR(2);

  3. 修改列名

    修改ssex 为sex
    
    `ALTER TABLE students CHANGE ssex sex CHAR(2);`
    
  4. 删除列

    ALTER TABLE students DROP sid;

  • 查询表

    SHOW TABLES; 查询该数据库下的所有表名称;

    DESC 表名; 查询表结构。

注意,以上的操作都是基于选择一个数据库使用之上USE 数据库名;即可。

返回顶部

DML

我们先创建一个学生表:

CREATE TABLE students(
    sid		int,
	sname	varchar(20),
    sage	int,
    ssex	char(1)
);
  1. 给表中添加数据:

    INSERT INTO students (sid,sname,sage,ssex) VALUES(1,'张三',19,'男');
    

    列名要和后面的值匹配上,写几个列名,就要赋几个值。

    注意,字符类型和日期类型赋值时要用引号括起来,单引号还是双引号没有要求。

    简写形式:

    INSERT INTO students VALUES(1,'张三',19,'男');
    

    这么写必须给所有的列都赋上值,不想赋值的就写null,否则会报错。

  2. 删除表中数据:

  • 删除表中所有数据

    DELETE FROM 表名;
    
  • 删除部分数据

    DELETE FROM 表名 WHERE 条件;
    
  • TRUNCATE 删除数据

    TRUNCATE TABLE 表名;
    

    这么做会删除所有记录,先删除表,再创建表。

  1. 修改表中数据

    UPDATE 表名 SET 列名1=1,列名2=2…… WHERE 条件;
    

    比如我们要修改张三的数据:

    UPDATE students SET sid=0,sage=20 WHERE sname='张三';
    

返回顶部

DQL

常用的语法

			SELECT 
   				selection_list /*要查询的列名称*/
   			FROM 
   				table_list /*要查询的表名称*/
   			WHERE 
   				condition /*行条件*/
   			GROUP BY 
   				grouping_columns /*对结果分组*/
   			HAVING 
   				condition /*分组后的行条件*/
   			ORDER BY 
   				sorting_columns /*对结果排序*/
   			LIMIT 
   				offset_start, row_count /*结果限定*/

模糊查询 like

  • 通配符
    _ :匹配单个任意字符
    %:匹配多个任意字符
  • 举例
    我们先来创建一张员工表供下面举例使用:
-- 建表
CREATE TABLE emp(
	empno		INT,  			-- 编号
	ename		VARCHAR(50), 	-- 姓名
	job			VARCHAR(50),	-- 职位
	mgr			INT,			-- 领导编号
	hiredate	DATE,			-- 入职日期
	sal			DECIMAL(7,2),	-- 薪水
	comm		decimal(7,2),	-- 奖金
	deptno		INT				-- 部门编号
) ;

插入数据:

INSERT INTO emp values(7369,'SMITH','CLERK',7902,'1980-12-17',800,NULL,20);
INSERT INTO emp values(7499,'ALLEN','SALESMAN',7698,'1981-02-20',1600,300,30);
INSERT INTO emp values(7521,'WARD','SALESMAN',7698,'1981-02-22',1250,500,30);
INSERT INTO emp values(7566,'JONES','MANAGER',7839,'1981-04-02',2975,NULL,20);
INSERT INTO emp values(7654,'MARTIN','SALESMAN',7698,'1981-09-28',1250,1400,30);
INSERT INTO emp values(7698,'BLAKE','MANAGER',7839,'1981-05-01',2850,NULL,30);
INSERT INTO emp values(7782,'CLARK','MANAGER',7839,'1981-06-09',2450,NULL,10);
INSERT INTO emp values(7788,'SCOTT','ANALYST',7566,'1987-04-19',3000,NULL,20);
INSERT INTO emp values(7839,'KING','PRESIDENT',NULL,'1981-11-17',5000,NULL,10);
INSERT INTO emp values(7844,'TURNER','SALESMAN',7698,'1981-09-08',1500,0,30);
INSERT INTO emp values(7876,'ADAMS','CLERK',7788,'1987-05-23',1100,NULL,20);
INSERT INTO emp values(7900,'JAMES','CLERK',7698,'1981-12-03',950,NULL,30);
INSERT INTO emp values(7902,'FORD','ANALYST',7566,'1981-12-03',3000,NULL,20);
INSERT INTO emp values(7934,'MILLER','CLERK',7782,'1982-01-23',1300,NULL,10);

利用模糊查询来操作:

-- 比如我现在想匹配员工表中名字首个字母是S开头的
SELECT name FROM emp WHERE name LIKE 'S%';
-- 匹配名字第二个字母是a的
SELECT name FROM emp WHERE name LIKE '_a%'; 

返回顶部

字段控制

  • 修改字段的别名 AS (可以省略)
-- 比如我现在想给empno这个列名其别名id
SELECT empno AS id FROM emp;
-- 给运算字段起别名
select empno,ename,(sal+comm) as 总收入 from emp;
-- 给表起别名
SELECT empno,ename FROM emp AS 员工表;

这里看不出起别名的用处,它更多的用在子查询(大量的代码嵌套)中,我们可以通过起别名来简化代码。

  • 字段运算
    1. null参与的运算,结果都为null
    2. 上面求总收入的例子中,并不是所有的员工都有comm,如果comm为null的话,那么sal+null就是null,显然我们不想要这样的结果。
    3. 要解决这个问题,得使用IFNULL这个字段,把null当做0来使用。
-- ifnull的作用演示
SELECT empno,ename,sal+IFNULL(comm,0) FROM emp;

返回顶部

去重与排序

SECECT sal FROM emp ORDER BY sal;
-- 比如我现在想查询工资的分布情况
sal      
---------
800.00   
950.00   
1100.00  
1250.00  
1250.00  
1300.00  
1500.00  
1600.00  
2450.00  
2850.00  
2975.00  
3000.00  
3000.00  
5000.00  
-- 我们会得到重复的值,这显然是不合理的,需要去重
SELECT DISTINCT sal FROM emp ORDER BY sal;
sal      
---------
800.00   
950.00   
1100.00  
1250.00  
1300.00  
1500.00  
1600.00  
2450.00  
2850.00  
2975.00  
3000.00  
5000.00  
-- 排序默认为ASC升序排列,你也可以降序排列DESC
SELECT DISTINCT sal FROM emp ORDER BY sal DESC;
-- 不去重的情况下,比如我们要按工资大小排序来查看所有人的信息
SELECT * FROM emp ORDER BY sal DESC;  -- *表示所有列
empno  ename   job           mgr  hiredate    sal      comm     deptno  
------  ------  ---------  ------  ----------  -------  -------  --------
  7839  KING    PRESIDENT  (NULL)  1981-11-17  5000.00  (NULL)         10
  7788  SCOTT   ANALYST      7566  1987-04-19  3000.00  (NULL)         20
  7902  FORD    ANALYST      7566  1981-12-03  3000.00  (NULL)         20
  7566  JONES   MANAGER      7839  1981-04-02  2975.00  (NULL)         20
  7698  BLAKE   MANAGER      7839  1981-05-01  2850.00  (NULL)         30
  7782  CLARK   MANAGER      7839  1981-06-09  2450.00  (NULL)         10
  7499  ALLEN   SALESMAN     7698  1981-02-20  1600.00  300.00         30
  7844  TURNER  SALESMAN     7698  1981-09-08  1500.00  0.00           30
  7934  MILLER  CLERK        7782  1982-01-23  1300.00  (NULL)         10
  7654  MARTIN  SALESMAN     7698  1981-09-28  1250.00  1400.00        30
  7521  WARD    SALESMAN     7698  1981-02-22  1250.00  500.00         30
  7876  ADAMS   CLERK        7788  1987-05-23  1100.00  (NULL)         20
  7900  JAMES   CLERK        7698  1981-12-03  950.00   (NULL)         30
  7369  SMITH   CLERK        7902  1980-12-17  800.00   (NULL)         20
  -- 我们发现工资会有重复的情况,那么可以指定第二排序条件
  SELECT * FROM emp ORDER BY sal DESC, ename ASC;
empno  ename   job           mgr  hiredate    sal      comm     deptno  
------  ------  ---------  ------  ----------  -------  -------  --------
  7839  KING    PRESIDENT  (NULL)  1981-11-17  5000.00  (NULL)         10
  7902  FORD    ANALYST      7566  1981-12-03  3000.00  (NULL)         20
  7788  SCOTT   ANALYST      7566  1987-04-19  3000.00  (NULL)         20
  7566  JONES   MANAGER      7839  1981-04-02  2975.00  (NULL)         20
  7698  BLAKE   MANAGER      7839  1981-05-01  2850.00  (NULL)         30
  7782  CLARK   MANAGER      7839  1981-06-09  2450.00  (NULL)         10
  7499  ALLEN   SALESMAN     7698  1981-02-20  1600.00  300.00         30
  7844  TURNER  SALESMAN     7698  1981-09-08  1500.00  0.00           30
  7934  MILLER  CLERK        7782  1982-01-23  1300.00  (NULL)         10
  7654  MARTIN  SALESMAN     7698  1981-09-28  1250.00  1400.00        30
  7521  WARD    SALESMAN     7698  1981-02-22  1250.00  500.00         30
  7876  ADAMS   CLERK        7788  1987-05-23  1100.00  (NULL)         20
  7900  JAMES   CLERK        7698  1981-12-03  950.00   (NULL)         30
  7369  SMITH   CLERK        7902  1980-12-17  800.00   (NULL)         20

返回顶部

聚合函数

聚合函数就是用来做纵向运算的函数,常见的有以下几种:

聚合函数 解释
COUNT() 统计指定列不为NULL的记录行数
MAX() 计算指定列的最大值,如果指定列是字符串类型那么使用字符串排序运算
MIN() 计算指定列的最小值,如果指定列是字符串类型那么使用字符串排序运算
SUM() 计算指定列的数值和,如果指定列的类型不是数值类型,那么计算结果为0
AVG() 计算指定列的平均值,如果指定列的类型不是数值类型,那么计算结果为0

顺便介绍分组查询:GROUP BY()
GROUP BY()和聚合函数相互结合,可以查出很有意义的数据:

-- 查询各部门的平均工资
SELECT deptno,AVG(sal)FROM emp GROUP BY deptno; 


deptno  AVG(sal)     
------  -------------
    10  2916.666667  
    20  2175.000000  
    30  1566.666667  
-- 查询每个部门工资大于1500的人数
SELECT deptno,COUNT(sal) FROM emp WHERE sal>1500 GROUP BY deptno;
-- where 是对分组前的条件限定 也就是说不满足条件的 不参与分组


deptno  count(sal)  
------  ------------
    10             2
    20             3
    30             2

返回顶部

WHERE和HAVING 的区别

  • WHERE:在分组之前对条件进行限定,不满足条件,就不会参与分组。
  • HAVING:在分组之后对结果进行筛选。
-- 查询平均工资大于2000的部门
SELECT deptno,AVG(sal) AS avgsal FROM emp GROUP BY deptno HAVING avgsal>2000;

deptno  avgsal       
------  -------------
    10  2916.666667  
    20  2175.000000  
-- 查询各个部门员工工资大于1500的平均工资,并且平均工资大于2000的部门
-- 思路:首先查询各个部门,那肯定要按部门编号分组,分组的条件限定是 员工工资大于1500的材参与分组 计算出平均工资 然后再对结果进行筛选 筛选出平均工资大于2000的部门
SELECT deptno,AVG(sal) FROM emp WHERE sal>1500 GROUP BY deptno HAVING AVG(sal)>2000; 

deptno  avg(sal)     
------  -------------
    10  3725.000000  
    20  2991.666667  
    30  2225.000000  

返回顶部

分页查询 LIMIT

-- 语法:
LIMIT 开始的记录索引,每一页显示的条数
-- 索引从0开始,开始的记录索引 = (页码-1) * 每一页显示的条数。

SELECT * FROM emp LIMIT 0,5;


empno  ename   job          mgr  hiredate    sal      comm     deptno  
------  ------  --------  ------  ----------  -------  -------  --------
  7369  SMITH   CLERK       7902  1980-12-17  800.00   (NULL)         20
  7499  ALLEN   SALESMAN    7698  1981-02-20  1600.00  300.00         30
  7521  WARD    SALESMAN    7698  1981-02-22  1250.00  500.00         30
  7566  JONES   MANAGER     7839  1981-04-02  2975.00  (NULL)         20
  7654  MARTIN  SALESMAN    7698  1981-09-28  1250.00  1400.00        30
  
SELECT * FROM emp LIMIT 5,5;


empno  ename   job           mgr  hiredate    sal      comm    deptno  
------  ------  ---------  ------  ----------  -------  ------  --------
  7698  BLAKE   MANAGER      7839  1981-05-01  2850.00  (NULL)        30
  7782  CLARK   MANAGER      7839  1981-06-09  2450.00  (NULL)        10
  7788  SCOTT   ANALYST      7566  1987-04-19  3000.00  (NULL)        20
  7839  KING    PRESIDENT  (NULL)  1981-11-17  5000.00  (NULL)        10
  7844  TURNER  SALESMAN     7698  1981-09-08  1500.00  0.00          30
  
SELECT * FROM emp LIMIT 10,5;


 empno  ename   job         mgr  hiredate    sal      comm    deptno  
------  ------  -------  ------  ----------  -------  ------  --------
  7876  ADAMS   CLERK      7788  1987-05-23  1100.00  (NULL)        20
  7900  JAMES   CLERK      7698  1981-12-03  950.00   (NULL)        30
  7902  FORD    ANALYST    7566  1981-12-03  3000.00  (NULL)        20
  7934  MILLER  CLERK      7782  1982-01-23  1300.00  (NULL)        10

返回顶部

约束

约束是对插入数据的一种限制,保证数据的有效性和完整性。
主要分为以下几种:

  1. 主键约束
  2. 非空约束
  3. 唯一约束
  4. 自增长约束
  5. 外键约束

主键约束 PRIMARY KEY

特点:非空且唯一,而且一张表中只能有一个主键
来看看主键的添加语法:

  • 方式一:建表的同时添加约束
CREATE TABLE test(
	userid		INT,
	username 	VARCHAR(32) PRIMARY KEY,
	age			INT
);
  • 方式二:建表的同时在约束区域(字段声明后)添加约束
CREATE TABLE test_1(
	userid		INT,
	username 	VARCHAR(32),
	age			INT,
	PRIMARY KEY(username)
);
  • 方式三:建表之后,通过修改表结构来添加约束
CREATE TABLE test_2(
	userid		INT,
	username 	VARCHAR(32),
	age			INT
);
ALTER TABLE test_2 add PRIMARY KEY(username);

加上了主键约束的字段在插入数据时就不能写null,而且不能有重复值:

INSERT INTO test1 VALUES(01,'张三',19); -- 插入成功
INSERT INTO test1 VALUES(02,'张三',20); -- 插入失败
-- 提示Duplicate entry '张三' for key 'PRIMARY'
INSERT INTO test1 VALUES(02,null,20);
-- 提示Column 'username' cannot be null

下面学习如何删除主键约束,分两种情况:

  • case 1 : 这个字段只有主键约束
CREATE TABLE test(
	userid		INT ,
	username 	VARCHAR(32) PRIMARY KEY,
	age			INT
);
ALTER TABLE test DROP PRIMARY KEY;
-- 这么做只删除了唯一约束,但是没有删除非空约束
-- 删除非空约束可以通过修改字段来实现,在原来的字段声明后加上NULL即可
ALTER TABLE test MODIFY username VARCHAR(32) NULL;
-- 这样就解除了主键约束
  • case 2:这个字段有自增长约束
CREATE TABLE test2(
	userid		INT AUTO_INCREMENT PRIMARY KEY,
	username 	VARCHAR(32) ,
	age			INT
);
-- 有自增长约束的字段一定是INT型,我们第一步先删除自增长约束
ALTER TABLE test2 CHANGE userid userid INT;
-- 然后再删除主键约束
ALTER TABLE test2 DROP PRIMARY KEY;
ALTER TABLE test2 MODIFY userid INT NULL;

返回顶部

唯一约束 UNIQUE

被修饰的字段在插入数据时要求唯一(不限制null):
语法与上面的类似:

CREATE TABLE test(
	userid		INT UNIQUE,
	username 	VARCHAR(32) UNIQUE,
	age			INT 
);
CREATE TABLE test(
	userid		INT,
	username 	VARCHAR(32),
	age			INT,
	UNIQUE(userid),
	UNIQUE(username)
);
CREATE TABLE test(
	userid		INT,
	username 	VARCHAR(32),
	age			INT,
);
ALTER TABLE test ADD UNIQUE(userid);
ALTER TABLE test ADD UNIQUE(username);

返回顶部

联合约束

值得注意的是:

ALTER TABLE test ADD UNIQUE(userid);
ALTER TABLE test ADD UNIQUE(username);

这种添加方式是给每个字段单独添加唯一约束,此时:

INSERT INTO test values(01,'james',36); -- 插入数据成功
INSERT INTO test values(02,'james',25); -- 插入数据失败
INSERT INTO test values(01,'curry',30); -- 插入数据失败
-- userid和username都单独设置了唯一约束,插入数据时,任意一个重复就会报错

这显然不是我们想要的结果,我们的重复数据应该是全部一样才报错,此时就要使用到联合约束。

ALTER TABLE test ADD UNIQUE(userid,username);

现在就可以插入部分重复的数据了:

INSERT INTO test values(01,'james',36); -- 插入数据成功
INSERT INTO test values(02,'james',25); -- 插入数据成功
INSERT INTO test values(01,'curry',30); -- 插入数据成功
INSERT INTO test values(01,'james',36); -- 插入数据失败

如果使用主键约束两个字段那就是加粗样式联合主键,这个主键的唯一性并不冲突。
返回顶部

非空约束 NOT NULL

特点:被修饰的字段不能为null

CREATE TABLE test(
	userid		INT NOT NULL,
	username 	VARCHAR(32) NOT NULL,
	age			INT NOT NULL,
);

这里非空约束见名知意,不再赘述。
返回顶部

自增长约束 AUTO_INCREMENT

特点:

  • 被修饰的字段支持自增,一般为INT型的字段
  • 被修饰的字段必须是键,一般是PRIMARY KEY
CREATE TABLE test(
	id INT AUTO_INCREMENT PRIMARY KEY,
	age INT,
	username VARCHAR(32)
);

这样的话我们在插入数据时就可以直接给userid赋NULL值,数据库会自动帮助我们设置增长的数字来赋给userid:

INSERT INTO test VALUES(NULL,20,"张三");
INSERT INTO test VALUES(NULL,26,"李四");
INSERT INTO test VALUES(NULL,22,"王五");


    id     age  username  
------  ------  ----------
     1      20  张三    
     2      26  李四    
     3      22  王五    

当然你也可以指定id的值,那么后面插入的数据的id值就会从你指定的这个数开始增长:

INSERT INTO test VALUES(10,23,"赵六");
INSERT INTO test VALUES(NULL,56,"鬼脚七");


  id     age  	username   
------  ------  -----------
     1      20  张三     
     2      21  李四     
     3      26  王五     
    10      23  赵六     
    11      56  鬼脚七  

注意:你可以把这个自增长约束理解为只能前进不能后退的指针,当你把这张表中以有的数据删除之后,指针并没有后退,下次插入数据时,会从删除前的数字开始增长:

DELETE FROM test ;
INSERT INTO test VALUES(NULL,20,"张三");

  id     age  	username  
------  ------  ----------
    12      20  张三    

如果想要更换增长的数字,就摧毁这张表,重新建立:

TRUNCATE test;
-- 摧毁这张表,重新建立一张新表

有了自增长约束,每一行数据都有一个独有的值,那么我们的增删改查工作就可以以id为条件来限制数据,非常方便,自增长约束通常和主键约束配合使用。
返回顶部

约束间的配合使用

我们已经知道了主键具有约束数据非空且唯一的作用,但是主键只能设置一个,那么如果其他列也想要这种效果该怎么办呢?我们可以把非空约束和唯一约束来配合使用:

CREATE TABLE test(
	id			INT AUTO_INCREMENT PRIMARY KEY,
	username	varchar(20) UNIQUE NOT NULL,
	age			INT UNIQUE NOT NULL
);

这样就给所有字段设置了非空且唯一的约束,任何字段的重复都是不允许的。
返回顶部

枚举限制数据 ENUM

这里简单介绍一下枚举限制数据:

CREATE TABLE test(
	id			INT AUTO_INCREMENT PRIMARY KEY,
	username	varchar(20) ,
	age			INT ,
	sex 		ENUM('男','女')
);

sex列就被限制只能是 男 或者 女 中的一个。
返回顶部

外键约束

ER图

ER图就是实体联系图。

  • 用矩形表示实体
  • 用椭圆表示属性
  • 用菱形表示关系
  1. ER图的实体(entity)即数据模型中的数据对象,用长方体来表示,每个实体都有自己的实体成员(entity member)或者说实体对象(entity instance),实体成员(entity member)/实体实例(entity instance) 不需要出现在ER图中。

  2. ER图的属性(attribute)即数据对象所具有的属性,用椭圆形表示,属性分为唯一属性( unique attribute)和非唯一属性,唯一属性指的是唯一可用来标识该实体实例或者成员的属性,用下划线表示,一般来讲实体都至少有一个唯一属性。

  3. ER图的关系(relationship)用来表现数据对象与数据对象之间的联系,关系用菱形来表示。

ER图中关联关系有三种:

  • 1对1(1:1) :1对1关系是指对于实体集A与实体集B,A中的每一个实体至多与B中一个实体有关系;反之,在实体集B中的每个实体至多与实体集A中一个实体有关系。

  • 1对多(1:N) :1对多关系是指实体集A与实体集B中至少有N(N>0)个实体有关系;并且实体集B中每一个实体至多与实体集A中一个实体有关系。

  • 多对多(M:N) :多对多关系是指实体集A中的每一个实体与实体集B中至少有M(M>0)个实体有关系,并且实体集B中的每一个实体与实体集A中的至少N(N>0)个实体有关系。

我们可以使用ER图来设计表,比如说我们现在要设计一个网络商城,那么必有的表就有:商品表,用户表,订单表。
这三张表之间是有一定联系的,比如一个用户可以有很多个订单,每个订单会有很多商品。
商品和订单之间就是多对多的关系,为了处理多对多的关系,我们一般会引入一张中间表,存放这两张表的主键,这样就可以将多对多的关系拆分为两个一对多的关系,为了保证数据的有效性和完整性,需要在中间表上添加两个外键约束即可。用户和订单是一对多的关系,订单和商品又是一对多的关系,在这里,订单表就是所谓的中间表。

开发中处理一对多的关系,我们会在多表中添加一个外键,名称一般为主表的名称_id,字段类型一般和主表的主键的类型保持一致。

比如在这里,我们往订单表orders里面添加外键user_id。

外键约束就是用来保证数据的有效性和完整性,多表一方叫从表,一表一方叫主表,我们通常还会在从表添加外键约束中去管理主表的主键。

  • 格式:
    alter table 多表名称 add foreign key(外键名称) references 一表名称(主键);
  • 例如:
    alter table orders add foreign key(user_id) references user(id);

添加了外键约束之后 两张表之间就有了联系,有如下特点:

  1. 主表中不能删除从表中已经被引用的数据
  2. 从表中不能添加主表中不存在的数据

那么如果我们想要删除主表中的数据怎么办呢?

  1. 级联删除
    ALTER TABLE orders ADD FOREIGN KET(user_id) REFERENCE user(id) ON DELETE CASCADE;
    添加了级联删除后,就可以直接删除主表中的数据,同时,从表中相关的数据会被一同删除
    当然你也可以加上级联更新:
    ALTER TABLE orders ADD FOREIGN KET(user_id) REFERENCE user(id) ON DELETE CASCADE ON UPDATE CASCADE;
  2. 先把从表中相关的数据删除,在删除主表中的数据

在开发过程中,我们并不推荐使用外键约束,因为在后期测试工作量会很大。
我们现在来新建用户表和订单表来说明问题:

-- 主表
CREATE TABLE users(
			id INT PRIMARY KEY AUTO_INCREMENT,  -- 主表主键
			username VARCHAR(20)
		);

-- 从表 
CREATE TABLE orders(
			id INT PRIMARY KEY AUTO_INCREMENT,	-- 从表主键
			totalprice DOUBLE,
			user_id INT
		);

-- 给从表添加外键约束
ALTER TABLE orders ADD FOREIGN KEY(user_id) REFERENCES users(id);
-- 插入数据
INSERT INTO users VALUES (3,"张三");
INSERT INTO users VALUES (NULL,"李四");
INSERT INTO users VALUES (NULL,"王五");
INSERT INTO orders VALUES (NULL,1000,3);
INSERT INTO orders VALUES (NULL,1200,4);
INSERT INTO orders VALUES (NULL,3000,5);
INSERT INTO orders VALUES (NULL,5000,3);
INSERT INTO orders VALUES (NULL,1600,4);
-- 表结构	
 id  	username  
------  ----------
     3  张三    
     4  李四    
     5  王五    
     6  赵六       
-- 表结构
 id   	price  	user_id  
------  ------  ---------
     1    1314          3
     2    1314          3
     3      15          4
     4     315          5
     5    1014     (NULL)

返回顶部

多表查询

笛卡尔积(交叉连接)

在数学中,两个集合X和Y的笛卡儿积(Cartesian product),又称直积,表示为X × Y,是其第一个对象是X的成员而第二个对象是Y的一个成员的所有可能的有序对。
假设集合A={a,b},集合B={0,1,2},则两个集合的笛卡尔积为{(a,0),(a,1),(a,2),(b,0),(b,1), (b,2)}。
在数据库中,无条件的联合查询是没有实际意义的,它会返回这两张表的笛卡尔积,我们用上面的用户表和订单表来演示一下:

-- 拼接两个表
SELECT * FROM USER,orders;
-- 用户表	
 id  	username  
------  ----------
     3  张三    
     4  李四    
     5  王五    
     6  赵六       
-- 订单表表
 id   	price  	user_id  
------  ------  ---------
     1    1314          3
     2    1314          3
     3      15          4
     4     315          5
     5    1014     (NULL)
     
-- 笛卡尔积     
    id  username      id   price  user_id  
------  --------  ------  ------  ---------
     3  张三             1    1314          3
     4  李四             1    1314          3
     5  王五             1    1314          3
     6  赵六             1    1314          3
     3  张三             2    1314          3
     4  李四             2    1314          3
     5  王五             2    1314          3
     6  赵六             2    1314          3
     3  张三             3      15          4
     4  李四             3      15          4
     5  王五             3      15          4
     6  赵六             3      15          4
     3  张三             4     315          5
     4  李四             4     315          5
     5  王五             4     315          5
     6  赵六             4     315          5
     3  张三             5    1014     (NULL)
     4  李四             5    1014     (NULL)
     5  王五             5    1014     (NULL)
     6  赵六             5    1014     (NULL)

简单来讲笛卡尔积就是把两张表的列相加,行相乘,即A表中的每一行都会和B表中的每一行数据拼接,那么自然地,B表中的每一行数据就会和A表中的每一行数据拼接。
这样就完成了没有条件的多表联合查询,我们给它加上条件,就会得到具有实际意义的数据。
返回顶部

内连接

  • 显式的内连接
    select a.*,b.* from a,b inner join b on ab的连接条件

inner join on 不符合条件的数据不展示,形成的中间表为两个表经过ON条件过滤后的笛卡尔积

  • 隐式的内连接
    select a.*,b.* from a,b where 连接条件

从左表中取出每一条记录,去右表中与所有的记录进行匹配;匹配必须是某个条件是左表中与右表中相同,才会保留结果,否则不保留;

-- 查询所有用户的订单详情,没有订单显示null

-- 左连接
SELECT user.*,orders.* FROM USER LEFT  OUTER JOIN orders ON user.`id`=orders.`user_id`;
-- 右连接	
SELECT user.*,orders.* FROM orders RIGHT OUTER JOIN USER ON user.`id`=orders.`user_id`;

    id  username      	id   price  	user_id  
------  --------  	------  ------ 	 ---------
     3  张三             1    1314          3
     3  张三             2    1314          3
     4  李四             3      15          4
     5  王五             4     315          5
     6  赵六        (NULL)  (NULL)     (NULL)

返回顶部

外连接

  • 左外连接
    SELECT a.*,b.* FROM a left outer join b on 连接条件;
    left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录;
  • 右外连接
    SELECT a.*,b.* FROM a right outer join b on 连接条件;
    outer 可以省略。
    right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录;
-- 查询所有用户的订单详情,没有订单显示null

-- 左外连接
SELECT user.*,orders.* FROM USER LEFT  OUTER JOIN orders ON user.`id`=orders.`user_id`;
-- 右外连接 
SELECT user.*,orders.* FROM orders RIGHT OUTER JOIN USER ON user.`id`=orders.`user_id`;

    id  username      id   price  user_id  
------  --------  ------  ------  ---------
     3  张三             1    1314          3
     3  张三             2    1314          3
     4  李四             3      15          4
     5  王五             4     315          5
     6  赵六        (NULL)  (NULL)     (NULL)
-- 查询所有订单的用户详情

-- 右连接
SELECT orders.*,user.* FROM USER RIGHT OUTER JOIN orders ON user.`id`=orders.`user_id`;
-- 左连接
SELECT orders.*,user.* FROM orders LEFT OUTER JOIN USER ON user.`id`=orders.`user_id`;

    id   price  user_id      id  username  
------  ------  -------  ------  ----------
     1    1314        3       3  张三    
     2    1314        3       3  张三    
     3      15        4       4  李四    
     4     315        5       5  王五    
     5    1014   (NULL)  (NULL)  (NULL)    

返回顶部

内连接和外连接的区别

有两种基本类型的的连接,inner和outer连接。两种类型的主要区别在于,即使是在连接条件不满足的情况下,外部连接也会在结果集内返回行,而内部连接不会在结果集类返回行 。
当外部连接不满足连接条件时,通常返回一个表中的列,但是第二个表中没有返回值就返回null。
返回顶部

子查询

一个主查询的条件要依赖于另外一个子查询的结果。

-- 一. 查询出订单的价格大于300的所有用户信息。

	-- 分步查询
		-- 先找出价格大于300的订单的用户ID 3 5
		SELECT orders.`user_id` FROM orders WHERE orders.`price`>300;
		-- 再通过id找出用户信息
		SELECT user.* FROM USER WHERE user.id IN (3,5,NULL);
	-- 合并-子查询
		SELECT user.* FROM USER WHERE user.id IN (SELECT orders.`user_id` FROM orders WHERE orders.`price`>300);
  
	    id  username  
	------  ----------
	     3  张三    
	     5  王五    


-- 二.查询订单价格大于300的订单信息及相关用户的信息。
	-- 内连接
		SELECT user.*,orders.* FROM USER,orders WHERE user.id=orders.`user_id` AND orders.`price`>300;
	-- 子查询
		-- 找出所有订单价格大于三百的订单信息作为临时表
		SELECT user.*,tmp.* FROM USER,(SELECT * FROM orders WHERE price>300) AS tmp WHERE user.`id`=tmp.user_id;

	    id  username      id   price  user_id  
	------  --------  ------  ------  ---------
	     3  张三             1    1314          3
	     3  张三             2    1314          3
	     5  王五             4     315          5
		

返回顶部

自连接

有时候我们需要在一张表中查询出有实际意义的数据,那就需要把它适时地看作两张表:
我们先来新建一张员工表:

CREATE TABLE emp(
	empno		INT,
	ename		VARCHAR(50),
	job			VARCHAR(50),
	mgr			INT,
	hiredate	DATE,
	sal			DECIMAL(7,2),
	comm		DECIMAL(7,2),
	deptno		INT
) ;
-- 插入数据
INSERT INTO emp VALUES(7369,'SMITH','CLERK',7902,'1980-12-17',800,NULL,20);
INSERT INTO emp VALUES(7499,'ALLEN','SALESMAN',7698,'1981-02-20',1600,300,30);
INSERT INTO emp VALUES(7521,'WARD','SALESMAN',7698,'1981-02-22',1250,500,30);
INSERT INTO emp VALUES(7566,'JONES','MANAGER',7839,'1981-04-02',2975,NULL,20);
INSERT INTO emp VALUES(7654,'MARTIN','SALESMAN',7698,'1981-09-28',1250,1400,30);
INSERT INTO emp VALUES(7698,'BLAKE','MANAGER',7839,'1981-05-01',2850,NULL,30);
INSERT INTO emp VALUES(7782,'CLARK','MANAGER',7839,'1981-06-09',2450,NULL,10);
INSERT INTO emp VALUES(7788,'SCOTT','ANALYST',7566,'1987-04-19',3000,NULL,20);
INSERT INTO emp VALUES(7839,'KING','PRESIDENT',NULL,'1981-11-17',5000,NULL,10);
INSERT INTO emp VALUES(7844,'TURNER','SALESMAN',7698,'1981-09-08',1500,0,30);
INSERT INTO emp VALUES(7876,'ADAMS','CLERK',7788,'1987-05-23',1100,NULL,20);
INSERT INTO emp VALUES(7900,'JAMES','CLERK',7698,'1981-12-03',950,NULL,30);
INSERT INTO emp VALUES(7902,'FORD','ANALYST',7566,'1981-12-03',3000,NULL,20);
INSERT INTO emp VALUES(7934,'MILLER','CLERK',7782,'1982-01-23',1300,NULL,10);

-- 员工表
empno  ename   job           mgr  hiredate    sal      comm     deptno  
------  ------  ---------  ------  ----------  -------  -------  --------
  7369  SMITH   CLERK        7902  1980-12-17  800.00   (NULL)         20
  7499  ALLEN   SALESMAN     7698  1981-02-20  1600.00  300.00         30
  7521  WARD    SALESMAN     7698  1981-02-22  1250.00  500.00         30
  7566  JONES   MANAGER      7839  1981-04-02  2975.00  (NULL)         20
  7654  MARTIN  SALESMAN     7698  1981-09-28  1250.00  1400.00        30
  7698  BLAKE   MANAGER      7839  1981-05-01  2850.00  (NULL)         30
  7782  CLARK   MANAGER      7839  1981-06-09  2450.00  (NULL)         10
  7788  SCOTT   ANALYST      7566  1987-04-19  3000.00  (NULL)         20
  7839  KING    PRESIDENT  (NULL)  1981-11-17  5000.00  (NULL)         10
  7844  TURNER  SALESMAN     7698  1981-09-08  1500.00  0.00           30
  7876  ADAMS   CLERK        7788  1987-05-23  1100.00  (NULL)         20
  7900  JAMES   CLERK        7698  1981-12-03  950.00   (NULL)         30
  7902  FORD    ANALYST      7566  1981-12-03  3000.00  (NULL)         20
  7934  MILLER  CLERK        7782  1982-01-23  1300.00  (NULL)         10

查询emp表中所有员工的上司的姓名:
我们假设有两张表,一张员工表,一张上司表,如果员工表的mgr=上司表中的empno,就说明这个empno对应的人名就是他的上司。

-- 查询
SELECT a.empno AS 员工编号,a.ename AS 员工,b.empno AS 上司编号,b.ename AS 上司 
FROM emp AS a,emp AS b WHERE a.mgr = b.empno ORDER BY a.`empno`;


-- 返回

	员工编号  	员工  		上司编号  上司  
------------  ------  ------------  --------
        7369  SMITH           7902  FORD    
        7499  ALLEN           7698  BLAKE   
        7521  WARD            7698  BLAKE   
        7566  JONES           7839  KING    
        7654  MARTIN          7698  BLAKE   
        7698  BLAKE           7839  KING    
        7782  CLARK           7839  KING    
        7788  SCOTT           7566  JONES   
        7844  TURNER          7698  BLAKE   
        7876  ADAMS           7788  SCOTT   
        7900  JAMES           7698  BLAKE   
        7902  FORD            7566  JONES   
        7934  MILLER          7782  CLARK       

返回顶部

复制表

开发中我们会经常用到旧表中的结构或者数据,学会怎样复制表会很方便:

  1. 列名和数据都复制
-- 举例来说比如我们要复制上面的那张员工表
CREATE TABLE myEmp AS SELECT * FROM emp;
-- 或者
CREATE TABLE myEmp AS SELECT * FROM emp WHERE TRUE;
-- 或者
CREATE TABLE myEmp AS SELECT * FROM emp WHERE 1=1;
-- 结论:只要在WHERE后面跟上TRUE或者结果为TRUE的判断式
-- 当然,WHERE也可以省略,默认复制列名和数据
  1. 只复制列名,不复制数据
CREATE TABLE myEmp2 AS SELECT * FROM emp WHERE FALSE;
-- 或者
CREATE TABLE myEmp2 AS SELECT * FROM emp WHERE 1=2;
-- 结论:只要在WHERE后面跟上TRUE或者结果为FALSE的判断式
  1. 复制个别列名
CREATE TABLE myEmp3 AS SELECT emp.id,emp.name FROM emp WHERE FALSE;
  1. 复制一张临时表
CREATE TABLE myEmp4 AS (语句);

返回顶部

并集 UNION

-- 查询10号和20号部门的员工信息

-- 方法一:
SELECT * FROM emp WHERE deptno = 10 OR deptno = 20;
-- 方法二:
SELECT * FROM emp WHERE deptno = 10
UNION 
SELECT * FROM emp WHERE deptno = 20;

 empno  ename   job           mgr  hiredate    sal      comm    deptno  
------  ------  ---------  ------  ----------  -------  ------  --------
  7782  CLARK   MANAGER      7839  1981-06-09  2450.00  (NULL)        10
  7839  KING    PRESIDENT  (NULL)  1981-11-17  5000.00  (NULL)        10
  7934  MILLER  CLERK        7782  1982-01-23  1300.00  (NULL)        10
  7369  SMITH   CLERK        7902  1980-12-17  800.00   (NULL)        20
  7566  JONES   MANAGER      7839  1981-04-02  2975.00  (NULL)        20
  7788  SCOTT   ANALYST      7566  1987-04-19  3000.00  (NULL)        20
  7876  ADAMS   CLERK        7788  1987-05-23  1100.00  (NULL)        20
  7902  FORD    ANALYST      7566  1981-12-03  3000.00  (NULL)        20

返回顶部

存储过程

定义

存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,存储在数据库中,经过第一次编译后调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。
返回顶部

语法

  • 创建存储过程
DELIMITER $$

CREATE
    /*[DEFINER = { user | CURRENT_USER }]*/
    PROCEDURE `库名`.`存储过程名`()
    /*LANGUAGE SQL
    | [NOT] DETERMINISTIC
    | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
    | SQL SECURITY { DEFINER | INVOKER }
    | COMMENT 'string'*/
    BEGIN
		方法体
    END$$

DELIMITER ;
  • 调用存储过程
CALL 存储过程名();
  • 删除存储过程
DROP PROCEDURE 存储过程名();
  • 查看存储过程
show procedure status\G;  -- 查看所有的存储过程状态
show create procedure 存储过程名\G; -- 查看创建存储过程的语句

返回顶部

delimiter $ 的含义

delimiter是mysql中用来定义结束标记的,mysql客户端中结束标记默认是分号’;’。如果一次输入的语句较多,并且语句中间有分号,这时需要新指定一个特殊的结束符。delimiter $ 表示mysql用$表示mysql语句结束,过程结束后肯定会有一句delimiter ;恢复成默认的。
返回顶部

变量

变量的分类

https://blog.csdn.net/jq_ak47/article/details/52087484

  • 全局变量:MYSQL内置变量,存在任何一个会话当中,会话是你跟数据库建立的一次连接,全局变量影响服务器整体操作。当服务器启动时,它将所有全局变量初始化为默认值。这些默认值可以在选项文件中或在命令行中指定的选项进行更改。要想更改全局变量,必须具有SUPER权限。全局变量作用于server的整个生命周期,但是不能跨重启。即重启后所有设置的全局变量均失效。要想让全局变量重启后继续生效,需要更改相应的配置文件。
  • 用户变量:在客户端链接到数据库实例整个过程中用户变量都是有效的。mysql中用户变量不用事前申明,在用的时候直接用“@变量名”使用就可以了,select语句一般用来输出用户变量,比如select
    @变量名,用于输出数据源不是表格的数据。
  • 会话变量:服务器为每个连接的客户端维护一系列会话变量。在客户端连接数据库实例时,使用相应全局变量的当前值对客户端的会话变量进行初始化。设置会话变量不需要特殊权限,但客户端只能更改自己的会话变量,而不能更改其它客户端的会话变量。会话变量的作用域与用户变量一样,仅限于当前连接。当当前连接断开后,其设置的所有会话变量均失效。
  • 局部变量:定义在存储过程中或者函数中的变量,局部变量只在当前begin/end代码块中有效

返回顶部

参数类型

  • IN 输入参数
  • OUT 输出参数
  • INOUT 输入输出参数

来看几个实例:

  • 案例一:根据传进来的员工编号返回员工的所有信息
-- 存储过程中可以存放一些简单的逻辑
-- 这个存储过程用来根据传进来的员工编号返回员工的所有信息
DELIMITER $$ 

CREATE 
	PROCEDURE `mydemo`.`mypro1`(IN num INT) -- 输入参数的定义
	BEGIN
		SELECT * FROM emp WHERE empno=num;
	END$$
	
DELIMITER;
-- 查询编号为7369的员工信息
CALL mypro1(7369);
-- 返回
empno  ename   job        mgr  hiredate    sal     comm    deptno  
------  ------  ------  ------  ----------  ------  ------  --------
  7369  SMITH   CLERK     7902  1980-12-17  800.00  (NULL)        20
  • 案例二:输出helloworld
DELIMITER $$ 
CREATE 
    PROCEDURE `mydemo`.`mypro2`(OUT mytext VARCHAR(20)) -- 输出参数的定义
    BEGIN
	SET mytext = 'helloworld';
    END$$    	
DELIMITER;

-- 我们需要定义一个用户变量去接收程序返回的值
SET @s; --定义
CALL mypro2(@s); -- 调用程序
SELECT @s; -- 查询

-- 返回
@s          
------------
helloworld  
  • 案例三:删除指定编号的员工信息,并返回剩余员工数
DELIMITER $$ 
	CREATE 
	PROCEDURE mypro3(INOUT num INT) -- 形参的定义
	BEGIN
		DELETE FROM emp WHERE empno = num;
		SELECT COUNT(*) FORM emp INTO num;
	END$$
DELIMITER;


SET @num=7369;
CALL mypro3(@num);
SELECT @num;

返回顶部

IF条件判断语句

案例:输入数字返回星期

DELIMITER $$ 
CREATE 
	PROCEDURE proIf(IN num INT,OUT result VARCHAR(10)) -- 形参的定义
		BEGIN
			IF num=1 THEN
			  SET result = '星期1';
			ELSEIF num=2 THEN
			  SET result = '星期2';
			ELSEIF num=3 THEN
			  SET result = '星期3';
			ELSEIF num=4 THEN
			  SET result = '星期4';
			ELSEIF num=5 THEN
			  SET result = '星期5';
			ELSEIF num=6 THEN
			  SET result = '星期6';
			ELSEIF num=7 THEN
			  SET result = '星期7';
			ELSE
			  SET result = 'undefined'
			END IF;-- 结束条件语句
		END$$
DELIMITER;

返回顶部

WHILE DO循环语句

案例:求1~100的和

DELIMITER $$ 
CREATE 
	PROCEDURE sum1(IN num INT,OUT result INT) -- 形参的定义
			BEGIN
			DECLARE i INT DEFAULT 1; -- 定义一个局部变量默认值为1
			DECLARE sum INT DEFAULT 0;	-- 定义一个局部变量默认值为0
				WHILE(i<=num) DO
					SET sum = sum + i;
					SET i = i+1;
				END WHILE; -- 记得结束循环
				SET result = num;
			END$$
DELIMITER;

CALL myproWhile(100,@r);
SELECT @r;

返回顶部

其他几种循环方式

-- 第二种 loop 循环
/*loop 循环语法:
loop_name:loop
        if 条件 THEN -- 满足条件时离开循环
                leave loop_name;  -- 和 break 差不多都是结束训话
        end if;
end loop;
*/

-- 实例:
DELIMITER $$ 
CREATE PROCEDURE sum2(a INT)
BEGIN
        DECLARE SUM INT DEFAULT 0;
        DECLARE i INT DEFAULT 1;
        loop_name:LOOP -- 循环开始
            IF i>a THEN 
                LEAVE loop_name;  -- 判断条件成立则结束循环  好比java中的 boeak
            END IF;
            SET SUM=SUM+i;
            SET i=i+1;
        END LOOP;  -- 循环结束
        SELECT SUM; -- 输出结果
END $$
DELIMITER;
-- 执行存储过程
CALL sum2(100);
-- 删除存储过程
DROP PROCEDURE IF EXISTS  sum2;


-- 第三种 repeat 循环
/*repeat 循环语法
repeat
    循环体
until 条件 end repeat;
*/


-- 实例;
DELIMITER $$
CREATE PROCEDURE sum3(a INT)
BEGIN
        DECLARE SUM INT DEFAULT 0;
        DECLARE i INT DEFAULT 1;
        REPEAT -- 循环开始
            SET SUM=SUM+i;
            SET i=i+1;
        UNTIL i>a END REPEAT; -- 循环结束
        SELECT SUM; -- 输出结果
END$$
DELIMITER;
-- 执行存储过程
CALL sum3(100);

返回顶部

控制循环的两个关键字

  • leave 相当于 break
  • iterate相当于continue

返回顶部

触发器 TRIGGER

触发器是与表有关的数据库对象,在满足定义条件时触发,并执行触发器中定义的语句集合。

触发器尽量少的使用,因为不管如何,它还是很消耗资源,如果使用的话要谨慎的使用,确定它是非常高效的:触发器是针对每一行的;对增删改非常频繁的表上切记不要使用触发器,因为它会非常消耗资源。

触发器是数据库中的一个对象,相当于JS中的监听器,触发器可以监听增删改 三个动作比如说我想监听一张表,只要我增删改了这张表中的数据,我就可以触发这个触发器,去往另外一张表中记录一下日志。

触发器的创建

DELIMITER $$

CREATE
    /*[DEFINER = { user | CURRENT_USER }]*/
    TRIGGER `库名`.`触发器名` BEFORE/AFTER INSERT/UPDATE/DELETE --可选择的事件名
    ON `目标表`.`<Table Name>`
    FOR EACH ROW BEGIN  -- 针对每一行
		-- 具体实现
    END$$

DELIMITER ;

-- BEFORE 行为发生之前就触发
-- AFTER 行为发生之后触发

比如少我们现在有一张学生表student,和一张日志表logger:

-- 新建学生表
CREATE TABLE student(sname VARCHAR(10),sage INT);
-- 插入数据
INSERT INTO student VALUES("张三",20);
INSERT INTO student VALUES("李四",30);
INSERT INTO student VALUES("王五",40);
-- 表
sname     sage  
------  --------
张三           20
李四           30
王五           40

-- 新建logger表来写日志
CREATE TABLE logger(sname VARCHAR(10),sage INT,text varchar(100),time timestamp);

返回顶部

插入

现在要求当我学生表中新插入数据时,就往日志表中写一条记录:

-- 新建触发器
DELIMITER$$
CREATE
    TRIGGER tri AFTER INSERT
    ON student
    FOR EACH ROW
    BEGIN 
		INSERT INTO logger VALUES(new.sname,new.sage,"插入了一行数据",NULL);
    END$$
DELIMITER;
-- 现在往学生表中插入一条数据
INSERT INTO student VALUES("赵六",24);
-- 学生表
sname     sage  
------  --------
张三            20
李四            30
王五            40
赵六            24
-- 日志
sname    sage  	text                   	time  
------  ------  ---------------------  ---------------------
赵六       24  	插入了一行数据          	2019-03-14 19:09:36

返回顶部

更新

当学生表中数据发生改变,往日志表中写一条记录:

DELIMITER$$ 
CREATE
    TRIGGER tri2 AFTER UPDATE
    ON student
    FOR EACH ROW
    BEGIN
		INSERT INTO logger VALUES(old.sname,old.sage,"此行数据更新",NULL);
		INSERT INTO logger VALUES(new.sname,new.sage,"更新后的数据",NULL);
    END$$
DELIMITER; 
-- 更新数据
UPDATE student SET sname="张三三",sage=19 WHERE sname = "张三";
-- 学生表
sname     sage  
------  --------
张三三          19
李四            30
王五            40
赵六            24
-- 日志
sname        sage  text                                  time  
---------  ------  ---------------------  ---------------------
赵六             24  插入了一行数据           2019-03-14 19:09:36
张三             20  此行数据更新             2019-03-14 19:26:48
张三三           19  更新后的数据             2019-03-14 19:26:48

返回顶部

删除

当学生表中的数据被删除,往日志表中写一条记录:

DELIMITER$$
CREATE 
    TRIGGER tri3 AFTER DELETE
    ON student
    FOR EACH ROW
    BEGIN
		INSERT INTO logger VALUES(old.sname,old.sage,"此行数据删除",NULL);
    END$$
DELIMITER;
-- 删除
DELETE FROM student WHERE sname='张三三';
-- 学生表
sname        sage  
---------  --------
李四             30
王五             40
赵六             24
-- 日志
sname        sage  text                                  time  
---------  ------  ---------------------  ---------------------
赵六             24  插入了一行数据           2019-03-14 19:09:36
张三             20  此行数据更新             2019-03-14 19:26:48
张三三           19  更新后的数据             2019-03-14 19:26:48
张三三           19  此行数据删除             2019-03-14 19:44:45

返回顶部

触发器的删除

DROP TRIGGER tri;
DROP TRIGGER tri2;
DROP TRIGGER tri3;

引擎InnoDB和MyISAM的区别

这两种都是mysql的数据库的存储引擎的类型,一个支持事物等数据库的高级功能,一个不支持。两个各有用处,各有优劣。

  1. InnoDB不支持FULLTEXT类型的索引。
  2. InnoDB中不保存表的 具体行数,也就是说,执行select count() from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count()语句包含 where条件时,两种表的操作是一样的。
  3. 对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引。
  4. DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除。
  5. LOAD TABLE FROM MASTER操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用。

另外,InnoDB表的行锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表。简而言之:
MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持。 MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持,外键等高级数据库功能。
任何一种表都不是万能的,只用恰当的针对业务类型来选择合适的表类型,才能最大的发挥MySQL的性能优势.
下面来看一些常见的关于引擎的操作:

  1. 查看表信息,里面包括使用的引擎类型,字符编码,表结构等mysql> show create table 表名;

  2. 可以执行以下命令来切换非事务表到事务(数据不会丢失),innodb表比myisam表更安全:
    alter table 表名 type=innodb;

  3. innodb表不能用repair table命令和myisamchk -r table_name
    但可以用check table 表名,以及mysqlcheck [OPTIONS] database [tables]

  4. 启动mysql数据库的命令行中添加了以下参数可以使新发布的mysql数据表都默认为使用事务(
    只影响到create语句。)
    default-table-type=InnoDB

  5. 临时改变默认表类型可以用:
    set table_type=InnoDB;

返回顶部

索引

索引在MySQL中也叫做“键”,即’key’,是存储引擎用于快速查找记录的一种数据结构。当表中的数据越来越多时,索引对于性能的影响愈发重要。索引优化是对查询性能优化最有效的手段,它能够轻易将查询性能提高好几个数量级。通俗来讲,它就相当于新华字典的音序表,能够帮助你快速的找到你想要的数据。

索引的分类

  • 普通索引
  • 唯一索引
  • 全文索引
  • 单列索引
  • 多列索引
  • 空间索引

返回顶部

创建索引

  • 方法一

语法:

create table 表名(
			字段名 数据类型 [约束],
			字段名2 数据类型2 [约束],
            [unique|fulltext|spatial] index|key
            [索引名](字段名[(长度)] [ASC|DESC])   -- 注意中括号里面的是可选项
	);
  1. 演示:创建普通索引
CREATE TABLE t1(
    sid INT,
    sname VARCHAR(32),
    INDEX(sname) -- 给name 字段建立普通索引 注意字段不要带引号
);

-- 查询建表语句		
SHOW CREATE TABLE t1;

-- 返回
Table   Create Table                                                                                                                                        
------  --------------------------------------
t1      CREATE TABLE `t1` (                                                                                                                                 
          `sid` int(11) DEFAULT NULL,                                                                                                                       
          `sname` varchar(32) DEFAULT NULL,                                                                                                                 
          KEY `sname` (`sname`)                                                                                                                             
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8                                                                                                                
  1. 演示:创建唯一索引
CREATE TABLE t2(
    	tid INT,
    	tname VARCHAR(32),
    	UNIQUE INDEX(tname)  -- 创建唯一索引,这列的值不能重复
);
  1. 创建全文索引
    一般针对这个字段是 text类型,比如存了一篇文章
 CREATE TABLE t3(
 	tid INT,
 	tname VARCHAR(32),
 	tlog TEXT,
 	FULLTEXT INDEX myIndex (tlog)  -- 这个myIndex是你起的索引名,一般省略不写
 );
  1. 创建多列索引
 CREATE TABLE t4(
 	tid INT,
 	tname VARCHAR(32),
 	tlog VARCHAR(100),
 	INDEX my_Index (tname,tlog)  -- 给 tname 和 tlog 两个列都建立索引
 ); 
  • 方式二

语法:

create [unique|fulltext|spatial] index 索引名 on 表名 (字段名[(长度)] [ASC|DESC]);

演示: student表已经存在,给里面的字段添加索引

-- 创建普通索引例子
	CREATE INDEX myIndex ON student (sname);
-- 创建唯一索引例子
	CREATE UNIQUE INDEX myIndex ON student (sname);
-- 创建全文索引例子
	CREATE FULLTEXT INDEX myIndex ON t3 (tlog);
-- 创建多列索引例子
	CREATE INDEX myIndex ON student (sname,slog);

  • 方式三
    通过使用修改表的语句 alter table 给某个字段建立索引

语法:

alter table 表名 add [unique|fulltext|spatial] index 索引名 on 表名 (字段名[(长度)] [ASC|DESC]);

演示:

-- 创建普通索引的例子
	ALTER TABLE student ADD INDEX MyIndex(sname);
-- 创建唯一索引的例子
	ALTER TABLE student ADD UNIQUE INDEX MyIndex(sname);
-- 创建多列索引
    ALTER TABLE student ADD INDEX MyIndex(sname,slog);	

返回顶部

管理索引

  • 查看索引show create table 表名\G;
  • 删除索引drop index 索引名 on 表名;

返回顶部

测试索引的效率

写一个存储过程往表中存20000条数据。

DELIMITER$$
CREATE 
    PROCEDURE myinsert()
    BEGIN
	DECLARE i INT DEFAULT 1;
	WHILE (i<=20000) DO
		INSERT INTO test VALUES(i,"这是个测试");
		SET i = i+1;
	END WHILE;
    END$$
DELIMITER; 

CALL myinsert();
-- 没添加索引查询
SELECT * FROM test WHERE id=20000;
-- 执行耗时   : 0.061 sec
-- 传送时间   : 0.003 sec
-- 总耗时      : 0.065 sec
EXPLAIN SELECT * FROM test WHERE id=20000;

    id  select_type  TABLE   TYPE    possible_keys  KEY     key_len  ref       ROWS  Extra        
------  -----------  ------  ------  -------------  ------  -------  ------  ------  -------------
     1  SIMPLE       test    ALL     (NULL)         (NULL)  (NULL)   (NULL)   20375  USING WHERE  

-- 添加索引后查询
CREATE INDEX index1 ON test(id);
SELECT * FROM test WHERE id=20000;
-- 执行耗时   : 0.007 sec
-- 传送时间   : 0.015 sec
-- 总耗时      : 0.023 sec
EXPLAIN SELECT * FROM test WHERE id=20000;

    id  select_type  TABLE   TYPE    possible_keys  KEY     key_len  ref       ROWS  Extra        
------  -----------  ------  ------  -------------  ------  -------  ------  ------  -------------
     1  SIMPLE       test    ref     index1         index1  5        const        1  USING WHERE  
-- 可以看出,效率大大提升

返回顶部