mysql

xMySql

1.数据库基础

1.1常见的数据模型

(1)层次模型:用树状结构表示实体类型及实体间联系的数据模型称为层次模型,有且只有一个根结点;其他结点有且仅有一个父结点网状模型

(2)网状模型:用有向图结构表示实体类型及实体间联系的数据模型称为网状模型,允许有一个以上的节点无双亲。至少有一个节点可以有多于一个的双亲

(3)关系模型:以二维表来描述数据。关系模型中,每个表有多个字段列和记录行,每个字段列有固定的属性(数字、字符、日期等)

数据完整性

1、域完整性:字段/列------非空、缺省

2、实体完整性:记录/行------主键、唯一键

3、引用完整性:表与表之间------外键

1.2 实体与关系·

实体是指客观存在并可相互区别的事物,实体既可以是实际的事物,也可以是抽象的概念或关系。

实体之间有3种关系,分别如下。

(1)一对一关系:是指表A中的一条记录在表B中有且只有一条相匹配的记录。在一对一关系中,大部分相关信息都在一个表中。

(2)一对多关系:是指表A中的行可以在表B中有许多匹配行,但是表B中的行只能在表A中有一个匹配行。

(3)多对多关系:是指关系中每个表的行在相关表中具有多个匹配行。在数据库中,多对多关系的建立是依靠第三个表(称作连接表)实现的,连接表包含相关的两个表的主键列,然后从两个相关表的主键列分别创建与连接表中的匹配列的关系。

1、一对多:比如说一个班级有很多学生,可是这个班级只有一个班主任。在这个班级中随便找一个人,就会知道他们的班主任是谁;知道了这个班主任就会知道有哪几个学生。这里班主任和学生的关系就是一对多。

3、一对一:比如说一个班级有很多学生,他们分别有不同的学号。一个学生对应一个学号,一个学号对应一个学生;通过学号能找到学生,通过学生也能得到学号,不会重复。这里学生和学号的关系就是一对一。

4、多对多:比如说一个班级有很多学生,他们有语文课、数学课、英语课等很多课。一门课有很多人上,一个人上很多门课。这里学生和课程的关系就是多对多

2.数据库操作

2.1链接数据库

[root@host]# mysql -u root -p   
Enter password:******  # 登录后进入终端

2.2创建数据库

CREATE DATABASE 数据库名;
create database db_admin;
create schema db_admin1;

2.3创建指定字符集的数据库

CREATE DATABASE db_test
CHARACTER SET = GBK;

2.4 查看数据库

SHOW DATABASES;
//筛选以db_开头的数据库名称
SHOW DATABASES LIKE 'db_%';

2.5选择数据库

USE 数据库名
USE db_admin;

2.6修改数据库

//修改数据库db_admin,设置默认字符集和校对规则
ALTER DATABASE db_admin
    DEFAULT CHARACTER SET gbk
    DEFAULT COLLATE gbk_chinese_ci;

2.7删除数据库

DROP {DATABASE|SCHEMA} [IF EXISTS] 数据库名;
DROP DATABASE db_admin;

注意

删除数据库的操作应该谨慎使用,一旦执行该操作,数据库的所有结构和数据都会被删除,没有恢复的可能,除非数据库有备份。

2.8mysql数据类型

  1. 整数类型:
    • TINYINT:小整数,范围从-128到127(有符号)或0到255(无符号)。
    • SMALLINT:小整数,范围从-32768到32767(有符号)或0到65535(无符号)。
    • INT:整数,通常使用最广泛,范围从-2147483648到2147483647(有符号)或0到4294967295(无符号)。
    • BIGINT:大整数,范围非常大,适合存储非常大的整数值。
  2. 浮点数类型:
    • FLOAT:单精度浮点数。
    • DOUBLE:双精度浮点数。
    • DECIMAL:精确的十进制数,用于存储精确小数值。
  3. 字符串类型:
    • CHAR:定长字符串。
    • VARCHAR:可变长度字符串。
    • TEXT:用于存储大文本数据的字符串。
  4. 日期和时间类型:
    • DATE:日期(年、月、日)。
    • TIME:时间(时、分、秒)。
    • DATETIME:日期和时间。
    • TIMESTAMP:时间戳,通常用于记录数据的修改时间。
  5. 二进制数据类型:
    • BINARY:定长二进制数据。
    • VARBINARY:可变长度二进制数据。
    • BLOB:用于存储大二进制对象(如图像、音频、视频等)的数据。
  6. 枚举和集合类型:
    • ENUM:用于定义一个字符串列表,列值必须从列表中选择。
    • SET:与ENUM类似,但可以选择多个值。
  7. 布尔类型:
    • BOOLEAN或BOOL:用于存储布尔值(true或false)。

这些是一些常见的MySQL数据类型。你可以根据你的数据需求选择合适的数据类型来定义表中的列。不同的数据类型具有不同的存储需求和性能特性,因此选择合适的数据类型对于数据库设计非常重要。

3.数据表操作

3.1创建数据表

CREATE TABLE 数据表名 (列名1 属性,列名2 属性…);
USE db_admin;
CREATE TABLE tb_admin(
   id int auto_increment primary key,
   user varchar(30) not null,
   password varchar(30) not null,
   createtime datetime);

3.2查看表结构

查看数据库里面多有的表

SHOW TABLES;

查看表的具体结构(字段,类型)

SHOW COLUMNS FROM tb_admin FROM db_admin;

使用DESCRIBE语句查看,DESCRIBE可以简写成DESC。在查看表结构时,也可以只列出某一列的信息

DESCRIBE 数据表名;
DESCRIBE 数据表名 列名;

USE db_admin;
DESC tb_admin;
DESC tb_admin user;

3.3修改表结构

ALTER TABLE语句允许指定多个动作,其动作间使用逗号分隔,每个动作表示对表的一个修改。

ADD [COLUMN] create_definition [FIRST | AFTER column_name ]     //添加新字段
| ADD INDEX [index_name] (index_col_name,...)                   //添加索引名称
| ADD PRIMARY KEY (index_col_name,...)                          //添加主键名称
| ADD UNIQUE [index_name] (index_col_name,...)                  //添加唯一索引
| ALTER [COLUMN] col_name {SET DEFAULT literal | DROP DEFAULT}  //修改字段默认值
| CHANGE [COLUMN] old_col_name create_definition                //修改字段名/类型
| MODIFY [COLUMN] create_definition                             //修改子句定义字段
| DROP [COLUMN] col_name                                        //删除字段名称
| DROP PRIMARY KEY                                              //删除主键名称
| DROP INDEX index_name                                         //删除索引名称
| RENAME [AS] new_tbl_name                                      //更改表名
1.添加字段
ALTER TABLE tb_admin ADD email varchar(50) not null ,
modify user varchar(40);
2.修改字段
ALTER TABLE table_name
CHANGE old_column_name new_column_name data_type;

ALTER TABLE db_admin.tb_usernew1
CHANGE COLUMN user username VARCHAR(30) NULL DEFAULT NULL ;
3.删除字段
ALTER TABLE tb_admin DROP email;

3.4修改表名

ALTER TABLE tb_usernew1 RENAME AS tb_userOld;
RENAME TABLE 数据表名1 To 数据表名2
RENAME TABLE tb_admin TO tb_user;

3.5复制表

CREATE TABLE [IF NOT EXISTS] 数据表名
    {LIKE 源数据表名 | (LIKE 源数据表名)}
    
CREATE TABLE tb_userNew
   LIKE tb_user;

使用该语法复制数据表时,将创建一个与源数据表相同结构的新表,该数据表的列名、数据类型空指定和索引都将被复制,但是表的内容是不会复制的。因此,新创建的表是一张空表。如果想要复制表中的内容,可以通过使用AS(查询表达式)子句来实现。

3.6删除表

DROP TABLE [IF EXISTS] 数据表名;
DROP TABLE tb_user;

4.表数据的增删改

4.1 添加数据

INSERT INTO table_name (column1, column2, ...) VALUES (value1, value2, ...);

INSERT INTO tb_admin VALUES(1,'mr','mrsoft','2014-09-05 10:25:20','88848@qq.com');

查看表内的数据

SELECT * FROM 数据表名称;
SELECT * FROM tb_admin;

插入查询结果

INSERT INTO db_database08.tb_admin
     (username,password)
     SELECT username,password FROM tb_usernew;

4.2更新数据

UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition;

update tb_usernew set password='123' where username='xiafang';

4.3删除数据

DELETE FROM table_name
WHERE condition;

delete from tb_usernew where id=3;

由于TRUNCATE TABLE语句会删除数据表中的所有数据,并且无法恢复

TRUNCATE [TABLE] 数据表名

5.数据的查询

5.1 基本查询语句

select selection_list        //要查询的内容,选择哪些列
from 数据表名                //指定数据表
where primary_constraint     //查询时需要满足的条件,行必须满足的条件
group by grouping_columns    //如何对结果进行分组
order by sorting_cloumns     //如何对结果进行排序
having secondary_constraint  //查询时满足的第二条件
limit count                  //限定输出的查询结果

5.2 单表查询

5.2.1 查询指定字段
SELECT 字段名 FROM 表名;
5.2.2查询指定字段
SELECT 字段名 FROM 表名;

select username,email from tb_usernew;
+----------+---------------------+
| username | email               |
+----------+---------------------+
| xiafang  | ipopoiggaoi@163.com |
| mr       | NULL                |
+----------+---------------------+	
5.2.3查询指定数据

如果要从很多记录中查询出指定的记录,那么就需要一个查询的条件。设定查询条件应用的是WHERE子句。通过它可以实现很多复杂的条件查询。在使用WHERE子句时,需要使用一些比较运算符来确定查询的条件

select * from tb_usernew where id=2;
+----+----------+----------+------------+-------+
| id | username | password | createtime | email |
+----+----------+----------+------------+-------+
|  2 | mr       | mrsoft   | NULL       | NULL  |
+----+----------+----------+------------+-------+
5.2.4带关键字IN的查询

关键字IN可以判断某个字段的值是否在指定的集合中。如果字段的值在集合中,则满足查询条件,该记录将被查询出来;如果不在集合中,则不满足查询条件。其语法格式如下。

SELECT * FROM 表名 WHERE 条件 [NOT] IN(元素1,元素2,...,元素n);

(1)[NOT]:是可选项,加上NOT表示不在集合内满足条件;

(2)元素:表示集合中的元素,各元素之间用逗号隔开,字符型元素需要加上单引号。

select * from tb_usernew where id in (1,2);
+----+----------+----------+---------------------+---------------------+
| id | username | password | createtime          | email               |
+----+----------+----------+---------------------+---------------------+
|  1 | xiafang  | 123      | 2014-09-05 10:25:20 | ipopoiggaoi@163.com |
|  2 | mr       | mrsoft   | NULL                | NULL                |
+----+----------+----------+---------------------+---------------------+
5.2.5带关键字BETWEEN AND的范围查询

键字BETWEEN AND可以判断某个字段的值是否在指定的范围内。如果字段的值在指定范围内,则满足查询条件,该记录将被查询出来。如果不在指定范围内,则不满足查询条件。其语法如下。

SELECT * FROM 表名 WHERE 条件 [NOT] BETWEEN 取值1 AND 取值2;

(1)[NOT]:可选项,表示不在指定范围内满足条件。

(2)取值1:表示范围的起始值。

(3)取值2:表示范围的终止值。

select * from tb_usernew where id between 2 and 3;
+----+----------+----------+---------------------+-------------+
| id | username | password | createtime          | email       |
+----+----------+----------+---------------------+-------------+
|  2 | mr       | mrsoft   | NULL                | NULL        |
|  3 | xiaobai  | 444      | 2018-02-03 00:00:00 | 333@183.com |
+----+----------+----------+---------------------+-------------+
5.2.6带LIKE的字符匹配查询

LIKE属于较常用的比较运算符,通过它可以实现模糊查询。它有两种通配符:“%”和下划线“_”。

(1)“%”可以匹配一个或多个字符,可以代表任意长度的字符串,长度可以为0。例如,“明%技”表示以“明”开头,以“技”结尾的任意长度的字符串。该字符串可以代表“明日科技”“明日编程科技”“明日图书科技”等字符串。

(2)“”只匹配一个字符。例如,m_n表示以m开头,以n结尾的3个字符。中间的“”可以代表任意一个字符。

