SQL

1、组合两个表

表1: Person

+——————-+————-+
| 列名 | 类型 |
+——————-+————-+
| PersonId | int |
| FirstName | varchar |
| LastName | varchar |
+——————-+————-+
PersonId 是上表主键

表2: Address

+——————-+————-+
| 列名 | 类型 |
+——————-+————-+
| AddressId | int |
| PersonId | int |
| City | varchar |
| State | varchar |
+——————-+————-+
AddressId 是上表主键

编写一个 SQL 查询,满足条件:无论 person 是否有地址信息,都需要基于上述两表提供 person 的以下信息:

注意审题: 题目说:无论 person 是否有地址信息,都需要基于上述两表提供 person 的以下信息: FirstName, LastName, City, State 也就是说,地址信息(City, State)的查询结果是Null是OK的。但是,姓名(FirstName, LastName)必须有。 2) 为啥不用Where? 因为where的实质就是根据你给的条件(personID相等),选取两表的公共部分。但是,因为PERSON表不是所有人都有地址信息的,但是ADDRESS表只显示有地址信息的人,这样选取出来的就是有地址信息的人,漏掉了没有地址信息的人。所以大家注意,where的本质就是过滤。 3) 如何连接?应该用PERSON表左连接(left join)ADDRESS表,保留person表的所有信息

left join … on

  1. select p.FirstName,p.LastName,a.City,a.State from Person p left join Address a on p.PersonId = a.PersonId

结果:

  1. {"headers": ["FirstName", "LastName", "City", "State"], "values": [["Allen", "Wang", null, null], ["Bob", "Alice", "New York City", "New York"]]}

2、查询重复

编写一个 SQL 查询,查找 Person 表中所有重复的电子邮箱。

示例:

+——+————-+
| Id | Email |
+——+————-+
| 1 | a@b.com |
| 2 | c@d.com |
| 3 | a@b.com |
+——+————-+

根据以上输入,你的查询应返回以下结果:

+————-+
| Email |
+————-+
| a@b.com |
+————-+

说明:所有电子邮箱都是小写字母。

  1. --解法1 count(email)>1
  2. select email from person group by email having count(email)>1
  3. --解法2
  4. select email from (select count(1) as t,email from person group by email)r where r.t>1;
  5. --解法3 效率最高
  6. select distinct(p1.Email) from Person p1 join Person p2 on p1.Email = p2.Email AND p1.Id!=p2.Id

一、查询

打开数据库

  1. USE sqlstudty;

