数据库笔记

###表的增删改差(CRUD:Creat,Retrieve,Update,Delete)

1. 插入是否更新

由于主键或者唯一键对应的值已经存在而导致插入失败

  • 选择性的进行同步更新操作

    1
    2
    insert ... on duplicate key update
    column = value [,column = value] ...
    • 举例

      1
      2
      3
      4
      5
      6
      7
      insert into students (id,sn,name)values (100,10010,'lalala') 
      on delicate key update sn=10010,name='lalala';

      三种情况
      -- 0 row affected: 表中有冲突数据,但冲突数据的值和 update 的值相等
      -- 1 row affected: 表中没有冲突数据,数据被插⼊
      -- 2 row affected: 表中有冲突数据,并且数据已经被更新
    • 查看函数获取受到影响的数据行数

      1
      select row_count();

####2.替换(replace)

  • 主键或者唯一键没有冲突,则直接插入
  • 主键或者唯一键有冲突,则删除后再插入
1
2
3
4
5
replace into students (sn,name) values (20001,'曹阿瞒');

两种情况
-- 1 row affected: 表中没有冲突数据,数据被插⼊
-- 2 row affected: 表中有冲突数据,删除后重新插⼊

Retrieve

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CREATE TABLE exam_result (   
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20) NOT NULL COMMENT '同学姓名',
yuwen float DEFAULT 0.0 COMMENT '语文成绩',
shuxue float DEFAULT 0.0 COMMENT '数学成绩',
yingyu float DEFAULT 0.0 COMMENT '英语成绩' );

INSERT INTO exam_result (name, yuwen, shuxue, yingyu) VALUES
('唐三藏', 67, 98, 56),
('孙悟空', 87, 78, 77),
('猪悟能', 88, 98, 90),
('曹孟德', 82, 84, 67),
('刘玄德',55,85,45),
('孙权', 70, 73, 78),
('宋公明',75,65,30);

####1.select列

  1. 全列查询

    1
    select * from 表明;
    • 查询的列越多,意味着传输的数据越大
    • 可能会影响引索的使用
  2. 指定列查询

    1
    2
    --- 指定列的顺序不需要按定义表的顺序进行查询
    select id,name,yuwen from exam_result;
  3. 查询字段为表达式

    1
    2
    --- 表达式不包含字段
    select id,name,666 from exam_result;
    1
    2
    --- 表达式包含一个字段
    select id,name,yuwen+10 from exam_result;
    1
    2
    --- 表达式包含多个字段,并且将查询结果指定别名
    select id,name,yuwen+shuxue+yingyu as total from exam_result;
  4. 查询结果去重(distinct)

    1
    select distinct shuxue from exam_result;

2.where条件

  1. >,>=,<,<= 英语不及格的同学以及英语成绩(<60)

    1
    select neme,yingyu from exam_result where yingyu<60;
  2. between a0 and a1 语文成绩在[80,90]分的同学以及语文成绩

    1
    2
    3
    select name,yuwen from exam_result where yuwen>80 and yuwen<90;
    --- 使用between...and... 条件
    select name,yuwen from exam_result where yuwen between 80 and 90;
  3. in(option,…) 数学成绩是58或者59或者98或者99分的同学及数学成绩

    1
    2
    3
    select name,shuxue from exam_result where shuxue=58 or shuxue=59 or shuxue=98 or shuxue=99;
    --- 使用 in 条件,满足其中任意一个条件即可
    select name,shuxue from exam_result where shuxue in (58,59,98,99);
  4. like 姓孙的同学及孙某同学

    1
    2
    --- % 匹配多个任意字符  _ 匹配一个任意字符
    select name from exam_result where name like '孙%' or '孙_';
  5. 语文成绩好于英语成绩的同学

    1
    select name,yuwen,shuxue from exam_result where yuwen>shuxue;
  6. 总分在200分一下的同学

    1
    2
    --- 注意:由于执行顺序的原因(后面详细说明),别名不能在where条件中使用
    select name ,yuwen+shuxue+yingyu as total from exam_result where yuwen+shuxue+yingyu<200;
  7. not 语文成绩>80且不姓孙的同学

    1
    select name,yuwen from exam_result where yuwen>80 and name not like '孙%';
  8. and,or 孙某同学,否则要求总成绩>200并且语文成绩<数学成绩并且英语成绩>80

    1
    select name,yuwen+shuxue+yingyu as total from exam_result where name like '孙_' or (yuwen+shuxue+yingyu>200 and yuwen<shuxue and yingyu>80);
  9. NULL查询

    <=>:等于,NULL安全,例如NULL<=>NULL的结果为TRUE(1)

    =:等于,NULL不安全,;例如NULL=NULL的结果为NULL

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    --- NULL和NULL比较,=和<=>的区别
    select null=null,null=1,null=0;
    +-----------+--------+--------+
    | null=null | null=1 | null=0 |
    +-----------+--------+--------+
    | NULL | NULL | NULL |
    +-----------+--------+--------+
    1 row in set (0.00 sec)

    select null<=>null,null<=>1,null<=>0;
    +-------------+----------+----------+
    | null<=>null | null<=>1 | null<=>0 |
    +-------------+----------+----------+
    | 1 | 0 | 0 |
    +-------------+----------+----------+
    1 row in set (0.03 sec)