select * from tb_usernew where username like 'x%a'; 以x开头 a结尾 中间任意
select * from tb_usernew where username like 'x_a%'; 以x开头 后一位任意 a 后面任意
5.2.7用关键字IS NULL查询空值

关键字IS NULL可以用来判断字段的值是否为空值(NULL)。如果字段的值是空值,则满足查询条件,该记录将被查询出来。如果字段的值不是空值,则不满足查询条件。其语法格式如下。

IS [NOT] NULL
select * from tb_usernew where username is null;

其中,“NOT”是可选项,表示字段不是空值时满足条件。

5.2.8带关键字AND的多条件查询

关键字AND可以用来联合多个条件进行查询。使用关键字AND时,只有同时满足所有查询条件的记录会被查询出来。如果不满足这些查询条件的其中一个,这样的记录将被排除掉。关键字AND的语法格式如下。

select * from 数据表名 where 条件1 and 条件2 [...AND 条件表达式n];

关键字AND连接两个条件表达式,可以同时使用多个关键字AND来连接多个条件表达式。

| id | username  | password | createtime          | email               |
+----+-----------+----------+---------------------+---------------------+
|  1 | xiafang   | 123      | 2014-09-05 10:25:20 | ipopoiggaoi@163.com |
|  2 | mr        | mrsoft   | NULL                | NULL                |
|  3 | xiaobai   | 444      | 2018-02-03 00:00:00 | 333@183.com         |
|  4 | xiadsada  | 444      | 2018-02-03 00:00:00 | 333@183.com         |
|  5 | ffiadsada | fafaf    | 2018-02-03 00:00:00 | 333@183.com         |
|  6 | xiaobai   | fafaf    | 2018-02-03 00:00:00 | 333@183.com   

select * from tb_usernew where username='xiaobai' and password='444';

+----+----------+----------+---------------------+-------------+
| id | username | password | createtime          | email       |
+----+----------+----------+---------------------+-------------+
|  3 | xiaobai  | 444      | 2018-02-03 00:00:00 | 333@183.com |
+----+----------+----------+---------------------+-------------+
5.2.9带关键字OR的多条件查询

关键字OR也可以用来联合多个条件进行查询,但是与关键字AND不同,关键字OR只要满足查询条件中的一个,那么此记录就会被查询出来;如果不满足这些查询条件中的任何一个,这样的记录将被排除掉。关键字OR的语法格式如下。

select * from 数据表名 where 条件1 OR 条件2 [...OR 条件表达式n];

关键字OR可以用来连接两个条件表达式。而且,可以同时使用多个关键字OR连接多个条件表达式。

select * from tb_usernew where username='xiaobai' or password='444';
+----+----------+----------+---------------------+-------------+
| id | username | password | createtime          | email       |
+----+----------+----------+---------------------+-------------+
|  3 | xiaobai  | 444      | 2018-02-03 00:00:00 | 333@183.com |
|  4 | xiadsada | 444      | 2018-02-03 00:00:00 | 333@183.com |
|  6 | xiaobai  | fafaf    | 2018-02-03 00:00:00 | 333@183.com |
+----+----------+----------+---------------------+-------------+
5.2.10用关键字DISTINCT去除结果中的重复行

使用关键字DISTINCT可以去除查询结果中的重复记录,语法格式如下。

select distinct 字段名 from 表名;
mysql> select username from tb_usernew;
+-----------+
| username  |
+-----------+
| xiafang   |
| mr        |
| xiaobai   |
| xiadsada  |
| ffiadsada |
| xiaobai   |
+-----------+

mysql> select distinct username from tb_usernew;
+-----------+
| username  |
+-----------+
| xiafang   |
| mr        |
| xiaobai   |
| xiadsada  |
| ffiadsada |
+-----------+
5.2.11用关键字ORDER BY对查询结果排序

使用关键字ORDER BY可以对查询的结果进行升序(ASC)和降序(DESC)排列,在默认情况下,ORDER BY按升序输出结果。如果要按降序排列可以使用DESC来实现。语法格式如下。

ORDER BY 字段名 [ASC|DESC];

(1)ASC表示按升序进行排序。

(2)DESC表示按降序进行排序。

说明对含有NULL值的列进行排序时,如果是按升序排列,NULL值将出现在最前面,如果是按降序排列,NULL值将出现在最后。

mysql> select * from tb_usernew order by id desc;
+----+-----------+----------+---------------------+---------------------+
| id | username  | password | createtime          | email               |
+----+-----------+----------+---------------------+---------------------+
|  6 | xiaobai   | fafaf    | 2018-02-03 00:00:00 | 333@183.com         |
|  5 | ffiadsada | fafaf    | 2018-02-03 00:00:00 | 333@183.com         |
|  4 | xiadsada  | 444      | 2018-02-03 00:00:00 | 333@183.com         |
|  3 | xiaobai   | 444      | 2018-02-03 00:00:00 | 333@183.com         |
|  2 | mr        | mrsoft   | NULL                | NULL                |
|  1 | xiafang   | 123      | 2014-09-05 10:25:20 | ipopoiggaoi@163.com |
+----+-----------+----------+---------------------+---------------------+

如果列中出现相同的值,也可以使用多个orderby,比较不同字段

mysql> select * from tb_usernew order by createtime desc,id desc;
+----+-----------+----------+---------------------+---------------------+
| id | username  | password | createtime          | email               |
+----+-----------+----------+---------------------+---------------------+
|  6 | xiaobai   | fafaf    | 2018-02-03 00:00:00 | 333@183.com         |
|  5 | ffiadsada | fafaf    | 2018-02-03 00:00:00 | 333@183.com         |
|  4 | xiadsada  | 444      | 2018-02-03 00:00:00 | 333@183.com         |
|  3 | xiaobai   | 444      | 2018-02-03 00:00:00 | 333@183.com         |
|  1 | xiafang   | 123      | 2014-09-05 10:25:20 | ipopoiggaoi@163.com |
|  2 | mr        | mrsoft   | NULL                | NULL                |
+----+-----------+----------+---------------------+---------------------+
5.2.12 用关键字GROUP BY分组查询

通过关键字GROUP BY可以将数据划分到不同的组中,实现对记录进行分组查询。在查询时,所查询的列必须包含在分组的列中,目的是使查询到的数据没有矛盾。

1.使用关键字GROUP BY来分组

单独使用关键字GROUP BY查询结果只显示每组的一条记录。

mysql> select * from tb_usernew group by password;
+----+-----------+----------+---------------------+---------------------+
| id | username  | password | createtime          | email               |
+----+-----------+----------+---------------------+---------------------+
|  1 | xiafang   | 123      | 2014-09-05 10:25:20 | ipopoiggaoi@163.com |
|  3 | xiaobai   | 444      | 2018-02-03 00:00:00 | 333@183.com         |
|  5 | ffiadsada | fafaf    | 2018-02-03 00:00:00 | 333@183.com         |
|  2 | mr        | mrsoft   | NULL                | NULL                |
+----+-----------+----------+---------------------+---------------------+

2.关键字GROUP BY与GROUP_CONCAT()函数一起使用

使用关键字GROUP BY和GROUP_CONCAT()函数查询,可以将每个组中的所有字段值都显示出来。

mysql> select id,GROUP_CONCAT(username),GROUP_CONCAT(password) from tb_usernew GROUP BY password;
+----+------------------------+------------------------+
| id | GROUP_CONCAT(username) | GROUP_CONCAT(password) |
+----+------------------------+------------------------+
|  1 | xiafang                | 123                    |
|  3 | xiaobai,xiadsada       | 444,444                |
|  5 | ffiadsada,xiaobai      | fafaf,fafaf            |
|  2 | mr                     | mrsoft                 |
+----+------------------------+------------------------+

使用 GROUP BY 关键字可以对结果集进行分组查询,通常与聚合函数(如SUM、COUNT、AVG等)一起使用,以便对每个分组的数据进行统计或计算。以下是 GROUP BY 的一般语法:

sqlCopy codeSELECT column1, column2, aggregate_function(column)
FROM table_name
WHERE condition
GROUP BY column1, column2;

其中:

  • column1, column2 是你希望按其进行分组的列。
  • aggregate_function(column) 是用于对每个分组执行聚合计算的函数(如SUM、COUNT、AVG等)。
  • table_name 是要查询的表的名称。
  • condition 是可选的筛选条件,用于过滤数据。

以下是一个示例,假设你有一个名为 sales 的表,其中包含销售数据,你想按销售区域分组,并计算每个区域的销售总额:

sqlCopy codeSELECT region, SUM(sales_amount) AS total_sales
FROM sales
GROUP BY region;

在这个示例中,我们按 region 列进行分组,并使用 SUM 函数计算每个区域的销售总额。AS total_sales 用于为结果列指定别名。

5.2.13用关键字LIMIT限制查询结果的数量

关键字LIMIT可以对查询结果的记录条数进行限定,控制它输出的行数

mysql> select * from tb_usernew order by createtime desc limit 3;
+----+-----------+----------+---------------------+-------------+
| id | username  | password | createtime          | email       |
+----+-----------+----------+---------------------+-------------+
|  6 | xiaobai   | fafaf    | 2018-02-03 00:00:00 | 333@183.com |
|  5 | ffiadsada | fafaf    | 2018-02-03 00:00:00 | 333@183.com |
|  3 | xiaobai   | 444      | 2018-02-03 00:00:00 | 333@183.com |
+----+-----------+----------+---------------------+-------------+

使用关键字LIMIT还可以从查询结果的中间部分取值。首先要定义两个参数,参数1是开始读取的第一条记录的编号(在查询结果中,第一个结果的记录编号是0,而不是1);参数2是要查询记录的个数。

mysql> select * from tb_usernew order by id asc limit 1,3;
+----+----------+----------+---------------------+-------------+
| id | username | password | createtime          | email       |
+----+----------+----------+---------------------+-------------+
|  2 | mr       | mrsoft   | NULL                | NULL        |
|  3 | xiaobai  | 444      | 2018-02-03 00:00:00 | 333@183.com |
|  4 | xiadsada | 444      | 2018-02-03 00:00:00 | 333@183.com |
+----+----------+----------+---------------------+-------------+

5.3聚合函数查询

COUNT()函数

COUNT()函数,对于除“”以外的任何参数,返回所选择集合中非NULL值的行的数目;对于参数“”,返回选择集合中所有行的数目,包含NULL值的行。没有WHERE子句的COUNT(*)是经过内部优化的,能够快速返回表中所有的记录总数。

mysql> select count(*) from tb_usernew;
+----------+
| count(*) |
+----------+
|        6 |
+----------+

mysql> select count(*) from tb_usernew where password='444';
+----------+
| count(*) |
+----------+
|        2 |
+----------+

SUM()函数

SUM()函数可以求出表中某个字段取值的总和。

mysql> select sum(id) from tb_usernew;
+---------+
| sum(id) |
+---------+
| 21      |
+---------+

AVG()函数

AVG()函数可以求出表中某个字段取值的平均值。

mysql> select avg(id) from tb_usernew;
+---------+
| avg(id) |
+---------+
| 3.5000  |
+---------+

MAX()函数

MAX()函数可以求出表中某个字段取值的最大值。

mysql> select max(id) from tb_usernew;
+---------+
| max(id) |
+---------+
|       6 |
+---------+

MIN()函数

MAX()函数可以求出表中某个字段取值的最小值。

mysql> select min(id) from tb_usernew;
+---------+
| min(id) |
+---------+
|       1 |
+---------+

5.4连接查询

下面有两张表,employees和departments

mysql> select * from employees;
+-------------+------------+-----------+---------------+
| employee_id | first_name | last_name | department_id |
+-------------+------------+-----------+---------------+
|           1 | John       | Doe       |             1 |
|           2 | Jane       | Smith     |             2 |
|           3 | Bob        | Johnson   |             1 |
|           4 | Alice      | Brown     |             2 |
|           5 | xiaoming   | djjdf     | NULL          |
|           6 | xiaohong   | fujjda    |             4 |
+-------------+------------+-----------+---------------+

mysql> select * from departments;
+---------------+-----------------+
| department_id | department_name |
+---------------+-----------------+
|             1 | HR              |
|             2 | IT              |
|             3 | Finance         |
|             4 | NULL            |
+---------------+-----------------+
5.4.1交叉连接

交叉连接(CROSS JOIN)是一种关系型数据库查询操作,它返回两个表的笛卡尔积,即将一个表的每一行与另一个表的每一行进行组合。