着重号 ` 用以标识字段,当关键字为字段时,以 着重号以区分.

意为表达 name 不是关键字,而是字段.

  1. SELECT `name` FROM USER;

查询的 可以是 常量、表达式、函数

  1. USE sqlstudy;
  2. SELECT 100;
  3. SELECT 12*12;
  4. SELECT VERSION();

起别名

  • 关键字 as
  • 使用 空格
  • 如想取的别名中含有特殊符号(空格、¥、#等),需将别名加上双引号
  1. SELECT 100 AS 常量;
  2. SELECT 100 常量1;
  3. SELECT 100 "特殊符号#"

去重

reader表

readerid readername identity gender school
S1001 胡峰 学生 计算机学院
S1002 包朦穗 学生 计算机学院
S1003 王文革 学生 计算机学院
S1004 高文丽 学生 计算机学院
S1005 郭鹏 老师 信息学院
  • 关键字 DISTINCT
    可以多字段,但是就没有意义了
    1. SELECT DISTINCT identity FROM reader;

identity
学生
老师

  1. SELECT identity FROM reader;

identity

学生
学生
学生
学生
老师

加号”+”的作用

  1. SELECT 100+100; # 200
  2. SELECT "100"+200; # 300 对于字符,会先尝试转换为数字,不能则转换为 0
  3. SELECT "tom"+100; # 100
  4. SELECT "tom"+"cat"; # 0
  5. SELECT NULL+100; # null 只要有null,则结果必定为null

拼接函数 CONCAT

  1. SELECT CONCAT(`readername`,`identity`) FROM reader

胡峰学生
包朦穗学生
王文革学生
高文丽学生
郭鹏老师

显示表结构

  1. DESC `reader`;

Field Type Null Key Default Extra


readerid char(10) NO PRI (NULL)
readername varchar(10) NO (NULL)
identity varchar(10) YES (NULL)
gender varchar(10) YES (NULL)
varchar(10) YES (NULL)
school
tel varchar(11) YES (NULL)

IFNULL

  • IFNULL(expr1,expr2)

判断expr1是否为空—-> null, 如果为空,以 expr2 代替显示.

  1. SELECT IFNULL(tel,'暂未填写电话') AS "ifnull",tel FROM reader;

ifnull tel


暂未填写电话 (NULL)
1234 1234
暂未填写电话 (NULL)
暂未填写电话 (NULL)
暂未填写电话 (NULL)

ISNULL

判断如果为空,返回1,否则返回0.

  1. SELECT ISNULL(tel) AS "iSnull",tel FROM reader;

iSnull tel


  1. 1 (NULL)
  2. 0 1234
  3. 1 (NULL)
  4. 1 (NULL)
  5. 0 110

二、条件查询

  • 基本语法
  1. select 字段 from 表名 where 条件
  2. SELECT * FROM reader WHERE gender = '男'
  • 条件运算符 | 条件运算符 | 备注 | | :—-: | —- | | > | 大于 | | < | 小于 | | != 或 <> | 不等于 | | = | 等于 | | >= | 大于等于 | | <= | 小于等于 |
  • 逻辑运算符 | 逻辑运算符 | 备注 | | :—-: | —- | | && 或 and | 且 | | || 或 or | 或 | | ! 或 not | 非 |

表示取到 年龄小于 18 或者 大于 60 的

三种表示方法:

  1. old < 18 or old > 60
  1. not (old >=18 and old= <60) # 不在 18-60之间
  1. not between 18 and 60 # 注意:包含了临界值,即18与60.

模糊查询

关键字

  • like
    1. where readername like '%王%'; # 查询 readername 含 '王' 的
  • 通配符 % 和 _
    1. where readername like '王_'; # 查询 readername 第一个字为'王',且readername为两个字的


转义字符 \
当我想查询的字段名包括 下划线 _ 时,我不想让它成为 通配符.

  1. where readername like '_王\_'; # 查询 readername为三个字符,且第二三个字为'王_'


或者 使用关键字 ESCAPE 达到同样效果 , $ 可替换任意字符
表示标识 $ 为转义字符

  1. where readername like '_王$_' ESCAPE '$';
  • between and
    1. between 18 and 60 # 注意:包含了临界值,即18与60.
  • in
    判断是否属于
    下面两句可达到相同效果.
    注意:列表中的值 类型需保持相同或兼容
    1. SELECT * FROM reader WHERE readername = '胡峰' OR readername = '郭鹏';
    2. SELECT * FROM reader WHERE readername IN ('胡峰','郭鹏');
  • is null

  • is not null

  1. where tel is null; # 判断电话为空的
  2. where tel is not null; # 不为空
  • 安全等于 <=>
    同义
    1. where tel is null;
    2. where tel <=> null;


但是 与 where tel = null; 不同义
同义

  1. where tel = 10086;
  2. where tel <=> 10086;


但是不能写为 where tel is 10086; 语句报错.

Mysql服务的登录与退出


登录:

  1. mysql [-h 主机名 -p 端口号] -u用户名 -p密码


退出:

  1. exit 或者 快捷键 Ctrl+C三、排序查询

三、排序查询 ORDER BY

  • 升序asc

  • 降序desc

  • 默认升序 即 asc可省

  • 除limit子句外,order by都放在最后.
    按字段排序

    1. SELECT * FROM reader ORDER BY `readerid` ASC # 排序写在最后


按表达式排序 或别名

  1. SELECT *,IFNULL(tel,0)*10 "表达式排序" FROM reader ORDER BY 表达式排序

readerid readername identity gender
school tel 表达式排序


S1001 胡峰是的 学生 男 计算机学院 (NULL) 0
S1003 大王_ 学生 女 计算机学院 (NULL) 0
S1004 高文丽但是 学生 女 计算机学院 (NULL) 0
S1005 郭鹏 老师 男 信息学院 110 1100
S1002 包朦穗啊是的 学生 女 计算机学院 1234 12340

LENGTH()函数 获取长度

一个汉字3个字节,

按照名字长度排序

  1. SELECT * FROM reader ORDER BY LENGTH(`readername`)
  1. SELECT *,LENGTH(`readername`) "名字长度" FROM reader ORDER BY 名字长度

readerid readername identity gender
school tel 名字长度


S1005 郭鹏 老师 男 信息学院 110 6
S1003 大王_ 学生 女 计算机学院 (NULL) 7
S1001 胡峰是的 学生 男 计算机学院 (NULL) 12
S1004 高文丽但是 学生 女 计算机学院 (NULL) 15
S1002 包朦穗啊是的 学生 女 计算机学院 1234 18

多字段排序,先写先排序

单行函数

字符函数

length() 长度

作用:获取参数值得字节数

utf-8 字符集

一个汉字占用3个字节

一个字母占用一个字节

例:统计type字段的长度

  1. SELECT `type`,LENGTH(TYPE) 'type长度' FROM books

concat() 拼接

作用:拼接字符串

upper、lower 大小写转换

作用:大小写转换

例:将bookId字段转换为小写

  1. SELECT `bookid`,LOWER(`bookid`) FROM `books`

substr、substring 切片

substr只是substring的简写,

注意:SQL中索引从1开始

  1. # 从索引4开始输出 ---> "的SUBSTR函数使用"
  2. SELECT SUBSTR('SQL的SUBSTR函数使用',4) out_put
  1. # 从索引5开始输出,长度为6的字符串 输出----> "SUBSTR"
  2. SELECT SUBSTR('SQL的SUBSTR函数使用',5,6) out_put

函数间可嵌套使用

instr 查索引

返回子串第一次出现的索引,找不到则返回 0

例:

  1. SELECT INSTR('你好!SQL','SQL') OUTPUT

trim 除空格

去除空格输出

  1. SELECT ' 张翠花 ' les,TRIM(' 张翠花 ') out_put

去除前后指定字符,注意,中间的无法去除

  1. # 输出 张a翠花
  2. SELECT TRIM('a' FROM 'aaaaaa张a翠花aaaaaaaa') out_put

lpad 左填充

填充在左边

  1. # 使用 '啊' 将 '你好' 填充至长度为5
  2. SELECT LPAD('你好',5,'啊') out_put
  3. # 输出 '啊啊啊你好'
  4. # 长度不足填充时,会截取
  5. SELECT LPAD('你好',1,'啊') out_put
  6. # 输出 '好'

rpad 右填充

  1. # 使用 '啊' 将 '你好' 填充至长度为5
  2. SELECT RPAD('你好',5,'啊') out_put
  3. # 输出 '你好啊啊啊'
  4. # 长度不足填充时,会截取
  5. SELECT RPAD('你好',1,'啊') out_put
  6. # 输出 '你'

replace 替换

注意:将所有出现的 都会 替换

  1. SELECT REPLACE('PHP是世界上最好的编程语言','PHP','java') out_put
  2. #输出 'java是世界上最好的编程语言'

数学函数

round 四舍五入

  1. SELECT ROUND(1.49),ROUND(1.5)
  2. #输出 1 2
  1. # 四舍五入保留小数点后两位
  2. SELECT ROUND(1.49321,2),ROUND(1.49754,2)
  3. # 输出 1.49 1.50

ceil与floor 向上取整与向下取整

  1. SELECT CEIL(1.001),FLOOR(1.999)
  2. # 输出 2 1

truncate 截取

  1. SELECT TRUNCATE(1.49754,2)
  2. # 保留小数点后两位 截断,不四舍五入 输出 1.49

mod 取余

mod(a,b) == a-a/b*b

  1. SELECT MOD(624,10)
  2. select 624%10
  3. # 同义

时间函数

now()

获取当前时间

curdate

curtime

  1. SELECT NOW()
  2. # 2021-11-03 23:36:37
  3. SELECT CURDATE()
  4. # 2021-11-03
  5. SELECT CURTIME()
  6. # 23:37:04

year

只 获取年份

  1. SELECT YEAR(NOW())
  2. # 2021
  3. SELECT YEAR('1998-07-24')
  4. # 1998
  5. SELECT YEAR(pubdate) '年' FROM `books`
  6. #

month / monthname

  1. SELECT MONTH(NOW())
  2. # 11
  3. SELECT MONTHNAME(NOW())
  4. # November 返回月份英文名字

datediff 日期差

计算两个日期之间相差的天数, MAX(pubdate) 减去 MIN(pubdate),交换将会得到负值

  1. SELECT DATEDIFF(MAX(`pubdate`),MIN(`pubdate`)) '相差天数' FROM `books`
  1. SELECT DATEDIFF(NOW(),'2001-05-23') '计算活了多少天'
  2. # 7470

时间格式转换 str_to_date

作用:识别 年月份

不同格式的话,SQL并不知道哪个是年,哪个是月,哪个是日,需 使用 str_to_date标识

  1. SELECT STR_TO_DATE('2020-3-25','%Y-%c-%d') out_put
  2. # 2020-03-25

日期转为字符 date-format

  1. SELECT DATE_FORMAT(NOW(),'%Y年%m月%d日')
  2. # 2021年11月03日

SQL - 图1

其他函数

  1. SELECT VERSION() #mysql版本
  2. SELECT DATABASE() # 当前数据库
  3. SELECT USER() # 当前用户

流程控制函数

if

  1. SELECT IF(10>9,'大','小') '比较大小10>9'

case

类似switch…case…

判断 number 字段的值与when 中是否相等,相等显示 对应then 中的值

  1. SELECT number,
  2. CASE number
  3. WHEN 23 THEN '23'
  4. WHEN 27 THEN '27'
  5. ELSE '其他'
  6. END AS 显示
  7. FROM books

多重if

  1. SELECT number,
  2. CASE
  3. WHEN number >20 THEN '大于20的'
  4. WHEN number=20 THEN '等于20'
  5. ELSE '小于20'
  6. END AS 判断
  7. FROM books

分组函数

基本功能:作统计使用

  • NULL都不参与
  • 都支持与distinct关键字
  • 和分组函数一同查询的字段有限制,group by
  1. # 行数不对等,count(*)只有一行,而type字段有 count(*) 行
  2. SELECT COUNT(*),type FROM books

COUNT(字段) 计数

统计字段出现的次数,不会统计为 NULL 的

例:想查询出teble表 名字 中,带有 “王” 的,有多少个人

  1. select count(name) from table where name like concat('%',王,'%')
  1. # 统计type字段有多少个学院,去重
  2. SELECT COUNT(DISTINCT `type`) FROM books
  1. # 统计行数,只要有一个字段不为NULL,就会被统计
  2. # COUNT(*) 最常用,效率最高
  3. SELECT COUNT(*) FROM books
  4. # 以下都可以得到相同结果
  5. select count(1) from books
  6. select count('一个常量') from books

SUM(字段) 求和

合计字段之和,NULL不参与运算求和

例:薪资表中,想统计一共发了多少薪资(每个人薪资之和)

  1. select sum(Salary) '总薪资' from table

avg 平均

平均值会保留小数点后4位,NULL不参与运算求和

sum与avg一般只处理数值类型的

max / min

NULL不参与

  1. SELECT SUM(`number`) '求和',AVG(`number`) '平均值',MAX(`number`) '最大值',MIN(`number`) '最小值' FROM books

分组查询group by

基本语法

  1. select '字段','分组字段/表达式/函数'
  2. from 表名
  3. [where 条件] 可省
  4. group by '分组字段/表达式/函数'
  5. [order by 字段] 可省

查询每个学院 number字段的和与平均值

  1. SELECT SUM(`number`),AVG(`number`),`type` FROM `books` GROUP BY `type`

having 关键字

添加分组后的筛选条件,—-先查询,进行having 筛选

放在最后

查询每个学院 number字段的和与平均值,且和大于100的

  1. SELECT SUM(`number`),AVG(`number`),`type` FROM `books` GROUP BY `type` HAVING SUM(`number`)>100

where关键字

—-> 查询时,根据条件筛选

分组前筛选—-where——-在group by前

分组后筛选——having—-在group by后

若where与having都可,优先where效更高