####3.排序结果

语法:

1
2
3
4
5
6
-- ASC 为升序(从小到大)
-- DESC 为降序(从大到小)
-- 默认 ASC

SELECT ... FROM table_name [WHERE ...]
ORDER BY column [ASC|DESC], [...];

注意:没有order by子句的查询,返回的顺序是未定义的,永远不要依赖这个顺序

  1. 同学及数学成绩,按数学成绩升序显示

    1
    select name,shuxue from exam_result order by shuxue;
  2. NULL视为比任何值都要小,升序出现在最上面,降序出现在最下面

  3. 查询同学各门成绩,一次按数学降序,英语升序,语文升序的方式显示

    1
    2
    --- 多字段排序,排序优先级随书写顺序
    select name,yuwen,shuxue,yingyu from exam_result order by shuxue desc,yingyu,yuwen;
  4. 查询同学及总分,由高到低

    1
    select name,yuwen+shuxue+yingyu as total from exam_result order by total desc;
  5. 查询姓孙的同学或者姓曹的同学数学成绩,结果按照数学成绩由高到低显示

    1
    select name,shuxue from exam_result where name like '孙%' or name like '曹%' order by shuxue desc;

4.筛选分页结果

语法:

1
2
3
4
5
6
7
8
9
10
---其实下标为0

---从0开始,筛选n条结果
SELECT ... FROM table_name [WHERE ...] [ORDER BY ...] LIMIT n

---从s开始,筛选n条结果
SELECT ... FROM table_name [WHERE ...] [ORDER BY ...] LIMIT s, n;

---从s开始,筛选n条结果,比第二种用法更明确,建议使用
SELECT ... FROM table_name [WHERE ...] [ORDER BY ...] LIMIT n OFFSET s;
  • 建议:队未知表进行查询时,最好加一条limit 1,避免因为表中数据过大,查询全表数据导致数据库卡死
  1. 按id进行分页,每页3条记录,分别显示第1,2,3页

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    --- 第一页
    select id,name,yuwen,shuxue,yingyu from exam_result order by id limit 3 offset 0;
    +----+-----------+-------+--------+--------+
    | id | name | yuwen | shuxue | yingyu |
    +----+-----------+-------+--------+--------+
    | 1 | 唐三藏 | 67 | 98 | 56 |
    | 2 | 孙悟空 | 87 | 78 | 77 |
    | 3 | 猪悟能 | 88 | 98 | 90 |
    +----+-----------+-------+--------+--------+

    --- 第二页
    select id,name,yuwen,shuxue,yingyu from exam_result order by id limit 3 offset 3;
    +----+-----------+-------+--------+--------+
    | id | name | yuwen | shuxue | yingyu |
    +----+-----------+-------+--------+--------+
    | 4 | 曹孟德 | 82 | 84 | 67 |
    | 5 | 刘玄德 | 55 | 85 | 45 |
    | 6 | 孙权 | 70 | 73 | 78 |
    +----+-----------+-------+--------+--------+

    --- 第三页,如果结果不足3个,不会对数据有影响
    select id,name,yuwen,shuxue,yingyu from exam_result order by id limit 3 offset 6;
    +----+-----------+-------+--------+--------+
    | id | name | yuwen | shuxue | yingyu |
    +----+-----------+-------+--------+--------+
    | 7 | 宋公明 | 75 | 65 | 30 |
    +----+-----------+-------+--------+--------+

####5.Updata

语法:

1
UPDATE table_name SET column = expr [, column = expr ...]    	[WHERE ...] [ORDER BY ...] [LIMIT ...]