mysql> select * from employees cross join departments;
+-------------+------------+-----------+---------------+---------------+-----------------+
| employee_id | first_name | last_name | department_id | department_id | department_name |
+-------------+------------+-----------+---------------+---------------+-----------------+
|           1 | John       | Doe       |             1 |             1 | HR              |
|           1 | John       | Doe       |             1 |             2 | IT              |
|           1 | John       | Doe       |             1 |             3 | Finance         |
|           1 | John       | Doe       |             1 |             4 | NULL            |
|           2 | Jane       | Smith     |             2 |             1 | HR              |
|           2 | Jane       | Smith     |             2 |             2 | IT              |
|           2 | Jane       | Smith     |             2 |             3 | Finance         |
|           2 | Jane       | Smith     |             2 |             4 | NULL            |
|           3 | Bob        | Johnson   |             1 |             1 | HR              |
|           3 | Bob        | Johnson   |             1 |             2 | IT              |
|           3 | Bob        | Johnson   |             1 |             3 | Finance         |
|           3 | Bob        | Johnson   |             1 |             4 | NULL            |
|           4 | Alice      | Brown     |             2 |             1 | HR              |
|           4 | Alice      | Brown     |             2 |             2 | IT              |
|           4 | Alice      | Brown     |             2 |             3 | Finance         |
|           4 | Alice      | Brown     |             2 |             4 | NULL            |
|           5 | xiaoming   | djjdf     | NULL          |             1 | HR              |
|           5 | xiaoming   | djjdf     | NULL          |             2 | IT              |
|           5 | xiaoming   | djjdf     | NULL          |             3 | Finance         |
|           5 | xiaoming   | djjdf     | NULL          |             4 | NULL            |
|           6 | xiaohong   | fujjda    |             4 |             1 | HR              |
|           6 | xiaohong   | fujjda    |             4 |             2 | IT              |
|           6 | xiaohong   | fujjda    |             4 |             3 | Finance         |
|           6 | xiaohong   | fujjda    |             4 |             4 | NULL            |
+-------------+------------+-----------+---------------+---------------+-----------------+
5.4.2内连接查询

内连接是最普遍的连接类型,而且是最匀称的,因为它们要求构成连接的每一部分的每个表的匹配,不匹配的行将被排除。

内连接的最常见的例子是相等连接,也就是连接后的表中的某个字段与每个表中的都相同。这种情况下,最后的结果集只包含参加连接的表中与指定字段相符的行。

隐式内连接

 select * from employees,departments where employees.department_id = departments.department_id;

显示内连接

select * from employees  join departments on employees.department_id = departments.department_id;
+-------------+------------+-----------+---------------+---------------+-----------------+
| employee_id | first_name | last_name | department_id | department_id | department_name |
+-------------+------------+-----------+---------------+---------------+-----------------+
|           1 | John       | Doe       |             1 |             1 | HR              |
|           2 | Jane       | Smith     |             2 |             2 | IT              |
|           3 | Bob        | Johnson   |             1 |             1 | HR              |
|           4 | Alice      | Brown     |             2 |             2 | IT              |
|           6 | xiaohong   | fujjda    |             4 |             4 | NULL            |
+-------------+------------+-----------+---------------+---------------+-----------------+
5.4.3外连接

外连接分为左外连接(LEFT JOIN)、右外连接(RIGHT JOIN)和全外连接3种类型。

左外连接(LEFT JOIN)是指将左表中的所有数据分别与右表中的每条数据进行连接组合,返回的结果除内连接的数据外,还包括左表中不符合条件的数据,并在右表的相应列中添加NULL值。

mysql> select * from employees left join departments on employees.department_id = departments.department_id;
+-------------+------------+-----------+---------------+---------------+-----------------+
| employee_id | first_name | last_name | department_id | department_id | department_name |
+-------------+------------+-----------+---------------+---------------+-----------------+
|           1 | John       | Doe       |             1 |             1 | HR              |
|           3 | Bob        | Johnson   |             1 |             1 | HR              |
|           2 | Jane       | Smith     |             2 |             2 | IT              |
|           4 | Alice      | Brown     |             2 |             2 | IT              |
|           6 | xiaohong   | fujjda    |             4 |             4 | NULL            |
|           5 | xiaoming   | djjdf     | NULL          | NULL          | NULL            |
+-------------+------------+-----------+---------------+---------------+-----------------+

右外连接(RIGHT JOIN)是指将右表中的所有数据分别与左表中的每条数据进行连接组合,返回的结果除内连接的数据外,还包括右表中不符合条件的数据,并在左表的相应列中添加NULL。

mysql> select * from employees right join departments on employees.department_id = departments.department_id;
+-------------+------------+-----------+---------------+---------------+-----------------+
| employee_id | first_name | last_name | department_id | department_id | department_name |
+-------------+------------+-----------+---------------+---------------+-----------------+
|           1 | John       | Doe       |             1 |             1 | HR              |
|           2 | Jane       | Smith     |             2 |             2 | IT              |
|           3 | Bob        | Johnson   |             1 |             1 | HR              |
|           4 | Alice      | Brown     |             2 |             2 | IT              |
|           6 | xiaohong   | fujjda    |             4 |             4 | NULL            |
| NULL        | NULL       | NULL      | NULL          |             3 | Finance         |
+-------------+------------+-----------+---------------+---------------+-----------------+
组合查询

全连接(FULL JOIN 或 FULL OUTER JOIN)是一种关系型数据库查询操作,它返回两个表中的所有行,包括匹配的行和不匹配的行。全连接结合了左外连接和右外连接的结果,因此它可以显示两个表中的所有数据,无论是否存在匹配的关联。

全连接的语法通常需要使用 UNION 运算符来合并左外连接和右外连接的结果,因为大多数关系型数据库管理系统(RDBMS)不直接支持 FULL JOIN 操作。

以下是一个使用 UNION 来模拟全连接的示例:

sqlCopy codeSELECT *
FROM table1
LEFT JOIN table2 ON table1.column = table2.column
UNION
SELECT *
FROM table1
RIGHT JOIN table2 ON table1.column = table2.column;
mysql> select * from employees left join departments on employees.department_id = departments.department_id union select * from employees right join departments on employees.department_id = departments.department_id;	
+-------------+------------+-----------+---------------+---------------+-----------------+
| employee_id | first_name | last_name | department_id | department_id | department_name |
+-------------+------------+-----------+---------------+---------------+-----------------+
|           1 | John       | Doe       |             1 |             1 | HR              |
|           3 | Bob        | Johnson   |             1 |             1 | HR              |
|           2 | Jane       | Smith     |             2 |             2 | IT              |
|           4 | Alice      | Brown     |             2 |             2 | IT              |
|           6 | xiaohong   | fujjda    |             4 |             4 | NULL            |
|           5 | xiaoming   | djjdf     | NULL          | NULL          | NULL            |
| NULL        | NULL       | NULL      | NULL          |             3 | Finance         |
+-------------+------------+-----------+---------------+---------------+-----------------+

UNION ALL 是将两个查询的结果直接合并起来,而UNION会去重

mysql> select * from employees left join departments on employees.department_id = departments.department_id union all select * from employees right join departments on employees.department_id = departments.department_id;	
+-------------+------------+-----------+---------------+---------------+-----------------+
| employee_id | first_name | last_name | department_id | department_id | department_name |
+-------------+------------+-----------+---------------+---------------+-----------------+
|           1 | John       | Doe       |             1 |             1 | HR              |
|           3 | Bob        | Johnson   |             1 |             1 | HR              |
|           2 | Jane       | Smith     |             2 |             2 | IT              |
|           4 | Alice      | Brown     |             2 |             2 | IT              |
|           6 | xiaohong   | fujjda    |             4 |             4 | NULL            |
|           5 | xiaoming   | djjdf     | NULL          | NULL          | NULL            |
|           1 | John       | Doe       |             1 |             1 | HR              |
|           2 | Jane       | Smith     |             2 |             2 | IT              |
|           3 | Bob        | Johnson   |             1 |             1 | HR              |
|           4 | Alice      | Brown     |             2 |             2 | IT              |
|           6 | xiaohong   | fujjda    |             4 |             4 | NULL            |
| NULL        | NULL       | NULL      | NULL          |             3 | Finance         |
+-------------+------------+-----------+---------------+---------------+-----------------+
12 rows in set (0.10 sec)
5.4.3自连接查询

自连接是一种数据库查询操作,它指的是在同一个表内创建两个或多个表的副本,并使用不同的表别名来关联它们,以便在同一表中查找相关数据。自连接通常用于处理包含层次结构或父子关系的数据

自连接的典型场景包括:

  1. 组织结构:在一个员工表中,使用自连接可以查找员工的上级或下级,构建组织结构图。
  2. 评论回复:在一个评论表中,使用自连接可以查找评论的回复以及回复的回复,创建评论线程。
  3. 朋友关系:在一个社交网络中,使用自连接可以查找用户之间的朋友关系,包括一度、二度、三度等关系。

自连接的一般语法如下:

SELECT a.column1, b.column2
FROM table AS a
JOIN table AS b ON a.related_column = b.related_column;

让我们考虑一个名为 employees 的表,其中包含员工的信息,每个员工都有一个唯一的ID,以及一个指向其直接上级的经理ID。我们可以使用自连接来查找每个员工的直接上级的姓名。以下是一个示例:

假设 employees 表的数据如下:

+----+--------+-----------+
| ID | Name   | ManagerID |
+----+--------+-----------+
| 1  | John   | 3         |
| 2  | Alice  | 3         |
| 3  | Bob    | 4         |
| 4  | Carol  | NULL      |
| 5  | David  | 4         |
+----+--------+-----------+

在这个示例中,员工表中包含了员工的ID、姓名和经理ID。注意,ManagerID 列包含 NULL 值,表示顶级经理没有上级。

现在,我们可以使用自连接来查找每个员工的直接上级的姓名:

SELECT e1.Name AS EmployeeName, e2.Name AS ManagerName
FROM employees AS e1
LEFT JOIN employees AS e2 ON e1.ManagerID = e2.ID;

查询的结果将如下所示:

+-------------+-------------+
| EmployeeName| ManagerName |
+-------------+-------------+
| John        | Bob         |
| Alice       | Bob         |
| Bob         | Carol       |
| Carol       | NULL        |
| David       | Carol       |
+-------------+-------------+

在这个结果中,我们得到了每个员工的姓名以及他们的直接上级的姓名。这个查询通过自连接将 employees 表与自身关联,使用别名 e1e2 分别表示员工和经理,然后通过员工的 ManagerID 列关联了他们。通过自连接,我们可以轻松地查找相关的数据,例如员工和他们的上级之间的关系。

5.5子查询

创建两张表用来做演示

+-------+---------+----------------------------+-------+
| em_id | em_name | em_time                    | dp_id |
+-------+---------+----------------------------+-------+
|     1 | 张三丰  | 2023-09-12 18:17:35.000000 |     1 |
|     2 | 李四    | 2023-07-13 18:17:55.000000 |     2 |
|     3 | 李弘毅  | 2022-06-15 18:18:12.000000 |     2 |
|     4 | 孙小梅  | 2019-10-15 18:18:47.000000 |     3 |
|     5 | 李菲    | 2023-02-15 18:19:06.000000 |     4 |
|     6 | 赵飞燕  | 2020-07-15 18:19:29.000000 |     3 |
|     7 | 裴钰    | 2023-06-28 18:20:08.000000 |     4 |
+-------+---------+----------------------------+-------+

+---------------+-----------------+
| department_id | department_name |
+---------------+-----------------+
|             1 | 设计部          |
|             2 | 研发部          |
|             3 | 采购部          |
|             4 | 运营部          |
+---------------+-----------------+
5.5.1 标量子查询

标量子查询:标量子查询返回的是单个值(数字,字符串,日期等),可以作为查询语句中的一个常量使用

常用的操作符:= <> > >= < <=

(1)查询研发部的所有员工信息

  • 首先从部门表中查询出研发部所对应的id
  • 在根据id从员工信息表中查出所对应的人员
mysql> select * from employee where dp_id =(select department_id from departments where department_name ='研发部');
+-------+---------+----------------------------+-------+
| em_id | em_name | em_time                    | dp_id |
+-------+---------+----------------------------+-------+
|     2 | 李四    | 2023-07-13 18:17:55.000000 |     2 |
|     3 | 李弘毅  | 2022-06-15 18:18:12.000000 |     2 |
+-------+---------+----------------------------+-------+

