SOTON~数据挖掘(1)~ SQL与MySQL

0.备注

本教程使用的是MySQL8.0.13社区版,使用内部的三个库:world, sys, sakila
回目录


1.数据库基础

1.主键:目的:用来唯一区分每一行的不同点(学生用id来区分,id是主键)

条件:

  • 任意两行不具备相同的主键
  • 每行有一个主键

要求:

  • 主键键值能够唯一区分表中的每一行
  • 每一行都应该有唯一标识自己的一列
  • 表中任何列都可以作为主键

2.参考书籍:《mysql必知必会》作者 Ben Forta

3.在线教程:www.mysqltutorial.org

回目录


2.WorkBench介绍与Hello World

a) SELECT关键字

1.要点

  • SQL不区分大小写,一般关键字用大写
  • 结束SQL语句需要使用分号(;)
  • SQL语句会忽略空格
  • 使用limit 限制返回结果行数(LIMIT几乎永远放在最后面)
  • 如果没有WHERE语句,则是随机取LIMIT个数

2.案例

从world库中的city表中随便取出10条数据

SELECT * 
FROM world.city LIMIT 10;

回目录


3.了解SQL

a) USE关键字

  • 指定工作目录,指定的是database(world),而不是table(city)(省的经常要写world.city)
#在当前的库中一直操作,不会跨库(优先执行指定的库,再执行工作空间)
USE sys;	#以后的所有表都默认从sys库中先找,除非指定具体的库
SELECT *
FROM sys_config, world.city LIMIT 10;

b) SHOW关键字

  • 显示当前数据库一共有多少个库(隐藏的库也能看到)
show databases;
  • 显示指定数据库中的所有表
USE world;
SHOW tables;
  • 显示一个表中的所有字段
SHOW columns from city;

回目录


4.数据排序

a) ORDER BY关键字

要求:

  • order by 位于 from子句之后
  • 最后用desc降序,asc 升序

代码:

  • 仅对一个字段进行排序
USE world;
SELECT *
FROM city ORDER BY name DESC;
  • 对两个字段进行排序,需要分组(先根据countrycode进行大体划分成小组排序,再根据前面的小组对population进一步排序)
USE world;
SELECT *
FROM city ORDER BY countrycode DESC ,population ASC;

回目录


5.过滤数据——WHERE

  • WHERE关键字(在SELECT后,多个过滤子句配合AND / OR使用)
  • WHERE子句中的圆括号决定了计算次序

a) AND关键词的使用

  • 条件:筛选来自ARE地区人口在10w~30w之间的城市
SELECT *
FROM world.city
WHERE population >= 100000 and population <= 300000 and countrycode = "ARE";

b) OR关键词的使用

  • 条件:筛选人口大于300w或者小于1000的城市
SELECT *
FROM world.city
WHERE population >=3000000 OR population <=1000;

c) AND和OR关键词的混合使用

  • 条件:人口>10w并且小于50w,代码为AFG或者NLD
SELECT *
FROM world.city
WHERE (population >=100000 AND population <= 500000) AND (countrycode = "AFG" OR countrycode = "NLD");

d) INNOT IN的使用

  • IN的使用
SELECT *
FROM world.city
WHERE (population >=100000 AND population <= 500000) AND countrycode IN ("AFG" , "NLD");
  • NOT的使用
SELECT *
FROM world.city
WHERE (population >=100000 AND population <= 500000) AND countrycode NOT IN ("AFG" , "NLD");

回目录


6.通配符

a) %

  • “abc%” 以abc开头,任意字符结尾的数据

  • ‘%abc’ 以abc结尾,任意字符开头的数据

  • ‘%abc%’ 任意字符开头和结尾,中间包含abc

  • 案例:从city表中筛选出来以Ab开头的name值

    USE world;
    SELECT *
    FROM city
    WHERE name LIKE "Ab%"
    

b) _

  • 有几个下划线,后面就匹配几个字符
  • 案例:找出以ab开头,但是后面只有三个字符的name字段
USE world;
SELECT *
FROM city
WHERE name LIKE "Ab___"

c) 注意

  • 通配符一般在WHERE语句中使用,而且使用LIKE作为操作符
  • 字符串中其实是分大小写的比如"Ab""ab",但是能用"ab"匹配"Ab"是mysql配置的语法,而不是sql的语法,自动忽略了字符串的大小写 ~ 可以通过后面的修改
  • 首尾有空格的时候,记得要将其算进去,就像"A C"用“ac%”是筛选不出来的,这个时候可以用Mysql的rtrim()函数,这样就能保证处理数据的时候去掉了首尾的空格,但是仍然不能取A Coruña (La Coruña)这样的数
  • 注意NULL值,否则正常也是选取不出来的。~ 下面会讲的