对查询到的接轨进行列值更新

  1. 将孙悟空同学的数学成绩变更为80分

    1
    2
    3
    update exam_result set shuxue=80 where name='孙悟空';
    Query OK, 1 row affected (0.14 sec)
    Rows matched: 1 Changed: 1 Warnings: 0
  2. 将曹孟德同学成绩变更为60分,语文成绩变更为70分

    1
    2
    3
    update exam_result set shuxue=60,yuwen=70 where name='曹孟德';
    Query OK, 1 row affected (0.08 sec)
    Rows matched: 1 Changed: 1 Warnings: 0
  3. 将总成绩倒数前三的3位同学的数学成绩加上30分

    1
    update exam_result set shuxue=shuxue+30 order by shuxue+yuwen+yingyu limit 3;
  4. 将所有同学的语文成绩更新为原来的2倍

    1
    update exam_result set yuwen=yuwen*2;

6.Delete

语法:

1
DELETE FROM  table_name [WHERE ...] [ORDER BY ...] [LIMIT ...]
  1. 删除孙悟空同学的考试成绩

    1
    delete from exam_result where name='孙悟空';
  2. 删除整张表数据

    1
    delete from 表明;
    • 截断表(truncate)

      语法:

      1
      TRUNCATE [TABLE] table_name
      • 注意:该操作慎用
        1. 只能对整张表操作,不能像delete一样针对部分数据操作
        2. 直接全部删除,不关心表中所存储的数据
        3. 会重置auto_increment项

7. 插入查询结果(将一张表查询结果插入到另外一张表中,再重命名即可)

语法:

1
INSERT INTO table_name [(column [, column ...])] SELECT ...
  • 案例:删除表中重复记录,重复的数据值能有一份

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    -- 创建原表数据
    mysql> CREATE TABLE duplicate_table (id int, name varchar(20));
    Query OK, 0 rows affected (0.45 sec)

    -- 插入测试数据
    mysql> INSERT INTO duplicate_table VALUES
    (100, 'aaa'),
    (100, 'aaa'),
    (200, 'bbb'),
    (200, 'bbb'),
    (200, 'bbb'),
    (300, 'ccc');
    Query OK, 6 rows affected (0.09 sec)
    Records: 6 Duplicates: 0 Warnings: 0

    -- 建立一张空表,空表的结构和原表的结构相同
    mysql> CREATE TABLE no_duplicate_table LIKE duplicate_table;
    Query OK, 0 rows affected (0.42 sec)

    -- 将原表去重后的数据插入到新表中
    mysql> INSERT INTO no_duplicate_table SELECT DISTINCT * FROM duplicate_table;
    Query OK, 3 rows affected (0.11 sec)
    Records: 3 Duplicates: 0 Warnings: 0

    -- 通过重命名表,实现原子操作(就不用担心在去重的过程中会有新的数据插入)
    mysql> RENAME TABLE duplicate_table TO old_duplicate_table, no_duplicate_table TO duplicate_table;
    Query OK, 0 rows affected (0.47 sec)

    -- 查看结果
    mysql> SELECT * FROM duplicate_table
    -> ;
    +------+------+
    | id | name |
    +------+------+
    | 100 | aaa |
    | 200 | bbb |
    | 300 | ccc |
    +------+------+
    3 rows in set (0.00 sec)

8. 聚合函数

函数 说明
COUNT([DISTINCT] expr) 返回查询到的数据的数量
SUM([DISTINCT] expr) 返回查询到的数据的总和
AVG([DISTINCT] expr) 返回查询到的数据的平均值
MAX([DISTINCT] expr) 返回查询到的数据的最大值
MIN([DISTINCT] expr) 返回查询到的数据的最小值
  1. 统计班级共有多少学生

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    -- 这里以 * 做统计,不受mull影响
    select count(*) from students;
    +----------+
    | count(*) |
    +----------+
    | 4 |
    +----------+
    1 row in set (0.05 sec)

    -- 使表达式做统计
    select count(10) from students;
    +-----------+
    | count(10) |
    +-----------+
    | 4 |
    +-----------+
    1 row in set (0.01 sec)
  2. 统计班级收集qq号有多少

    1
    2
    3
    4
    5
    6
    7
    8
    -- null不会计入结果
    select count(qq) from students;
    +-----------+
    | count(qq) |
    +-----------+
    | 1 |
    +-----------+
    1 row in set (0.00 sec)
  3. 统计本次考试的数学成绩分数个数

    1
    2
    3
    4
    select count(shuxue) from exam_result;

    统计去重后数学成绩分数个数
    select count(distinct shuxue) from exam_result;
  4. 统计数学成绩总分

    1
    select sum(shuxue) from exam_result;
  5. 统计平均总分

    1
    select avg(shuxue+yingyu+yuwen) from exam_result;
  6. 返回英语最高分

    1
    select max(yingyu) from exam_result;
  7. 返回>70分以上的数学最低分

    1
    select min(shuxue) from exam_result where shuxue>70;