(2)查询在李弘毅 入职之前的所有员工信息

  • 首先查出李弘毅 的入职日期
  • 在查询出小于该日期的所有员工关系信息
mysql> select * from employee where em_time <(select em_time from employee where em_name ='李弘毅 ');
+-------+---------+----------------------------+-------+
| em_id | em_name | em_time                    | dp_id |
+-------+---------+----------------------------+-------+
|     4 | 孙小梅  | 2019-10-15 18:18:47.000000 |     3 |
|     6 | 赵飞燕  | 2020-07-15 18:19:29.000000 |     3 |
+-------+---------+----------------------------+-------+
5.5.2 列子查询

列子查询:列子查询返回的是一列数据(可以是多行) 列子查询返回的是一列数据,可以和主查询的结果进行比较或者连接

(1)查询研发部和采购部的所有员工信息

  • 首先从部门表中查询出研发部和采购部所对应的id
  • 在根据两个id从员工信息表中查出所对应的人员
mysql> select * from employee where dp_id in (select department_id from departments where department_name ='研发部' or department_name ='采购部' );
+-------+---------+----------------------------+-------+
| em_id | em_name | em_time                    | dp_id |
+-------+---------+----------------------------+-------+
|     2 | 李四    | 2023-07-13 18:17:55.000000 |     2 |
|     3 | 李弘毅  | 2022-06-15 18:18:12.000000 |     2 |
|     4 | 孙小梅  | 2019-10-15 18:18:47.000000 |     3 |
|     6 | 赵飞燕  | 2020-07-15 18:19:29.000000 |     3 |
+-------+---------+----------------------------+-------+

(2)查询入职比采购部门所有员工都晚的人员信息

  • 查询采购部门的入职时间
  • 查询所有大于该时间的人员信息
mysql> select * from employee where em_time > all (select em_time from employee where dp_id = (select department_id from departments where department_name ='采购部') );
+-------+---------+----------------------------+-------+
| em_id | em_name | em_time                    | dp_id |
+-------+---------+----------------------------+-------+
|     1 | 张三丰  | 2023-09-12 18:17:35.000000 |     1 |
|     2 | 李四    | 2023-07-13 18:17:55.000000 |     2 |
|     3 | 李弘毅  | 2022-06-15 18:18:12.000000 |     2 |
|     5 | 李菲    | 2023-02-15 18:19:06.000000 |     4 |
|     7 | 裴钰    | 2023-06-28 18:20:08.000000 |     4 |
+-------+---------+----------------------------+-------+

(3)查询入职比采购部门任一员工都晚的人员信息

mysql> select * from employee where em_time > any (select em_time from employee where dp_id = (select department_id from departments where department_name ='采购部') );
+-------+---------+----------------------------+-------+
| em_id | em_name | em_time                    | dp_id |
+-------+---------+----------------------------+-------+
|     1 | 张三丰  | 2023-09-12 18:17:35.000000 |     1 |
|     2 | 李四    | 2023-07-13 18:17:55.000000 |     2 |
|     3 | 李弘毅  | 2022-06-15 18:18:12.000000 |     2 |
|     5 | 李菲    | 2023-02-15 18:19:06.000000 |     4 |
|     6 | 赵飞燕  | 2020-07-15 18:19:29.000000 |     3 |
|     7 | 裴钰    | 2023-06-28 18:20:08.000000 |     4 |
+-------+---------+----------------------------+-------+
5.5.3 行子查询

行子查询:行子查询返回的是一行数据,(可以是多列)行子查询返回的是一行数据,可以作为一个整体进行处理或者与其他行进行比较

改一下数据表结构

+-------+---------+----------------------------+-------+-----------+
| em_id | em_name | em_time                    | dp_id | em_salary |
+-------+---------+----------------------------+-------+-----------+
|     1 | 张三丰  | 2023-09-12 18:17:35.000000 |     1 | 3000.00   |
|     2 | 李四    | 2023-07-13 18:17:55.000000 |     2 | 5000.00   |
|     3 | 李弘毅  | 2022-06-15 18:18:12.000000 |     2 | 8899.00   |
|     4 | 孙小梅  | 2019-10-15 18:18:47.000000 |     3 | 7000.00   |
|     5 | 李菲    | 2023-02-15 18:19:06.000000 |     4 | 8777.00   |
|     6 | 赵飞燕  | 2020-07-15 18:19:29.000000 |     3 | 7000.00   |
|     7 | 裴钰    | 2023-06-28 18:20:08.000000 |     4 | 9889.00   |
+-------+---------+----------------------------+-------+-----------+

(1)查询和赵飞燕同一部门且薪水相同的人

  • 查询赵飞燕的薪水和部门id
  • 查询同时满足两个条件的员工信息
mysql> select * from employee where (dp_id,em_salary)=(select dp_id,em_salary from employee where em_name='赵飞燕');
+-------+---------+----------------------------+-------+-----------+
| em_id | em_name | em_time                    | dp_id | em_salary |
+-------+---------+----------------------------+-------+-----------+
|     4 | 孙小梅  | 2019-10-15 18:18:47.000000 |     3 | 7000.00   |
|     6 | 赵飞燕  | 2020-07-15 18:19:29.000000 |     3 | 7000.00   |
+-------+---------+----------------------------+-------+-----------+
5.5.4表子查询

表子查询返回的是多行多列,表子查询返回的是一个表或视图,可以嵌套在另一个查询语句中使用

常用的操作符 IN

(1)查询和“李宏毅”,“赵飞燕”薪资和部门相同的员工信息

mysql> select * from employee where (dp_id,em_salary) in(select dp_id,em_salary from employee where em_name='赵飞燕' or em_name='李弘毅');
+-------+---------+----------------------------+-------+-----------+
| em_id | em_name | em_time                    | dp_id | em_salary |
+-------+---------+----------------------------+-------+-----------+
|     2 | 李四    | 2023-07-13 18:17:55.000000 |     2 | 5000.00   |
|     3 | 李弘毅  | 2022-06-15 18:18:12.000000 |     2 | 5000.00   |
|     4 | 孙小梅  | 2019-10-15 18:18:47.000000 |     3 | 7000.00   |
|     6 | 赵飞燕  | 2020-07-15 18:19:29.000000 |     3 | 7000.00   |
+-------+---------+----------------------------+-------+-----------+
4 rows in set (0.11 sec)
5.5.5带关键字EXISTS的子查询

使用关键字EXISTS时,内层查询语句不返回查询的记录。而是返回一个真假值。如果内层查询语句查询到满足条件的记录,就返回一个真值(true),否则将返回一个假值(false)。当返回的值为true时,外层查询语句将进行查询;当返回的值为false时,外层查询语句不进行查询或者查询不出任何记录

练习

统计每个部门所用用的员工数量

mysql> SELECT department_id, department_name, COUNT(*) AS employee_count
FROM employee INNER JOIN departments ON employee.dp_id = departments.department_id GROUP BY department_id, department_name;

mysql> SELECT department_id, department_name, (select count(*) FROM employee where employee.dp_id = departments.department_id) AS employee_count from departments;
+---------------+-----------------+----------------+
| department_id | department_name | employee_count |
+---------------+-----------------+----------------+
|             1 | 设计部          |              1 |
|             2 | 研发部          |              2 |
|             3 | 采购部          |              2 |
|             4 | 运营部          |              2 |
+---------------+-----------------+----------------+

6.索引

索引是一种将数据库中单列或者多列的值进行排序的数据结构,可以高效的获取数据, 它是数据库表上的数据结构,用于快速查找表中的数据行。

MySQL的索引包括普通索引、唯一性索引、全文索引、单列索引、多列索引和空间索引等。

1.普通索引

普通索引,即不应用任何限制条件的索引,该索引可以在任何数据类型中创建。字段本身的约束条件可以判断其值是否为空或唯一。创建该类型索引后,用户在查询时,便可以通过索引进行查询。在某数据表的某一字段中,建立普通索引后。用户需要查询数据时,只需根据该索引进行查询即可。

2.唯一性索引

使用UNIQUE参数可以设置唯一索引。创建该索引时,索引的值必须唯一,通过唯一索引,用户可以快速定位某条记录,主键是一种特殊唯一索引。

3.全文索引

使用FULLTEXT参数可以设置索引为全文索引。全文索引只能创建在CHAR、VARCHAR或者TEXT类型的字段上。查询数据量较大的字符串类型的字段时,使用全文索引可以提高查询速度。例如,查询带有文章回复内容的字段,可以应用全文索引方式。需要注意的是,在默认情况下,应用全文搜索大小写不敏感。如果索引的列使用二进制排序后,可以执行大小写敏感的全文索引。

4.空间索引

使用SPATIAL参数可以设置索引为空间索引。空间索引只能建立在空间数据类型上,这样可以提高系统获取空间数据的效率。MySQL中只有MyISAM存储引擎支持空间检索,而且索引的字段不能为空值。

MySQL 索引有多种类型,不同类型的索引适用于不同的场景,具体如下:

  1. B-Tree 索引:这是最常见的索引类型,适用于大多数查询。B-Tree 索引按照排序顺序存储索引字段的值,使得范围查询和排序操作非常高效。
  2. 哈希索引:哈希索引适用于等值查询,它使用哈希算法将索引字段的值映射到索引表中的桶(buckets)。但是,哈希索引不支持范围查询或排序。
  3. 全文索引:全文索引用于全文搜索,可以用来在文本字段中执行全文搜索操作。
  4. 空间索引:空间索引适用于地理信息系统(GIS)或空间数据,用于存储地理坐标信息

6.1创建索引

1). 创建索引

CREATE [ UNIQUE | FULLTEXT ] INDEX index_name ON table_name (index_col_name,... ) ;

2). 查看索引

SHOW INDEX FROM table_name ;

3). 删除索引

 DROP INDEX index_name ON table_name ;

12.3.1唯一索引

##创建唯一索引
create unique index index_test1 
on tb_testindex(tid);

要求:创建唯一索引的列的值不能重复

##创建索引语法
create unique index <索引名> on <表名>(列名);

12.3.2普通索引

不要求创建索引的列的值的唯一性

##创建索引语法
create  index <索引名> on <表名>(列名);

##创建普通索引
create index index_test2 on tb_testindex(name);

12.3.3组合索引

##创建组合索引
create index index_test3 on tb_testindex(tid,name);

##语法
create index <index_name> on <table_name>(列名);

12.3.4全文索引

*MYSQL5.6版本新增的**,可以通过此索引进行全文检索操作,因为MYSQL全文检索不支持中文,因此这个全文检索不被开发者关注,在应用开发中,通常是通过搜索引擎(数据库中间键)实现全文检索的。

create fulltext index <index_name> on 表名(字段名);

6.2性能分析

通过 show [session|global] status 命令可以提供服务器状态信 息。通过如下指令,可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次:

-- session 是查看当前会话 ;
-- global 是查询全局数据 ;
SHOW GLOBAL STATUS LIKE 'Com_______';

show profiles 能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。通过have_profiling 参数,能够看到当前MySQL是否支持profile操作:

SELECT @@have_profiling ;
-- 查看每一条SQL的耗时基本情况
show profiles;
-- 查看指定query_id的SQL语句各个阶段的耗时情况
show profile for query query_id;
-- 查看指定query_id的SQL语句CPU的使用情况
show profile cpu for query query_id;

EXPLAIN 或者 DESC命令获取 MySQL 如何执行 SELECT 语句的信息,包括在 SELECT 语句执行 过程中表如何连接和连接的顺序。

-- 直接在select语句之前加上关键字 explain / desc
EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件 ;

6.3索引使用

mysql> select * from tb_user where phone=17799990012;
+----+--------+-------------+-----------------------+------------+-----+--------+--------+---------------------+
| id | name   | phone       | email                 | profession | age | gender | status | createtime          |
+----+--------+-------------+-----------------------+------------+-----+--------+--------+---------------------+
| 12 | 兰陵王 | 17799990012 | lanlinwang666@126.com | 工程造价   |  44 | 1      | 1      | 2001-04-09 00:00:00 |
+----+--------+-------------+-----------------------+------------+-----+--------+--------+---------------------+
1 row in set (0.15 sec)

