从零学习Mysql数据库-常见操作

134 阅读11分钟

1. 数据库类型

1.1 关系型数据库

像 MySQL、Oracle、DB2、SQL Server等。特点:

1. 创建很多二维数据表

2. 数据表之间相互关联,形成一对一、一对多、多对多的关系

3. 可以利用SQL语句在多张表中查询所需数据

4. 支持事务,对数据访问更加的安全

1.2 非关系型数据库

像 MongoDB、Redis、Memcached 等。特点:

   1.英文名:Not Only SQL,简称NoSQL
   
   2.存储数据自由,可以放Json格式的数据
   
   3. 基于Key-Value的对应关系,查询过程中不需要解析SQL,性能更好
   
   4.通常不支持事务,需要手动保证一些原子性的操作

1.3 适用场景

   1. 目前在公司进行后端开发(Node、Java、Go等),还是以关系型数据库为主
   
   2.对爬取大量的数据进行存储时,常用到NoSQL

2. SQL语句

2.1 SQL语句分类

   1. DDL:数据定义语言,可以通过DDL语句对数据库或者表进行创建、删除、修改等操作;
   
   2. DML:数据操作语言,可以通过DML语句对表进行:添加、删除、修改等操作
   
   3. DQL:数据查询语言,可以通过DQL从数据库中查询记录
   
   4. DCL:数据控制语言,对数据库、表格的权限进行相关访问控制操作
   
   学习的重点是DML和DQL
   

2.2 数据库操作

   1. 查看所有的数据库:SHOW DATABASES
   
   2. 使用某一个数据库:USE dataBaseName
   
   3. 查看当前正在使用的数据库:SELECT DATABASE();
   
   4. 创建数据库:CREATE DATABASE databaseName
   
   //如果不存在就创建
   
   CREATE DATABASE IF NOT EXISTS databaseName
   
   //设置数据库的字符集和排序规则
   
   CREATE DATABASE IF NOT EXISTS dataBaseName DEFAULT CHARACTER 
   SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
   
   5. 删除数据库:
   
   DROP DATABASE databaseName;

   DROP DATABASE IF EXIT databaseName;
   
   6. 修改数据库的字符集和排序规则
   
   ALTER DATABASE bilibili CHARACTER SET = utf8 COLLATE = utf8_unicode_ci;

2.2 数据表操作

   1. 查看当前数据库下所有的数据表:SHOW TABLES;
   
   2. 查看某一个表结构:DESC tableName;
   
   3. 创建数据表: 
   
   CREATE TABLE IF NOT EXISTS `users`(

                name VARCHAR(20)

                age INT,

                height DOUBLE

                 );

2.3 SQL数据类型

  1. 数字类型:
  
  TINYINT:1个字节,-2^8~2^8-1
  
  SMALLINT2个字节,-2^16~2^16-1
  
  MEDIUMINT:3个字节,-2^24~2^24-1
  
  INT4个字节,-2^32~2^32-1
  
  BIGINT8个字节,-2^64~2^64-1
  
  Floatdouble,DESIMAL
      
  DECIMAL(5,2) //总共5位包含2位小数的±数字
  
  2.日期类型
  
  YEAR以YYYY格式显示值:范围 19012155,和 0000
  
  DATE类型用于具有日期部分但没有时间部分的值:
  
  DATE以格式YYYY-MM-DD显示值,
  
  支持的范围是1000-01-01 00:00:009999-12-31 23:59:59;
  
  TIMESTAMP数据类型被用于同时包含日期和时间部分的值:
  
  TIMESTAMP以格式'YYYY-MM-DD hh:mm:ss'显示值;
  
  它的范围是UTC的时间范围:'1970-01-01 00:00:01''2038-01-19 03:14:07';
  
  3.字符串类型
  
  CHAR:创建表时为固定长度,长度可以是0255之间的任何值,在被查询时,会删除后面的空格
  
  VARCHAR:可变长度的字符串,长度可以指定为065535之间的值,在被查询时,不会删除后面的空格
  
  BINARYVARBINARY:用于存储二进制字符串,存储的是字节字符串
  
  BLOB:存储大的二进制类型
  
  TEXT:存储大的字符串类型
 