9.group by子句的使用

语法:在select中使用group by子句可以对指定列进行分组查询

1
select column1, column2, .. from table group by column;

案例:

  • 创建一个雇员信息表

    • emp 员工表
    • dept 部门表
    • salgrade 工资等级表
  • 显示每个部门的平均工资和最高工资

    1
    2
    3
    4
    5
    6
    7
    8
    select deptno,avg(sal),max(sal) from emp group by deptno;
    +--------+-------------+----------+
    | deptno | avg(sal) | max(sal) |
    +--------+-------------+----------+
    | 10 | 2916.666667 | 5000.00 |
    | 20 | 2175.000000 | 3000.00 |
    | 30 | 1566.666667 | 2850.00 |
    +--------+-------------+----------+
  • 显示每个部门的每种岗位的平均工资和最高工资

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    select avg(sal),min(sal),job,deptno from emp group by deptno,job;
    +-------------+----------+-----------+--------+
    | avg(sal) | min(sal) | job | deptno |
    +-------------+----------+-----------+--------+
    | 1300.000000 | 1300.00 | CLERK | 10 |
    | 2450.000000 | 2450.00 | MANAGER | 10 |
    | 5000.000000 | 5000.00 | PRESIDENT | 10 |
    | 3000.000000 | 3000.00 | ANALYST | 20 |
    | 950.000000 | 800.00 | CLERK | 20 |
    | 2975.000000 | 2975.00 | MANAGER | 20 |
    | 950.000000 | 950.00 | CLERK | 30 |
    | 2850.000000 | 2850.00 | MANAGER | 30 |
    | 1400.000000 | 1250.00 | SALESMAN | 30 |
    +-------------+----------+-----------+--------+
  • 显示平均工资低于2000的部门和它的平均工资

    1
    2
    3
    4
    5
    6
    7
    8
    -- aving与group by配合使用,对group by结构进行筛选
    select avg(sal) as avg_sal from emp group by deptno having avg_sal<2000;
    +-------------+
    | avg_sal |
    +-------------+
    | 1566.666667 |
    +-------------+
    1 row in set (0.16 sec)

面试题(重要):

  • SQL查询中各个关键字的执行先后顺序

    1
    from > on >join > where > group by > with > having > select > distinct > order by > limit

复合查询(重点)

基本查询回顾

  • 查询工资高于或岗位为manager的雇员,同时满足他们的姓名首字母为大写的J

    1
    select * from emp where (sal>500 or job='manager') and ename like 'J%';
  • 按照部门号升序雇员的工资降序排序

    1
    select * from emp order by deptno,sal desc;
  • 使用年薪进行将序排序 isnuff如果为第一个参数不为空就取第一个参数,为空取第二个参数

    1
    select ename,sal*12+ifnull(comm,0) as '年薪' from emp order by 年薪 desc
  • 显示工资最高的员工名字和岗位

    1
    select ename,job from emp where sal=(select max(sal) from emp);
  • 显示工资高于平均工资的员工信息

    1
    select ename,job from emp where sal>(select avg(sal) from emp);
  • 显示每个部门的平均工资和最高工资 fromat(29999,2)含义是小数点后保留两位

    1
    2
    3
    select deptno,max(sal),avg(sal) from emp group by deptno;

    select deptno, format(avg(sal), 2) , max(sal) from EMP group by deptno;
  • 显示平均工资低于2000的部门和它的平均工资

    1
    select deptno,avg(sal) avg_sal from emp group by deptno having avg_sal<2000;
  • 显示每中岗位的雇员总数,平均工资

    1
    2
    3
    select job,count(job),avg(sal) from emp group by job;

    select job,count(*), format(avg(sal),2) from EMP group by job;

多表查询

  • 显示雇员名,雇员工资以及所在部门的名字因为上面的数据来自emp和dept表,因此要联合查询

    1
    select emp.ename,emp.sal,dept.dname from emp,dept where emp.deptno=dept.deptno;
  • 显示部门号为10的部门名,员工名和工资

    1
    select ename,sal,dname from emp,dept where emp.deptno=dept.deptno and dept.deptno=10;
  • 显示各个员工姓名,工资,以及工资级别

    1
    select ename,sal,grade from emp,salgrade where emp.sal between losal and hisal;