create index ind_us_phone on tb_user(phone);
mysql> show index from tb_user;
+---------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name     | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| tb_user |          0 | PRIMARY      |            1 | id          | A         |          22 | NULL     | NULL   |      | BTREE      |         |               |
| tb_user |          1 | ind_us_phone |            1 | phone       | A         |          22 | NULL     | NULL   |      | BTREE      |         |               |
+---------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

mysql> select * from tb_user where phone=17799990012;
+----+--------+-------------+-----------------------+------------+-----+--------+--------+---------------------+
| id | name   | phone       | email                 | profession | age | gender | status | createtime          |
+----+--------+-------------+-----------------------+------------+-----+--------+--------+---------------------+
| 12 | 兰陵王 | 17799990012 | lanlinwang666@126.com | 工程造价   |  44 | 1      | 1      | 2001-04-09 00:00:00 |
+----+--------+-------------+-----------------------+------------+-----+--------+--------+---------------------+
1 row in set (0.10 sec)
6.3.1 最左前缀法则

如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始, 并且不跳过索引中的列。如果跳跃某一列,索引将会部分失效(后面的字段索引失效)。

explain select * from tb_user where profession = '软件工程' and age = 31 and status= '0';
explain select * from tb_user where profession = '软件工程' and age = 31;
explain select * from tb_user where age = 31 and status = '0';--失效

注意 : 最左前缀法则中指的最左边的列,是指在查询时,联合索引的最左边的字段(即是 第一个字段)必须存在,与我们编写SQL时,条件编写的先后顺序无关。

6.3.2范围查询

联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效。

explain select * from tb_user where profession = '软件工程' and age > 30 and status= '0';

当范围查询使用>= 或 <= 时,走联合索引了,但是索引的长度为54,就说明所有的字段都是走索引 的。 所以,在业务允许的情况下,尽可能的使用类似于 >= 或 <= 这类的范围查询,而避免使用 > 或 <

6.3.3索引失效情况

索引列运算,不要在索引列上进行运算操作, 索引将失效。

A. 当根据phone字段进行等值匹配查询时, 索引生效。

explain select * from tb_user where phone = '17799990015';

B. 当根据phone字段进行函数运算操作之后,索引失效。

explain select * from tb_user where substring(phone,10,2) = '15';

字符串不加引号

explain select * from tb_user where profession = '软件工程' and age = 31 and status= '0';
explain select * from tb_user where profession = '软件工程' and age = 31 and status= 0;

经过上面两组示例,我们会明显的发现,如果字符串不加单引号,对于查询结果,没什么影响,但是数 据库存在隐式类型转换,索引将失效。

模糊查询

如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。

explain select * from tb_user where profession like '软件%';
explain select * from tb_user where profession like '%工程';--失效
explain select * from tb_user where profession like '%工%';--失效

or连接条件

用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会 被用到。

explain select * from tb_user where id = 10 or age = 23;
explain select * from tb_user where phone = '17799990017' or age = 23;

由于age没有索引,所以即使id、phone有索引,索引也会失效。所以需要针对于age也要建立索引。

6.4sql提示

SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优 化操作的目的。

如果查询条件中的字段包含多个索引,例如包含单列索引和联合和索引,mysql会自动选择。

1). use index : 建议MySQL使用哪一个索引完成此次查询(仅仅是建议,mysql内部还会再次进 行评估)。

explain select * from tb_user use index(idx_user_pro) where profession = '软件工程';

2). ignore index : 忽略指定的索引。

explain select * from tb_user ignore index(idx_user_pro) where profession = '软件工程';

3). force index : 强制使用索引。

explain select * from tb_user force index(idx_user_pro) where profession = '软件工程';

6.5覆盖索引

尽量使用覆盖索引,减少select *。 那么什么是覆盖索引呢? 覆盖索引是指 查询使用了索引,并 且需要返回的列,在该索引中已经全部能够找到 。

explain select id, profession from tb_user where profession = '软件工程' and age =31 and status = '0' ;
explain select id,profession,age, status from tb_user where profession = '软件工程'and age = 31 and status = '0';
explain select id,profession,age, status, name from tb_user where profession = '软件工程' and age = 31 and status= '0' ;
explain select * from tb_user where profession = '软件工程' and age = 31 and status= '0';

联合索引为pro_age_sta, 从上述的执行计划我们可以看到,这四条SQL语句的执行计划前面所有的指标都是一样的,看不出来差 异。但是此时,我们主要关注的是后面的Extra,前面两天SQL的结果为 Using where; Using Index ; 而后面两条SQL的结果为: Using index condition 。

  1. Using where; Using Index 查找使用了索引,但是需要的数据都在索引列中能找到,所以不需 要回表查询数据
  2. Using index condition 查找使用了索引,但是需要回表查询数据

三四 语句查询的结果在索引中不完全包含,所以需要回表查询

6.6前缀索引

当字段类型为字符串(varchar,text,longtext等)时,有时候需要索引很长的字符串,这会让 索引变得很大,查询时,浪费大量的磁盘IO, 影响查询效率。此时可以只将字符串的一部分前缀,建 立索引,这样可以大大节约索引空间,从而提高索引效率。

1). 语法

create index idx_xxxx on table_name(column(n)) ;

示例: 为tb_user表的email字段,建立长度为5的前缀索引。

create index idx_email_5 on tb_user(email(5));

2). 前缀长度

select count(distinct email) / count(*) from tb_user ;
select count(distinct substring(email,1,5)) / count(*) from tb_user ;

7.事务

事务 是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系 统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。

注意: 默认MySQL的事务是自动提交的,也就是说,当执行完一条DML语句时,MySQL会立即隐 式的提交事务。

事务的四个特性(ACID属性):

  • 原子性(Atomicity):事务是一个不可分割的操作单元,要么全部执行成功,要么全部失败回滚。如果在事务执行过程中发生错误,所有的更改都将被撤消,数据库恢复到原始状态。
  • 一致性(Consistency):事务开始前和结束后,数据库必须处于一致的状态。事务在执行过程中不会破坏数据完整性约束。
  • 隔离性(Isolation):多个事务可以并发执行,但每个事务的操作应该被隔离,使得每个事务看起来好像是在独立的环境中运行。这可以通过锁定和多版本控制等机制来实现。
  • 持久性(Durability):一旦事务成功提交,其更改将永久保存在数据库中,即使发生系统故障也不会丢失。

7.1操作事务

主备表结构

create table account(
id int primary key AUTO_INCREMENT comment 'ID',
name varchar(10) comment '姓名',
money double(10,2) comment '余额'
) comment '账户表';
update account set money = money - 1000 where name = '张三';
update account set money = money + 1000 where name = '李四';
-- 开始事务
START TRANSACTION;

-- 在事务中执行SQL语句
update account set money = money - 1000 where name = '张三';
update account set money = money + 1000 where name = '李四';

-- 提交事务
COMMIT;

-- 或者回滚事务
ROLLBACK;

上述SQL语句示例中,START TRANSACTION; 用于开始一个新的事务,COMMIT; 用于提交事务,将更改永久保存到数据库,而 ROLLBACK; 用于回滚事务,撤消所有未提交的更改。

1). 查看/设置事务提交方式

SELECT @@autocommit ;
SET @@autocommit = 0 ;--0为手动提交

2). 开启事务

START TRANSACTION 或 BEGIN ;

7.2事务并发问题

7.3事务的隔离级别

1). 查看事务隔离级别

SELECT @@TRANSACTION_ISOLATION;

mysql> SELECT @@TRANSACTION_ISOLATION;
+-------------------------+
| @@TRANSACTION_ISOLATION |
+-------------------------+
| REPEATABLE-READ         |
+-------------------------+

2). 设置事务隔离级别

SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED |READ COMMITTED | REPEATABLE READ | SERIALIZABLE }

注意:事务隔离级别越高,数据越安全,但是性能越低。

8.视图

视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视 图的查询中使用的表,并且是在使用视图时动态生成的。视图的作用如下:

1). 简单 视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些被经常使用的查询可以被定义为视 图,从而使得用户不必为以后的操作每次指定全部的条件。

2). 安全 数据库可以授权,但不能授权到数据库特定行和特定的列上。通过视图用户只能查询和修改他们所能见 到的数据

3). 数据独立 视图可帮助用户屏蔽真实表结构变化带来的影响。

8.1语法

1). 创建

CREATE [OR REPLACE] VIEW 视图名称[(列名列表)] AS SELECT语句 [ WITH [CASCADED | LOCAL ] CHECK OPTION ]

2). 查询

查看创建视图语句:SHOW CREATE VIEW 视图名称;
查看视图数据:SELECT * FROM 视图名称 ...... ;

3). 修改MySQL中通过CREATE OR REPLACE VIEW语句和ALTER语句来修改视图

方式一:CREATE [OR REPLACE] VIEW 视图名称[(列名列表)] AS SELECT语句 [ WITH
[ CASCADED | LOCAL ] CHECK OPTION ]
方式二:ALTER VIEW 视图名称[(列名列表)] AS SELECT语句 [ WITH [ CASCADED |
LOCAL ] CHECK OPTION ]

4). 删除

DROP VIEW [IF EXISTS] 视图名称 [,视图名称] ...

演示用例:

-- 创建视图
create or replace view stu_v_1 as select id,name from student where id <= 10;
-- 查询视图
show create view stu_v_1;
select * from stu_v_1;
select * from stu_v_1 where id < 3;
-- 修改视图
create or replace view stu_v_1 as select id,name,no from student where id <= 10;
alter view stu_v_1 as select id,name from student where id <= 10;
-- 删除视图
drop view if exists stu_v_1;

上述我们演示了,视图应该如何创建、查询、修改、删除,那么我们能不能通过视图来插入、更新数据 呢? 接下来,做一个测试。

create or replace view stu_v_1 as select id,name from student where id <= 10 ;
select * from stu_v_1;
insert into stu_v_1 values(6,'Tom');
insert into stu_v_1 values(17,'Tom22');

视图里面操作的数据存在基表中,对视图的更新其实就是对表的更新

执行上述的SQL,我们会发现,id为6和17的数据都是可以成功插入的。 但是我们执行查询,查询出 来的数据,却没有id为17的记录。

因为我们在创建视图的时候,指定的条件为 id<=10, id为17的数据,是不符合条件的,所以没有查 询出来,但是这条数据确实是已经成功的插入到了基表中。 如果我们定义视图时,如果指定了条件,然后我们在插入、修改、删除数据时,是否可以做到必须满足 条件才能操作,否则不能够操作呢? 答案是可以的,这就需要借助于视图的检查选项了。

8.2检查选项

当使用WITH CHECK OPTION子句创建视图时,MySQL会通过视图检查正在更改的每个行,例如 插 入,更新,删除,以使其符合视图的定义。 MySQL允许基于另一个视图创建视图,它还会检查依赖视 图中的规则以保持一致性。为了确定检查的范围,mysql提供了两个选项: CASCADED 和 LOCAL ,默认值为 CASCADED 。

1). CASCADED 级联。

比如,v2视图是基于v1视图的,如果在v2视图创建的时候指定了检查选项为 cascaded,但是v1视图 创建时未指定检查选项。 则在执行检查时,不仅会检查v2,还会级联检查v2的关联视图v1。

2). LOCAL 本地。

比如,v2视图是基于v1视图的,如果在v2视图创建的时候指定了检查选项为 local ,但是v1视图创 建时未指定检查选项。 则在执行检查时,知会检查v2,不会检查v2的关联视图v1。

8.3视图的更新

要使视图可更新,视图中的行与基础表中的行之间必须存在一对一的关系。如果视图包含以下任何一 项,则该视图不可更新:

A. 聚合函数或窗口函数(SUM()、 MIN()、 MAX()、 COUNT()等)

B. DISTINCT

C. GROUP BY

D. HAVING

E. UNION 或者 UNION ALL

mysql> create view t_user_v_3 as select count(*) from tb_user where id<7;
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t_user_v_3 values(10);
--error:1471 - The target table t_user_v_3 of the INSERT is not insertable-into

9.存储过程

存储过程是事先经过编译并存储在数据库中的一段 SQL 语句的集合,这些语句被当作整体存入MySQL数据库服务器中.调用存储过程可以简化应用开发 人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的。 存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。

9.1基本语法

1). 创建

CREATE PROCEDURE 存储过程名称 ([ 参数列表 ])
BEGIN
-- SQL语句
END ;