案例:取出以Ac开头的name字段(包含首个字母是空格的情况)

USE world;
SELECT *
FROM city
WHERE name LIKE rtrim("Ac%");

回目录


7.数据处理函数

a) 文本处理函数

  • 删除两侧空格(而非中间空格)
    • Rtrim() ~ 删除字符串右边空格
    • ltrim() ~ 删除字符串左边空格
  • Length() ~ 计算字符串长度,可以在SELECT中使用,也可以在WHERE中使用
  • 将全部字符以大写 / 小写的形式展现出来
    • Upper() ~ 字符串大写
    • Lower() ~ 字符串小写
  • substring(name,a,b)函数 ~ 对name字段,从第a个位置开始,取b个长度(如果选取100超出字符串长度,则以字符串最大长度为基准)
  • left() / right()函数 ~ 选取字段最左面 / 右面的前几个数据
  • replace()函数
    • 1.格式:replace(str1,str2,str3)
    • 2.用法:用str3替换str1字段中处出现的str2字符串
      • 1.某个参数为NULL,则整体为NULL
      • 2.如果没有str3,则str1中包含的str2会被删除

案例:

一、关于length( )函数

1.每一条数据的name字段的数值有多长?

SELECT id,name,length(name) as name_length
FROM world.city LIMIT 100;

2.name字段长度≥10的才会被展现出来

SELECT id,name,length(name) as name_length
FROM world.city 
WHERE length(name) >= 10;

但是,不可以像下面这么写,因为name_length只是在展现的时候会显示出来,但是world.city表中没有name_length这个字段,因此需要直接写函数完整写法,不能写as后面的新命名名称

SELECT id,name,length(name) as name_length
FROM world.city 
WHERE name_length >= 10;

二、关于substring()函数 ~ 选取子集:只打印name字段的前三个字母(从第一个开始算,往后数3个)

SELECT id, name, substring(name,1,3)
FROM world.city;

三、关于left()right()函数

SELECT id,name,left(name,3)
FROM world.city;

四、关于upper()lower()函数

SELECT id,name,upper(name) as upper_name
FROM world.city;

五、关于replace()函数:将name字段中的"am"替换成“xxoo”

SELECT id,name, replace(name,"am","xxoo")
FROM world.city;

b) 日期和时间处理函数

很多库中都会有日期和时间:订单什么时间发生的,数据是什么时候被记录在库中的

  • 一、curdate() 返回当前日期
SELECT curdate()
  • 二、curtime() 返回当前时间
SELECT curtime()
  • 三、返回日期字段的具体部分date()year()month()day()hour()minute()dayofweek()

案例一:

#先看一下有几个字段
SHOW columns from sys.sys_config;

#从set_time字段的时间戳找出日期部分
SELECT date(set_time)
FROM sys.sys_config;

案例二:

#找到sakila.film的时间戳字段,得到的是last_update
SELECT * FROM sakila.film

#从last_update字段中找到日期部分
SELECT date(last_update)
FROM sakila.film

例:选取出来2006年2月15号的数据

SELECT date(last_update)
FROM sakila.film
WHERE date(last_update) = "2006-02-15";

注意:遇到年月日这样需求的例子,条件中遇到年月日,对时间戳最好date()一下,这样万无一失

例:选取2006-02-15到2006-02-28日的数据

SELECT date(last_update)
FROM sakila.film
WHERE date(last_update) >= "2006-02-15" and date(last_update) <= "2006-02-28";

当然也可以用提取时间戳具体元素来实现

SELECT date(last_update)
FROM sakila.film
WHERE year(last_update) = '2006'
and month(last_update) = '02'
and day(last_update) between '15' and '28';

或者

SELECT date(last_update)
FROM sakila.film
WHERE year(last_update) = '2006'
and month(last_update) = '02'
and day(last_update)>=15 and day(last_update)<=30;
  • 四、比较两个时间的差datediff(),返回天数(当前时间-last_update)
SELECT datediff(now(),last_update)
FROM sakila.film;

回目录


8.汇总函数

主要就是求最值、均值、求和、统计个数(以上所有的函数都不会对NULL值进行操作)