2.4 数据表约束

  主键:PRIMARY KEY,一张表中,我们为了区分每一条记录的唯一性,必须有一个字段是永远不会重复,并且不会为空的,这个字段我们通常会将它设置为主键
  
  a. 主键是表中唯一的索引
  
  b. 并且必须是NOT NULL的,不设置,MySQL隐式自动设置NOT NULL
  
  c. 开发中主键字段应该是和业务无关的,尽量不要使用业务字段来作为主键
  
  唯一:UNIQUE,某些字段在开发中我们希望是唯一的,不会重复的,比如手机号码、身份证号码等,这个字段我们可以使用UNIQUE来约束
  
  a. 使用UNIQUE约束的字段在表中必须是不同的
  
  b. UNIQUE 索引允许NULL包含的列具有多个值NULL
  
  不能为空:NOT NULL,某些字段我们要求用户必须插入值,不可以为空
  
  默认值:DEFAULT,某些字段我们希望在没有设置值时给予一个默认值
  
  自动递增:AUTO_INCREMENT,某些字段我们希望不设置值时可以进行递增,比如用户的id

现在我们利用上面的约束创建一张表

  CREATE TABLE IF NOT EXISTS `users`(

            id INT PRIMARY KEY AUTO_INCREMENT,

            name VARCHAR(20) NOT NULL,

            age INT DEFAULT 0,

            telPhone VARCHAR(20) DEFAULT '' UNIQUE NOT NULL

    );

2.5 修改数据表

   修改表名

    ALTER TABLE `moments` RENAME TO `moment`;

    添加一个新的列

    ALTER TABLE `moment` ADD `publishTime` DATETIME;

    ALTER TABLE `moment` ADD `updateTime` DATETIME;

    删除一列数据

    ALTER TABLE `moment` DROP `updateTime`;

    修改列的名称

    ALTER TABLE `moment` CHANGE `publishTime` `publishDate` DATE;

    修改列的数据类型

    ALTER TABLE `moment` MODIFY `id` INT;
    
    插入一条数据
    
    INSERT INTO `products` (`title`, `description`, `price`, `publishTime`) 
    
    VALUES ('iPhone', 'iPhone12只要998', 998.88, '2020-10-10');
    
    删除数据
    
    DELETE FROM `products`; //删除所有数据
    
    DELETE FROM `products` WHERE `title` = 'iPhone';//删除符合条件的数据
    
    修改数据
    
    UPDATE `products` SET `title` = 'iPhone12', `price` = 1299.88;//会修改表中所有的数据
    //会修改符合条件的数据
    
    UPDATE `products` SET `title` = 'iPhone12', `price` = 1299.88 WHERE `title` = 'iPhone'; 
    
    //如果我们希望修改完数据后,直接可以显示最新的更新时间
    
    ALTER TABLE `products` ADD `updateTime` TIMESTAMP DEFAULT CURRENT_TIMESTAMP 
    
    ON UPDATE CURRENT_TIMESTAMP;     

2.6 DQL 语句

就是从一个或者多个表中检索选中的行,格式如下

    SELECT select_expr [, select_expr]...

    [FROM table_references]

    [WHERE where_condition]

    [ORDER BY expr [ASC | DESC]]

    [LIMIT {[offset,] row_count | row_count OFFSET offset}]

    [GROUP BY expr]

    [HAVING where_condition]

现在进行实操,准备表,在navicate中粘贴,再把数据传入


    CREATE TABLE IF NOT EXISTS `products` (

    id INT PRIMARY KEY AUTO_INCREMENT,

    brand VARCHAR(20),

    title VARCHAR(100) NOT NULL,

    price DOUBLE NOT NULL,

    score DECIMAL(2,1),

    voteCnt INT,

    url VARCHAR(100),

    pid INT

);

现在已经放入了很多数据:

image.png