参数由3部分组成,它们分别是输入输出类型、参数名称和参数类型。其形式为[ IN | OUT | INOUT ]param_name type。其中,IN表示输入参数;OUT表示输出参数;INOUT表示既可以输入也可以输出;param_name参数是存储过程参数名称;type参数指定存储过程的参数类型,该类型可以为MySQL数据库的任意数据类型。

2). 调用

CALL 名称 ([ 参数 ]);

3). 查看

SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA = 'xxx'; -- 查询指
定数据库的存储过程及状态信息
SHOW CREATE PROCEDURE 存储过程名称 ; -- 查询某个存储过程的定义

##根据数据库名查询当前数据库中的存储过程
show procedure status where db="testdb2";

4). 删除

DROP PROCEDURE [ IF EXISTS ] 存储过程名称 ;

注意: 在命令行中,执行创建存储过程的SQL时,需要通过关键字 delimiter 指定SQL语句的 结束符。

alter procedure <proc_name> 特征1 [特征2] [特征3]....
存储过程的特征参数:
1.contains sql表示子程序包含sql语句,但不包含读或写数据的语句;
2.no sql 表示子程序中不包含sql语句;
3.reads sql data表示子程序中包含读数据的语句 
4.modifies sql data 表示子程序中包含写数据的语句
5.sql security{DEFINER/INVOKER}指明谁有权限来执行
  DEFINER表示只有定义者自己才能够执行
  invoker表示调用者可以执行
6.comment "string"表示注释信息

举例子:
alter procedure proc_test1 reads sql data;

演示示例:

-- 存储过程基本语法
-- 创建
create procedure p1()
begin
select count(*) from student;
end;
-- 调用
call p1();
-- 查看
select * from information_schema.ROUTINES where ROUTINE_SCHEMA = 'itcast';
show create procedure p1;
-- 删除
drop procedure if exists p1;

9.2变量

在MySQL中变量分为三种类型: 系统变量、用户定义变量、局部变量。

9.2.1 系统变量

系统变量 是MySQL服务器提供,不是用户定义的,属于服务器层面。分为全局变量(GLOBAL)、会话 变量(SESSION)。

1). 查看系统变量

SHOW [ SESSION | GLOBAL ] VARIABLES ; -- 查看所有系统变量
SHOW [ SESSION | GLOBAL ] VARIABLES LIKE '......'; -- 可以通过LIKE模糊匹配方式查找变量
SELECT @@[SESSION | GLOBAL].系统变量名; -- 查看指定变量的值

2). 设置系统变量

SET [ SESSION | GLOBAL ] 系统变量名 = 值 ;
SET @@[SESSION | GLOBAL]系统变量名 = 值 ;

注意: 如果没有指定SESSION/GLOBAL,默认是SESSION,会话变量。

A. 全局变量(GLOBAL): 全局变量针对于所有的会话。

B. 会话变量(SESSION): 会话变量针对于单个会话,在另外一个会话窗口就不生效了。

mysql> show session variables like 'auto%';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| auto_generate_certs      | ON    |
| auto_increment_increment | 1     |
| auto_increment_offset    | 1     |
| autocommit               | ON    |
| automatic_sp_privileges  | ON    |
+--------------------------+-------+

ysql> select @@session.autocommit;
+----------------------+
| @@session.autocommit |
+----------------------+
|                    1 |
+----------------------+
mysql> set session autocommit=0;
mysql> select @@session.autocommit;
+----------------------+
| @@session.autocommit |
+----------------------+
|                    0 |
+----------------------+
1 row in set (0.11 sec)
9.2.2用户变量

用户定义变量 是用户根据需要自己定义的变量,用户变量不用提前声明,在用的时候直接用 "@变量 名" 使用就可以。其作用域为当前连接。

1). 赋值

SET @var_name = expr [, @var_name = expr] ... ;
SET @var_name := expr [, @var_name := expr] ... ;

赋值时,可以使用 = ,也可以使用 := 。

方式二:

SELECT @var_name := expr [, @var_name := expr] ... ;
SELECT 字段名 INTO @var_name FROM 表名;

2). 使用

SELECT @var_name ;

注意: 用户定义的变量无需对其进行声明或初始化,只不过获取到的值为NULL。

mysql> set @myname = 'itcast';
set @myage := 10;
set @mygender := '男',@myhobby := 'java';
mysql> select @myname,@myage,@mygender,@myhobby;
+---------+--------+-----------+----------+
| @myname | @myage | @mygender | @myhobby |
+---------+--------+-----------+----------+
| itcast  |     10 || java     |
+---------+--------+-----------+----------+
9.2.3 局部变量

局部变量 是根据需要定义的在局部生效的变量,访问之前,需要DECLARE声明。可用作存储过程内的 局部变量和输入参数,局部变量的范围是在其内声明的BEGIN ... END块。

1). 声明

DECLARE 变量名 变量类型 [DEFAULT ... ] ;

变量类型就是数据库字段类型:INT、BIGINT、CHAR、VARCHAR、DATE、TIME等。

2). 赋值

SET 变量名 = 值 ;
SET 变量名 := 值 ;
SELECT 字段名 INTO 变量名 FROM 表名 ... ;

演示示例:

mysql> create procedure p2()
begin
declare user_count int default 0;
select count(*) into user_count from tb_user;
select user_count;
end;
Query OK, 0 rows affected (0.00 sec)
mysql> call p2();
+------------+
| user_count |
+------------+
|         22 |
+------------+

9.3参数

参数的类型,主要分为以下三种:IN、OUT、INOUT。 具体的含义如下:

用法:

CREATE PROCEDURE 存储过程名称 ([ IN/OUT/INOUT 参数名 参数类型 ])
BEGIN
-- SQL语句
END ;

案例:

create procedure p4(in score int, out result varchar(10))
begin
    if score >= 85 then
    set result := '优秀';
    elseif score >= 60 then
    set result := '及格';
    else
    set result := '不及格';
    end if;
end;
-- 定义用户变量 @result来接收返回的数据, 用户变量可以不用声明
call p4(18, @result);
select @result;
+---------+
| @result |
+---------+
| 不及格  |
+---------+

9.4分支/循环语句

if

IF 条件1 THEN
.....
ELSEIF 条件2 THEN -- 可选
.....
ELSE -- 可选
.....
END IF;

在if条件判断的结构中,ELSE IF 结构可以有多个,也可以没有。 ELSE结构可以有,也可以没有。

案列:

create procedure p3()
begin
    declare score int default 58;
    declare result varchar(10);
    if score >= 85 then
        set result := '优秀';
    elseif score >= 60 then
        set result := '及格';
    else
        set result := '不及格';
    end if;
    select result;
end;
call p3();

case

-- 含义: 当case_value的值为 when_value1时,执行statement_list1,当值为 when_value2时,
执行statement_list2, 否则就执行 statement_list
CASE case_value
    WHEN when_value1 THEN statement_list1
    [ WHEN when_value2 THEN statement_list2] ...
    [ ELSE statement_list ]
END CASE;

语法2:

-- 含义: 当条件search_condition1成立时,执行statement_list1,当条件search_condition2成
立时,执行statement_list2, 否则就执行 statement_list
CASE
    WHEN search_condition1 THEN statement_list1
    [WHEN search_condition2 THEN statement_list2] ...
    [ELSE statement_list]
END CASE;

2). 案例 根据传入的月份,判定月份所属的季节(要求采用case结构)。

1-3月份,为第一季度

4-6月份,为第二季度

7-9月份,为第三季度

10-12月份,为第四季度

create procedure p6(in month int)
begin
    declare result varchar(10);
    case
        when month >= 1 and month <= 3 then
        	set result := '第一季度';
		when month >= 4 and month <= 6 then
			set result := '第二季度';
		when month >= 7 and month <= 9 then
			set result := '第三季度';
		when month >= 10 and month <= 12 then
			set result := '第四季度';
		else
			set result := '非法参数';
		end case ;
	select concat('您输入的月份为: ',month, ', 所属的季度为: ',result);
end;
call p6(16);

while

1). 介绍

while 循环是有条件的循环控制语句。满足条件后,再执行循环体中的SQL语句。具体语法为:

-- 先判定条件,如果条件为true,则执行逻辑,否则,不执行逻辑
WHILE 条件 DO
	SQL逻辑...
END WHILE;

2). 案例 计算从1累加到n的值,n为传入的参数值。

-- A. 定义局部变量, 记录累加之后的值;
-- B. 每循环一次, 就会对n进行减1 , 如果n减到0, 则退出循环
create procedure p7(in n int)
begin
    declare total int default 0;
    while n>0 do
        set total := total + n;
        set n := n - 1;
    end while;
    select total;
end;
call p7(100);

repeat

1). 介绍

repeat是有条件的循环控制语句, 当满足until声明的条件的时候,则退出循环 。具体语法为:

-- 先执行一次逻辑,然后判定UNTIL条件是否满足,如果满足,则退出。如果不满足,则继续下一次循环
REPEAT
    SQL逻辑...
    UNTIL 条件
END REPEAT;

2). 案例 计算从1累加到n的值,n为传入的参数值。(使用repeat实现)

-- A. 定义局部变量, 记录累加之后的值;
-- B. 每循环一次, 就会对n进行-1 , 如果n减到0, 则退出循环
create procedure p8(in n int)
begin
    declare total int default 0;
    repeat
        set total := total + n;
        set n := n - 1;
    until n <= 0
    end repeat;
    select total;
end;
call p8(10);
call p8(100);

loop

1). 介绍

LOOP 实现简单的循环,如果不在SQL逻辑中增加退出循环的条件,可以用其来实现简单的死循环。

LOOP可以配合一下两个语句使用:

  • LEAVE :配合循环使用,退出循环。
  • ITERATE:必须用在循环中,作用是跳过当前循环剩下的语句,直接进入下一次循环。
[begin_label:] LOOP
SQL逻辑...
END LOOP [end_label];
LEAVE label; -- 退出指定标记的循环体
ITERATE label; -- 直接进入下一次循环

2). 案例一

计算从1累加到n的值,n为传入的参数值。

-- A. 定义局部变量, 记录累加之后的值;
-- B. 每循环一次, 就会对n进行-1 , 如果n减到0, 则退出循环 ----> leave xx
create procedure p9(in n int)
begin
    declare total int default 0;
    sum:loop
        if n<=0 then
       	 leave sum;
    end if;
        set total := total + n;
        set n := n - 1;
    end loop sum;
    select total;
end;
call p9(100);

3). 案例二

计算从1到n之间的偶数累加的值,n为传入的参数值。

-- A. 定义局部变量, 记录累加之后的值;
-- B. 每循环一次, 就会对n进行-1 , 如果n减到0, 则退出循环 ----> leave xx
-- C. 如果当次累加的数据是奇数, 则直接进入下一次循环. --------> iterate xx
create procedure p10(in n int)
begin
    declare total int default 0;
    sum:loop
        if n<=0 then
        	leave sum;
        end if;
        if n%2 = 1 then
            set n := n - 1;
            iterate sum;
        end if;
        set total := total + n;
        set n := n - 1;
    end loop sum;
select total;
end;
call p10(100);

9.5游标

游标在存储过程和函数中使用光标可以实现逐条读取结果集中的记录

是用来存储查询结果集的数据类型 , 在存储过程和函数中可以使用游标对结果集进行循环的处理。游标的使用包括游标的声明、OPEN、FETCH 和 CLOSE,其语法分别如下。

A. 声明游标

DECLARE 游标名称 CURSOR FOR 查询语句 ;

B. 打开游标

OPEN 游标名称 ;

C. 获取游标记录

FETCH 游标名称 INTO 变量 [, 变量 ] ;

D. 关闭游标

CLOSE 游标名称 ;

2). 案例 根据传入的参数uage,来查询用户表tb_user中,所有的用户年龄小于等于uage的用户姓名 (name)和专业(profession),并将用户的姓名和专业插入到所创建的一张新表 (id,name,profession)中。

-- 逻辑:
-- A. 声明游标, 存储查询结果集
-- B. 准备: 创建表结构
-- C. 开启游标
-- D. 获取游标中的记录
-- E. 插入数据到新表中
-- F. 关闭游标
create procedure p11(in uage int)
begin
    declare uname varchar(100);
    declare upro varchar(100);
    declare u_cursor cursor for select name,profession from tb_user where age <=uage;
    drop table if exists tb_user_pro;
    create table if not exists tb_user_pro(
    id int primary key auto_increment,
    name varchar(100),
    profession varchar(100)
    );
    open u_cursor;
    while true do
        fetch u_cursor into uname,upro;
        insert into tb_user_pro values (null, uname, upro);
    end while;
    close u_cursor;