自连接

自连接是指在同一张表连接查询

  • 显示员工ford的上级领导的编号和姓名(mgr是员工领导的编号–empno)

    • 使用的子查询

      1
      select empno,ename from emp where empno=(select mgr from emp where ename='ford');
    • 使用多表查询 – 使用别名将一张表分成两份分别使用

      1
      select leader.empno,leader.ename from emp leader,emp worker where leader.empno=worker.mgr and worker.ename='ford';

子查询

单行子查询
  • 显示smith同一部门的员工

    1
    select ename from emp where deptno=(select deptno from emp where ename='smith');
多行子查询
  • in关键字:查询和10号部门的工作相同的雇员的名字,岗位,工资,部门号,但是不包含10自己

    1
    select ename,job,sal,empno from emp where job in(select job from emp where deptno=10) and deptno<>10;
  • all关键字:显示工资比部门30的所有员工的工资高的员工的姓名,工资和部门号

    1
    select ename,sal,deptno from emp where sal>all(select sal from emp where deptno=30);
  • any关键字:显示工资比部门30的任意员工的工资高的员工的姓名,工资和部门号

    1
    select ename, sal, deptno from emp where sal > any(select sal from emp where deptno=30);
多列子查询
  • 查询和SMITH的部门和岗位完全相同的所有雇员,不含SMITH本人

    1
    select ename from emp where (deptno, job)=(select deptno, job from emp where ename='smith') and ename<>'smith'

在from子句中使用子查询

  • 显示高于自己部门平均工资的员工的姓名、部门、工资、平均工资

    • 将子查询当做成一个临时表
    1
    2
    3
    select ename,deptno,sal,avg(sal) from emp,
    (select avg(sal) avg_sal,deptno dt from emp group by deptno) tmp
    where emp.sal>tmp.avg_sal and emp.deptno=tmp.dt;
  • 显示每个部门的信息(部门号,编号,地址)和人员数量

    • 使用多表查询
    1
    select dept.dname,dept.deptno,dept.loc,count(*) from emp,dept where emp.deptno=dept.deptno group by dept.deptno;
    • 使用子查询 – 将emp表当做成临时表去使用
    1
    select dept.dname,dept.deptno,dept.loc,mycount from dept,(select count(*) mycount,deptno from emp group by deptno) tmp where dept.deptno=tmp.deptno;

合并查询

  • union 该操作符用于取得两结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行。

    • 将工资大于2500或职位为manager的人找出来

      1
      2
      select ename,sal,job from emp where sal>2500 union
      select ename,sal,job from emp where job='manager';
  • union all 该操作符取得两个结果的并集。当使用该操作符时,不会去掉集中的重复行

    • 将工资大于2500或职位为manager的人找出来

      1
      2
      select ename,sal,job from emp where sal>2500 union all
      select ename,sal,job from emp where job='manager';

在SQL语句中 || 用于连接两个字符串

表的内连和外连

内连接

  • 语法:

    1
    select 字段 from 表1 inner join 表2 on 连接条件 and 其他条件
  • 显示smith的名字和部门名称

    1
    select ename,dname from emp inner join dept on emp.deptno=dept.deptno and ename='smith';

外连接

外连接分为左外连接和右外连接

#####左外连接

  • 语法:

    1
    select 字段名 from 表明1 left join 表明2 on 连接条件
  • 案例:

    1
    2
    3
    4
    -- 建两张表 
    create table stu (id int, name varchar(30)); -- 学生表
    insert into stu values(1,'jack'),(2,'tom'),(3,'kity'),(4,'nono'); create table exam (id int, grade int); -- 成绩表
    insert into exam values(1, 56),(2,76),(11, 8);
    • 查询所有学生的成绩,如果这个学生没有成绩,也要将学生的个人信息显示出来

      1
       

数据类型

数据类型分类


数值类型

类型 字节 最小值 最大值
(带符号/无符号) (带符号/无符号)
TINYINT 1 -128(-2^7) 127(2^7-1)
0 255(2^8-1)
SMALLINT 2 -32768(-2^15) 32767(2^15-1)
0 65535(2^16-1)
MEDIUMINT 3 -8388608 8388607
0 16777215
INT 4 -2147483648 2147483647
0 4294967295
BIGINT 8 -9223372036854775808 9223372036854775807
0 18446744073709551615
  • 在MySQL中,整形是可以指定是有符号和无符号,默认是有符号的。

  • 通过UNSINGNED来表明字段是无符号的

    1
    mysql> create table tt2(num tinyint unsigned);
    • 注意:尽量不要使用unsingned,对于int来兴可能放不下的数据,unsingned int同样可能存放不下,还不如在设计时将int提升为bigint类型,但是对于优化器而言,unsingned确实有一定的用处,比如当你要查询的数据为负数时,unsingned会直接返回,并不会进行查询。

