数据库是测开工程师面试中必考的部分,如果没有mysql基础,或者学习过但是忘记了,可以先看第二个部分的MySQL语法,有部分大厂会要求写查询语句。本篇文章包含本次求职过程中遇到的所有MySQL问题,亲测应付各个大厂的测开面试毫无压力👉
目录
- 一、面试题
- 1. 基础理论
- 什么是数据表、字段、主键、外键、视图
- 什么是数据库的三大范式?
- 2. 数据库和锁
- 什么是数据库事务?
- 事务有哪些性质?
- 并发事务的控制方式有哪些?
- 脏读、幻读、不可重复读
- MySQL 事务的隔离级别
- 如何实现4个隔离级别
- Mysql的锁
- 是什么意向锁?它有什么作用?
- 乐观锁和悲观锁
- MVCC
- undo log段
- 快照读 和 实时读
- 如何解决幻读问题
- 3. 存储和索引
- MySQL的存储引擎
- MySIAM 和 InnoDB
- 数据库索引
- 索引的作用
- Hash索引和B+树索引
- B+ 树
- 聚集索引和覆盖索引
- 索引下推
- 什么情况下数据库索引会失效?
- 慢查询怎么排查
- 大表查询怎么优化
- 为什么不建议使用外键
- 你平时是怎么使用数据库索引的,有哪些高效利用索引的经验和技巧?
- 二、SQL语法
- 0. 基础概念
- 1. 查询
- 基础单表查询 where/like/order/limit/offset/distinct
- 多表联合查询 inner/left/right join
- 分组聚合查询 group by
- having 子句
- 组合查询 union
- 表达式查询
- 子查询
- 表达式
- 2. 更新
- 3. 插入
- 4. 删除
- 5.建表语句
- 6. 建库语句
一、面试题
1. 基础理论
什么是数据表、字段、主键、外键、视图
- 主键:主键(primary key), 一般关系数据表中,都会有一个属性列设置为 主键(primary key)。主键是唯一标识一条数据的,不会重复复(想象你的身份证号码)。一个最常见的主键就是auto-incrementing integer(自增ID,每写入一行数据ID+1, 当然字符串,hash值等只要是每条数据是唯一的也可以设为主键。借助主键(primary key)(当然其他唯一性的属性也可以),我们可以把两个表中具有相同 主键ID的数据连接起来(因为一个ID可以简要的识别一条数据,所以连接之后还是表达的同一条数据)(你可以想象一个左右连线游戏)。
- 表的外键:是另一表的主键, 外键可以有重复的, 可以是空值,用来和其他表建立联系用的,一个表可以有多个外键
什么是数据库的三大范式?
什么是范式:
- 数据库范式是数据表设计的规范,在范式规范下,数据库里每个表存储的重复数据降到最少(这有助于数据的一致性维护),表和表之间不再有很强的数据耦合,可以独立的增长。
三大范式:
- 第一范式:
- 强调原子性,数据库的列不能再分
- 比如说:地址列:中国湖南长沙,这是可以拆分成三个列的(国家,省份,城市),因此不满足第一范式
- 第二范式:
- 满足第一范式
- 必须有主键
- 其他非主键的列完全依赖主键,而不能只依赖主键的一部分
- 第三范式:
- 满足第二范式
- 非主键的列必须直接依赖主键,而不能出现依赖传递的现象
- 比如说非主键A列依赖非主键B列,非主键B列依赖主键C列
2. 数据库和锁
什么是数据库事务?
- 数据库事务(Database Transaction) 是一系列的数据库操作,是数据库应用的基本逻辑单位,事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。
- 要么全部执行,要么不执行。
- 可以理解为有一堆数据库操作组合而成的原子操作。
事务有哪些性质?
ACID:
- 原子性(atomicity):事务中包括的诸操作要么全部执行,要么不执行。
- 一致性(consistency) :事务必须是使数据库从一个一致性状态变到另一个一致性状态。拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
- 隔离性(isolation) :一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性(durability) :一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响
并发事务的控制方式有哪些?
脏读、幻读、不可重复读
- 脏读:事务A读取到了事务B未提交的数据
- 不可重复读:同一个事务多次读同一个数据得到的结果不一致,在多次查询间隔有其他事务修改了记录
- 幻读:事务A按照一定的条件读取数据,期间事务B事务新增了数据,事务B再次按照相同的条件读取时,发现了之前没有读到的数据
MySQL 事务的隔离级别
- 读未提交:事务可以读取其他事务未提交的结果,会导致脏读、不可重复读、幻读
- 读已提交:🍓Oracle等多数数据库默认隔离级别🍓,事务只能读取其他事务已经提交过的数据,会导致不可重复读、幻读问题
- 可重复读::🍓InnoDB默认级别🍓,保证同一个事务多次读取同一个数据的结果是一致的,会导致幻读问题
- 序列化:强制事务排序,最高隔离级别,能够避免所有并发问题,但是并发性能非常低
如何实现4个隔离级别
- 读未提交:所有的读不加锁,所有的写加行锁
- 读已提交:MVCC
- 可重复读:MVCC和 Next-key Lock
- 序列化:读写都会加锁
Mysql的锁
按照锁的粒度划分:行级锁、表级锁、页级锁
- 行级锁:粒度最细的锁,表示只针对当前操作的行加锁,可以大大减少数据库操作的冲突,但是开销较大
- 表级锁:粒度最大的锁,表示对当前操作的整张表进行加锁,实现简单,消耗资源较少,但是冲突多
- 页级锁:行级锁和表级锁折中的方案,一次锁定相邻的一组记录
按锁级别分类:共享锁、排他锁、意向锁
- 共享锁( shared lock, S ):又称读锁,是读取操作创建的锁,其他用户可以并发读,但是不能修改,直到释放所有的共享锁
- 排他锁( exclusive lock, X ):又称写锁,如果事务对数据加上了排他锁,其他事物不能再对它加任何锁。获准排他锁的事务可以读也可以写
- 意向锁(intention shared lock, IS):是一种表级锁,目的是为了在一个事务中揭示下一行将要被请求的锁的类型,是InnoDB自动加的,不需要用户干预。分为意向共享锁和意象排他锁
是什么意向锁?它有什么作用?
意向锁是一种表级锁,它是为了解决事务锁冲突而存在的。
- 事务A锁住了表的某一行a,加上了行锁,只读不写
- 事务B锁住了整个表,加上排他锁(写锁),允许读写
- 理论上事务B可以对行a进行修改,这样就产生了锁冲突
- 事务B在锁住整个表之前,需要遍历表中的每一行,检查是否有锁冲突,但是这样是低效的,于是就有了意向锁
当有了意向锁之后
- 事务A先申请某一行的意向共享锁,申请成功后再申请行锁
- 事务B判断该表是否被其他事务的表锁锁住
- 事务B判断该表是否有意向共享锁,如果有,事务B无法对B加上写锁。
注意事项:
- 申请意向锁的动作是数据库完成的,当事务申请某一行的行锁时,就会自动地申请该表的意向锁
- 意向共享锁和意向排他锁是表级锁,和行级的共享锁和排他锁不会冲突。
乐观锁和悲观锁
乐观锁和悲观锁是数据库并发控制采用的主要技术
- 悲观锁:假定会发生并发冲突
- 对数据进行加锁,直到提交事务
- 实现方式:数据库的锁机制
- 乐观锁:假定不会发生并发冲突
- 在提交操作时才检查数据是否被修改过
- 实现方式:一般使用版本号或者CAS算法实现。给表增加version字段,提交之前检查version是否与原来的一样,判断有没有修改
MVCC
- MVCC(MultiVersion Concurrency Control 多版本并发控制):将同一份数据保留多个版本,进而实现并发控制。
- 提高并发性能,在高并发的场景下,MVCC的开销比行级锁小,更高效
- InnoDB实现的MVCC是一种乐观锁
实现原理:
- MVCC是通过 read view 和 版本链实现的
- 版本链保存有历史记录,通过read view 判断当前版本是否可见
- 如果不可见,就再从版本链中找到上一个版本,直到找到一个可见的版本
undo log段
- 是一种逻辑日志,是旧数据的备份。有两个作用:用于事务回滚和为MVCC提供老版本的数据。
- 当delete一条记录时,undo log会记录一条对应的insert记录;当updata一条记录时,会记录一条对应相反的update记录
快照读 和 实时读
快照读:
- 在一个事务内,多次执行SELECT语句读取到的数据都是事务开始状态的数据,这样就解决了幻读问题
- MySQL的默认隔离级别是 可重复读,在这种隔离级别下,普通的SELECT语句都是快照读
- 实现方式:在可重复读级别下,使用MVCC+undo log
实时读:
- 读取的是最新版本, 并且对读取的记录加锁, 阻塞其他事务同时改动相同记录
- 实现方式:在可重复读级别下,使用record lock 记录锁和gap lock来实现 (Next-Key Lock是Record Lock和Gap Lock的组合)
- 一般使用这两种SELECT进行查询的就是实时读
- SELECT *** FOR UPDATE – 在查询时会先申请X排他锁
- SELECT *** IN SHARE MODE – 在查询时会先申请S共享锁
如何解决幻读问题
- 快照读的幻读使用MVCC解决幻读,并发高,但是读取的是历史数据
- 当前读的幻读使用间隙锁解决,并发低,但是读取的是实时数据
3. 存储和索引
MySQL的存储引擎
定义:
- 数据库的存储引擎是数据库的底层组织软件,数据库使用引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能。
分类:
- InnoDB:默认的事务型存储引擎,使用最广泛,基于聚簇索引
- MyISAM:数据以紧密格式存储,访问速度快
- Memory:将数据全部存放在内存中,访问速度快
- Archive:适合存储大量独立的、作为历史记录的数据,有压缩功能,高效插入,但是不支持索引
MySIAM 和 InnoDB
- MyISAM只支持表级锁,InnoDB支持表级锁和行级锁(默认为行级锁)
- MyISAM不支持事务,InnoDB支持事务,具有事、回滚、崩溃修复能力
- MyISAM不支持外键,不支持MVCC
数据库索引
- 索引是一种数据结构,用于提高数据库的查询效率
- 优点:提高数据的查询速度,提高系统的性能
- 缺点:占用额外的存储空间,增加了存储成本;建立索引需要时间,增加了系统的开销
索引的作用
- 数据是存储在磁盘上的,如果没有索引,需要将所有的数据加载到内存进行查询,需要多次读取磁盘
- 有了索引之后,不需要加载所有的数据,B+树的高度一般在2-4层,只需要读取2-4次磁盘,加快了读取速度
Hash索引和B+树索引
- InnoDB引擎的索引类型有 B+树索引和哈希表索引,默认是B+树索引
- hash索引:对于每一行数据,将索引列的哈希值作为哈希表的key,将指向数据行的指针作为哈希表的value。时间复杂度是o(1)
Hash索引和B+树索引的区别
- hash索引不支持排序,不支持范围查找,不支持模糊查询
- hash索引会存在hash冲突,性能相对不稳定
B+ 树
B树和B+树都是多路平衡查找树,但是他们有以下区别:
- B+树的所有数据都存储在叶子结点上,非叶子结点只存储索引。B树的所有结点都存储数据
- B+树的所有叶子结点之间有指针相连,而B树没有
B+树的优点:
- B+树支持顺序查找(因为叶子全部顺序链在一起了)
- B+树有更好的查询性能、更少的I/O次数 (因为非叶子结点只存储索引,不存储数据)
- 查找速度稳定(查询结束时一定是停在树的叶子结点,而B树可能会停在任意的结点上)
聚集索引和覆盖索引
聚集索引
- 聚集索引又叫主键索引,如果没有主键,InnoDB选择一个不为null的唯一索引列作聚集索引
- 叶子结点存储的是数据表的某一行数据,叶子结点之间通过双向链表链接。
普通索引:
- 叶子结点不存储行数据,只存储索引值和主键值
回表:
- 第一次检索普通索引,找到对应的主键
- 第二次检索主键索引,找到对应的数据
- 这个过程叫做回表
回表流程:表格中有三列数据:id(主键)、name、age。查找age=48对应主键的过程是一个普通索引,根据主键检索对应数据的过程是主键索引
组合索引: 如果为某一个字段都建立一个索引表会非常浪费,对于那些使用不频繁的列可以采用组合索引
组合索引举例:
索引的最左匹配原则:
- 这是在组合索引中出现的概念,最左匹配即最左优先
- 索引 abc_index:(a,b,c),只会在条件中带有(a)、(a,b)、(a,b,c) 的三种类型的查询中使用到
- 这是因为先索引到a条件之后,b条件会局部有序,加快查询
- 所以,select * from table where b =‘1’ and c =‘2’; 不会走这个索引
覆盖索引:
- 只需要一棵索引树就能获取到所需要的列数据,无需回表,速度更快
- 实现方法:将被查询的字段全部建立到联合索引中去。即:为了实现覆盖索引,要先实现组合索引
索引下推
- 索引下推是MySQL5.6的新特性,用于优化数据机构的查询
- 有了索引下推的优化,存储引擎层会在回表查询之前对数据进行过滤,有效减少引擎回表查询的次数
SELECT * FROM user_info WHERE name LIKE “大%” AND level = 1;
在MySQL5.6版本以前会先检索name,由于这里的name是模糊查询,所有可以检索出两条数据,然后再进行两次回表查询
有了索引下推之后,查找到那组name条件的行时,会马上进行回表查询,在这个例子中,第一项数据就已经满足了条件,所以第二项不会再进行回表查询了。
什么情况下数据库索引会失效?
- 违背最左原则会导致索引失效 (联合索引 abc_index:(a,b,c) 为例)
- 查询条件中缺失最高优先级a,此时B+树不知道第一步应该查哪个结点,从而不走索引,开始全表查询
- 查询条件中缺失中间优先级b,B+树根据a完成了第一步查找,但是不知道第二步应该找谁,只能回表查询
- 以“%abc”开头的like查询,B+树不知道如何开始,因此索引会失效
- 查询条件中使用or连接,会导致索引失效
- 查询条件中列类型是字符串,但是没有用引号,可能导致类型不同发生隐式转换,使索引失效
- 当对某个字段使用了函数之后不能走索引:对字段进行函数操作之后可能会破坏索引的有序性,所以优化器放弃走索引
慢查询怎么排查
- 慢查询:查询很慢的查询
- 慢查询日志:用来记录mysql中响应时间超过long_query_time的语句
- 默认是关闭的,需要手动启动
- 主要有三个重要的属性:long_query_time:默认是10秒;slow_query_log:是否打开慢查询日志;slow_query_log_file:慢查询文件所在的位置
排查过程:
- 使用慢查询日志查看过去的慢查询:SELECT * FROM slow_log where start_time > ‘2019/05/19 00:00:00’;
- 使用show processlist查看当前正在运行的进程(该命令显示的信息都来自information_schama中的processlist表中)
- 在processlist中查询运行时间超过某个值的线程 :select * from information_schem.processlist where Command != ‘Sleep’ and Time > 300 order by Time desc;
- 在sql语句前面加上explain可显示该语句的执行计划 (如果使用了索引,extra列会显示 using index)
慢查询优化:
- 如果没走索引就建立合适的索引
- 如果使用了非预期的索引导致查询慢,要修改为正确的索引
- 如果是数据表太大,即使走了索引也很慢,这是要考虑数据库分表
大表查询怎么优化
- 合理建立索引:
- 利用缓存:使用Redis等缓存热点数据,提高查询效率
- 限定数据的范围:必须用户在查询历史信息的时候,可以控制在一个月的时间内
- 分库分表:对表进行垂直拆分和水平拆分
- 冷热数据分离:几个月不常用的数据放在冷库中,比较新的数据存放在热库中
为什么不建议使用外键
外键的优点:外键可以保证数据的完整性和一致性,级联操作方便;将数据完整性判断托付给了数据库完成,减少了程序的代码量
外键的缺点:
- 并发问题:在使用外键的情况下,每次修改数据库都需要去另一个表检查数据,需要获得额外的锁,若在高并发大流量事务场景下,使用外键容易造成死锁
- 扩展性问题:外键依赖于数据库本身的特性,迁移不方便
- 不利于分库分表:在水平拆分和分库的情况下,外键是无法生效的。将数据关系的维护放入应用程序中,为将来的分库分表省去了很多麻烦。
你平时是怎么使用数据库索引的,有哪些高效利用索引的经验和技巧?
二、SQL语法
0. 基础概念
什么是SQL
- SQL, 全称为Structured Query Language(结构化查询语言)。
- 数据库是用来存储大量数据的一种软件,SQL是用来操作数据库里数据的工具,具体来说SQL可以做数据查询,数据更新,写入数据等等。
- 如果把数据库比作盘子,那数据就是盘子里的菜,SQL则是你的筷子。
不同的数据库SQL的差异
1. 查询
http://xuesql.cn/lesson/select_queries_introduction
基础单表查询 where/like/order/limit/offset/distinct
- 查询所有 select *
- 条件查询(数值型):where / and / or / >= / between and
-- SELECT * FROM movies where year not between 2000 and 2010;
-- SELECT * FROM movies where id<6;
-- SELECT * FROM movies where year>=2010 and length_minutes<120;
- 条件查询(字符串型):like / 通配符 %
-- 注意字符串要用双引号括起来,不然会被识别为列
-- SELECT * FROM movies where title like "%Toy Story%";
-- SELECT * FROM movies where director="John Lasseter";
-- SELECT * FROM movies where director !="John Lasseter";
-- SELECT * FROM movies where title like "WALL-%";
SELECT * FROM movies where year=1998 and title like "A Bug's Life";
- 查询结果Filtering过滤 和 sorting排序:distinct、order by ASC/DESC、limit offset
SELECT column, another_column, …
FROM mytable
WHERE condition(s)
ORDER BY column ASC/DESC
LIMIT num_limit OFFSET num_offset;
-- 排序+去重
-- SELECT distinct director FROM movies order by director;
-- 降序排序并指定数量 ASC升序 DESC降序
-- SELECT * FROM movies order by year DESC limit 4;
-- SELECT * FROM movies order by title ASC limit 5;
-- offset和limit搭配使用,offset表示从哪里开始截取,limit表示截取的数量
-- SELECT * FROM movies order by title ASC limit 5 offset 5;
-- SELECT * FROM movies order by title ASC limit 5 offset 5;
-- 【结果排序】如果按片长排列,John Lasseter导演导过片长第3长的电影是哪部,列出名字即可
SELECT title FROM movies where Director="John Lasseter" order by Length_minutes DESC limit 1 offset 2;
to do :分支 case when
多表联合查询 inner/left/right join
SELECT column, another_table_column, …
FROM mytable (主表)
INNER JOIN another_table (要连接的表)
ON mytable.id = another_table.id (想象一下刚才讲的主键连接,两个相同的连成1条)
WHERE condition(s)
ORDER BY column, … ASC/DESC
LIMIT num_limit OFFSET num_offset;
-- SELECT * FROM Movies
-- inner join Boxoffice on Boxoffice.Movie_id=Movies.Id;
-- SELECT * FROM Movies
-- inner join Boxoffice on Boxoffice.Movie_id=Movies.Id
-- where Domestic_sales < International_sales;
-- SELECT * FROM Movies
-- inner join Boxoffice on Boxoffice.Movie_id=Movies.Id
-- order by Rating DESC;
SELECT Director,International_sales FROM Movies
inner join Boxoffice on Boxoffice.Movie_id=Movies.Id
order by International_sales DESC
limit 1 offset 0;
- INNER JOIN 只会保留两个表都存在的数据(交集),意味着一些数据的丢失,在某些场景下会有问题。真实世界中两个表存在差异很正常,所以我们需要更多的连表方式,也就是左连接LEFT JOIN,右连接RIGHT JOIN 和 全连接FULL JOIN。这几个 连接方式都会保留不能匹配的行。
- 和INNER JOIN 语法几乎是一样的. 我们看看这三个连接方法的工作原理:在表A 连接 B, LEFT JOIN保留A的所有行,不管有没有能匹配上B 反过来 RIGHT JOIN则保留所有B里的行。最后FULL JOIN 不管有没有匹配上,同时保留A和B里的所有行
- 这些Join也可以写作 LEFT OUTER JOIN, RIGHT OUTER JOIN, 或 FULL OUTER JOIN, 和 LEFT JOIN, RIGHT JOIN, and FULL JOIN 等价.
--请输入sql
-- select distinct Building from Employees where Building!="null";
-- select distinct Building_name, Role from Buildings
-- left join Employees on Buildings.Building_name=Employees.Building;
select distinct Building_name,Capacity from Employees
inner join Buildings on Buildings.Building_name=Employees.Building;
分组聚合查询 group by
group by 搭配一些聚合函数使用:
- COUNT(), COUNT(column) 计数!COUNT() 统计数据行数,COUNT(column) 统计column非NULL的行数.
- MIN(column) 找column最小的一行.
- MAX(column) 找column最大的一行.
- AVG(column) 对column所有行取平均值.
- SUM(column) 对column所有行求和.
SELECT AGG_FUNC(column_or_expression) AS aggregate_description, …
FROM mytable
WHERE constraint_expression
GROUP BY column;
- GROUP BY 数据分组语法可以按某个col_name对数据进行分组,如:GROUP BY Year指对数据按年份分组, 相同年份的分到一个组里。如果把统计函数和GROUP BY结合,那统计结果就是对分组内的数据统计了.
- GROUP BY 分组结果的数据条数,就是分组数量,比如:GROUP BY Year,全部数据里有几年,就返回几条数据, 不管是否应用了统计函数.
--请输入sql
-- SELECT Name,max(Years_employed) FROM employees;
-- SELECT Role,avg(Years_employed) FROM employees group by Role;
-- SELECT Building,sum(Years_employed) FROM employees group by Building;
-- 【难题】每栋办公室按人数排名,不要统计无办公室的雇员
SELECT Building,count(Name) FROM employees
where Building is not null
group by Building;
having 子句
- 在 GROUP BY 分组语法中,我们知道数据库是先对数据做WHERE,然后对结果做分组,如果我们要对分组完的数据再筛选出几条如何办?
- HAVING 语法将用来解决这个问题,他可以对分组之后的数据再做SELECT筛选.
- HAVING 和 WHERE 语法一样,只不过作用的结果集不一样. 在我们例子数据表数据量小的情况下可能感觉 HAVING没有什么用,但当你的数据量成千上万属性又很多时也许能帮上大忙 .
SELECT group_by_column, AGG_FUNC(column_expression) AS aggregate_result_alias, …
FROM mytable
WHERE condition
GROUP BY column
HAVING group_condition;
--请输入sql
-- SELECT count(Name) FROM employees where Role="Artist";
-- SELECT Role,count(Name) FROM employees group by role;
-- SELECT SUM(Years_employed) FROM employees where Role="Engineer";
【难题】按角色分组算出每个角色按有办公室和没办公室的统计人数(列出角色,数量,有无办公室,注意一个角色如果部分有办公室,部分没有需分开统计)
select role,
case when building is null then 0 else 1 end as have_b
,count(name)
from employees
group by role,have_b;
有点超纲了啊,还没学case when 呢
组合查询 union
在大多数开发中,使用一条SELECT查询就会返回一个结果集。如果,我们想一次性查询多条SQL语句,并将每一条SELECT查询的结果合并成一个结果集返回。就需要用到Union操作符,将多个SELECT语句组合起来,这种查询被称为并(Union)或者复合查询。
SELECT * FROM employees where Years_employed>8
union
SELECT * FROM employees where Years_employed<2;
使用Union All操作符来取消自动合并功能(在合并的时候去重)
SELECT * FROM employees where Years_employed>8
union all
SELECT * FROM employees where Years_employed>6;
多表查询:
SELECT Building FROM employees where Building=“1e”
union
SELECT Building_name FROM Buildings where Building_name=“2e”;
表达式查询
可以在查询中,使用简单的表达式进行查询
SELECT particle_speed / 2.0 AS half_particle_speed (对结果做了一个除2)
FROM physics_data
WHERE ABS(particle_position) * 10.0 >500(条件要求这个属性绝对值乘以10大于500);
-- SELECT Id,Title,(Domestic_sales+International_sales)/1000000 FROM movies
-- left join Boxoffice on movies.Id=Boxoffice.Movie_id
-- SELECT Id,Title,Rating*10 FROM movies
-- left join Boxoffice on movies.Id=Boxoffice.Movie_id
-- SELECT Id,Title,Year FROM movies where year%2=0;
-- 【难题】John Lasseter导演的每部电影每分钟值多少钱,告诉我最高的3个电影名和价值就可以
select Title,(Domestic_sales+International_sales)/Length_minutes as value from movies
join Boxoffice on movies.Id=Boxoffice.Movie_id
where Director="John Lasseter"
order by value DESC
limit 3 offset 0;
子查询
子查询指一个查询语句嵌套在另一个查询语句内的查询。
SELECT * FROM sc WHERE score>(SELECT MAX(score) FROM sc WHERE s_id='02');
表达式
--请输入sql
-- SELECT Director,count(Title) FROM movies group by Director;
【难题】按导演分组计算销售总额,求出平均销售额冠军(统计结果过滤掉只有单部电影的导演,列出导演名,总销量,电影数量,平均销量)
SELECT Director,sum(Domestic_sales+International_sales) s ,count(id) c,avg(Domestic_sales+International_sales) a
FROM movies as m
join Boxoffice as b on m.Id=b.Movie_id
group by Director
having c>1 -- having在分组之后的结果中进行筛选
order by a DESC
limit 1 offset 0;
这才是完整的SELECT查询
SELECT DISTINCT column, AGG_FUNC(column_or_expression), …
FROM mytable
JOIN another_table
ON mytable.column = another_table.column
WHERE constraint_expression
GROUP BY column
HAVING constraint_expression
ORDER BY column ASC/DESC
LIMIT count OFFSET COUNT;
2. 更新
注意update+表名,set后面加具体的修改信息
UPDATE tb_courses_new
SET course_name=‘DB’,course_grade=3.5
WHERE course_id=2;
3. 插入
注意要加括号:insert into values
INSERT INTO tb_courses
(course_id,course_name,course_grade,course_info)
VALUES(1,‘Network’,3,‘Computer Network’);
4. 删除
delete from
DELETE FROM tb_courses
WHERE course_id=4;
5.建表语句
create table 表名(
字段名1 数据类型,
字段名2 数据类型,
字段名3 数据类型
…
);
# 建表
CREATE TABLE games
(yr INT NOT NULL PRIMARY KEY,
city VARCHAR(20)
);
# 插入
INSERT INTO games(yr,city) VALUES (2004,'Athens');
insert into games(yr,city) values(2005,'ChangSha');
# 删除
delete from games where yr=2004;
# 更新
update games set city='Shanghai' where yr=1998;
6. 建库语句
CREATE DATABASE testDB