日志
MySQL 的日志默认保存位置为 /usr/local/mysql/data
1日志类型与作用:
1.redo 重做日志:达到事务一致性(每次重启会重做)
作用:确保日志的持久性,防止在发生故障,脏页未写入磁盘。重启数据库会进行redo log执行重做,达到事务一致性
2.undo 回滚日志
作用:保证数据的原子性,记录事务发生之前的一个版本,用于回滚,innodb事务可重复读和读取已提交 隔离级别就是通过mvcc+undo实现
3.errorlog 错误日志
作用:Mysql本身启动,停止,运行期间发生的错误信息
-
slow query log 慢查询日志
作用:记录执行时间过长的sql,时间阈值(10s)可以配置,只记录执行成功
另一个作用:在于提醒优化 -
bin log 二进制日志
作用:用于主从复制,实现主从同步
记录的内容是:数据库中执行的sql语句
6.relay log 中继日志
作用:用于数据库主从同步,将主库发来的bin log保存在本地,然后从库进行回放
- general log 普通日志
作用:记录数据库的操作明细,默认关闭,开启后会降低数据库性能
mysql -u root -P
show variables like 'general%'; #查看通用查询日志是否开启
show variables like 'log_bin%'; #查看二进制日志是否开启
show variables like '%slow%'; #查看慢查询日功能是否开启
show variables like 'long_query_time'; #查看慢查询时间设置
set global slow_query_log=ON; #在数据库中设置开启慢查询的方法
PS: variables 表示变量 like 表示模糊查询
##配置文件
vim /etc/my.cnf
[mysqld]
##错误日志,用来记录当MySQL启动、停止或运行时发生的错误信息,默认已开启
log-error=/usr/local/mysql/data/mysql_error.log #指定日志的保存位置和文件名
##通用查询日志,用来记录MySQL的所有连接和语句,默认是关闭的
general_log=ON
general_log_file=/usr/local/mysql/data/mysql_general.log
##二进制日志(binlog),用来记录所有更新了数据或者已经潜在更新了数据的语句,记录了数据的更改,可用于数据恢复,默认已开启
log-bin=mysql-bin
或
log_bin=mysql-bin
##慢查询日志,用来记录所有执行时间超过long_query_time秒的语句,可以找到哪些查询语句执行时间长,以便提醒优化,默认是关闭的
s1ow_query_log=ON
slow_query_log_file=/usr/local/mysql/data/mysql_slow_query.log
long_query_time=5 #设置超过5秒执行的语句被记录,缺省时为10秒
##复制段
log-error=/usr/local/mysql/data/mysql_error.log
general_log=ON
general_log_file=/usr/local/mysql/data/mysql_general.log
log-bin=mysql-bin
slow_query_log=ON
slow_query_log_file=/usr/local/mysql/data/mysql_slow_query.log
long_query_time=5
systemctl restart mysqld
#xxx(字段)
xxx% 以xxx为开头的字段
%xxx 以xxx为结尾的字段
%xxx% 只要出现xxx字段的都会显示出来
xxx 精准查询
#二进制日志开启后,重启mysql 会在目录中查看到二进制日志
cd /usr/local/mysql/data
ls
mysql-bin.000001 #开启二进制日志时会产生一个索引文件及一个索引列表
索引文件:记录更新语句
索引文件刷新方式:
1、重启mysql的时候会更新索引文件,用于记录新的更新语句
2、刷新二进制日志
mysql-bin.index:
二进制日志文件的索引
备份
1为什么要备份
灾难恢复:硬件故障、软件故障、自然灾害、黑客攻击、误操作测试等数据丢失场景
2备份类型
完全备份,部分备份
完全备份:整个数据集
部分备份:只备份数据子集,如部分库或表
完全备份、增量备份、差异备份
增量备份:仅备份最近一次完全备份或增量备份(如果存在增量)以来变化的数据,备份较
快,还原复杂
冷、温、热备份
冷备:读、写操作均不可进行,数据库停止服务
温备:读操作可执行;但写操作不可执行
热备:读、写操作均可执行
MyISAM:温备,不支持热备
InnoDB:都支持
物理和逻辑备份
物理备份:直接复制数据文件进行备份,与存储引擎有关,占用较多的空间,速度快
逻辑备份:从数据库中“导出”数据另存而进行的备份,与存储引擎无关,占用空间少,速度慢,可
能丢失精度
3实际操作
3.1 冷备份:
环境准备:
use kgc;
create table if not exists info1 (
id int(4) not null auto_increment,
name varchar(10) not null,
age char(10) not null,
hobby varchar(50),
primary key (id));
insert into info1 values(1,'user1',20,'running');
insert into info1 values(2,'user2',30,'singing');
InnoDB 存储引擎的数据库在磁盘上存储成三个文件: db.opt(表属性文件)、表名.frm(表结构文件)、表名.ibd(表数据文件)。
1.物理冷备份与恢复
systemctl stop mysqld
yum -y install xz
#压缩备份
tar Jcvf /opt/mysql_all_$(date +%F).tar.xz /usr/local/mysql/data/
mv /usr/local/mysql/data/ /opt/
#解压恢复
tar Jxvf /opt/mysql_all_2020-11-22.tar.xz -C /usr/local/mysql/data/
cd /usr/local/mysql/data
mv /usr/local/mysql/data/* ./
注意是移动不是复制,复制貌似会无法启动mysql,复制注意权限问题。
2. mysqldump 备份与恢复(温备份)
(1)、完全备份一个或多个完整的库 (包括其中所有的表)
mysqldump -uroot -p[密码] --databases 库名1 [库名2] ... > /备份路径/备份文件名.sql #导出的就是数据库脚本文件
例:
mysqldump -u root -p --databases kgc > /opt/kgc.sql
#备份一个kgc库
mysqldump -u root -p --databases mysql kgc > /opt/mysql-kgc.sql
#备份mysql与 kgc两个库
(2)、完全备份 MySQL 服务器中所有的库
mysqldump -u root -p[密码] --all-databases > /备份路径/备份文件名.sql
例:
mysqldump -u root -p --all-databases > /opt/all.sql
(3)、完全备份指定库中的部分表
mysqldump -u root -p[密码] 库名 [表名1] [表名2] ... > /备份路径/备份文件名.sql
例:
mysqldump -u root -p [-d] kgc info1 info2 > /opt/kgc_info1.sql
#使用“-d”选项,说明只保存数据库的表结构
#不使用“-d"选项,说明表数据也进行备份
#做为一个表结构模板
(4)查看备份文件
grep -v "^--" /opt/kgc_info1.sql | grep -v "^/" | grep -v "^$"
3.2 mysqldump 备份与恢复
完全备份一个或多个完整的库 (包括其中所有的表)
mysqldump -u root -p[密码] --databases 库名1 [库名2] ... > /备份路径/备份文件名.sql #导出的就是数据库脚本文件
例:
mysqldump -u root -p --databases kgc > /opt/kgc.sql
#备份一个kgc库
mysqldump -u root -p --databases mysql kgc > /opt/mysql-kgc.sql
#备份mysql与 kgc两个库
(2)、完全备份 MySQL 服务器中所有的库
mysqldump -u root -p[密码] --all-databases > /备份路径/备份文件名.sql
例:
mysqldump -u root -p --all-databases > /opt/all.sql
(3)、完全备份指定库中的部分表
mysqldump -u root -p[密码] 库名 [表名1] [表名2] ... > /备份路径/备份文件名.sql
例:
mysqldump -u root -p [-d] kgc info1 info2 > /opt/kgc_info1.sql
#使用“-d”选项,说明只保存数据库的表结构
#不使用“-d"选项,说明表数据也进行备份
#做为一个表结构模板
(4)查看备份文件
grep -v "^--" /opt/kgc_info1.sql | grep -v "^/" | grep -v "^$
mysql 完全恢复
模拟删库
drop database 库名;
mysql -u root -p123123 < /backup/bbs.sql
#恢复数据库操作
#恢复数据表
当备份文件中只包含表的 备份时,而不包含创建库时的语句,执行操作时必须指定库名,且目标库必须存在。
mysqldump -u root -p123123 kgc info > /backup/kgc_info.sql
#只备份表
drop database kgc;
create database kgc;
mysql -u root -p123123 kgc < /backup/kgc_info.sql
#######恢复表时需要指定对应的库##########
3.3增量备份与恢复
MySQL数据库增量恢复
1.一般恢复
将所有备份的二进制日志内容全部恢复
2.基于位置恢复
数据库在某一时间点可能既有错误的操作也有正确的操作
可以基于精准的位置跳过错误的操作
发生错误节点之前的一个节点,上一次正确操作的位置点停止
3.基于时间点恢复
跳过某个发生错误的时间点实现数据恢复
在错误时间点停止,在下一个正确时间点开始
一、增备实验
1.开启二进制日志功能
vim /etc/my.cnf
[mysqld]
log-bin=mysql-bin
binlog_format = MIXED #可选,指定二进制日志(binlog)的记录格式为MIXED(混合输入)
server-id = 1 #可加可不加该命令
#二进制日志(binlog)有3种不同的记录格式:
#STATEMENT (基于SQL语句)、
#ROW(基于行)、
#MIXED(混合模式),
#默认格式是STATEMENT
[root@localhost backup]#ls /usr/local/mysql/data
#二进制日志位置
2 可以每周对数据库或表进行完全备份
mysqldump -uroot -p123123 kgc info >/backup/kgc_info.sql
mysqldump -uroot -p123123 --all-databases >/backup/kgc_info.sql
3.可以每天进行增量备份操作,生成新的二进制文件(mysql-bin.000002)
mysqladmin -uroot -p123123 -p flush-logs
4插入新数据
use info
insert into info values(2,'gk',22);
insert into info values(3,'jk',23);
5再次生成新的二进制日志文件
mysqladmin -uroot -p123123 flush-logs
#之前的操作会保存在上一个二进制文件中,之后的操作会保存在新的二进制文件中
6.查看二进制日志文件
cp /usr/loacal/mysql/data/mysql-bin.000002 /opt/
mysqlbinlog --no-defaults --base64-output=decode-rows -v /opt/mysql-bin.000002
增量恢复:
1模拟丢失文件
delete from info where id=1;
mysqlbinlog --no-defaults /opt/mysql-bin.000002 |mysql -u -root -p123123
3.3-1增量备份与恢复
实际操作
#首先修改配置文件启用二进制日志
[root@localhost ~]#vim /etc/my.cnf
#修改二进制日志文件
[mysqld]
log-bin=mysql-bin
binlog_format = MIXED
[root@localhost ~]#systemctl restart mysqld.service
#重启服务
root@localhost ~]#ls /usr/local/mysql/data/
#查看日志文件是否生成 mysql-bin.000001
auto.cnf ib_buffer_pool ib_logfile0 ibtmp1 mysql-bin.000001 performance_schema
bbs ibdata1 ib_logfile1 mysql mysql-bin.index sys
[root@localhost ~]#mysql -uroot -p123123
#进入数据库创建环境
mysql> create database ky15;
#创建数据库 ky15
mysql> use ky15
#进入数据库 ky15
mysql> create table info (id int , name char(20),age int,address char(50),hobby char(50));
mysql> desc info;
#查看表结构
+---------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+----------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | char(20) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| address | char(50) | YES | | NULL | |
| hobby | char(50) | YES | | NULL | |
+---------+----------+------+-----+---------+-------+
5 rows in set (0.00 sec)
mysql> insert into info values(1,'jk',18,'南京','吃饭');
mysql> insert into info values(2,'ll',19,'北京','跳舞');
#插入数据
mysql> select * from info;
+------+------+------+---------+--------+
| id | name | age | address | hobby |
+------+------+------+---------+--------+
| 1 | jk | 18 | 南京 | 吃饭 |
| 2 | ll | 19 | 北京 | 跳舞 |
+------+------+------+---------+--------+
2 rows in set (0.00 sec)
mkdir /backup
#创建存放备份文件的目录
mysqldump -uroot -p123123 ky15 info > /backup/ky15_info.sql
mysqldump: [Warning] Using a password on the command line interface can be insecure.
#备份ky15 下的info表
mysqladmin -uroot -p123123 flush-logs
#刷新日志
ls /usr/local/mysql/data/
#查看是否生成了新的 二进制日志
auto.cnf ib_buffer_pool ib_logfile0 ibtmp1 mysql mysql-bin.000002 performance_schema
bbs ibdata1 ib_logfile1 ky15 mysql-bin.000001 mysql-bin.index sys
#数据库中插入新的文件
mysql> use ky15
#进入数据库 ky15
mysql> insert into info values(3,'lili',23,'北京','唱歌');
Query OK, 1 row affected (0.00 sec)
mysql> insert into info values(4,'lihua',23,'北京','骂人');
Query OK, 1 row affected (0.00 sec)
mysql> select * from info;
+------+-------+------+---------+--------+
| id | name | age | address | hobby |
+------+-------+------+---------+--------+
| 1 | jk | 18 | 南京 | 吃饭 |
| 2 | ll | 19 | 北京 | 跳舞 |
| 3 | lili | 23 | 北京 | 唱歌 |
| 4 | lihua | 23 | 北京 | 骂人 |
+------+-------+------+---------+--------+
4 rows in set (0.00 sec)
cat /usr/local/mysql/data/mysql-bin.000002
mysqlbinlog --no-defaults --base64-output=decode-rows -v /usr/local/mysql/data/mysql-bin.000002
#查看日志文件
mysql> drop database ky15;
Query OK, 1 row affected (0.00 sec)
#模拟删库
mysql> create database ky15;
mysql -uroot -p123123 ky15 < ky15_info.sql
mysql: [Warning] Using a password on the command line interface can be insecure.
#恢复表
mysql> show tables;
mysql> select * from info;
+------+------+------+---------+--------+
| id | name | age | address | hobby |
+------+------+------+---------+--------+
| 1 | jk | 18 | 南京 | 吃饭 |
| 2 | ll | 19 | 北京 | 跳舞 |
+------+------+------+---------+--------+
2 rows in set (0.00 sec)
mysqlbinlog --no-defaults /usr/local/mysql/data/mysql-bin.000002 |mysql -uroot -p123123
mysql: [Warning] Using a password on the command line interface can be insecure.
#恢复 增量备份
mysql> select * from info;
+------+-------+------+---------+--------+
| id | name | age | address | hobby |
+------+-------+------+---------+--------+
| 1 | jk | 18 | 南京 | 吃饭 |
| 2 | ll | 19 | 北京 | 跳舞 |
| 3 | lili | 23 | 北京 | 唱歌 |
| 4 | lihua | 23 | 北京 | 骂人 |
+------+-------+------+---------+--------+
4 rows in set (0.00 sec)
3.4断点恢复
基于位置恢复
删除库中的数据
mysql> delete from info where id=3;
Query OK, 1 row affected (0.01 sec)
mysql> delete from info where id=4;
Query OK, 1 row affected (0.00 sec)
mysql> select * from info;
+------+------+------+---------+--------+
| id | name | age | address | hobby |
+------+------+------+---------+--------+
| 1 | jk | 18 | 南京 | 吃饭 |
| 2 | ll | 19 | 北京 | 跳舞 |
+------+------+------+---------+--------+
2 rows in set (0.00 sec)
只恢复其中之一
[root@localhost backup]#mysqlbinlog --no-defaults --base64-output=decode-rows -v /usr/local/mysql/data/mysql-bin.000002 >/backup/mysqlbin.log
#查看 相关的序号
[root@localhost backup]#mysqlbinlog --no-defaults --stop-position='601' /usr/local/mysql/data/mysql-bin.000002 |mysql -uroot -p123123
#从头开始 到601结束
mysql> select * from info;
+------+------+------+---------+--------+
| id | name | age | address | hobby |
+------+------+------+---------+--------+
| 1 | jk | 18 | 南京 | 吃饭 |
| 2 | ll | 19 | 北京 | 跳舞 |
| 3 | lili | 23 | 北京 | 唱歌 |
+------+------+------+---------+--------+
3 rows in set (0.00 sec)
[root@localhost backup]#mysqlbinlog --no-defaults --start-position='601' /usr/local/mysql/data/mysql-bin.000002 |mysql -uroot -p123123
mysql: [Warning] Using a password on the command line interface can be insecure.
#从601 开始
mysql> select * from info;
+------+-------+------+---------+--------+
| id | name | age | address | hobby |
+------+-------+------+---------+--------+
| 1 | jk | 18 | 南京 | 吃饭 |
| 2 | ll | 19 | 北京 | 跳舞 |
| 4 | lihua | 23 | 北京 | 骂人 |
+------+-------+------+---------+--------+
3 rows in set (0.00 sec)
基于时间点
#格式只将position 改为datetime 时间日期 格式 年-月-日 时:分:秒
[root@localhost backup]#mysqlbinlog --no-defaults --start-datetime='2021-11-29 14:31:14' /usr/local/mysql/data/mysql-bin.000002 |mysql -uroot -p123123
mysql: [Warning] Using a password on the command line interface can be insecure.
SQL高级语言
1导入数据库
mysql> source /backup/hellodb_innodb.sql;
#将脚本导入 source 加文件路径
2. select
显示表格中的一个或者多个字段中所有的信息
语法:
select 字段名 from 表名;
例子
select * from info;
select name from info;
select name,id,age from info;
3. distinct
distinct 查询不重复记录
中文含义:/dɪˈstɪŋkt/ 不同的 明显的
语法:
select distinct 字段 from 表名﹔
例子:
select distinct age from students;
#去除年龄字段中重复的
select distinct gender from students;
#查找性别
4. where
where 有条件的查询
语法:select '字段' from 表名 where 条件
select name,age from students where age < 20;
#显示name和age 并且要找到age 小于20的
5.and;or
and 且 or 或
语法:
select 字段名 from 表名 where 条件1 (and|or) 条件2 (and|or)条件3;
例子:
select name,age from students where 30 > age and age > 20;
select name,age,classid from students where 30 > age and age > 20 and classsid=3;
select name,age from students where gender='m' or(30 > age and age > 20);
#男的 或 30到20岁之间
6.in
in:
显示已知值的资料
语法:
select 字段名 from 表名 where 字段 in ('值1','值2'....);
例子:
select * from students where StuID in (1,2,3,4);
select * from students where ClassID in (1,4);
7.between
between:
显示两个值范围内的资料
语法:
select 字段名 from 表名 where 字段 between '值1' and '值2';
包括 and两边的值
例子:
select * from students where name between 'ding dian' and 'ling chong';
#一般不使用在字符串上
select * from students where stuid between 2 and 5;
#id2-到5 的信息
select * from students where age between '22' and '30';
不需要表中一定有该字段,只会将22 到30 已有的都显示出来
8.like 通配符
通配符通常是和 like 一起使用
语法:
select 字段名 from 表名 where 字段 like 模式
select * from students where name like 's%';
通配符 | 含义 |
---|---|
% | 表示零个,一个或者多个字符 |
_ | 下划线表示单个字符 |
A_Z | 所有以A开头 Z 结尾的字符串 ‘ABZ’ ‘ACZ’ 'ACCCCZ’不在范围内 下划线只表示一个字符 AZ 包含a空格z |
ABC% | 所有以ABC开头的字符串 ABCD ABCABC |
%CBA | 所有以CBA结尾的字符串 WCBA CBACBA |
%AN% | 所有包含AN的字符串 los angeles |
_AN% | 所有 第二个字母为 A 第三个字母 为N 的字符串 |
9. order by
order by 按关键字排序
语法:
select 字段名 from 表名 where 条件 order by 字段 [asc,desc];
select age,name from students order by age;
#正向排序
select name,age from info order by age desc;
#反向排序
可以加上where
select name,age from students where classid=3 order by age;
#显示 name和age字段的数据 并且只显示classid字段为3 的 并且以age字段排序
10.函数
10.1数学函数
函数 | 含义 |
---|---|
abs(x) | 返回x 的 绝对值 |
rand() | 返回0到1的随机数 |
mod(x,y) | 返回x除以y以后的余数 |
power(x,y) | 返回x的y次方 |
round(x) | 返回离x最近的整数 |
round(x,y) | 保留x的y位小数四舍五入后的值 |
sqrt(x) | 返回x的平方根 |
truncate(x,y) | 返回数字 x 截断为 y 位小数的值 |
ceil(x) | 返回大于或等于 x 的最小整数 |
floor(x) | 返回小于或等于 x 的最大整数 |
greatest(x1,x2…) | 返回返回集合中最大的值 |
least(x1,x2…) | 返回返回集合中最小的值 |
例子:
select abs(-1);
select rand();
select mod(6,4);
select power(2,2);
select round(2.5);
select round(3.1415926345,3);
select truncate(3.1415926345,3);
select ceil(2.6);
select floor(2.4);
select least(22,33,44);
select greatest(55,66,88);
10.2聚合函数
函数 | 含义 |
---|---|
avg() | 返回指定列的平均值 |
count() | 返回指定列中非 NULL 值的个数 |
min() | 返回指定列的最小值 |
max() | 返回指定列的最大值 |
sum(x) | 返回指定列的所有值之和 |
例子:
select avg(age) from students;
#求表中年龄的平均值
select sum(age) from students;
#求表中年龄的总和
select max(age) from students;
#求表中年龄的最大值
select min(age) from students;
#求表中年龄的最小值
select count(classid) from students;
#求表中有多少非空记录
select count(distinct gender) from students;
select count(*) from students;
如果某表只有一个字段使用*不会忽略空记录
如果count后面加上明确字段会忽略空记录
#####思考空格字符 会被匹配么?########
insert into students values(26,' ',22,'m',3,1);
10.3 字符串函数
函数 | 描述 |
---|---|
trim() | 返回去除指定格式的值 |
concat(x,y) | 将提供的参数 x 和 y 拼接成一个字符串 |
substr(x,y) | 获取从字符串 x 中的第 y 个位置开始的字符串,跟substring()函数作用相同 |
substr(x,y,z) | 获取从字符串 x 中的第 y 个位置开始长度为z 的字符串 |
length(x) | 返回字符串 x 的长度 |
replace(x,y,z) | 将字符串 z 替代字符串 x 中的字符串 y |
upper(x) | 将字符串 x 的所有字母变成大写字母 |
lower(x) | 将字符串 x 的所有字母变成小写字母 |
left(x,y) | 返回字符串 x 的前 y 个字符 |
right(x,y) | 返回字符串 x 的后 y 个字符 |
repeat(x,y) | 将字符串 x 重复 y 次 |
space(x) | 返回 x 个空格 |
strcmp(x,y) | 比较 x 和 y,返回的值可以为-1,0,1 |
reverse(x) | 将字符串 x 反转 |
例子
#trim:
语法:
select trim (位置 要移除的字符串 from 字符串)
其中位置的值可以是
leading(开始)
trailing(结尾)
both(起头及结尾)
要移除的字符串:从字符串的起头、结尾或起头及结尾移除的字符串,缺省时为空格。#区分大小写
select trim(leading 'Sun' from 'Sun Dasheng');
select trim(both from ' Sun Dasheng ');
#去除空格
#length:
select name,length(name) from students;
#计算出字段中记录的字符长度
#replace
语法:select replace(字段,'原字符''替换字符') from 表名;
select replace(name,'ng','gl') from students;
#把ng换成gl
#concat:
语法:select concat(字段1,字段2)from 表名
select concat(name,classid) from students;
elect concat(name,classid) from students where classid=3;
select name || classid from students where classid=3;
select concat(name,'\t',classid) from students where classid=3;
#substr:
语法:select substr(字段,开始截取字符,截取的长度) where 字段='截取的字符串'
select substr(name,6) from students where name='Sun Dasheng';
select substr(name,6,2) from students where name='Sun Dasheng';
11 group by
group by:
对group by 后面的字段的查询结果进行汇总分组,通常是结合聚合函数一起使用的
group by 有一个原则,就是select 后面的所有列中,没有使用聚合函数的列必须出现在 group by 的后面。
语法:
select 字段1,sum(字段2) from 表名 group by 字段1;
例子:
select classid,sum(age) from students group by classid;
#求各个班的年龄总和
select classid,avg(age) from students group by classid;
#求平均年龄
select classid,count(age) from students group by classid;
12having
having:用来过滤由group by语句返回的记录集,通常与group by语句联合使用
having语句的存在弥补了where关键字不能与聚合函数联合使用的不足。如果被SELECT的只有函数栏,那就不需要GROUP BY子句。
语法:SELECT 字段1,SUM("字段")FROM 表格名 GROUP BY 字段1 having(函数条件);
select classid,avg(age) from students group by classid where age > 30;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where age > 30' at line 1
select classid,avg(age) from students group by classid having age > 30;
ERROR 1054 (42S22): Unknown column 'age' in 'having clause'
select classid,avg(age) from students group by classid having avg(age) > 30;