bit类型

  • 语法:

    1
    bit[(M)]:位字段类型。M表示每个值的位数,范围从1到64.如果M被忽略,默认为1

    范例:

    1
    mysql> create table tt2 (id int,a bit(64));
  • bit字段在显示时,是按照ASCII码对应的值显示。

    1
    2
    3
    4
    5
    6
    7
    8
    mysql> insert into tt2 values(65,65);
    Query OK, 1 row affected (0.11 sec)
    mysql> select * from tt2;
    +------+----------+
    | id | a |
    +------+----------+
    | 65 | A |
    +------+----------+
  • 如果我们只需要在表中存放0或1,这时就可以定义bit(1)。这样就可以节省空间。

    1
    mysql> create table tt3 (a bit(1));
  • 使用十进制形式查看表中数据

    • 语法:
    1
    select 字段名称+0 from 表名称:
    1
    2
    3
    4
    5
    6
    mysql> select a+0 from tt2;
    +------+
    | a+0 |
    +------+
    | 65 |
    +------+
  • 使用其它方式产看表中数据

    • 二进制查看表中数据
    1
    select bin(字段名称) from 表名称;
    • 八进制查看表中数据
    1
    select oct(字段名称) from 表名称;
    • 十六进制查看表中数据
    1
    select hex(字段名称) from 表名称;

    注意:使用以上函数查看表中数据存储的数据类型时,是不能食用引索查询的

小数类型

float:
  • 语法:

    1
    float[(m,d)][unsigned]:m定点数指定显示长度,d指定小数位数,占用空间4个字节
    • float(4,2)表示的范围是:-99.99~99.99,MySQL在保存值会进行四舍五入

      1
      mysql> create table tt4 (id int,a float(4,2) unsigned);

关于符号问题:

  • 当将数据定义为float(4,2) unsingned时,由于是无符号,范围是0~99.99

  • 当向表中插入一个负数时,比如-0.1,就会直接保存(超出范围),而不是进行四舍五入

    • 但是当我们要强制将数插入到表中时,可以使用如下语法:
    1
    insert ignore into 表名称 values(id:100,data:-0.1);

    范例:

    1
    2
    3
    4
    5
    6
    7
    8
    mysql> insert ignore into tt4 values(100,-0.1);
    Query OK, 1 row affected, 1 warning (0.14 sec)
    mysql> select * from tt4;
    +------+------+
    | id | a |
    +------+------+
    | 100 | 0.00 |
    +------+------+
decimal:
  • 语法:

    1
    decimal(m,d) [unsigned]:定点数m指定长度,d指定小数位数

decimal与float的区别

  • 精度不一样:float表示的进度大约是小数点后7位,高于7位就有可能造成进度的丢失(关于精度丢失,由于二进制小数点一直不能乘到1,小数点后面总会有数字),decimal表示的精度更加准确,M最大为65,D最大为30,它是将数值以字符串的形式存储的。
    • 建议:如果希望小数的精度高,推荐使用decimal

字符串类型

char:
  • 语法:

    1
    char(L):固定长度字符串,L是可以存储的长度,单位为字符,最大长度可以为255
    • 注意:char最多规定255个字符长度
varchar:
  • 语法:

    1
    varchar(L):可变长度字符串,L表示字符长度,最大长度65535个字节

    关于varchar(len),len到底为多大,该len值和标的编码密切相关:

    • varchar长度可以指定为0~65535之间的值,但是其中有1~2个字节用于记录数据大小

    • 当使用表的编码是utf8时,varchar(n)的参数n最大值为65535/2=21844(因为utf8中,每个每个字符占用3个字节),如果编码为gbk,varchar(n)的参数n最大值为65535/2=32766(在gbk编码中,一个字节占用2个字节)。

      1
      mysql> create table tt6(name varchar(21844)) charset=utf8;
char和varchar比较
实际存储 char(4) varchar(4) char占用字节 varchar占用字节
abcd abcd abcd 4*3=12 4*3+1=13
A A A 4*3=12 1*3+1=4
Abcde × × 数据超出长度 数据超出长度

