1.Tcp网络编程步骤:
客户端步骤:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;
public class ClientDemo {
public static void main(String[] args) throws IOException {
//1:创建客户端的套接字
Socket s = new Socket("localhost", 10086);
//2:获取管道输出流并转换成高效字符缓冲输出流
//第1层:字符缓冲输出流
//第2层:字符转换输出流
//第3层:字节输出流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//3 获取输入流
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名");
String userName = sc.nextLine();
System.out.println("请输入密码");
String password = sc.nextLine();
//3:写数据
bw.write(userName);
bw.newLine();
bw.write(password);
bw.flush();
//bw.close(); //切记,缓冲流写数据,需要刷空!!!
//告诉服务器。客户端这边数据写入完毕
s.shutdownOutput();
//4:读取从服务器响应的数据
String info = br.readLine(); //阻塞式方式
System.out.println(info);
//4:关闭套接字
s.close();
}
}
服务端步骤:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class ServerDemo {
public static Map<String, String> getUserInfo() throws FileNotFoundException, IOException{
//创建属性集合列表(配置文件)
Properties p = new Properties();
//将文件内容加载到属性集合列表中,该方法的(参数是 Reader r)
p.load(new FileInputStream("user.properties"));
String userName = p.getProperty("userName");
String password = p.getProperty("password");
Map<String, String> map = new HashMap<>();
map.put("userName", userName);
map.put("password", password);
return map;
}
public static void main(String[] args) throws IOException {
//1:创建服务器端套接字
ServerSocket ss = new ServerSocket(10086);
//2:监听客户端的套接字
Socket s = ss.accept();
//3:获取输入流
//第1层:字符缓冲输入流
//第2层:字符转换输入流
//第3层:字节输入流
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//4:获取输出流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//4:读取数据
String userName = br.readLine();
String password = br.readLine();
//5:判断用户名和密码
//获取正确的用户名和密码
Map<String, String> map = getUserInfo();
System.out.println(userName);
System.out.println("--------------------------");
if(map.get("userName").equals(userName)&&map.get("password").equals(password)){
//需要将响应返回给客户端
//System.out.println("登陆成功");
bw.write("登陆成功");
bw.flush();
}else{
bw.write("登陆失败");
bw.flush();
}
//5:关闭客户端套接字
s.close();
}
}
2.反射的核心思想:
就是获取类的字节码文件对象,-------字节码文件对象获取类的构造方法并且创建该类的对象
字节码文件获取类的成员方法,并去调用
字节码文件对象获取类的成员方法,并去调用
3.面试题:(获取一个类的字节吗文件对象的方式)
反射思想:
1) 通过类加载器--------效验语法以及类的初始化,获取类的字节码文件对象(Class.forName())
2)通过字节码文件对象
获取这个类的构造器的类Constructor,创建实例,使用方法
获取这个类的成员方法的Method类,调用成员方法
获取这这个类的成员变量Field,可以给成员变量赋值
1)Object类的getClass()方法:任意java类对象.getClass()----Class
2)任意Java类型的. Class属性 类名.Class---
3)Class就是代表正在运行的类字节码文件,使用静态方法
public static Class<?> forName(String className) throws ClassNotFoundException 在开发中常用的
**注意:**
参数:必须是当前类的"全限定名称" 包名.类名
使用: Class.forName("包名.类名");
反射的基本使用:(代码示例)
public class Test_main {
public static void main(String[] args) throws Exception{
//获取类的字节码文件
Class s = Class.forName("com.qf_01.day2.test_1.Person");
//获取当前类的构造器对象(获取公共的构造方法)
Constructor con = s.getConstructor();
//创建实例对象
Object obj = con.newInstance();
System.out.println("----------------给公共字段赋值--------------------------");
//通过字节码文件获取公共字段在File类中的实例对象
Field nameFile = s.getField("name");
nameFile.set(obj,"公共的字段");
System.out.println(obj);
System.out.println("--------------给私有的字段赋值(默认字段一样)-----------------");
//通过字节码文件给非公共的字段赋值
Field ageFile = s.getDeclaredField("age");
ageFile.setAccessible(true); //抑制java语言访问检查
ageFile.set(obj,45);
System.out.println(obj);
System.out.println("--------------带参数的非公共的构造方法-----------------------");
//获取指定的非公共的有参构造
Constructor dcon = s.getDeclaredConstructor(String.class, int.class, String.class);
dcon.setAccessible(true); //抑制java语言访问检查
//实例有参的对象
Object obj1 = dcon.newInstance("私有", 11, "有参构造");
System.out.println(obj1);
System.out.println("---------------调用类的私有成员方法------------------");
//获取当前类公共成员方法在Method类中的实例
Method showMethod = s.getMethod("show");
//通过invoke()方法掉用show();
showMethod.invoke(obj);
System.out.println("---------------调用非公共的有参数的成员方法---------------------");
//获取当前类非公共的成员方法在Method中的实例
Method funMethod = s.getDeclaredMethod("fun", String.class);
funMethod.setAccessible(true); //抑制java 访问检查
//通过invoke()调用fun()方法
funMethod.invoke(obj1,"我是私有的有参的成员方法");
}
}
java–jdk动态代理:
代理模式:-----代理角色帮助真是角色 增强功能!
静态代理:
动态代理:
jdk动态代理:jdk提供java.lang.reflest.Proxy
特点:在程序执行过程中就产生了代理,前提必须有一个接口!
cglib动态代理
/* 代理设计模式---->结构型设计模式
* 代理描述:让代理角色帮助真实角色完成一件事情!(业务代码)
* 静态代理
* 代理角色和真实角色必须同一个接口!
* 创建线程的方式第二种:自定义一个类 实现Runnable接口
* class Thread implements Runnable
*
* 弊端:结构组成太繁琐,真实角色,代理角色,必须实现同一个接口
* 动态代理
* jdk动态代理:jdk提供的java.lang.reflet.Proxy
* 前提:必须有一个接口
* 通过反射的方式直接将接口的方法进行调用,然后产生代理对象
*
* / 静态方法:
//java.lang.reflect.Proxy静态方法
/* public static Object newProxyInstance(
ClassLoader loader, 当前代理实例要实现接口的类加载器
Class<?>[] interfaces, 当前代理实例要实现的接口列表的字节码文件对象
InvocationHandler h) 代理实例调用接口的方法的处理程序!
这个类型接口--->里面需要将增强代码和业务代码分离
throws IllegalArgumentException*/
* //给代理实例的处理程序---调用接口中的方法,对真实角色的方法进行增强!
*/
public class MyInvocationHandler implements InvocationHandler {
//要对哪个真实角色产生代理
private Object target ;
public MyInvocationHandler(Object target){ //测试类中 UserDao ud = new UserDaoImpl() ;
this.target = target ;
}
//第一个参数:代理实例
//第二个参数:要调用真实角色的里面method------>add()/update()/delete()/select()
//第三个参数:调用方法里面传递实际参数,如果没有参数不用传参
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("权限校验");
//调用方法
Object obj = method.invoke(target, args); //真实角色的方法的调用
System.out.println("产生日志");
return obj;
}
}
数据库的基本操作语句:
** DDl语句:----针对数据库的操作:**
1)查询MySQL服务端默认的数据库有哪些:
show databases; //查询有哪些库
2)创建数据库:
方式一:create database 库名; //直接创建
方式二:create database if not exists 库名;如果没有这个库则直接创建
3)查询指定数据库的信息
show create database 库名;
4)修改数据库默认的字符集
alter database 库名 default character set 字符集名称;
5)删除库;
方式一: drop database 库名; //直接删除
方式二:drop database if exists 库名;
关于MySQL中常见的字段类型:
varchar(字符长度) : 字符串------类似于java中的String
int :整数类型-----默认里面字符长度int(11) ,
double : 小数类型
double(值1 ,值2 ):
举例: double(4,2);表示小数类型4位数,保留2位小数
data:日期类型:-----"2208-8-23"
datatime:日期时间类型: ----"2208-8-23 16:02:50"
timestaple:时间戳 :
比如网购中下单的一瞬间的时间:
** DDl语句针对表的操作:**
1) 创建表之前需要指定在那个库操作:
use 库名 ;------选择指定的库(如果没有指定默认创建在MySQL库中
2)创建表的 语法:
create table 表名(
字段名称1 字段类型 ,
字段名称2 字段类型,
字段名称3 字段类型
);
3)查询库中有哪些表:
show tables;
4)查询表的结构
desc 表名;
** 修改表:**
1)修改表的字段名称
alter table 表名 change 旧的字段名称 新的字段名称 以前的字段类型
2)修改表的字段类型;
alter table 表名 modify 字段名称 新的数据类型;
3)给表添加一个新的字段
alter table 表名 add 字段名称 字段类型
4)给表删除一个字段
alter table 表名 drop 字段名称;
5)给表名重命名
alter table 以前的表名 rename to 新的表名;
6)复制一张一模一样的表
create table 新表名 like 以前的表名;
7)删除表
drop table 表名;
DML语句针对表的数据(记录)的操作语句:
给表插入数据
语法1:插入全表数据
insert into 表名 values(值1, 值2, 值3....);
语法2:插入全表数据,一次插入多条
insert into 表名 values(值1, 值2, 值3....) ,(值1, 值2, 值3....),(值1, 值2, 值3....);
语法3:插入部分字段,没有插入的字段的值,默认值是null(没有值),
insert into 表名(字段1, 字段二, 字段三...) values(值1, 值2, 值3...)
语法4:插入部分字段,也可以一次插入多条
insert into 表名 (字段1, 字段2.....部分字段) values(值1, 值2,....部分值), (值1, 值2,....部分值)..
插入表的记录语法注意事项
1)如果是插的全表记录,字段值一定 要和类型匹配
2)插入全表数据(或插入部分字段),字段值得总数量和值的总数量要对应上
修改表记录 (带条件修改)
语法1:带条件修改(修改单个字段)
update 表名 set 字段名1 = 值1 where 字段名2 = 值2 ;
语法2:带条件修改(修改多个字段)
update 表名 set 字段名称1 = 值1, 字段名称2 = 值2,...where 字段名称1 = 值1 ,字段名称2 = 值2,...
语法2:不带条件修改多个字段: (很少用)
update 表名 set 字段名 = 值; (将该字段的所有值都修改)
语法3:带条件修改: 条件和条件中间 and (并列关系)
update 表名 set 字段名 = 值 where 字段名1 = 值1 and 字段名2 = 值2;
删除表的记录
语法1:带单个条件删除
delete from 表名 where 字段名 = 值;
语法2:带多个条件删除
delete from 表名 where 字段名 = 值 and 字段2 = 值2;
语法3:不带条件,删除全部数据(表的结构并没有删除)
delete from 表名;
delete from 表名 和 truncate table 表名 有什么区别?(面试题)
前者:
1)仅仅只是删除全部数据,表还是存在的(表的结构还是存在的,;)
2)针对非业务字段id 一般:主键(非空且唯一))自增长约束(字段会在上一次的基础上自动增加1)
后者:
1)它不仅仅是删除全表数据,还会将表结构删除,并重新生成一张新表
2)直接影响自增长主键 id值
查询表的记录:
1)基本查询:
查询表的所有记录:
select * from 表名;
– 2)查询全表字段,给定别名 as ,as可以省略
SELECT
id AS ‘学号’,
NAME AS ‘姓名’,
gender AS ‘性别’,
age AS ‘年龄’,
address AS ‘地址’,
birthday AS ‘出生日期’
FROM
student ;
– as可以省略
3)查询指定字段
select 字段名称…from 表名 ;
4)字段求和(字段类型一致)
– 需求:查询所有学生的姓名以及他们的对应的总成绩信息
SELECT
NAME ‘姓名’,
(math+english) ‘总成绩’
FROM
student2;
– 问题:数学成绩有内容的,英语成绩null ,两个相加的结果 null
– 如果null值,mysql有自己的函数,ifnull(字段名称,给一个值); 如果这个字段名称是null,给一个固定值
Dql语句,带条件查询:
– 基本格式:select 字段列表 from 表名 where 条件;
– 条件:1)里面使用基本运算符,比较运算符:<,>=,<=,==,!=(mysql的不等于可以使用这个<>)
– 赋值运算符: =
– 逻辑运算符:java中的&&,|| mysql提供:and并列 , or或
– 某个范围的数据: 字段名称>=值1 and 字段名称<=值2;
– mysql 提供了between 值1 and 值2;
– 查询或的关系:字段名称 = 值1 or 字段名称= 值2 or 字段名称= 值3
– mysql提供:字段名称 in(值1,值2,值3…):相当于字段名称是值1或值2的或者值3的
– 2)模糊查询 like
– 3)聚合函数
– 4)排序查询
– 5)分组查询
– 6)筛选查询
– 7)分页查询
– 需求1:查询学生的年龄是20岁的所有学生信息
SELECT
*
FROM
student2
WHERE
age = 20 ;
– 需求2:查询学生的年龄大于25的学生学号,姓名,年龄,性别,和住址信息
SELECT
id ‘学号’,
NAME ‘姓名’,
age ‘年龄’,
sex ‘性别’,
address ‘住址’
FROM
student2
WHERE age > 25 ;
– 需求3:查询学生年龄在18到22之间所有的学生信息
– 方式1:
SELECT
*
FROM
student2
WHERE
age >= 18 && age <= 22 ;
– 方式2
SELECT
*
FROM
student2
WHERE
age >= 18 AND age <= 22 ; – Mysql提供多个条件并列是and关键字
– 方式3:mysql提供 字段名称 between 值1 and 值2 ; 两者之间
SELECT
*
FROM
student2
WHERE
age
BETWEEN 18 AND 22 ;
– 需求4):查询学生年龄在是18岁或者22岁或者55岁的学生的所有信息
– 方式1:—java的||
SELECT
*
FROM
student2
WHERE
age = 18 || age = 22 || age = 55;
– 方式2: mysql提供表示或的关系的关键字:or
SELECT
*
FROM
student2
WHERE age = 18
OR age = 22
OR age = 55 ;
– 方式3:mysql提供的语法
– select 字段列表 from 表名 字段名称 in(值1,值2,值3…值n): 和上面的等价
SELECT
*
FROM
student2
WHERE age IN (18, 22, 55) ;
– 需求:查询学生年龄不是20岁的学生的信息
SELECT
*
FROM
student2
WHERE
age !=20 ;
– mysql提供不等于<>
SELECT
*
FROM
student2
WHERE
age <> 20 ;
– 需求:查询英语成绩不是null的学生所有信息
/*
select
*
from
student2
where
english != null ;-- 查询某个字段不等于null, mysql语言中 is not null
/
SELECT
*
FROM
student2
WHERE
english IS NOT NULL ;
– 查询某个字段为null的学生所有信息 mysql 某个字段为null 字段名称 is null
/
SELECT
*
FROM
student2
WHERE
english = null ;
*/
SELECT
*
FROM
student2
WHERE
english IS NULL ;
– 2)模糊查询 like
– select 字段列表 from 表名 where 字段名称 like ‘模糊的语法’ ;
– a) like 后面使用 ‘%其他字符%’ 或者 ‘字符%’ %:代表任意多个或者单个字符(开发中模糊搜索最频繁的)
– b)_: 一个下划线 代表单个字符,
– 需求:查询学生中所有姓马的学生所有信息
SELECT
*
FROM student2
WHERE
NAME
LIKE ‘%马%’ ;
– 或者下面
SELECT
*
FROM student2
WHERE
NAME
LIKE ‘马%’ ;
– 查询学生中姓名是三个字符的学生所有信息
SELECT
*
FROM
student2
WHERE NAME LIKE ‘___’ ;
– 查询mysql数据库中的所有带character 字符集的变量信息;
SHOW VARIABLES LIKE ‘%character%’ ;
– 全部变成utf8—>如果要在dos出窗口去查询表的数据,就出现乱码
– 在dos窗口临时更改character_set_client /character_set_results/character_set_server改成gbk就可以在dos展示中文
– 3)聚合函数 —>查询出的结果是一个单行单列的数据
– count(非业务字段:比如id字段,在实际开发中非空并且唯一) 查询(统计)总条数
– count(业务字段:比如 具体信息字段):可能是null值,不会统计
– avg(字段名称):求平均分
– max(字段名称):求这列的最大值
– min(字段名称):求这列最小值
– sum(字段名称):这列求和
– 需求:统计student2表总记录数
/*
select
count(english) ‘总条数’ – english 业务字段,可能有null,不会统计这条数据
from
student2 ;
*/
SELECT
COUNT(id) ‘总条数’
FROM
student2 ;
– 需求:查询数学成绩最高分
SELECT
MAX(math)
FROM
student2; – 99分
– 需求: 查询数学成绩最高分的学生信息
– 1)查询数学最高分
– select max(math) from student2;
– 2)查询出数学成绩是99分的学生信息
– select * from student2 where math = 99 ;
– 类似于Java中 int a= 20 ; int b = 30 ; int x = a+b ;
SELECT
*
FROM
student2
WHERE math =
(SELECT MAX(math) FROM student2);
– 需求:查询数学平均分
SELECT AVG(math) FROM student2;
– 需求:查询出数学成绩大于数学平均成绩的学生信息;
– 1)先查询出来数学平均分是多少
– SELECT AVG(math) FROM student2;
– 2) 查询数学成绩大于 79.5的学生所有信息
/*
select
*
from
student2
where
math > 79.5;
*/
SELECT
*
FROM
student2
WHERE math >
(SELECT
AVG(math)
FROM
student2) ;
– select 嵌套 select 执行效率非常低!
– 需求:求出 英语总成绩,(求和)
SELECT SUM(IFNULL(english,0)) ‘英语总成绩’ FROM student2;
INSERT INTO student2 VALUES(9,‘高圆圆’,43,‘女’,‘西安’,97,87) ;
– 4)排序查询 order by
– select 字段列表 from order by 字段名称 排序规则;
– 排序规则不写:默认asc 升序 ,desc 降序
– 需求: 按照学生成绩的升序排序
SELECT
*
FROM
student2
ORDER BY
math DESC ; – 字段名称后面没有携带排序规则,默认就是asc升序
– 需求:数学成绩大于70的学生,按照学生成绩升序排序
– 携带条件排序,where条件必须放在order by 前面,不能放后面
SELECT
*
FROM
student2
WHERE math > 70
ORDER BY
math ASC ;
– 针对多个字段同时排序,前面字段如果它的值相同,应该按照后面的字段来进行排序
– select 字段列表 from order by 字段名称 排序规则,字段名称2 排序规则2… ;
– 需求:学生的数学成绩降序排序,英语成绩升序排序,查询所有学生信息
SELECT
*
FROM
student2
ORDER BY
math DESC , – 数学降序排序
english ASC ; – 英语升序排序
– 5)分组查询 group by
– 基本语法: select 字段列表 from 表名 group by 分组字段;
– 分组字段可以在select后面查询的 ,group by的后面不能使用聚合函数
– 需求:按照性别分组,统计每个组的总人数
SELECT
sex ‘性别’,
– 查询分组字段
COUNT(id) ‘总人数’
FROM
student2
GROUP BY sex ;-- 按性别分组
– 带条件进行分组查询, where关键字,还有group by 关键字
– goup by 后面不能使用where条件,where 条件必须放在group by 前面,先满足条件,再参与分组
– 需求:按照性别分组,统计每个组的总人数,条件:数学成绩不大于70分的不参与分组
– 错误的写法
/*
select
sex ‘性别’,
count(id) ‘总人数’
from
student2
group by sex
where math > 70 ;
*/
SELECT
sex ‘性别’,
COUNT(id) ‘总人数’
FROM
student2
WHERE math > 70
GROUP BY sex ;
– 6)筛选having
– where,group by,having :先是满足条件,然后参与分组,在进行筛选!
– 在上面的基础上
– 需求
– 按照性别分组,统计每个组的总人数,条件:数学成绩不大于70分的不参与分组, 筛选出总人数大于2的这一组
SELECT
sex ‘性别’,
COUNT(id) ‘总人数’
FROM
student2
WHERE math > 70
GROUP BY sex
HAVING COUNT(id)>2 ;
– 上面这个格式优化
SELECT
sex ‘性别’,
COUNT(id) 总人数 – 别名
FROM
student2
WHERE math > 70
GROUP BY sex
HAVING 总人数 > 2 ;
SELECT * FROM student2;
分页查询:(limit)
MySQL 数据库的分页查询关键字 limit
语法格式:
select 字段名称 from 表名 limit 起始行数, 每页显示的条数
起始行数-- 从0开始
起始行数 = (当前页码-1) * 每页显示的条数
-- 现在每页显示两条, 查询第一页数据
SELECT * FROM student2 LIMIT 0,2;
-- 查询第二页数据
SELECT * FROM student2 LIMIT 2,2;
-- 查询第三页数据
SELECT * FROM student2 LIMIT 4,2;
-- 查询第四页数据
SELECT * FROM student2 LIMIT 6,2;
数据库的备份和还原:(两种方式)
方式一:
图形界面化方式,直接借助工具
备份:
鼠标选中备份的库名,右键----->backup/export—>选中sql脚本 backupdatabase as sql dump
最上面选中 structure and data 以及选中备份本地磁盘路径—执行即可
还原:新建库–>use 库名----->在库上面右键—>import导入---->选中要执行的sql脚本文件
方式二:
命令行的方式去备份和还原:
备份:
以管理员的身份进入dos窗口,不需要登录MySQL
输入指令mysqldump -uroot -p密码 要备份的库名 > 指定本地磁盘上的路径D:-------\指定sql脚本文件名称,sql
还原:
在dos窗口,要登录MySQL
mysql -uroot -p ----回车 输入密码
登录成功之后
将之前的库删除
新建库
使用库
source 本地磁盘上的备份的sql脚本文件,自动会执行!
–数据库的约束
约束是操作数据库的一种行为,插入null,重复数据(非业务字段) 等等---(不符合规范的数据)
1)默认约束 default
2)非空约束 not null
3)唯一约束 unique
4)主键约束 primary key
5)自增长约束 auto_increment
6)外键约束 foreign key
7)级联操作 cascade ,必须前提是从在外键
(级联删除 on delete cascade 级联修改 on update cascade)
-- 1)默认约束 default
--作用范围:就是在插入部分字段的时候,没有插入的字段就默认值是null 或者 给定的值
--注意事项
默认约束对直接插入的null值不起作用,只是在没有插入的字段值才起作用
--通过sql语句将默认约束删除--直接修改字段类型(即可)
alter table 表名 modify 字段 字段类型;
--通过sql语句添加默认约束--直接修改字段类型
alter table 表名 modify gender varchar(3) default '女';
--2)非空约束 not null
CREATE TABLE stu(
id INT,
NAME VARCHAR(10) NOT NULL, -- 非空约束
age INT
);
--sql语句如何删除 --直接修改字段类型
alter table 表名 modify 字段名 字段类型
--通过sql语句直接添加非空约束
alter table 表名 modify 字段名 字段类型 not null;
--3)唯一约束 (实际业务中真实有效的业务必须唯一)
CREATE TABLE stu(
id INT,
NAME VARCHAR(10),
telephone VARCHAR(11) UNIQUE -- 加入唯一约束
) ;
-- 通过sql语句修改删除 唯一约束
alter table 表名 drop index (索引名)默认是字段名;
-- 通过sql语句修改表,加入唯一约束
alter table 表名 add constraint 申明约束名 unique(字段名);
--4)主键约束 primary key (一般情况下都是作用在非业务字段 )
特点: 非空且唯一
CREATE TABLE stu(
id INT PRIMARY KEY, -- 加入主键约束
NAME VARCHAR(10)
);
-- 通过sql语句删除主键约束
alter table 表名 drop primary key;<注意:此时只是删除了主键索引,非空约束还在>
-- 通过sql语句 添加主键约束
alter table 表名 add constraint 主键约束名 primary key(id字段名)
--5)自增长约束 auto_increment, 一般和主键一起搭配使用
CREATE TABLE stu(
id INT PRIMARY KEY AUTO_INCREMENT,-- id字段主键并且是自增长的
NAME VARCHAR(10),
age INT
);
-- 通过sql语句修改表可以删除自增长约束
alter table 表名 drop modify 字段名称 字段类型 去掉auto_increment约束
-- 通过sql语句 添加自增长约束
alter table 表名 字段名称 字段类型 auto_increment;
-- 6)外键约束 foreign key
语法:
constraint (声明) 外键约束名称
foreign key(作用在从表的字段名)
references(关联) 主表名(主键字段名);
注意:加入外键约束 插入和删除都比较麻烦
删除的时候先要将从表的内容删除,然后将主表的内容在删除,解决如下
-- 加入级联删除和级联修改(cascade)
CREATE TABLE employee(
id INT PRIMARY KEY AUTO_INCREMENT , -- 员工编号
NAME VARCHAR(10), -- 姓名
gender VARCHAR(3), -- 性别
dept_id INT, -- 员工所在的部门的编号
CONSTRAINT -- 声明
dept_emp_fk -- 外键约束名
FOREIGN KEY (dept_id) -- 作用在从表的指定字段上
REFERENCES -- 关联
dept(id) -- 主表名(主键字段名) ;
ON DELETE CASCADE -- 级联删除
ON UPDATE CASCADE -- 级联修改
数据库常见理论面试题(三大范式)
概述:数据库的范式(--- 是设计数据库的一种规范要求)
标准规范:三大范式(规范):每个范式程递次规范,范式级别越大,数据库中字段冗余度越小
1NF : 表中的每一列是不能再拆分的原子数据
2NF : 在1NF 的基础上
特点一)每一张表只能描述一件事情;
特点二)非主键字段必须完全依赖于主键字段
3NF : 在2NF的基础上
--核心思想:非主键之间不能产生传递依赖
举例 -- C字段依赖于B字段,B字段依赖于A字段----->
C依赖于A字段
多表关系(理论关系):
一对一的关系(是特例,是对对多的一种特例,比如身份证和人)
一对多的关系(开发中频繁用的,比如用户和订单)
多对多的关系(开发中常见的,比如部门和员工之间)
多表查询(重点):
一,内连接查询;
1)隐式内连接(推荐)
2)显示内连接
二,外连接查询;
1)左外连接(推荐)
2)右外连接
三,子查询;
select嵌套查询
多表查询中的问题:
- A表中有m条记录,B表中有n条记录,直接查询A和B表的数据
---->结果 m*n 的总记录数(出现了笛卡尔积现象,原因是两张表之间没有关系)
查询注意事项:(三要素)
- 查询哪些表
- 查询这些表的哪些字段
- 表和表之间的关系是什么(连接条件)
内连接查询使用:
1>隐式连接(推荐:多去使用where条件,这个算sql优化的一种方式)
语法:select 字段列表 from 表名1,表名2 where 连接条件;
--举例:
-- 需求:要查询员工表和部门表的所有信息
SELECT
*
FROM
employee,
dept
WHERE
employee.dept_id = dept.id ;
2>显示内连接
语法:
select 字段名称 from 表名1 (inner) join 表名2 on 连接条件; 如果有多个条件后面and连接
--举例
-- 需求:查询员工表的编号,姓名,工资,入职日期,以
--及部门表的部门名称
SELECT
e.id '员工编号',
e.`name` '员工姓名',
e.`salary` '工资',
e.`join_date` '入职日期',
d.`name` '部门名称'
FROM
employee e
INNER JOIN dept d -- inner可以省略
ON e.`dept_id` = d.`id` ;
外连接查询使用:
1>左外连接(经常使用)
使用原则: 将A表(左表) 和 B表的交集数据(有连接条件的数据) 以及A表的所有全部查询
语法: select 字段名 from 表名1 left outer join 表名2 on 连接条件 ;
-- outer 可以省略不写
2> 右外连接
使用原则:将A表(左表) 和 B表(右表)的交集数据以及右表左右的数据查询
语法:select 字段名 from 表名1 right outer join 表名2 on 连接条件;
子查询:
select嵌套select 查询法
1)情况一:在where条件后面使用运算符之类去进行嵌套查询
--1)查询出最高工资是多少
-- select max(salary) from employee ;-- 15000
-- 2) 查询员工工资是15000的员工姓名,年龄,工资以及部门信息
SELECT
e.`name` '姓名',
e.age '年龄',
e.`salary`,
d.`name` '部门名称'
FROM
employee e,
dept d
WHERE e.`dept_id` = d.`id`
AND e.`salary` =
(SELECT
MAX(salary)
FROM
employee) ;
2)情况二:使用in(值1, 值2, 值3,..) in集合语句
-- 需求:查询出在市场部和财务部的员工所有信息以及部门所有信息
– 1)查询出市场部和财务部的部门id号是多少
– select id from dept where name = ‘财务部’ or name= ‘市场部’ ;
– 2)查询在2号部门和3号部门的所有员工信息以及部门信息
SELECT
e.*,
d.*
FROM
employee e,
dept d
WHERE e.`dept_id` = d.`id`
AND e.`dept_id` IN
(SELECT
id
FROM
dept
WHERE NAME = '财务部'
OR NAME = '市场部') ;
– 3)子查询情况3
– 将某条select语句查询的 结果当做一个"虚表",
– 使用这个虚表和其他表关联查询
– 需求:查询:入职日期大于’2017-12-30’的员工所有信息以及部门名称信息
– 查询出入职日期大于’2017-12-30’的员工信息- —>查出来的结果当做一个表(虚表),使用这个虚表和部门表关联查询
SELECT
t.id '员工编号',
t.name '员工姓名',
t.age '年龄',
t.salary '工资',
t.join_date '入职时间',
d.`name` '部门名称'
FROM
(SELECT
*
FROM
employee
WHERE employee.`join_date` > '2017-12-30') t
LEFT OUTER JOIN dept d -- 部门表
ON t.dept_id = d.id ;
-- 优化---纯使用where条件 隐式内连接
SELECT
e.*,
d.`name` '部门名称'
FROM
employee e,
dept d
WHERE e.`dept_id` = d.`id`
AND e.`join_date` > '2017-12-30' ;