end;
call p11(30);

9.6存储函数

1). 介绍

存储函数是有返回值的存储过程,存储函数的参数只能是IN类型的。具体语法如下:

CREATE FUNCTION 存储函数名称 ([ 参数列表 ])
RETURNS type [characteristic ...]
BEGIN
    -- SQL语句
    RETURN ...;
END ;

characteristic说明:

  • DETERMINISTIC:相同的输入参数总是产生相同的结果
  • NO SQL :不包含 SQL 语句。
  • READS SQL DATA:包含读取数据的语句,但不包含写入数据的语句。

2). 案例 计算从1累加到n的值,n为传入的参数值。

create function fun1(n int)
returns int deterministic
begin
	declare total int default 0;
	while n>0 do
        set total := total + n;
        set n := n - 1;
	end while;
	return total;
end;
select fun1(50);

10.触发器

10.1介绍

触发器就是一种特殊的存储过程。触发器和存储过程一样,是一个能够完成特定功能、存储在数据服务器上的SQL片段,但是触发器无需调用,当对数据表中的数据执行DML操作时会自动触发这个SQL片段的执行,无需手动调用。

触发器是与表有关的数据库对象,指在insert/update/delete之前(BEFORE)或之后(AFTER),触 发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性 , 日志记录 , 数据校验等操作 。

使用别名OLD和NEW来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。现在触发器还 只支持行级触发,不支持语句级触发。

10.2语法

1). 创建

CREATE TRIGGER trigger_name
BEFORE/AFTER INSERT/UPDATE/DELETE
ON tbl_name FOR EACH ROW -- 行级触发器
BEGIN
	trigger_stmt ;
END;

2). 查看

SHOW TRIGGERS ;

3). 删除

DROP TRIGGER [schema_name.]trigger_name ; -- 如果没有指定 schema_name,默认为当前数据库 。

案例

-- 准备工作 : 日志表 user_logs
create table user_logs(
    id int(11) not null auto_increment,
    operation varchar(20) not null comment '操作类型, insert/update/delete',
    operate_time datetime not null comment '操作时间',
    operate_id int(11) not null comment '操作的ID',
    operate_params varchar(500) comment '操作参数',
    primary key(`id`)
)engine=innodb default charset=utf8;

A. 插入数据触发器