如何选择定长或变长字符串?

  • 如果数据长度都一样,就使用定长(char),比如:身份证,手机号,md5

    • md5是一种加密方法,加密后的数据是不能逆行使用的,也就是说要查看用户密码信息,需要通过数据查询匹配来实现

      1
      2
      3
      4
      5
      6
      7
      如果数据库表User中有一列为passwd,存放的是md5加密的数据,如何更新新的数据。
      update user set passwd=md5("123321") where uName="lihua";
      插入新的数据:
      insert into user(uName,passwd) values("xiaoqiang",md5("123321")) ;
      这样存放在数据中的密码信息就是保密存放的,但是通过md5加密后的数据是不能逆向使用的,也就是说如果查看用户的密码信息,则需要通过数据查询匹配来实现。
      比如需要进行用户身份认证,则需要执行下面查询语句:
      select * from user where uName="lihua" and passwd=md5("123321");
  • 如果数据有变化,就用变长(varchar),比如:名字,地址,但是你保证最长的数据能够存进去

  • 定长的磁盘空间比较浪费,但是效率高

  • 变长的磁盘空间比较节省,但是效率低

注意:temp会根据varchar最大长度去创建临时表

反射

反射机制

初识反射

反射:指的是对象的反响处理。根据对象倒退类的组成

  • 在默认情况下,先要导入一个包,而后才能产生类的实例化对象,处理流程根据包名.类名找到类

  • 而这里的“反”就是对象来取得对象的来源信息,反射的核心类Class类(专门描述其他类的组成)

    • Objectlei中取得对象的class对象

      1
      public final native Class<?> getClass();
      • Class描述接口与类的组成,Class对象由JVM在第一次加载类的产生,并且全局唯一

      范例:

      1
      2
      3
      4
      5
      6
      public class mytest {
      public static void main(String[] args) throws ClassNotFoundException{
      Class<?> cls=Class.forName("java.util.Date");
      System.out.println(cls.getName());
      }
      }

在反射的世界中,看重的不再时一个对象,而是对象身后的组成(类,构造,普通,成员等)

取得任意类Class对象的三种方法:

  1. 调用对象 .getClass()取得Class对象,由Object类提供
  2. 类名调用 .class取得Class对象
  3. Class.forName(类全名称)取得Class对象
  • 在三中方法中只有第一种方法会产生类的实例化对象之外,其他两种均不会产生实例化对象。取得Class对象,可以通过反射实例化对象,在Class类中定义如下方法:

    1
    2
    public T newInstance()
    throws InstantiationException,IllegalAccessException

    范例:反射实例化对象

    1
    2
    3
    4
    5
    6
    7
    public class mytest {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
    Class<?> cls=Class.forName("java.util.Date");
    Object obj=cls.newInstance(); //实例化对象,等价于new java.util,Date()
    System.out.println(obj);
    }
    }

反射与工厂模式

  • 在传统的工厂模式中,每当我们增加一个接口的子类都需要去修改工厂类,为了解决该类问题,可以通过反射来处理,Class类可以使用newInstance()实例化对象,而Class.for通过Name()能够接收类名称。这样一来就避免了直接实例化(new)带来的问题,而是直接通过接收类名去实例化对象,避免了对工厂类的频繁修改。

    范例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    /**
    * 反射与工厂模式
    */
    package www.Dyson;
    interface IFruit{
    public void eat();
    }
    class Orange implements IFruit{
    @Override
    public void eat() {
    System.out.println("eat an orange");
    }
    }
    class Apple implements IFruit{
    @Override
    public void eat() {
    System.out.println("eat an apple");
    }
    }
    class fruitFactroy{
    //使用static修饰,当主方法中调用该方法通过反射实例化一个对象时
    //使用过静态方法调用的,所以getInstance方法必须也是静态方法,否则
    //会报无法从静态上下文中引用非静态方法的错误
    public static IFruit getInstance(String fruitName){
    IFruit iFruit=null;
    try {
    //通过.forName取得class对象
    Class<?> cls=Class.forName(fruitName);
    try {
    //通过反射取得实例化对象
    iFruit = (IFruit) cls.newInstance();
    } catch (InstantiationException e) {
    e.printStackTrace();
    } catch (IllegalAccessException e) {
    e.printStackTrace();
    }
    } catch (ClassNotFoundException e) {
    e.printStackTrace();
    }
    return iFruit;
    }
    }
    public class ReflectAndFactory {
    public static void main(String[] args)throws Exception{
    IFruit iFruit=fruitFactroy.getInstance("www.Dyson.Apple");
    iFruit.eat();
    }
    }

    在主方法中调用该方法通过反射实例化一个·对象,而主方法是一个静态方法,所以该方法也必须为静态方法。