a) 返回单一结果

  • 求出world.city表中population中的最大值
SELECT max(population), min(population), avg(population), sum(population), count(population)
FROM world.city;
  • 计算表中一共有多少条数据(如果表中有NULL值,则结果可能和count(population)会不一样)
SELECT count(*)
FROM world.city;

b) 返回分类的唯一值:DISTINCT

(只能跟在SELECT后面,而且是查找后面所有列的唯一值,如:九坤出的如何保证再加进来的值不是重复的,如果第一行是abc,第二行还是abc,那么两行就会被DISTINCT成一个结果)

  • 看countrycode中有哪些值?
SELECT DISTINCT countrycode
FROM world.city;
  • 查看countrycode下面有多少个唯一值
SELECT count(DISTINCT countrycode)
FROM world.city;

回目录


9.分组函数

  • 正常统计是按照每个分类进行计算(求和/平均),比如对班级成绩求平均:英语、语文、数学(先分组再平均)。使用group by进行分组,使用having代替WHERE对分组进行过滤(现在应用的每一个函数,都是对group by小组的实施的,而不是对全体数据,也不是对单个样本)

案例:

a) 单一分组

  • 对每个countrycodepopulation求平均
SELECT countrycode,avg(population) as avg_population, sum(population) as sum_population
FROM world.city
GROUP BY countrycode; 
  • 千万不要这么写。有结果,但是是错的(因为id没有被countrycode分组,不应该出现在SELECT中),运行成功不代表分组没有错误(mysql可能有将一个组内随机取出来一个数,取第一个标号,取最后一个标号这样的机制。)
SELECT id,countrycode,avg(population) as avg_population, sum(population) as sum_population
FROM world.city
GROUP BY countrycode; 

b) 多个分组

  • 多个分组(两个组合起来才是唯一值)
SELECT countrycode,district,avg(population) as avg_population, sum(population) as sum_population
FROM world.city
GROUP BY countrycode,district; 

c) 条件过滤having

  • 只展显avg(population)>1000000的(WHERE是针对每一条样本,HAVING作用在每一个GROUP上)
SELECT countrycode,district,avg(population) as avg_population, sum(population) AS sum_population
FROM world.city
GROUP BY countrycode,district
HAVING avg(population)>=1000000 and (sum(population))>=1000000;

回目录


10.子查询 / 嵌套查询

  • 会有非常多的复杂的需求需要用到子查询(即将之前列出来的SQL语句变成一个临时的新表,被上一层的查询调用)
  • 子查询可以出现在FROMWHERE

案例:

a) 用在FROM

  • 1.用WHERE语句查询平均人口大于100w的语句
SELECT *
FROM
(SELECT countrycode,district,avg(population) as avg_population, sum(population) AS sum_population FROM world.city GROUP BY countrycode,district) AS a
WHERE a.avg_population >= 1000000;

或者省略AS

SELECT *
FROM
(SELECT countrycode,district,avg(population) as avg_population, sum(population) AS sum_population FROM world.city GROUP BY countrycode,district) a
WHERE a.avg_population >= 1000000;

b) 用在WHERE

  • 2.查询population大于avg_population的所有数据(用countrycode分组后,但是这样查询不到结果,应该让population逐条比较)
SELECT *
FROM world.city
WHERE population >= (SELECT avg(population) FROM world.city GROUP BY countrycode)

正确做法:去掉GROUP BY

SELECT *
FROM world.city
WHERE population >= (SELECT avg(population) FROM world.city)

由此注意:WHERE中可以用子查询,但是子查询中比较的数量要一致,前面是一条population,后面就不应该是一个分组后的population,这是多个结果

回目录


11.表联结

  • 作用:将同一个库里的多张表进行联结,用SELECT选取数据。一共有三种INNER JOIN(交集)、LEFT JOIN(左面为主)、RIGHT JOIN(右面为主)

  • 例如:world库中,有citycountrycountrylanguage三张表,我想知道每个city中的人说什么语言,就用citycountrylanguage这两个表相联结

  • 一般通过主键进行表的联结,但是也可以通过任何列进行两两联结,一般通过两两相同的列组合(主键:唯一不重复的值)

    • 如何查看主键(看到Key上有PRI字样的就是主键)
    SHOW columns FROM world.country
    

案例

  • 通过countrycodecode进行组合

    • 以下方式很老,不推荐!

      USE world;
      SELECT city.*, country.*
      FROM city,country
      WHERE city.countrycode = country.code;
      