基本的查询

  查询所有的数据并且显示所有的字段

  SELECT * FROM `products`
  
  查询特定的列title、brand、price

  SELECT title, brand, price FROM `products`
  
  给字段起别名
  
  SELECT title as t, brand as b, price as p FROM `products

WHERE条件查询

   查询价格小于1000的手机
   
   SELECT * FROM `products` WHERE price < 1000; 

   查询价格大于等于2000的手机

   SELECT * FROM `products` WHERE price >= 2000;
   
   查询品牌是华为,并且小于2000元的手机
   
   SELECT * FROM `products` WHERE `brand` = '华为' and `price` < 2000;

   SELECT * FROM `products` WHERE `brand` = '华为' && `price` < 2000;
   
   查询10002000的手机(包含10002000SELECT * FROM `products` WHERE price BETWEEN 1000 and 2000;
   
   查看括号中的结果

   SELECT * FROM `products` WHERE brand in ('华为', '小米');
   
   like关键字,%表示匹配任意个的任意字符,_表示匹配一个的任意字符
   
   //查询所有以v开头的title

   SELECT * FROM `products` WHERE title LIKE 'v%';

   //查询带M的title

   SELECT * FROM `products` WHERE title LIKE '%M%';

  //查询带M的title必须是第三个字符(注意M前面的下划线写了2个)

  SELECT * FROM `products` WHERE title LIKE '__M%';       

查询结果排序

  ORDER BY 排序
  
  ASC:升序排列; DESC:降序排列
  
  SELECT * FROM `products` WHERE brand = '华为' or price < 1000 ORDER BY price ASC;
  
  LIMIT 分页查询 LIMIT 30 OFFSET 0:查询30个,从第0个开始查
  
  SELECT * FROM `products` LIMIT 30 OFFSET 0;
  
  SELECT * FROM `products` LIMIT 30 OFFSET 30; 
  
  可以简写
  
  SELECT * FROM `products` LIMIT 90, 30;

聚合函数:对值集合进行操作的组(集合)函数

  华为手机价格的平均值

  SELECT AVG(price) FROM `products` WHERE brand = '华为';

  计算所有手机的平均分

  SELECT AVG(score) FROM `products`;

  手机中最低和最高分数

  SELECT MAX(score) FROM `products`;

  SELECT MIN(score) FROM `products`;

  计算总投票人数

  SELECT SUM(voteCnt) FROM `products`;

  计算所有条目的数量

  SELECT COUNT(*) FROM `products`;

  华为手机的个数

  SELECT COUNT(*) FROM `products` WHERE brand = '华为'

Group By,事实上聚合函数相当于默认将所有的数据分成了一组,现在我们希望划分多个组,比如按照品牌划分,这样就可一用到Group By

  SELECT brand,

      COUNT(*) as count,

      ROUND(AVG(price),2) as avgPrice,

      MAX(price) as maxPrice,

      MIN(price) as minPrice,

      AVG(score) as avgScore

  FROM `products` GROUP BY brand;

现在就按照品牌分组了 image.png

我们也可以用HAVING来给Group By查询到的结果添加一些约束,下面语句是筛选出平均价格在4000以下,并且平均分在7以上的品牌

  SELECT brand,

      COUNT(*) as count,

      ROUND(AVG(price),2) as avgPrice,

      MAX(price) as maxPrice,

      MIN(price) as minPrice,

      AVG(score) as avgScore

      FROM `products` GROUP BY brand

  HAVING avgPrice < 4000 and avgScore > 7;
  
  从语句上看 hHAVING 和 WHERE 都是条件判断,不过是右不同的地方:
  
  1. HAVING 跟在GROUP BY 后面
  
  2. HAVING 可以接聚合函数 WHERE 不行

image.png

2.7 多张表的操作

在上面的product表中,对应的品牌还需要包含其他的信息,比如品牌的官网,品牌的世界排名,品牌的市值等等

如果我们直接在商品中去体现品牌相关的信息,会存在一些问题:

  1. products表中应该表示的都是商品相关的数据,应该有另外一张表来表示brand的数据

  2. 多个商品使用的品牌是一致时,会存在大量的冗余数据

因此,我们可以将所有的brand的数据单独放在一张表中,执行下面创建brand 表的SQL语句

  CREATE TABLE IF NOT EXISTS `brand`(

  id INT PRIMARY KEY AUTO_INCREMENT,

  name VARCHAR(20) NOT NULL,

  website VARCHAR(100),

  worldRank INT

    );

写入数据

image.png

创建外键

  1. 创建表添加外键约束,我们需要在创建表的()最后添加如下语句
  FOREIGN KEY (brand_id) REFERENCES brand(id);
  1. 如果是表已经创建好,额外添加外键
  ALTER TABLE `products` ADD FOREIGN KEY (brand_id) REFERENCES brand(id);
  1. 将products中的brand_id关联到brand中的id的值
  UPDATE `products` SET `brand_id` = 1 WHERE `brand` = '华为';

  UPDATE `products` SET `brand_id` = 4 WHERE `brand` = 'OPPO';

  UPDATE `products` SET `brand_id` = 3 WHERE `brand` = '苹果';

  UPDATE `products` SET `brand_id` = 2 WHERE `brand` = '小米';

image.png

外键存在时更新和删除数据

  1. 如果products中引用的外键被更新了或者删除了,这个时候会出现什么情况呢?执行如下语句
  UPDATE `brand` SET id = 100 WHERE id = 1;

image.png 可以看到会报错的,外键关联不能修改

  1. 如何进行更新呢 我们可以给更新或者删除时设置几个值:
  `RESTRICT`(默认属性):当更新或删除某个记录时,会检查该记录是否有关联的外键记录,有的话会报错的,不允许更新或删除;
    
  `NO ACTION`:和RESTRICT是一致的,是在SQL标准中定义的
    
  `CASCADE`:当更新或删除某个记录时,会检查该记录是否有关联的外键记录,有的话, 更新:那么会更新对应的记录; 删除:那么关联的记录会被一起删除掉
    
  `SET NULL`:当更新或删除某个记录时,会检查该记录是否有关联的外键记录,有的话,将对应的值设置为NULL
  1. 修改外键更新时的动作

查看表结构,找到外键名称

 SHOW CREATE TABLE `products`;

删除之前的外键

 ALTER TABLE `products` DROP FOREIGN KEY products_ibfk_1;

添加新的外键,并且设置新的action

  ALTER TABLE `products` ADD FOREIGN KEY (brand_id)
                         REFERENCES brand
                         ON UPDATE CASCADE ON DELETE CASCADE;

现在测试下修改了 brand表的id,products表的brand_id是否同时修改

  UPDATE `brand` SET id = 100 WHERE id = 1;

image.png image.png

2.8 多表查询

我们希望查询到产品的同时,显示对应的品牌相关的信息,因为数据是存放在两张表中,所以这个时候就需要进行多表查询

如果我们直接通过查询语句希望在多张表中查询到数据,这个时候是什么效果呢

   SELECT * FROM `products`, `brand`;  

得到的时 products数据总条数 * brand数据总条数,叫笛卡尔乘积

事实上,查出来的很多数据时没有意义的,我们可以用where来筛选

   SELECT * FROM `products`, `brand` WHERE `products`.brand_id = `brand`.id;

image.png

很显然,如果我们希望获得某些特定的数据也很艰难,此时可以用SQL JOIN操作 image.png

左连接:我们希望获取到的是左边所有的数据(以左表为主)

无论左边的表是否有对应的brand_id的值对应右边表的id,左边的数据都会被查询出来

image.png

  SELECT * FROM `products` LEFT JOIN `brand` ON `products`.brand_id = `brand`.id;

  SELECT * FROM `products` LEFT JOIN `brand` ON `products`.brand_id = `brand`.id

  WHERE brand.id IS NULL;

右连接:我们希望获取到的是右边所有的数据(以右表为主)

无论右边的表是否有id值对应左边表的id,右边的数据都会被查询出来

image.png

  SELECT * FROM `products` LEFT JOIN `brand` ON `products`.brand_id = `brand`.id;

  SELECT * FROM `products` LEFT JOIN `brand` ON `products`.brand_id = `brand`.id

  WHERE brand.id IS NULL;

内连接:内连接是表示左边的表和右边的表都有对应的数据关联

内连接有其他的写法:CROSS JOIN或者 JOIN都可以

  SELECT * FROM `products` INNER JOIN `brand` ON `products`.brand_id = `brand`.id;

全连接:SQL规范中全连接是使用FULL JOIN,但是MySQL中并没有对它的支持,我们需要使用 UNION 来实现

image.png

  (SELECT * FROM `products` LEFT JOIN `brand` ON `products`.brand_id = `brand`.id)

  UNION

  (SELECT * FROM `products` RIGHT JOIN `brand` ON `products`.brand_id = `brand`.id);
  

  (SELECT * FROM `products` LEFT JOIN `brand` ON `products`.brand_id = `brand`.id WHERE `brand`.id IS NULL)

  UNION
  
  (SELECT * FROM `products` RIGHT JOIN `brand` ON `products`.brand_id = `brand`.id WHERE `products`.id IS NULL);

下面准备一个多对多的案例练习,比如学生可以选择多门课程,一个课程可以被多个学生选择

先建立2张表,一张学生表,一张课程表

 CREATE TABLE IF NOT EXISTS `students`(

 id INT PRIMARY KEY AUTO_INCREMENT,

 name VARCHAR(20) NOT NULL,

 age INT

 );
 
 CREATE TABLE IF NOT EXISTS `courses`(

  id INT PRIMARY KEY AUTO_INCREMENT,

  name VARCHAR(20) NOT NULL,

  price DOUBLE NOT NULL

  );

往表中插入数据

 INSERT INTO `students` (name, age) VALUES('why', 18);

 INSERT INTO `students` (name, age) VALUES('tom', 22);

 INSERT INTO `students` (name, age) VALUES('lilei', 25);

 INSERT INTO `students` (name, age) VALUES('lucy', 16);

 INSERT INTO `students` (name, age) VALUES('lily', 20);

 INSERT INTO `courses` (name, price) VALUES ('英语', 100);

 INSERT INTO `courses` (name, price) VALUES ('语文', 666);

 INSERT INTO `courses` (name, price) VALUES ('数学', 888);

 INSERT INTO `courses` (name, price) VALUES ('历史', 80);

我们需要一个关系表来记录两张表的数据关系

 CREATE TABLE IF NOT EXISTS `students_select_courses`(

    id INT PRIMARY KEY AUTO_INCREMENT,

    student_id INT NOT NULL,

    course_id INT NOT NULL,

    FOREIGN KEY (student_id) REFERENCES students(id) ON UPDATE CASCADE,

    FOREIGN KEY (course_id) REFERENCES courses(id) ON UPDATE CASCADE

 );
 
  why 选修了 英文和数学

  INSERT INTO `students_select_courses` (student_id, course_id) VALUES (1, 1);

  INSERT INTO `students_select_courses` (student_id, course_id) VALUES (1, 3);

  lilei选修了 语文和数学和历史

  INSERT INTO `students_select_courses` (student_id, course_id) VALUES (3, 2);

  INSERT INTO `students_select_courses` (student_id, course_id) VALUES (3, 3);

  INSERT INTO `students_select_courses` (student_id, course_id) VALUES (3, 4);

image.png

image.png

image.png

问题1.查询所有的学生选择的所有课程

  SELECT
  
  stu.id studentId, stu.name studentName, cs.id courseId, cs.name courseName, cs.price coursePrice
  
  FROM `students` stu
  
  JOIN `students_select_courses` ssc
  
  ON stu.id = ssc.student_id
  
  JOIN `courses` cs
  
  ON ssc.course_id = cs.id;

image.png

问题2.查询所有学生选课情况

  SELECT
  
  stu.id studentId, stu.name studentName, cs.id courseId, cs.name courseName, cs.price coursePrice
  
  FROM `students` stu
  
  LEFT JOIN `students_select_courses` ssc
  
  ON stu.id = ssc.student_id
  
  LEFT JOIN `courses` cs
  
  ON ssc.course_id = cs.id;

image.png

问题3.查询单个学生选课情况

  why同学选择了哪些课程
  
  SELECT
  
  stu.id studentId, stu.name studentName, cs.id courseId, cs.name courseName, cs.price coursePrice
  
  FROM `students` stu
  
  LEFT JOIN `students_select_courses` ssc
  
  ON stu.id = ssc.student_id
  
  LEFT JOIN `courses` cs
  
  ON ssc.course_id = cs.id
  
  WHERE stu.id = 1;

image.png

问题4.哪些学生没有选课

  SELECT
  
  stu.id studentId, stu.name studentName, cs.id courseId, cs.name courseName, cs.price coursePrice
  
  FROM `students` stu
  
  LEFT JOIN `students_select_courses` ssc
  
  ON stu.id = ssc.student_id
  
  LEFT JOIN `courses` cs
  
  ON ssc.course_id = cs.id
  
  WHERE cs.id IS NULL;

image.png

问题5.哪些课程没有被学生选择

  SELECT
  
  stu.id studentId, stu.name studentName, cs.id courseId, cs.name courseName, cs.price coursePrice
  
  FROM `students` stu
  
  RIGHT JOIN `students_select_courses` ssc
  
  ON stu.id = ssc.student_id
  
  RIGHT JOIN `courses` cs
  
  ON ssc.course_id = cs.id
  
  WHERE stu.id IS NULL;

image.png

都为null 说明课程都被选了。