create trigger tb_user_insert_trigger
after insert on tb_user for each row
begin
	insert into user_logs(id, operation, operate_time, operate_id, operate_params)
	VALUES
    (null, 'insert', now(), new.id, concat('插入的数据内容为:
    id=',new.id,',name=',new.name, ', phone=', NEW.phone, ', email=', NEW.email, ',
    profession=', NEW.profession));
end;

测试:

-- 查看
show triggers ;
-- 插入数据到tb_user
insert into tb_user(id, name, phone, email, profession, age, gender, status,
createtime) VALUES (26,'三皇子','18809091212','erhuangzi@163.com','软件工
程',23,'1','1',now());

B. 修改数据触发器

create trigger tb_user_update_trigger
after update on tb_user for each row
begin
    insert into user_logs(id, operation, operate_time, operate_id, operate_params)
    VALUES
    (null, 'update', now(), new.id,
    concat('更新之前的数据: id=',old.id,',name=',old.name, ', phone=',
    old.phone, ', email=', old.email, ', profession=', old.profession,
    ' | 更新之后的数据: id=',new.id,',name=',new.name, ', phone=',
    NEW.phone, ', email=', NEW.email, ', profession=', NEW.profession));
end;

测试:

-- 查看
show triggers ;
-- 更新
update tb_user set profession = '会计' where id = 23;
update tb_user set profession = '会计' where id <= 5;

C. 删除数据触发器

create trigger tb_user_delete_trigger
after delete on tb_user for each row
begin
    insert into user_logs(id, operation, operate_time, operate_id, operate_params)
    VALUES
    (null, 'delete', now(), old.id,
    concat('删除之前的数据: id=',old.id,',name=',old.name, ', phone=',
    old.phone, ', email=', old.email, ', profession=', old.profession));
end;

测试:

-- 查看
show triggers ;
-- 删除数据
delete from tb_user where id = 26;

11.备份与还原

备份数据库:

使用mysqldump命令来备份MySQL数据库。以下是备份的基本语法:

mysqldump -u [username] -p [database_name] > [backup_file.sql]
  • [username]是数据库用户名。
  • [database_name]是要备份的数据库名称。
  • [backup_file.sql]是备份文件的名称。

例如,要备份一个名为mydatabase的数据库,可以执行以下命令:

mysqldump -u root -p mydatabase > backup.sql

系统会要求您输入数据库密码以确认备份。

还原数据库:

要还原数据库,您可以使用mysql命令,将备份文件的内容导入到MySQL中。以下是还原数据库的基本语法:

mysql -u [username] -p [database_name] < [backup_file.sql]
  • [username]是数据库用户名。
  • [database_name]是要还原到的数据库名称。
  • [backup_file.sql]是备份文件的名称。

例如,要还原名为mydatabase的数据库,可以执行以下命令:

mysql -u root -p mydatabase < backup.sql

系统会要求您输入数据库密码以确认还原。

请确保备份和还原操作都在合适的环境中进行,以防止意外数据丢失。此外,备份和还原数据库时,应确保数据库服务器处于正常运行状态。

12.与sqlserver差异

1.sql语句结束标志

mysql以;结束一条SQL语句;SQL server 以;go或不写结束都可以。不过建议写sql server的时候以go结束,因为sql server很多语句要以等一个语句结束后才能写下一个语句,不然会报批处理错误(深受其害)。

2.查看表结构数量等

mysql 语句

-- 查看系统内所有数据库
show databases;
-- 查询数据库内所有表
show tables;
-- 显示表结构
desc 表名;1.2.3.4.5.6.

sql server语句

-- 查看系统内所有数据库
SELECT name, database_id, create_date  FROM sys.databases  ;
-- 查询数据库内所有表
select * from sysobjects where xtype= 'U'  ;
-- 显示表结构
sp_help/sp_columns 表名;1.2.3.4.5.6.

相比来说,mysql 的更为简洁。

3.查询前几条记录

查询前10条记录:
mysql 语句
select * from student limit 10; sql server 语句
select top 10 * from student ;

4.获取当前时间

MySQL写法:now() SQLServer写法:getdate()

5. 从数据库定位到某张表

mysql写法:库名.表名

select password from Info.users where userName='boss'

Sqlserver写法:库名.dbo.表名 ;或者:库名…表名 (注:中间使用两个点)

select password from Info.dbo.users where userName='boss'

或者

select password from Info..users where userName='boss'

6. 自增字段

  • MySQL使用AUTO_INCREMENT

    CREATE TABLE users (
      id INT AUTO_INCREMENT PRIMARY KEY,
      username VARCHAR(50)
    );
    
  • SQL Server使用IDENTITY

    CREATE TABLE users (
      id INT IDENTITY(1,1) PRIMARY KEY,
      username VARCHAR(50)
    );
    

7.字符串连接

  • MySQL使用CONCAT函数:

    SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM employees;
    
  • SQL Server使用+运算符:

    SELECT first_name + ' ' + last_name AS full_name FROM employees;
    

8.存储过程和触发器

在MySQL中创建存储过程:

DELIMITER //
CREATE PROCEDURE procedure_name(parameter_list)
BEGIN
    -- 存储过程的SQL语句
END //
DELIMITER ;
  • DELIMITER 语句用于更改语句分隔符,通常设置为//,以便在存储过程内使用分号。
  • CREATE PROCEDURE 用于创建存储过程。
  • procedure_name 是存储过程的名称。
  • parameter_list 包含存储过程的参数。
  • 存储过程的主体在BEGINEND之间。

在MySQL中创建触发器:

DELIMITER //
CREATE TRIGGER trigger_name
    trigger_time trigger_event
    ON table_name FOR EACH ROW
BEGIN
    -- 触发器的SQL语句
END //
DELIMITER ;
  • DELIMITER 语句用于更改语句分隔符,通常设置为//
  • CREATE TRIGGER 用于创建触发器。
  • trigger_name 是触发器的名称。
  • trigger_time 指定触发器的时间,例如BEFOREAFTER
  • trigger_event 指定触发器的事件,例如INSERTUPDATEDELETE
  • table_name 是触发器关联的表。
  • 触发器的主体在BEGINEND之间。

在SQL Server中创建存储过程:

CREATE PROCEDURE procedure_name
    @parameter1 datatype1,
    @parameter2 datatype2
AS
BEGIN
    -- 存储过程的SQL语句
END;
  • CREATE PROCEDURE 用于创建存储过程。
  • procedure_name 是存储过程的名称。
  • @parameter1@parameter2 等是存储过程的参数。
  • 参数需要指定数据类型,如datatype1datatype2
  • 存储过程的主体在BEGINEND之间。

在SQL Server中创建触发器:

CREATE TRIGGER trigger_name
ON table_name
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
    -- 触发器的SQL语句
END;
  • CREATE TRIGGER 用于创建触发器。
  • trigger_name 是触发器的名称。
  • table_name 是触发器关联的表。
  • AFTER INSERT, UPDATE, DELETE 指定触发器在哪些事件后触发。
  • 触发器的主体在BEGINEND之间。

这些是MySQL和SQL Server在创建存储过程和触发器时的基本语法区别。需要注意的是,具体的语法和功能可能会根据不同的数据库版本和配置而有所不同,因此在实际使用中,应根据数据库系统的文档和要求进行具体的语法编写。

9.修改字段

MySQL 修改表字段:

  1. 修改字段类型

    若要修改字段的数据类型,可以使用ALTER TABLE命令,并使用MODIFY关键字:

    ALTER TABLE table_name
    MODIFY column_name new_data_type;
    

    例如,将名为age的字段的数据类型更改为INT

    ALTER TABLE employees
    MODIFY age INT;
    
  2. 添加或删除字段

    若要添加新字段,可以使用ALTER TABLE命令,并使用ADD关键字:

    ALTER TABLE table_name
    ADD new_column_name data_type;
    

    若要删除字段,可以使用ALTER TABLE命令,并使用DROP关键字:

    ALTER TABLE table_name
    DROP column_name;
    
  3. 修改字段名称

    若要修改字段的名称,可以使用ALTER TABLE命令,并使用CHANGE关键字:

    ALTER TABLE table_name
    CHANGE old_column_name new_column_name data_type;
    

    例如,将名为old_name的字段的名称更改为new_name

    ALTER TABLE employees
    CHANGE old_name new_name VARCHAR(50);
    

SQL Server 修改表字段:

  1. 修改字段类型

    若要修改字段的数据类型,可以使用ALTER TABLE命令,并使用ALTER COLUMN子句:

    ALTER TABLE table_name
    ALTER COLUMN column_name new_data_type;
    

    例如,将名为age的字段的数据类型更改为INT

    ALTER TABLE employees
    ALTER COLUMN age INT;
    
  2. 添加或删除字段

    若要添加新字段,可以使用ALTER TABLE命令,并使用ADD关键字:

    ALTER TABLE table_name
    ADD new_column_name data_type;
    

    若要删除字段,可以使用ALTER TABLE命令,并使用DROP COLUMN子句:

    ALTER TABLE table_name
    DROP COLUMN column_name;
    
  3. 修改字段名称

    若要修改字段的名称,需要创建一个新的表,并将数据从旧表迁移到新表。SQL Server不直接支持重命名字段的操作。

需要注意的是,修改表字段是一个敏感的操作,应谨慎进行,以确保不会丢失数据或引发其他问题。在执行此类操作之前,建议进行备份并谨慎考虑可能的后果

10.重命名数据库和复制表

重命名数据库

sqlserver若要重命名数据库,可以使用sp_renamedb存储过程:

EXEC sp_renamedb 'old_database_name', 'new_database_name';

在SQL Server中,可以使用sp_rename存储过程来重命名表。以下是使

sp_rename的示例:

EXEC sp_rename 'old_table_name', 'new_table_name';

在MySQL中复制表:

  1. 使用CREATE TABLE AS SELECT(CTAS)

    使用CREATE TABLE语句和SELECT语句,您可以创建一个新表,并从现有表中选择数据,然后将数据插入到新表中。这是一种简单的复制表的方法。

    CREATE TABLE new_table AS
    SELECT * FROM old_table;
    

    这将创建一个名为new_table

    新表,并将old_table中的所有数据复制到new_table中。

  2. 使用INSERT INTO

    另一种复制表的方法是使用INSERT INTO语句,将一个表的数据插入到另一个表中。

    INSERT INTO new_table
    SELECT * FROM old_table;
    

    请确保目标表new_table已经存在,并且其结构与源表old_table兼容。

在SQL Server中复制表:

  1. 使用SELECT INTO

    使用SELECT INTO语句,您可以创建一个新表并从现有表中选择数据,然后将数据插入到新表中。这是一种简单的复制表的方法。

    SELECT *
    INTO new_table
    FROM old_table;
    

11.NULL值处理

MySQL中的NULL值:

  1. 默认情况下,MySQL中的列可以存储NULL值。这意味着如果您不明确为列指定NOT NULL约束,那么该列可以包含NULL值。

  2. MySQL使用IS NULL和IS NOT NULL操作符来测试NULL值的存在。例如:

    SELECT * FROM table_name WHERE column_name IS NULL;
    
  3. MySQL中的聚合函数会忽略NULL值。例如,使用SUM函数时,NULL值将被忽略,不会计入总和。

SQL Server中的NULL值:

  1. 默认情况下,SQL Server中的列不允许存储NULL值。这意味着如果您不明确为列指定NULL约束,那么该列不允许包含NULL值。

  2. SQL Server也使用IS NULL和IS NOT NULL操作符来测试NULL值的存在。例如:

    SELECT * FROM table_name WHERE column_name IS NULL;
    
  3. SQL Server中的聚合函数不会忽略NULL值。这意味着使用SUM函数时,包含NULL值的行将被计入总和,除非您使用特定的函数来处理NULL值,如ISNULL()或COALESCE()。

  4. SQL Server还支持ANSI_NULLS设置。这个设置可以影响对NULL值的比较行为。如果设置为ON(默认情况下),则比较NULL值时将返回未知(UNKNOWN)而不是TRUE或FALSE。您可以使用以下命令来更改此设置:

    SET ANSI_NULLS OFF;
    

需要注意的是,虽然MySQL和SQL Server在处理NULL值时存在一些差异,但在大多数情况下,它们的行为是合理且一致的。在编写SQL查询时,请注意数据库系统的特定行为,以确保您的查询按预期运行。此外,根据具体的业务需求,您可以使用COALESCE()、ISNULL()等函数来处理NULL值,以满足您的要求。

12.数据类型

共同的数据类型:

  1. 整数类型
    • MySQL和SQL Server都支持整数数据类型,如INTBIGINTSMALLINT等,用于存储整数值。
  2. 浮点和定点类型
    • MySQL和SQL Server都支持浮点数据类型,如FLOATDOUBLE,以及定点数数据类型,如DECIMALNUMERIC,用于存储小数值。
  3. 日期和时间类型
    • 两者都支持日期和时间数据类型,如DATETIMEDATETIMETIMESTAMP等,用于存储日期和时间信息。
  4. 字符和文本类型
    • MySQL和SQL Server都支持字符和文本数据类型,如CHARVARCHARTEXT等,用于存储文本信息。
  5. 二进制数据类型
    • 两者都支持二进制数据类型,如BINARYVARBINARYBLOBIMAGE等,用于存储二进制数据。

差异的数据类型:

  1. 布尔类型
    • MySQL支持BOOLEANBOOL用于表示布尔值(真/假),而SQL Server通常使用BIT数据类型,其中0表示假,1表示真。
  2. 自动增量字段
    • MySQL使用AUTO_INCREMENT关键字来创建自动增量字段,通常用于主键。而SQL Server使用IDENTITY属性来实现相同的功能。
  3. 字符串类型的长度
    • 在定义字符或文本类型时,MySQL通常需要指定长度,如VARCHAR(255),而SQL Server则可以使用VARCHAR(MAX)表示可变长度字符串,而不需要显式指定长度。
  4. GUID/UUID类型
    • SQL Server具有UNIQUEIDENTIFIER类型,用于存储全局唯一标识符(GUID/UUID),而MySQL通常使用CHAR(36)BINARY(16)来存储UUID。
  5. 枚举和集合类型
    • MySQL支持ENUMSET类型,用于存储有限的集合值。SQL Server没有相应的类型,通常使用其他数据类型和约束来表示相似的概念。

13.添加约束

在MySQL中添加约束:

  1. 主键约束(Primary Key)

    在MySQL中,您可以使用PRIMARY KEY关键字定义主键约束,通常在表的创建时添加:

    CREATE TABLE table_name (
        column_name INT PRIMARY KEY,
        -- other columns
    );
    

    您还可以使用ALTER TABLE命令来添加主键约束:

    ALTER TABLE table_name
    ADD PRIMARY KEY (column_name);
    
  2. 外键约束(Foreign Key)

    MySQL支持外键约束,但要求使用存储引擎支持外键,如InnoDB。外键约束可以在表的创建时或后续使用ALTER TABLE命令添加:

    CREATE TABLE orders (
        order_id INT PRIMARY KEY,
        customer_id INT,
        FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
    );
    

    或者:

    ALTER TABLE orders
    ADD FOREIGN KEY (customer_id) REFERENCES customers(customer_id);
    
  3. 唯一约束(Unique Constraint)

    MySQL允许您使用UNIQUE关键字定义唯一约束,用于确保列中的值是唯一的:

    CREATE TABLE table_name (
        column_name INT UNIQUE,
        -- other columns
    );
    

    或者使用ALTER TABLE命令添加唯一约束。

在SQL Server中添加约束:

  1. 主键约束(Primary Key)

    在SQL Server中,您可以使用PRIMARY KEY关键字定义主键约束,通常在表的创建时添加:

    CREATE TABLE table_name (
        column_name INT PRIMARY KEY,
        -- other columns
    );
    

    或者使用ALTER TABLE命令添加主键约束。

    ALTER TABLE table_name
    ADD CONSTRAINT constraint_name PRIMARY KEY (column1, column2, ...);
    
  2. 外键约束(Foreign Key)

    SQL Server支持外键约束,它可以在表的创建时或后续使用ALTER TABLE命令添加:

    CREATE TABLE orders (
        order_id INT PRIMARY KEY,
        customer_id INT,
        FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
    );
    

    或者使用ALTER TABLE命令添加外键约束。

    ALTER TABLE table_name
    ADD CONSTRAINT constraint_name 
    FOREIGN KEY (column1, column2, ...)
    REFERENCES referenced_table (referenced_column1, referenced_column2, ...);
    
  3. 唯一约束(Unique Constraint)

    SQL Server允许您使用UNIQUE关键字定义唯一约束,用于确保列中的值是唯一的:

    CREATE TABLE table_name (
        column_name INT UNIQUE,
        -- other columns
    );
    

    或者使用ALTER TABLE命令添加唯一约束。

    ALTER TABLE table_name
    ADD CONSTRAINT constraint_name UNIQUE (column1, column2, ...);
    

    12。mysql和Oracle差异

    自动递增列:

    • 在MySQL中使用AUTO_INCREMENT:

      CREATE TABLE my_table (id INT AUTO_INCREMENT, name VARCHAR(50));
      
    • 在Oracle中使用序列(Sequence)和触发器:

      CREATE SEQUENCE my_seq START WITH 1;
      CREATE TABLE my_table (id NUMBER, name VARCHAR2(50));
      CREATE OR REPLACE TRIGGER my_trigger
      BEFORE INSERT ON my_table
      FOR EACH ROW
      BEGIN
        SELECT my_seq.nextval INTO :new.id FROM dual;
      END;
      

字符串连接:

  • 在MySQL中使用CONCAT函数:

    SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM employees;
    
  • 在Oracle中使用 ||

    运算符:

    SELECT first_name || ' ' || last_name AS full_name FROM employees;
    

分页查询:

  • 在Oracle中使用ROWNUM:

    SELECT * FROM my_table WHERE ROWNUM <= 10;
    
  • 在MySQL中使用LIMIT和OFFSET:

    SELECT * FROM my_table LIMIT 10 OFFSET 0;
    

NULL值的处理:

  • 在Oracle中使用 IS NULL 和 IS NOT NULL

    SELECT * FROM my_table WHERE my_column IS NULL;
    
  • 在MySQL中也使用IS NULL和IS NOT NULL,但也支持= NULL和!= NULL:

    SELECT * FROM my_table WHERE my_column IS NULL;
    SELECT * FROM my_table WHERE my_column = NULL;
    

存储过程

oracle

create or replace procedure P_ADD_FAC(
   id_fac_cd  IN ES_FAC_UNIT.FAC_CD%TYPE)  as

mysql

DROP PROCEDURE IF EXISTS `SD_USER_P_ADD_USR`;
create procedure P_ADD_FAC(
       id_fac_cd  varchar(100))

参数写法不同

oracle

 1.   procedure P_ADD_FAC(
       id_fac_cd  IN ES_FAC_UNIT.FAC_CD%TYPE)
       
2.    function func_name(
               gw_id  in(out)  varchar2 )

mysql

1.create procedure P_ADD_FAC(
     (in) id_fac_cd  varchar(100))


2.create function func_name(
   gw_id varchar100))

过程和函数的声明变量的位置不同

oracle

声明变量在beginend体之前

mysql

声明变量在begin...end体内,begin之后其他任何内容之前

触发器

Oracle可以在一个触发器触发insert,delete,update事件.
Mysql每个触发器只支持一个事件. 也就是说,目前每个trigger需要拆分成3个mysql trigger.

with

​ Oracle 中可用with来构建一个临时表,但MySQL不支持with,对应临时表,MySQL可通过小括号的方式来处理,但构建的临时表必须设置临时表名。

-- Oracle with使用
WITH TMPTAB AS (
	SELECT A.DEPID FROM FW_DEPARTMENT A
)
SELECT DEPID
FROM TMPTAB

-- MySQL 构建临时表使用(此处必须给括号中的临时表设置表名)
select b.depid
from (
	select depid
	from fw_department
) b

MySQL中命令默认commit,但是Oracle需要手动提交

使用ALTER TABLE语句添加约束时,Oracle和MySQL之间存在一些差异,包括语法和支持的约束类型。以下是一些主要的区别:

1. 添加主键约束:*

  • 在 Oracle 中,可以使用 ALTER TABLE 来添加主键约束。

在 Oracle 中:

ALTER TABLE employees
ADD CONSTRAINT pk_employee_id PRIMARY KEY (employee_id);
  • 在 MySQL 中,也可以使用 ALTER TABLE 来添加主键约束。

在 MySQL 中:

ALTER TABLE employees
ADD PRIMARY KEY (employee_id);

13.crm了解

客户关系管理系统(CRM)是企业管理客户与服务的一个集成化的平台,它集成了各种功能和工具,包括客户管理,销售管理,市场营销,商机管理,售后服务支持,数据报告分析

CRM客户管理系统介绍 CRM客户管理系统由客户信息管理、销售过程自动化(SFA)、营销自动化(MA)、客户服务与支持( CSS )管理、客户分析( CA )系统 5 大主要功能模块组成。

在管理客户、管理销售的业务场景中,你是不是总是碰到过如下问题:

1.客户状态无法查询

客户信息无法查重,跟进节点搞不清,项目推进一波三折

2.企业管理成本高

销售离职带走客户,管理成本增高,老板总是为员工做嫁衣

3.数据分析耗时长

日、月营收环比不清晰,每年财务做一次数据分析耗时将近1个月

4.运营获客转化难

客户画像不精准,运营⼈⼒成本增⾼,客情难掌控,精准营销难实现

5.销售复盘质量差

客户流失率高,资源难沉淀

  • 人员流动带走客户资源?内部抢单撞单问题难规避?客户、订单等核心资源没有沉淀,缺乏数据分析,企业成长慢;

业务进展慢效率低

  • 从线索收集到客户报备、报价审批、项目管理、合同管理、订单处理……业务进展慢,跨部门协同能力弱,客户转化率不高;

客户满意度不高

  • 售后响应不及时,服务团队无考核,效率低,客户满意度不高;目标客户不清晰,产品满意度不高

crm发挥额作用

  1. 提高营销效率

  2. 提升销售业绩

  3. 提升客户满意度

  4. 提高协同效率

  5. 提升业务流程的效率

posted @   释然jsqwe  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示