a) inner join

  • 可阅读性强的代码

    SELECT a.*, b.*
    FROM
    临时表 a
    组合方式 inner join
    临时表 b
    组合联结处 on
    
  • 组合两张表的方法

    USE world;
    SELECT a.*, b.*
    FROM
    (SELECT * FROM city) a
    inner join
    (SELECT * FROM country) b
    on a.countrycode = b.code
    
  • 组合三张表的方法(inner join

    USE world;
    SELECT a.*, b.*, c.language
    FROM
    (SELECT * FROM city) a
    inner join
    (SELECT * FROM country) b
    on a.countrycode = b.code
    inner join
    (SELECT * FROM countrylanguage) c
    on c.countrycode = b.code #inner join的话写a写b都无所谓,因为是共同出现,但是left join和right join就不一样了,左面/右面出现的都会有,另一面就没有
    

b) left join

  • 组合三张表(left join

    USE world;
    SELECT a.*, b.*, c.language
    FROM
    (SELECT * FROM city) a
    left join
    (SELECT * FROM country) b
    on a.countrycode = b.code
    left join
    (SELECT * FROM countrylanguage) c
    on c.countrycode = a.countrycode; #left join一般写前面/左面的临时表进行联结
    
  • 检查left join是否对齐count(*)——计算返回的条数(正常情况是不会对齐的:a是学生信息表,b是学生成绩表,但是有两个没来考试),用LIMIT随机展现出来100条

    • inner join ~ 挑选共同出现的那一块(共1861条结果)
    SELECT count(*)
    FROM  
    (SELECT * FROM city) a
    inner join
    (SELECT * FROM country LIMIT 100) b
    on a.countrycode = b.code;
    
    • left join ~ 会将a出现的都展现出来(共4079条结果)
    SELECT count(*)
    FROM  
    (SELECT * FROM city) a
    left join
    (SELECT * FROM country LIMIT 100) b
    on a.countrycode = b.code;
    

    c) 对于NULL值

    • 显示具体数据,看到有的是NULL数值(NULL值会被忽略计算)
    SELECT a.*,b.*
    FROM  
    (SELECT * FROM city) a
    left join
    (SELECT * FROM country LIMIT 100) b
    on a.countrycode = b.code;
    
    • 这时再来统计一下:看到count(*)=4079,而count(b.code)=1861,因为有的b.code是NULL值,因此left join统计结果会出现差异,而inner join会取两者相同部分,所以都是1861条数据
    SELECT count(*), count(b.code)
    FROM  
    (SELECT * FROM city) a
    left join
    (SELECT * FROM country LIMIT 100) b
    on a.countrycode = b.code;
    
  • 作业:OUT JOIN

回目录


12.组合查询-UNION

  • 作用:将使用不同条件的多个查询结果组合在一起,返回在一个表中(几个表的结果是上下连接在一起的)
  • 原则:
    • 必须由两条或者两条以上的select语句组成,语句之间用union分隔
    • union中的每个查询必须包含相同的列,表达式或聚集函数sum(), avg()
    • 列数据类型必须兼容

a) 单一组合

SELECT id,name FROM world.city WHERE population >= 1000000
union
SELECT id,name FROM world.city WHERE population <= 100000

注意:

  • SELECT返回的字段一定要是一模一样,不可以如下(应该是同一个表中同一个字段,严格意义讲:只要返回的数据类型是一致的,就不会报错,但是最后返回的东西就会很尴尬,究竟是population还是countrycode,还是district。)
SELECT id,name,population FROM world.city WHERE population >= 1000000
union
SELECT id,name,countrycode FROM world.city WHERE population <= 100000
union
SELECT id,name,district FROM world.city WHERE population <= 500000

b) 去重与不去重union all

  • union会自动对数据进行去重,如果不需要请使用union all

    • 以下操作返回3562条(上面238条,下面3562条,上面的值被包含在下面的值中)
    SELECT id,name FROM world.city WHERE population >= 1000000
    union
    SELECT id,name FROM world.city WHERE population >= 100000
    
    • 以下会返回两个结果的加和(238+3562=3800)
    SELECT id,name FROM world.city WHERE population >= 1000000
    union all
    SELECT id,name FROM world.city WHERE population >= 100000
    

c) 结果排序

  • 对展现结果进行排序——order by只能用于最后一条select语句

    SELECT id,name FROM world.city WHERE population >= 1000000
    union all
    SELECT id,name FROM world.city WHERE population >= 100000
    ORDER BY id DESC;
    