反射与类操作

取得父类&父接口信息

  • 取得类的包名称:

    1
    public Package getPackage();
  • 取得父类的Class对象:

    1
    public native Class<? super T> getSuperclass();
  • 取得实现的父接口:

    1
    public Class<?>[] getInterfaces();

通过反射可以取得类结构上的所有关键信息

以上所有父类:只能取得子类直接继承的父类

反射与构造方法 – Constructor(描述类构造方法信息)只能取得本类中的构造方法,与集成无关

取得类中指定参数构造方法
  • 取得类中权限只有public的指定构造方法

    1
    public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
  • 取得类中任意权限的指定构造方法

    1
    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
取的类中所有构造方法 —— 重要 ——
  • 取得类中所有权限为public的构造方法

    1
    public Constructor<?>[] getConstructors() throws SecurityException
  • 取得类中所有权限构造方法

    1
    public Constructor<?>[] getDeclaredConstructors() throws SecurityException

Class类的newInstance()实际调用的是类中的无参构造。若类中没有无参构造或者权限不是public,则无法使用Class类调用!!!

  • Constructor类的如下方法

    1
    public T newInstance(Object ... initargs) throws InstantiationException, IllegalAccessException,               IllegalArgumentException, InvocationTargetException
    • 如果类中没有无参构造,则只能调用Constructor类提供的newInstance方法使用有参构造来实例化对象

    范例:通过Constructor类实例化对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package Dyson;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    class Person_p{
    private String name="Dyson";
    private int age=18;
    public Person_p(String name, int age) {
    this.name = name;
    this.age = age;
    }
    @Override
    public String toString() {
    return "Person{" +
    "name='" + name + '\'' +
    ", age=" + age +
    '}';
    }
    }
    public class mytest {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    Class<?> cls=Person_p.class;
    Constructor<?> con=cls.getConstructor(String.class,int.class);
    System.out.println(con.newInstance("Dyson",18));
    }
    }

反射调用普通方法(核心 – Method)—— 重要 ——

取得类中指定名称与参数的普通方法
1
public Method getMethod(String name,Class<?>... parameterTypes)
1
public Method getDeclaredMethod(String name,Class<?>... parmeterTypes)
取得类中所有普通方法
  • 取得本类以及父类所有权限为public的普通方法(包含静态方法)

    1
    public Method[] getmethods() throws SecurityException
  • 取得本类中所有方法(包含private方法)

    1
    public Method[] getDeclaredmethods() throws SecurityException
  • 调用 – 通过invoke可以取得设置属性信息,不再局限于某一个具体的对象,而是可以通过Object类型进行对所有类的方法调用。

    1
    2
    3
    4
    5
    6
    /**
    *反射调用普通对象
    *obj类的实例化对象
    *args普通方法参数
    */
    public Object invoke(Object obj, Object... args)throws IllegalAccessException, IllegalArgumentException,InvocationTargetException

反射调用类中属性(Field)

取得类中指定名称属性
1
public Field getField(String name)     父类
1
public Field getDeclaredField(String name)     本类
取得类中所有属性 – 同Meyhod一样
1
public Field[] getFields throws SecurityException()     父类
1
public Field[] getDeclaredFields throws SecurityException()     本类
设置与取得值 – 属性值为public
  • 设置属性值

    1
    2
    3
    4
    5
    /**
    *obj类的实例化对象
    *value所有设置的值
    */
    public void set(Object obj, Object value)
  • 取得属性值

    1
    2
    3
    4
    /**
    *obj类的实例化对象
    */
    public Object get(Object obj)
  • 取得属性值

    1
    public Class<?> getType()
动态破坏封装(反射特性)可以处理private权限的属性 – 只是在一次JVM进程中破坏,且只能通过反射来调用
  • Constuctor,Method,Field类都继承AccessibleObject类,此类中只有一个破坏的方法

    1
    public void setAccessible(boolean flag) throws SecurityException

    范例:通过反射操作private权限的属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package Dyson;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    class Person_p{
    private String name;
    }
    public class mytest {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
    Class<?> cls=Class.forName("Dyson.Person_p");
    //实例化对象
    Object obj=cls.newInstance();
    //取得要操纵的属性
    Field field=cls.getDeclaredField("name");
    //破坏封装
    field.setAccessible(true);
    //设置属性内容
    field.set(obj,"Dyson");
    //取得属性
    System.out.println(field.get(obj));
    }
    }