回目录


13.表的创建与操作

  • 将之前操作返回的结果保存下来,需要创建一张表进行保存
  • 格式
create table 库名.表名
as
要返回的操作结果

注意

  • 1.字段名重复 ~ 需要将重复字段名逐个手写更正(访问结果字段名可以重复,存表时需遵守规范:字段名不能重复)
SELECT a.name as name_city, a.countrycode as code_city, a.population as population_city;

a) 将查询结果存入表(创建临时表)

案例

  • 将之前联结的结果保存在一个表中
USE world;
CREATE TABLE if NOT exists world.test1
AS

SELECT a.id, a.name as name_city, a.countrycode as code_city, a.district, a.population as population_city, b.*, c.*
FROM
(SELECT * FROM city) AS a
LEFT JOIN
(SELECT * FROM country LIMIT 100) AS b
ON a.countrycode = b.code
LEFT JOIN
(SELECT * FROM countrylanguage) AS c
ON c.countrycode = a.countrycode;

b) 查询临时创建表

  • 可以当做一个正常的表来查询结果
SELECT * FROM test1 LIMIT 100

c) 删除临时创建表

  • 删除表(用过 / 创建错误)
drop table if exists world.test1

d) 创建最底层表

  • 需要指定

    • 表的名字

    • 列的名字,列的数据类型

    • 是否允许NULL值

    • 哪些列是主键?(唯一且不重复的一列)【可以没有主键,主键也可以用多个字段名来设置】

      CREATE TABLE IF NOT exists world.testa
      (id int,
      name char(15),
      primary key(id,name)
      )
      
    • 列与列之间用什么分隔符来分割

  • 需要注意

    • 创建表之前要安排和布局好结构,想好这个表是否是自己想要的,以免后期修改或者删除
    • 规范:创建好了表,就不要随意修改和丢弃(因为mysql的操作不可逆,丢弃了就找不回来了)
    #需求:创建一个表,以id,name,countrycode,population,gnp,language为字段,以id为主键
    CREATE TABLE IF NOT exists world.test2
    (id int,
    name char(15),
    countrycode char(5),
    population int,
    gnp float,
    language char(15)
    )
    

e) 插入查询结果——insert

  • 向上面的test2表中插入查询结果(返回的字段数要和定义的字段数相同)

    USE world;
    INSERT INTO world.test2
    
    SELECT a.id, a.name as name_city, a.countrycode as code_city, a.population, b.gnp, c.language
    FROM
    (SELECT * FROM city) AS a
    LEFT JOIN
    (SELECT * FROM country LIMIT 100) AS b
    ON a.countrycode = b.code
    LEFT JOIN
    (SELECT * FROM countrylanguage) AS c
    ON c.countrycode = a.countrycode;
    

回目录


14.更新数据——ALTER

  • 可以修改字段名,也可以修改字段类型
  • 注意,如果不加WHERE语句,就是对所有的行进行insertupdatedelete(一定要附加上WHERE条件,否则就是对全表进行修改)

a) 增加字段名

ALTER TABLE world.test2
add new_name char(80);

SELECT * FROM world.test2;

b) 删除一个字段

ALTER TABLE world.test2
drop gnp;

SELECT * FROM world.test2;

c) 根据条件更改某行值(语法没有问题,但会被警告没有权限)

  • 修改非常有风险,一般认为修改库不是去做好事,一般人也没有delete和update的权限

  • 在使用where条件前先使用select进行测试,保证正确无误,因为Mysql是没有撤销功能的

UPDATE world.test2
SET population = 0
WHERE id = 129;

再例如

UPDATE world.test2
SET new_name = 1
WHERE new_name is NULL;

d) 根据条件删除行值

DELETE FROM world.test2
WHERE new_name is NULL;

注意:DELETEDROP的区别

  • delete是删除记录,但是表还在,表示空表
  • drop是删除整个表,整个表都不存在了

回目录


15.NULL值

  • 使用格式:is null / is not null

a) 筛选不是NULL值的数

案例:筛选出来不是NULL值的数

SELECT *
FROM world.test2
WHERE new_name IS NOT NULL;

b) 创建表的时候,不允许是NULL值

CREATE TABLE IF NOT exists world.test2
(id int not null,#不允许插入NULL
name char(15) null,#允许插入NULL
countrycode char(5),
population int,
gnp float,
language char(15)
)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值