Servlet概述

什么是Servlet?

Servlet是由SUN公司提供的一门动态Web资源开发技术

静态Web资源:不同的人,在不同的条件下访问后看到的是相同的效果,这样的资源叫做静态Web资源(html、css、js等)
动态Web资源:在不同的访问条件下看到的是不同的效果,这样的资源叫做动态Web资源(Servlet、jsp、.NET、PHP等)

Servlet本质上是一段Java程序,和之前的Java程序不同的是,Servlet程序无法独立运行,需要将Servlet程序放在服务器中(比如tomcat服务器),由服务器调用才可以执行。

Servlet: 服务器端的Java程序.

Servlet是运行在服务器端的Java程序,其作用是什么?

其作用是对服务器接收过来的请求进行处理(作用为处理请求)

开发Servlet程序

开发Servlet程序的步骤

第一步: 写一个类,实现一个Servlet接口或者继承Servlet接口的子类(GenericServlet/HttpServlet),并实现其中的方法

Servlet接口|-- GenericServlet类(抽象类)|-- HttpServlet类

第二步: 在web应用的web.xml文件中配置Servlet程序对外访问的路径。

Eclipse在创建一个Servlet时,会在web.xml文件中生成Servlet配置,所以不需要我们手动配置。

使用Eclipse创建Web项目

以上是Web项目在工程视图(Project)和包视图(package)下结构,推荐使用包视图!

1、创建一个Web工程: 在左侧窗口中, 点击鼠标右键 —> New —>
Dynamic Web Project

2、接着会弹出如下窗口:

注意:

(1) 3.0版本不会创建web.xml文件,
并且创建Servlet时也不会在web.xml文件中生成Servlet相关的配置信息, 记得改为2.5。

(2) Target runtime选项中如果没有可选的服务器,可点击右侧的"New
Runtime…"进行配置。

详细操作步骤在《5.2配置Target runtime(Web项目运行环境)》

3、Eclipse中创建的Web工程的目录结构:

(1) day09: 工程名称/项目名称
(2) src: 源码目录, 创建的java源文件、配置文件(properties、xml文件等)都可以放在src源码目录下
(3) build/classes: 编译文件的输出目录, src源码目录中的文件编译后会输出到classes目录下。其中的classes目录在发布时会放在WEB-INF目录下,随着项目一起发布到服务器中
(4) WebContent: 就是Web应用的目录,其中可以存放 html、css、js、jsp、图片以及编译后的class文件、jar包、web.xml文件等. 将来发布项目到服务器,其实就是将WebContent中的所有内容一起发布到服务器中。
(5) WebContent/WEB-INF/lib: 用于存放当前项目所依赖的jar包。比如要访问mysql数据库,需要导入mysql驱动包,直接将jar包拷贝到lib目录下即可!(也不用再去做 build path --> add to build path)

使用Eclipse创建Servlet

1、选中项目中的src目录,鼠标右键 —> New —> Servlet

2、在弹出的窗口中,根据提示填写内容:

3、点击finish即可完成Servlet创建过程, 创建好的Servlet如下:

通过Eclipse创建Servlet,默认继承HttpServlet。由于HttpServlet也是Servlet接口的子类,让HelloServlet继承HttpServlet,相当于间接实现了Servlet接口。

继承HttpServlet类,默认会覆盖doGet方法和doPost方法,两个方法的作用为:

*** doGet方法:**当浏览器发送请求的方式为GET提交时, 将会调用doGet方法来处理请求

*** doPost方法:**当浏览器发送请求的方式为POST提交时, 将会调用doPost方法来处理请求

提示:如果当GET提交和POST提交处理代码相同时,可以将代码写在其中一个方法里(例如写在doGet中),并在另外一个方法(例如doPost)中调这个方法。这样一来,不管是GET提交还是POST提交,最终doGet方法都会执行,都会对请求进行处理!!

Servlet在web.xml中的配置

在通过Eclipse创建Servlet时,会自动在web.xml文件中进行Servlet相关信息的配置

(注意:如果是复制Servlet类文件,但配置信息不会跟着复制,需要自己手动添加配置,否则复制的Servlet将无法访问!)

<servlet><servlet-name>HelloServlet</servlet-name><servlet-class>com.tedu.HelloServlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>HelloServlet</servlet-name><url-pattern>/HelloServlet</url-pattern>
</servlet-mapping>

关于上面的配置信息:

a) Eclipse每创建一个Servlet,就会在web.xml文件中添加两个标签:<servlet>和<servlet-mapping>标签(可以将这两个标签看成一个组的标签)

**b) **<servlet>和<servlet-mapping>标签内都会有一个<servlet-name>标签,标签的内容可以更改,但要求更改后的这两个<servlet-name>标签的内容也必须一致。

c) <servlet-class>标签用于配置Servlet类的全限定类名(即包名+类名)

需要注意:如果在创建Servlet后修改了Servlet类的名称,这个地方也要一起更改,否则将会出现"ClassNotFoundException" 即类找不到异常

**d) **<url-pattern>标签用于配置浏览器以什么路径访问当前Servlet(即Servlet对外访问的路径),默认的路径是:/类名

例如:上面为HelloServlet配置的<url-pattern>为**/HelloServlet**,因此我们在浏览器中的访问路径则为:

http://主机名/web项目访问路径**/HelloServlet**

运行Servlet程序、访问测试

1、访问Servlet方式一:

若是第一次运行,需要先创建tomcat服务器,即在Servers窗口中点击链接可创建一个tomcat服务器,且只需创建一次即可!

(1)发布项目到服务器:在服务器上右键 --> 点击 "add and remove" 将当前web项目发布到服务器中,并点击完成。

(2)启动tomcat服务器:在服务器上右键 Start 即可启动服务器

(3)通过浏览器访问Servlet:打开本地浏览器,通过路径访问,即可访问Servlet程序

http://localhost:端口/项目名称/HelloServlet

2、访问Servlet方式二:

(1) 在运行的Servlet上点击右键 –-> “Run As” —> "1 Run on Server"

(2) 在弹出的窗口中,直接点击完成即可!!!

(3) 运行结果如下:

或者打开浏览器,复制上图中的路径:

http://localhost:8080/Hello/HelloServlet,粘贴到浏览器的地址栏中,回车访问:

Eclipse如何发布一个Web应用

当通过eclipse将day09项目发布到服务器中,是直接将day09拷贝到服务器中对应的目录下吗?

发布的过程如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QAvjr3gp-1618393303358)(D:/1930200215/CGB2/第二阶段讲义02.assets/
)]

Eclipse默认发布Web应用的位置

Tomcat服务器中默认只有一台虚拟主机,叫做localhost主机,而localhost主机发布web应用的位置是webapps。将项目发布到localhost主机中,但为什么项目没有在webapps目录下?

--------------------------------------------------------------------------------------------------------

默认情况下,发布一个Web应用到localhost主机中,只需要将Web应用的目录拷贝到webapps目录下即可完成发布!

而将Eclipse和Tomcat整合之后,通Eclipse发布项目到tomcat服务器中,发布的路径默认被改成了:

[工作空间]\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps

如何修改Eclipse发布Web应用到[tomcat]/webapps目录下:

(1)关闭服务器,将服务器中的所有应用移除

(2)在服务器上右键 --> clean

(3)双击tomcat服务器,在弹出窗口中找到 Server location, 选择第二个选项

并将下方的Deploy Path改为: webapps 改完后,Ctrl+s保存配置即可!!

Servlet调用过程

通过浏览器访问服务器中的一个Servlet程序,这个Servlet程序是如何执行的?又是如何被调用的?

localhost:8080/day08/HelloServlet

参考<<Servlet调用过程图>>

扩展内容

添加Servlet模版

通过Eclipse可以直接创建一个Servlet类,这相比通过记事本等文本编辑工具创建Servlet,可以节省配置Servlet的时间,提高了我们的开发效率。

但是通过Eclipse生成的Servlet类中包含了许多我们不需要的注释和默认实现代码,这些每次都删除也非常占用时间。

接下来可以通过添加模版代码的形式,来生成Servlet的内容,以便于提高我们的开发效率。

1、先创建一个Servlet,将其中的内容修改为自己期望的模版格式,并复制其中的内容,例如:

package ${enclosing_package};import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** author: bjzhangsz@tedu.cn* datetime: ${date} ${time}*/
public class ${primary_type_name} extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/html;charset=utf-8");PrintWriter out = response.getWriter();}protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doGet(request, response);}
}

2、点击菜单栏中的window --> Preferences

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GrFQMxDb-1618393303359)(D:/1930200215/CGB2/第二阶段讲义02.assets/
)]

3、在出现的窗口左侧依次点击:Java --> Editor --> templates
–>(在右边的窗口中) 点击New… :

4、在出现的新窗口中填写如下内容:

5、替换包路径和类名(作用是在新建Servlet生成的Servlet模版中使用当前类的包路径和类型)

效果如下:

效果如下:

6、点击OK保存,创建新的Servlet文件,测试

将Servlet中的所有内容全选删除,并输入"servlet",接着按 “Alt+ /”
提示即可生成自己想要的Servlet模版内容!

效果如下:

Servlet3.0的特性

上面使用的是Servlet2.5版本,其实JavaEE5.0规范的子规范,要求运行环境最低是JDK5.0,tomcat5.0。

而Servlet3.0规范是JavaEE6.0的子规范,其要求运行环境最低是JDK6.0,tomcat7.0。若要使用Servlet3.0规范,则需要在创建动态Web项目时就要指定。具体用法是,在Eclipse中创建动态Web工程时,指定创建的“动态Web模块版本”为3.0或以上版本,此时创建的Web工程中默认是没有web.xml文件的。

思考:如果没有web.xml文件,那么如何配置Servlet的访问路径呢?

通过注解方式进行配置Servlet访问路径。下面来几个Servlet3.0的示例。

1、在3.0以上版本的动态Web项目中创建一个Servlet,如下:

@WebServlet("/HelloServlet")
public class HelloServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {PrintWriter out = response.getWriter();out.write( "Hello Servlet3.0.."+new Date() );}
}

其中@WebServlet("/HelloServlet") 这个注解的作用就是配置当前Servlet的访问路径为/HelloServlet,完善doGet方法中的代码,直接运行Servlet。

可以在浏览器中看到如下效果:

2、@WebServlet注解中可以配置多个访问路径

在@WebServlet注解中配置的访问路径,其实前面省略了value属性,完整写法为:

@WebServlet(value="/HelloServlet"),只不过value可以省略

由于在源码中value属性其本质是一个字符串数组,因此可以为value属性赋值为一个数组直接量。例如:@WebServlet(value={})

将HelloServlet类上的注解改为如下形式:

@WebServlet(value={"/HelloServlet", "/hello01", "/hello02"})
public class HelloServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {PrintWriter out = response.getWriter();out.write( "Hello Servlet3.0.."+new Date() );}
}

在浏览器中运行效果为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RsXlgF4u-1618393303371)(D:/1930200215/CGB2/第二阶段讲义02.assets/image-20200630003451294.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vVWysxsj-1618393303372)(D:/1930200215/CGB2/第二阶段讲义02.assets/image-20200630003512303.png)]

3、可以将value属性替换为urlPattern属性

@WebServlet(urlPatterns={"/HelloServlet", "/hello01", "/hello02"})
public class HelloServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {PrintWriter out = response.getWriter();out.write( "Hello Servlet3.0.."+new Date() );}
}

在浏览器中运行效果为:

request和response介绍

request是代表HTTP请求信息的对象,response是代表HTTP响应信息的对象。

当浏览器发请求访问服务器中的某一个Servlet时,服务器将会调用Servlet中的service方法来处理请求。在调用service方法之前会创建出request和response对象。

其中request对象中封装了浏览器发送给服务器的请求信息(请求行、请求头、请求实体等),response对象中将会封装服务器要发送给浏览器的响应信息(状态行、响应头、响应实体),在service方法执行完后,服务器再将response中的数据取出,按照HTTP协议的格式发送给浏览器。

request对象

获取请求参数

问题1:什么是请求参数?

所谓的请求参数,就是浏览器发送给服务器的数据(不区分请求方式),例如:通过表单向服务器提交的用户名、密码等,或者在超链接后面通过问号提交的数据,都是请求参数。

http://localhost/day10/TestParam?user=zhangsan&pwd=123&like=篮球&like=足球

问题2:如何获取请求参数?

(1)request.getParameter(String paramName)
//-- 根据请求参数的名字获取对应的参数值,返回值是一个字符串;
//-- 如果一个参数有多个值,该方法只会返回第一个值。
//-- 如果获取的是一个不存在的参数,返回值为null
(2)request.getParameterValues(String paramName)
//-- 根据请求参数的名字获取该名字对应的所有参数值组成的数组,返回值是一个字符串数组,其中包含了这个参数名对应的所有参数值
//-- 如果获取的是一个不存在的参数,返回值为null

代码示例:

//1.获取请求参数中的用户名(user)
String user = request.getParameter("user");
System.out.println( "user="+user );//2.获取请求参数中的爱好(like)
String[] like = request.getParameterValues( "like" );
System.out.println( "like="+Arrays.toString( like ) );

问题3:如何解决获取请求参数时的中文乱码问题?

在获取中文的请求参数时,可能会出现乱码问题(和请求方式、tomcat服务器版本有关),具体可以分为以下三种情况:

(1)如果请求是GET提交,并且tomcat是8.0及以后的版本,GET提交的中文参数,在获取时不会出现乱码问题!(8.0以后的tomcat包括8.0在获取GET提交的中文参数时,已经处理中文乱码问题。)

(2)如果请求是POST提交,不管是哪个版本的tomcat服务器,在获取中文参数时,都会出现乱码问题。因为tomcat底层在接收POST提交的参数时,默认会使用iso8859-1编码接收,而这个编码中没有中文字符,所以在接收中文参数时,一定会出现中文乱码问题!

解决方法是:通知服务器在接收POST提交的参数时,使用utf-8编码来接收!

request.setCharacterEncoding("utf-8");

注意:这行代码不会影响GET提交,只对POST提交有效!

这行代码要放在任何获取参数的代码之前执行!

(3)如果请求是GET提交,并且tomcat是7.0及以前的版本,GET提交的中文参数,在获取时会出现乱码问题!

解决方法:在[tomcat安装目录]/ conf/server.xml文件的(修改端口的)Connector标签上,添加一个 URIEncoding=“utf-8” 属性,如下:

<Connector port="80" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443"URIEncoding="utf-8" />

同时在[Eclipse]/Servers/[当前tomcat服务器对应的配置目录]/server.xml文件中,在Connector标签上,添加一个 URIEncoding=“utf-8” 属性,同上!

实现请求转发

请求转发是服务器内部资源的一种跳转方式,即当浏览器发送请求访问服务器中的某一个资源(A)时,该资源将请求转交给另外一个资源(B)进行处理并且由资源B做出响应的过程,就叫做请求转发。

请求转发和重定向都是资源的跳转方式,但是跳转的过程有所不同。

请求转发的特点:

(1)转发是一次请求,一次响应
(2)请求转发前后,浏览器的地址栏地址不会发生变化。(浏览器--访问--> A --转发--> B,地址栏地址始终指向A的地址)
(3)请求转发前后的两个资源必须属于同一个Web应用,否则将无法进行转发。(A--转发-->B,A和B必须属于同一个Web应用!)
(4)在转发时,可以带数据到目的地(比如,从A转发到B,可以从A带数据给B,需要配合域对象才可以)

请求转发实现:

request.getRequestDispatcher(url地址/转发到资源的地址).forward(req, res);

代码示例:

//从当前Servlet转发到 index.jsp(http://localhost/day10/index.jsp)
//request.getRequestDispatcher("/index.jsp").forward(request, response);
request.getRequestDispatcher("index.jsp").forward(request, response);

作为域对象使用

request在实现转发时,通过request.setAttribute方法和request.getAttribute方法带数据到目的地时,就是通过request对象中的map集合带数据,这个request对象上的map集合以及request对象所在的范围即称之为是一个域对象。

在request对象上提供了往域对象(map)中存数据的方法以及取数据的方法:

request.setAttribute(String attrName, Object attrValue);
-- 往request域中存入一个域属性,属性名(key)只能是字符串,属性值(value)可以是任意类型。
request.getAttribute(String attrName);
-- 根据属性名(key)获取对应的属性值(value)。返回的是一个Object类型的对象。

request域对象所具备的三大特征:

**生命周期:**在服务器调用Servlet程序的service方法之前,会创建代表请求的request对象,在请求处理完,响应结束时,会销毁request对象。

**作用范围:**在一次请求范围内,可以通过request域带数据到目的地。

**主要功能:**和请求转发配合使用,从Servlet带数据到JSP(带数据到目的地)

**扩展内容:**request对象的getParametergetAttribute方法有什么区别?

  • getParameter()方法是用于获取(从浏览器发送过来的)请求参数的,请求参数不能设置,只能是浏览器发送给服务器,在服务器端再通过getParameter方法获取请求中的参数
  • getAttribute()方法是用于从request域中获取域属性时用的,域属性得先存入到域中(即得先通过setAttribute方法将数据存入request域中),再通过getAttribute()方法从域中获取。

response对象

response是代表HTTP响应信息的对象。

向客户端发送数据

PrintWriter out = response.getWriter();

由于服务器在通过response获取的流发送数据时,默认使用iso8859-1编码,而这个编码中没有中文字符,所以在通过response获取的流发送中文数据时,会出现乱码问题。

解决方法是:在响应数据之前,通知服务器使用utf-8发送数据。

/*  通知服务器在响应数据时,使用utf-8编码* 也能通知浏览器使用utf-8接收服务器发送的数据 */
response.setContentType( "text/html;charset=utf-8" );
PrintWriter out = response.getWriter();
out.write( "你好" );

实现重定向

当浏览器向服务器发请求访问某一个资源A,资源A在响应时通知浏览器需要再进一步请求才能获取到对应的资源,浏览器再次发请求访问服务器中的资源B,最终由资源B响应浏览器要获取的资源,这个过程叫做重定向。

重定向的特点:

(1)重定向是两次请求、两次响应
(2)重定向前后,浏览器的地址栏地址会发生变化。(因为两次请求都是通过浏览器发起,浏览器知道这个跳转的过程,因此地址栏地址会变化)
(3)重定向前后的request对象不是同一个(因为重定向是两次请求,服务器会根据两次请求创建两个不同的request对象,request对象不是同一个,也就不能在重定向时通过request带数据到目的地。)
(4)重定向前后的两个资源可以是来自不同的web应用,甚至可以是来自不同的服务器。(进行跳转的两个资源之间没有限制)

实现代码:

response.sendRedirect(所重定向到资源的URL地址);

代码示例:

//测试1: 从当前Servlet(day10/TestRedirect)重定向到day10/index.jsp
// http://localhost/day10/TestRedirect
// http://localhost/day10/index.jsp
response.sendRedirect( "http://localhost/day10/index.jsp" );
response.sendRedirect( "/day10/index.jsp" );
response.sendRedirect( "/index.jsp" ); //错误路径
response.sendRedirect( "index.jsp" ); //正确路径//测试2: 从当前Servlet重定向到day09/index.jsp
response.sendRedirect( "http://localhost/day09/index.jsp" );//测试3: 从当前Servlet重定向到百度首页
response.sendRedirect( "http://www.baidu.com" );

总结-1:请求转发(forward)和重定向(redirect)的区别?

(1)请求转发是一次请求,一次响应; 而重定向是两次请求两次响应
(2)请求转发前后地址栏地址不会发生变化; 而重定向前后地址栏地址会发生变化
(3)请求转发前后的request对象是同一个,可以配合request域对象带数据到目的地; 而重定向前后的request对象不是同一个, 不能结合request域对象在重定向前后带数据.
(4)请求转发要求两个资源必须属于同一个Web应用; 而进行重定向的两个资源可以是同一个Web应用,也可以不是同一个Web应用,甚至可以是来自于不同的主机或服务器.

总结-2:什么时候用请求转发(forward)?什么时候用重定向(redirect)?

(1)如果希望跳转前后地址栏地址不会发生变化, 只能使用转发; 如果希望跳转前后地址栏地址会发生变化, 只能使用重定向
(2)如果希望在跳转前后, 能够通过request对象带数据到目的地, 只能使用转发
(3)如果仅仅是做一个跳转,没有其他要求,此时推荐使用转发(转发是一次请求,一次响应,可以减少访问服务器的次数,降低服务器的压力)

onse.sendRedirect( “http://www.baidu.com” );


总结-1:请求转发(forward)和重定向(redirect)的区别?

(1)请求转发是一次请求,一次响应; 而重定向是两次请求两次响应
(2)请求转发前后地址栏地址不会发生变化; 而重定向前后地址栏地址会发生变化
(3)请求转发前后的request对象是同一个,可以配合request域对象带数据到目的地; 而重定向前后的request对象不是同一个, 不能结合request域对象在重定向前后带数据.
(4)请求转发要求两个资源必须属于同一个Web应用; 而进行重定向的两个资源可以是同一个Web应用,也可以不是同一个Web应用,甚至可以是来自于不同的主机或服务器.


总结-2:什么时候用请求转发(forward)?什么时候用重定向(redirect)?

(1)如果希望跳转前后地址栏地址不会发生变化, 只能使用转发; 如果希望跳转前后地址栏地址会发生变化, 只能使用重定向
(2)如果希望在跳转前后, 能够通过request对象带数据到目的地, 只能使用转发
(3)如果仅仅是做一个跳转,没有其他要求,此时推荐使用转发(转发是一次请求,一次响应,可以减少访问服务器的次数,降低服务器的压力)

# MySQL数据库概述
----------### 什么是数据库?所谓的数据库就是指存储和管理数据的仓库扩展内容1:数据库有哪些分类?(了解)

早期: 层次式数据库、网络型数据库
现在:关系型数据库、非关系型数据库


### 什么是关系型数据库?底层以二维表的形式保存数据的库就是关系型数据库stu-学生表| 学生编号 | 姓名   | 年龄 |
| -------- | ------ | ---- |
| 1001     | 刘沛霞 | 35   |
| 1002     | 陈子枢 | 18   |**扩展内容2:常见的关系型数据库有哪些?(了解)**- Sql Server:微软提供,收费,适用于一些中型或大型的项目中,在java中的使用占比不高(.NET中使用的较多)- Oracle:甲骨文公司提供,收费,适用于一些大型或者超大型的项目中,在java中的使用占比非常高- mysql:瑞典MySQLAB公司提供,免费开源,适用于一些小型或者中型的项目中,在Java中的使用占比较高(小巧轻量)mariadb其实就是MySQL的一个分支,用法和MySQL完全一样。- DB2:IBM公司提供,收费,在一些银行、金融等行业中使用较多。在java中的使用占比也不高。- Sqlite:迷你数据库,嵌入式设备中(安卓、苹果手机、pad)
- ...### 数据库相关概念**1、什么是数据库服务器**数据库服务器就是一个软件(比如mysql软件)将数据库软件安装在电脑上,当前电脑就是一个数据库服务器。就可以对外提供存取数据的服务在一个数据库服务器中可以创建多个数据库(dataBases),每一个数据库都是一个单独的仓库。**2、什么是数据库**数据库就是存储和管理数据的仓库,通常情况下,一个网站的中的所有数据会存放在一个数据库中。例如:

jd.com db_jd(数据库)
taobao.com db_taobao(数据库)


**3、什么是表**一个数据库中可以创建多张表,每张表用于存储一类信息(数据库),例如:jd.com中的用户数据     tb_user(表)jd.com中的商品数据      tb_product(表)jd.com中的订单数据       tb_order(表)...**4、什么表记录**一张表中可以包含多行表记录,每一行表记录用于存储某一个具体的数据| 学生编号 | 姓名   | 年龄   |
| -------- | ------ | ------ |
| 1001     | 刘沛霞 | 35     |
| 1002     | 陈子枢 | 18     |
| 。。。   | 。。。 | 。。。 |### 什么是SQL语言?SQL是一门用于操作关系型数据库的通用的语言(使用SQL可以操作所有的关系型数据库)使用SQL可以操作数据库、表、表记录(1)创建数据库、删除数据库、修改数据库、查询数据库(2)创建表、删除表、修改表、查询表(3)新增表记录、删除表记录、修改表记录、查询表记录使用SQL也可以操作存储过程/视图/索引等。提示:SQL是一个标准通用的操作关系型数据库的语言(普通话),每个数据库厂商为了增强自己数据库的功能,都提供了支持自己数据库的语言,称之为数据库的方言。方言不通用!连接mysql服务器
---------------通过命令行工具可以登录MySQL客户端,连接MySQL服务器,从而访问服务器中的数据。**1、连接mysql服务器:**

mysql -uroot -p密码


**-u:**后面的root是用户名,这里使用的是超级管理员root;**-p:(小写的p)**后面的root是密码,这是在安装MySQL时就已经指定的密码;**2、连接mysql服务器并指定IP和端口**:

mysql -uroot -proot -h127.0.0.1 -P3306


**-h:**后面给出的127.0.0.1是服务器主机名或ip地址,可以省略的,默认连接本机;**-P:(大写的P)**后面的3306是连接端口,可以省略,默认连接3306端口;**3、退出客户端命令:quit或exit或 \\q****4、FAQ:常见问题:**
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210414203726102.jpg#pic_center)解决方法:复制mysql安装目录下的bin目录的路径,将bin目录的路径添加到path环境变量中!!可以在cmd中通过 `echo %path%` 检查path环境变量的值。**扩展内容3:**(1)在cmd中连接mysql服务器之后,可以使用 \#、/**/、-- 等符号添加注释,例如:![在这里插入图片描述](https://img-blog.csdnimg.cn/20210414203742414.png#pic_center)(2)在cmd中连接mysql服务器之后,在书写SQL语句时,可以通过 \\c 取消当前语句的执行。例如:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210414203758511.png#pic_center)数据库及表操作
--------------### 创建、删除、查看数据库提示: (1)SQL语句对大小写不敏感。推荐关键字使用大写,自定义的名称(库名,表名,列名等)使用小写。```sql
SHOW DATABASES; -- 查看当前数据库服务器中的所有库
CREATE DATABASE mydb1; -- 创建mydb1库

(2)并且在自定义名称时,针对多个单词不要使用驼峰命名,而是使用下划线连接。(例如:tab_name,而不是 tabName )

– 01.查看mysql服务器中所有数据库

show databases; -- 查看所有的数据库
show tables; -- 查看所有的表

– 02.进入某一数据库(进入数据库后,才能操作库中的表和表记录)

– 语法:USE 库名;

use test; -- 进入/选中test库
use mysql; -- 进入/选中mysql库

– 查看已进入的库(了解)

select database();

– 03.查看当前数据库中的所有表

show tables;

– 04.删除mydb1库

– 语法:DROP DATABASE 库名;

drop database test; -- 删除test库

-- 思考:当删除的库不存在时,如何避免错误产生?

drop database mydb1; -- 删除的库不存在会报错
drop database if exists mydb1; -- 如果存在,则删除mydb1库

– 05.重新创建mydb1库,指定编码为utf8

– 语法:CREATE DATABASE 库名 CHARSET 编码;

– 需要注意的是,mysql中不支持横杠(-),所以utf-8要写成utf8;

create database mydb1 charset utf8;

– 如果不存在则创建mydb1;

create database if not exists mydb1 charset utf8;

– 06.查看建库时的语句(了解, 并验证数据库库使用的编码)

– 语法:SHOW CREATE DATABASE 库名;

show create database mydb1;

MySQL基本语法概述

创建、删除、查看表

– 07.进入mydb1库,删除stu学生表(如果存在)

– 语法:DROP TABLE 表名;

use mydb1;
drop table if exists stu;

– 08.创建stu学生表(编号[数值类型]、姓名、性别、出生年月、考试成绩[浮点型]),建表的语法:

CREATE TABLE 表名(列名 数据类型,列名 数据类型,...列名 数据类型
);

SQL语句:

use mydb1;
create table stu(id int,name varchar(20), -- 20表示该列中最多存20个字符gender varchar(10),birthday date,score double
);

– 09.查看stu学生表结构

– 语法:desc 表名

desc stu;

新增、更新、删除表记录

– 10.往学生表(stu)中插入记录(数据)

– 语法:INSERT INTO 表名(列名1,列名2,列名3…) VALUES(值1,值2,值3…);

insert into stu(id,name,gender,birthday,score) values(1,'tom','male','1985-3-4',90);
/* 插入中文数据前,先 set names gbk; (只对当前窗口有效) */
insert into stu values(2,'王海涛','male','1995-4-5',85);
insert into stu values(3,'刘沛霞','female','1985-6-7',80);
-- 查询stu表中的所有记录
select * from stu;

提示:

(1)当为所有列插入值时,可以省写列名,但值的个数和顺序必须和声明时列的个数和顺序保持一致!
(2)SQL语句中的值为字符串或日期时,值的两边要加上单引号(有的版本的数据库双引号也可以,但推荐使用单引号)。
(3)(针对cmd窗口)在插入数据之前,先设置编码:set names gbk;
或者用以下命令连接mysql服务器:mysql --default-character-set=gbk -uroot -proot
等价于:mysql -uroot -prootset names gbk;

– 11.查询stu表所有学生的信息

– 语法:SELECT 列名 | * FROM 表名

select * from stu;

– 12.修改stu表中所有学生的成绩,加10分特长分

– 修改语法: UPDATE 表名 SET 列=值,列=值,列=值…[WHERE子句];

update stu set score=score+10; -- mysql中不支持 +=

– 13.修改stu表中编号为1的学生成绩,将成绩改为83分。

update stu set score=83 where id=1;

提示:where子句用于对记录进行筛选过滤,保留符合条件的记录,将不符合条件的记录剔除。

– 14.删除stu表中所有的记录

– 删除记录语法: DELETE FROM 表名 [where子句]

delete from stu; -- 默认删除表中的所有记录

– 仅删除符合条件的

delete from stu where id>1; -- 删除id大于1的

查询表记录

– 准备数据: 以下练习将使用db10库中的表及表记录,请先进入db10数据库!!!

基础查询

SELECT 语句用于从表中选取数据。结果被存储在一个结果表中(称为结果集)。

语法:SELECT 列名称 | * FROM 表名

提示:(1) *(星号)为通配符,表示查询所有列。

(2)但使用 *(星号)有时会把不必要的列也查出来了,并且效率不如直接指定列名

– 15.查询emp表中的所有员工,显示姓名,薪资,奖金

select name,sal,bonus from emp;

– 16.查询emp表中的所有部门和职位

select dept,job from emp;

思考:如果查询的结果中,存在大量重复的记录,如何剔除重复记录,只保留一条?

– 在select之后、列名之前,使用DISTINCT 剔除重复的记录

select distinct dept,job from emp;

WHERE子句查询

WHERE子句查询语法:SELECT 列名称 | * FROM 表名称 WHERE 列 运算符 值

WHERE子句后面跟的是条件,条件可以有多个,多个条件之间用连接词(or | and)进行连接

下面的运算符可在 WHERE 子句中使用:

– 17.查询emp表中【薪资大于3000】的所有员工,显示员工姓名、薪资

select name,sal from emp
where sal>3000;

– 18.查询emp表中【总薪资(薪资+奖金)大于3500】的所有员工,显示员工姓名、总薪资

select name,sal+bonus from emp
where sal+bonus > 3500;

– ifnull(列名, 值)函数: 判断指定的列是否包含null值,如果有null值,用第二个值替换null值

select name,sal+ifnull(bonus,0) from emp
where sal+ifnull(bonus,0) > 3500;

– 注意查看上面查询结果中的表头,如何将表头中的 sal+bonus 修改为 “总薪资”

– 使用as可以为表头指定别名

select name as 姓名,sal+ifnull(bonus,0) as 总薪资 from emp
where sal+ifnull(bonus,0) > 3500;

– 另外as可以省略

select name 姓名,sal+ifnull(bonus,0) 总薪资 from emp
where sal+ifnull(bonus,0) > 3500;

– 19.查询emp表中【薪资在3000和4500之间】的员工,显示员工姓名和薪资

select name,sal from emp
where sal>=3000 and sal<=4500;
-- and:查询的结果必须同时满足and两边的条件

– 提示: between…and… 在…和…之间

select name,sal from emp
where sal between 3000 and 4500;
-- 包含头(3000)和尾(4500)

– 20.查询emp表中【薪资为 1400、1600、1800】的员工,显示员工姓名和薪资

select name,sal from emp
where sal=1400 or sal=1600 or sal=1800;
-- or: 只要满足or两边中的任何一个条件即可!

– 或者

select name,sal from emp
where sal in (1400,1600,1800);
-- in: 只要前面的列的值,满足in后面括号中的任何一个值即可!

– 21.查询薪资不为1400、1600、1800的员工,显示员工姓名和薪资

select name,sal from emp
where not(sal=1400 or sal=1600 or sal=1800);
-- 或
select name,sal from emp
where sal not in (1400,1600,1800);
-- not: 对当前条件进行取反

– 22.(自己完成) 查询emp表中【薪资大于4000和薪资小于2000】的员工,显示员工姓名、薪资。

select name,sal from emp
where sal>4000 or sal<2000;

– 23.(自己完成) 查询emp表中薪资大于3000并且奖金小于600的员工,显示员工姓名、薪资、奖金。

select name,sal,bonus from emp
where sal>3000 and bonus<600;

– 处理null值

select name,sal,bonus from emp
where sal>3000 and ifnull(bonus,0)<600;

– 24.查询没有部门的员工(即部门列为null值)

select * from emp where dept=null; -- 错误写法
select * from emp where dept is null;

– 思考:如何查询有部门的员工(即部门列不为null值)

select * from emp where not(dept is null);
-- 或
select * from emp where dept is not null;

模糊查询

LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式。

可以和通配符(%、_)配合使用,其中"%"表示0或多个任意的字符,"_"表示一个任意的字符

语法:SELECT 列 | * FROM 表名 WHERE 列名 LIKE 值

示例:

– 25.查询emp表中姓名中以"刘"字开头的员工,显示员工姓名。

-- 姓名中的第一个字必须是'刘',后面的都无所谓
select name from emp where name like '刘%';

– 26.查询emp表中姓名中包含"涛"字的员工,显示员工姓名。

-- '%涛%'包含: 以'涛'开头,以'涛'结尾,或者'涛'在中间的某一个位置
select name from emp where name like '%涛%';

– 27.查询emp表中姓名以"刘"开头,并且姓名为两个字的员工,显示员工姓名。

-- '刘_'表示以'刘'开头,并且姓名长度为2
select name from emp where name like '刘_';
-- '刘__'表示以'刘'开头,并且姓名长度为3
select name from emp where name like '刘__';

多行函数查询

多行函数也叫做聚合(聚集)函数,根据某一列或所有列进行统计。

常见的多行函数有:

多行函数 作用
COUNT( 列名 | * ) 统计结果集中指定列的记录的行数。
MAX( 列名 ) 统计结果集中某一列值中的最大值
MIN( 列名 ) 统计结果集中某一列值中的最小值
SUM( 列名 ) 统计结果集中某一列所有值的和
AVG( 列名 ) 统计结果集中某一列值的平均值

提示:(1)多行函数不能用在where子句中

(2)多行函数和是否分组有关,分组与否会直接影响多行函数的执行结果。

(3)多行函数在统计时会对null值进行过滤,直接将null值丢弃,不参与统计。

– 28.统计emp表中薪资大于3000的员工个数

-- 查询薪资大于3000的员工有哪些
select * from emp where sal>3000;
-- 统计上面的查询结果有多少行记录(也就是薪资大于3000的员工个数)
select count(*) from emp where sal>3000; -- 7
select count(id) from emp where sal>3000; -- 7
select count(bonus) from emp where sal>3000; -- 6(有误差,因为null值不参与统计)

– 29.求emp表中的最高薪资

-- 求员工表中的所有薪资
select sal from emp;
-- max(sal): 求sal这一列中的最大值
select max(sal) from emp;
select min(sal) from emp;

– 30.统计emp表中所有员工的薪资总和(不包含奖金)

-- sum(sal): 求sal这一列中所有值的总和
select sum(sal) from emp;
-- sum(bonus): 即使bonus中有null值,sum函数会剔除null,不让null参与统计
select sum(bonus) from emp;

– 31.统计emp表员工的平均薪资(不包含奖金)

-- avg(sal): 求sal这一列中所有薪资的平均值
select avg(sal) from emp;

多行函数需要注意的问题:

  • 多行函数和是否分组有关,如果查询结果中的数据没有经过分组,默认整个查询结果是一个组,多行函数就会默认统计当前这一个组的数据。产生的结果只有一个。

  • 如果查询结果中的数据经过分组(分的组不止一个),多行函数会根据分的组进行统计,有多少个组,就会统计出多少个结果。

select * from emp;

例如:统计emp表中的人数

-- 没有分组,默认所有员工是一个组,统计emp中的所有人数
select count(*) from emp; -- 12

结果返回的就是emp表中的所有人数

再例如:根据性别对emp表中的所有员工进行分组,再统计每组的人数,显示性别和对应人数

-- 按照性别分组,分为两个组,根据两个组统计,得出两个结果
select gender,count(*) from emp group by gender;

分组查询

GROUP BY 语句根据一个或多个列对结果集进行分组。

在分组的列上我们可以使用 COUNT,SUM,AVG,MAX,MIN等函数。

语法:SELECT 列 | * FROM 表名 [WHERE子句] GROUP BY 列;

– 32.对emp表,按照部门对员工进行分组,查看分组后效果。

-- 根据部门对员工分组, 部门这一列值相同的为一组(共3组)
select * from emp group by dept;
-- 对分组后的员工进行统计,统计每一组的人数
select dept,count(*) from emp group by dept;

– 33.对emp表按照职位进行分组,并统计每个职位的人数,显示职位和对应人数

-- 根据职位对员工分组, 职位这一列值相同的为一组(共3组)
select * from emp group by job;
-- 对上面分组后的员工进行统计,统计每一组的人数
select job,count(*) from emp group by job;

– 34.对emp表按照部门进行分组,求每个部门的最高薪资(不包含奖金),显示部门名称和最高薪资

-- 根据部门分组,部门这一列相同的为一组(共3组)
select * from emp group by dept;
-- 对上面分组后的员工进行统计,统计每一组中的最高薪资
select dept,max(sal) from emp group by dept;

排序查询

使用 ORDER BY 子句将结果集中记录根据指定的列排序后再返回

语法:SELECT 列名 FROM 表名 ORDER BY 列名 [ASC|DESC]

ASC(默认)升序,即从低到高;DESC 降序,即从高到低。

– 35.对emp表中所有员工的薪资进行升序(从低到高)排序,显示员工姓名、薪资。

-- 按照薪资sal升序排序(默认就是asc,即升序)
select name,sal from emp order by sal;
select name,sal from emp order by sal asc;

– 36.对emp表中所有员工的奖金进行降序(从高到低)排序,显示员工姓名、奖金。

-- 按照奖金bonus降序排序(desc是降序,不能省略)
select name,bonus from emp order by bonus desc;
-- 按照奖金bonus降序排序,对于奖金相同记录,再按照薪资进行升序排序
select name,bonus,sal from emp
order by bonus desc,sal asc;

分页查询

在mysql中,通过limit进行分页查询,查询公式为:

limit (页码-1)*每页显示记录数, 每页显示记录数

– 37.查询emp表中的所有记录,分页显示:每页显示3条记录,返回所有页的数据。

-- 每页显示3条, 查询第1页数据
select * from emp limit 0, 3;
-- 每页显示3条, 查询第2页数据
select * from emp limit 3, 3;
-- 每页显示3条, 查询第3页数据
select * from emp limit 6, 3;
-- 每页显示3条, 查询第4页数据
select * from emp limit 9, 3;
...

– 38.求emp表中薪资最高的前3名员工的信息,显示姓名和薪资

-- 先按照员工的薪资进行降序(从高到低)排序
select name,sal from emp order by sal desc;
-- 在上面查询的基础上,进行分页查询,每页显示3条,只查询第一页
select name,sal from emp order by sal desc limit 0,3;

其他函数

函数名 解释说明
curdate() 获取当前日期,格式是:年月日
curtime() 获取当前时间 ,格式是:时分秒
sysdate()/now() 获取当前日期+时间,格式是:年月日 时分秒
year(date) 返回date中的年份
month(date) 返回date中的月份
day(date) 返回date中的天数
hour(date) 返回date中的小时
minute(date) 返回date中的分钟
second(date) 返回date中的秒
CONCAT(s1,s2…) 将s1,s2 等多个字符串合并为一个字符串
CONCAT_WS(x,s1,s2…) 同CONCAT(s1,s2,…)函数,但是每个字符串之间要加上x,x是分隔符

– 39.查询emp表中所有【在1993和1995年之间出生】的员工,显示姓名、出生日期。

select name, birthday from emp
where birthday >= 1993 and birthday <= 1995;
-- 年月日不能和年份比较,错误!
-- 正确写法1: 将年月日中的年份提取出来,和年份进行比较
select name, birthday from emp
where year(birthday) >= 1993 and year(birthday) <= 1995;
-- 正确写法2: 将右面的年份转成年月日格式,和birthday进行比较
select name, birthday from emp
where birthday >= '1993-1-1' and birthday <= '1995-12-31';

– 40.查询emp表中本月过生日的所有员工

-- 查询本月过生日的员工
select * from emp where month( now() )=month( birthday );
-- 查询下个月过生日的员工(考虑特殊月份,12月、11月)
select * from emp
where ( month(now())+1 )%12=month(birthday)%12;

– 41.查询emp表中员工的姓名和薪资(薪资格式为: xxx(元) )

select name, concat(sal,'(元)') from emp;
-- 添加别名
select name 姓名, concat(sal,'(元)') 薪资 from emp;

– 补充练习:查询emp表中员工的姓名和薪资(薪资格式为: xxx/元 )

select name, concat(sal,'/元') from emp;
select name, concat_ws('/',sal,'元') from emp;

mysql的数据类型

数值类型

MySQL中支持多种整型,其实很大程度上是相同的,只是存储值的大小范围不同而已。

tinyint:占用1个字节,相对于java中的byte

smallint:占用2个字节,相对于java中的short

int:占用4个字节,相对于java中的int

bigint:占用8个字节,相对于java中的long

其次是浮点类型即:float和double类型

float:4字节单精度浮点类型,相对于java中的float

double:8字节双精度浮点类型,相对于java中的double

字符串类型

1、char(n) 定长字符串,最长255个字符。n表示字符数,例如:

– 创建user表,指定用户名为char类型,字符长度不超过10

create table user(username char(10),...
);

所谓的定长,是当插入的数据的长度小于指定的长度时,剩余的空间会用空格填充。(这样会浪费空间)

char类型往往用于存储长度固定的数据。

2、varchar(n) 变长字符串,最长不超过65535个字节,n表示字符数,一般超过255个字符,会使用text类型,例如:

iso8859-1码表:一个字符占用1个字节,1*n < 65535, n最多等于 65535
utf8码表:一个中文汉字占用3个字节,3*n < 65535,n最多等于 65535/3
GBK码表:一个中文汉字占用2个字节,2*n < 65535,n最多等于 65535/2

– 创建user表,指定用户名为varchar类型,长度不超过10个字符

create table user(username varchar(10)
);

所谓的不定长,是当插入的数据的长度小于指定的长度时,剩余的空间可以留给别的数据使用。(节省空间)

总结:长度固定的数据,用char类型,这样既不会浪费空间,效率也比较高

如果长度不固定,使用varchar类型,这样不会浪费空间。

3、大文本(长文本)类型

最长65535个字节,一般超过255个字符列的会使用text。

– 创建user表:

create table user(resume text
);

另,text也分多种,其中bigtext存储数据的长度约为4GB。

扩展内容3:(面试题)char(n)、varchar(n)、text都可以表示字符串类型,其区别在于:

(1)char(n)在保存数据时,如果存入的字符串长度小于指定的长度n,后面会用空格补全,因此可能会造成空间浪费,但是char类型的存储速度较varchar和text快。

因此char类型适合存储长度固定的数据,这样就不会有空间浪费,存储效率比后两者还快!

(2)varchar(n)保存数据时,按数据的真实长度存储,剩余的空间可以留给别的数据用,因此varchar不会浪费空间。

因此varchar适合存储长度不固定的数据,这样不会有空间的浪费。

(3)text是大文本类型,一般文本长度超过255个字符,就会使用text类型存储。

日期类型

date:年月日

time:时分秒

datetime:年月日 时分秒

timestamp:时间戳(实际存储的是一个时间毫秒值),与datetime存储日期格式相同。两者的区别是:

  • timestamp最大表示2038年,而datetime范围是1000~9999

  • timestamp在插入数据、修改数据时,可以自动更新成系统当前时间(后面用到时再做讲解)

mysql的字段约束

字段约束/列约束 --> 约束: 限制

主键约束

主键约束:如果为一个列添加了主键约束,那么这个列就是主键,主键的特点是唯一且不能为空。

主键的作用: 作为一个唯一标识,唯一的表示一条表记录(作用类似于人的身份证号,可以唯一的表示一个人一样。)

添加主键约束,例如将id设置为主键:

create table stu(id int primary key,...
);

如果主键是数值类型,为了方便插入主键(并且保证插入数据时,主键不会因为重复而报错),可以设置一个主键自增策略。

create table stu(id int primary key auto_increment,...
);

主键自增策略是指:设置了自增策略的主键,可以在插入记录时,不给id赋值,只需要设置一个null值,数据库会自动为id分配一个值(AUTO_INCREMENT变量,默认从1开始,后面依次+1),这样既可以保证id是唯一的,也省去了设置id的麻烦。

将id主键设置为自增:

create table stu(id int primary key auto_increment,...
);

非空约束

非空约束:如果为一个列添加了非空约束,那么这个列的值就不能为空,但可以重复。

添加非空约束,例如为password添加非空约束:

create table user(password varchar(50) not null,...
);

唯一约束

唯一约束:如果为一个列添加了唯一约束,那么这个列的值就必须是唯一的(即不能重复),但可以为空。

添加唯一约束,例如为username添加唯一约束及非空约束:

create table user(username varchar(50) unique not null,...
);

外键约束

外键其实就是用于通知数据库两张表数据之间对应关系的这样一个列。

这样数据库就会帮我们维护两张表中数据之间的关系。

(1) 创建表的同时添加外键

create table emp(id int,name varchar(50),dept_id int,foreign key(dept_id) references dept(id)
);

(1)如果是要表示两张表的数据之间存在对应关系,只需要在其中的一张表中添加一个列,保存另外一张表的主键,就可以保存两张表数据之间的关系。

但是添加的这个列(dept_id)对于数据库来说就是一个普通列,数据库不会知道两张表存在任何关系,因此数据库也不会帮我们维护这层关系。

(2)如果将dept_id列设置为外键,等同于通知数据库,部门表和员工表之间存在对应关系,dept_id列中的数据要参考部门的主键,数据库一旦知道部门和员工表之间存在关系,就会帮我们维护这层关系。

思考:如果在创建表时没有指定外键,那么后期该如何指定外键?以及如何删除外键?

表关系

常见的表关系分为以下三种:

一对多(多对一)·、一对一、多对多

多表查询

– 准备数据: 以下练习将使用db30库中的表及表记录,请先进入db30数据库!!!

连接查询

– 42.查询部门和部门对应的员工信息

select * from dept,emp;

上面的查询中存在大量错误的数据,一般我们不会直接使用这种查询。

笛卡尔积查询:所谓笛卡尔积查询就是指,查询两张表,其中一张表有m条记录,另一张表有n条记录,查询的结果是m*n条。

虽然笛卡尔积查询中包含大量错误数据,但我们可以通过where子句将错误数据剔除,保留下来的就是正确数据。

-- 通过where子句将笛卡尔积查询结果中错误的数据剔除,保留正确数据!
select * from dept,emp
where emp.dept_id=dept.id;
-- 因为id在两张表中都存在,所以为了区分,必须在列名前面加上[表名.]

通过where子句将笛卡尔积查询中的错误数据剔除,保留正确的数据,这就是连接查询!

上面的查询可以换成下面的查询:

select * from dept inner join emp
on emp.dept_id=dept.id;
-- 内连接查询,结果和上面的连接查询相同!

左外连接查询

– 43.查询【所有部门】及部门对应的员工,如果某个部门下没有员工,员工显示为null

-- 查询的结果中都是有员工的部门和有部门的员工
select * from dept, emp
where emp.dept_id=dept.id;
-- 如果要查询部门表中的所有部门,以及部门对应的员工
select * from dept left join emp
on emp.dept_id=dept.id;

左外连接查询:可以将左边表中的所有记录都查询出来,右边表只显示和左边相对应的数据,如果左边表中某些记录在右边没有对应的数据,右边显示为null即可。

右外连接查询

– 44.查询【所有员工】及员工所属的部门,如果某个员工没有所属部门,部门显示为null即可

-- 查询的结果中都是有员工的部门和有部门的员工
select * from dept, emp
where emp.dept_id=dept.id;
-- 如果要查询所有员工及员工对应的部门
select * from dept right join emp
on emp.dept_id=dept.id;

右外连接查询:可以将右边表中的所有记录都查询出来,左边表只显示和右边相对应的数据,如果右边表中某些记录在左边没有对应的数据,可以显示为null。

扩展:如果想将两张表中的所有数据都查询出来(左外+右外并去除重复记录),可以使用全外连接查询,但是mysql又不支持全外连接查询。

select * from dept left join emp on emp.dept_id=dept.id
union
select * from dept right join emp on emp.dept_id=dept.id;

可以使用union将左外连接查询的结果和右外连接查询的结果合并在一起,并去除重复的记录。例如:

需要注意的是:union可以将两条SQL语句执行的结果合并,但是有前提:

(1)两条SQL语句查询的结果列数必须一致

(2)两条SQL语句查询的结果列名、顺序也必须一致

并且union默认就会将两个查询中重复的记录去除(如果不希望去除重复记录,可以使用union all)

子查询练习

– 准备数据:以下练习将使用db40库中的表及表记录,请先进入db40数据库!!!

– 45.列出薪资比’王海涛’的薪资高的所有员工,显示姓名、薪资

-- 求出'王海涛'的薪资
select sal,name from emp where name='王海涛'; -- 2450
-- 求比'王海涛'薪资还高的员工
select name,sal from emp where sal>(select sal from emp where name='王海涛');
-- 子查询: 将一条SQL语句执行的结果作为另外一条SQL语句的条件来执行,这种查询叫做子查询

– 46.列出与’刘沛霞’从事相同职位的所有员工,显示姓名、职位。

-- 求出'刘沛霞'从事的职位
select job from emp where name='刘沛霞'; -- 推销员
-- 求出和'刘沛霞'从事相同职位的员工
select name,job from emp
where job=(select job from emp where name='刘沛霞');

– 47.列出薪资比’大数据部’部门(已知部门编号为30)所有员工薪资都高的员工信息,显示员工姓名、薪资和部门名称。

如果不考虑没有部门的员工

-- 连接查询员工表和部门表
select emp.name,sal,dept.name from emp,dept
where emp.dept_id=dept.id;
-- 求出大数据部门的最高薪资
select max(sal) from emp where dept_id=30; -- 3000
-- 求薪资比大数据部门最高薪资还高的员工
select emp.name,sal,dept.name from emp,dept
where emp.dept_id=dept.id and sal>(select max(sal) from emp where dept_id=30);

如果加上没有部门的员工

-- 左外连接查询员工表和部门表
select emp.name,sal,dept.name from emp left join dept
on emp.dept_id=dept.id;
-- 求出大数据部门的最高薪资
select max(sal) from emp where dept_id=30; -- 3000
-- 求薪资比大数据部门最高薪资还高的员工
select emp.name,sal,dept.name from emp left join dept
on emp.dept_id=dept.id
where sal>(select max(sal) from emp where dept_id=30);

多表查询练习

– 48.列出在’培优部’任职的员工,假定不知道’培优部’的部门编号,显示部门名称,员工名称。

-- 连接查询部门表和员工表
select dept.name, emp.name from emp,dept
where emp.dept_id=dept.id;
-- 求出部门名称为'培优部'的所有员工
select dept.name, emp.name from emp,dept
where emp.dept_id=dept.id and dept.name='培优部';-- 为emp和dept表分别指定表别名
select d.name, e.name from emp e,dept d
where e.dept_id=d.id and d.name='培优部';

– 49.(自查询)列出所有员工及其直接上级,显示员工姓名、上级编号,上级姓名

/* 将emp表看做两张表, emp e1(员工表)  emp e2(上级表)* 显示的列: e1.name, e2.id, e2.name* 查询的表: emp e1, emp e2* 连接条件: e1.topid=e2.id*/
select e1.name, e2.id, e2.name
from emp e1, emp e2
where e1.topid=e2.id;

– 50.列出最低薪资大于1500的各种职位,显示职位和该职位的最低薪资

-- 求出每种职位的最低薪资(将员工按照职位分组,求每组的最低薪资)
select job,min(sal) from emp group by job;-- 在上面查询的基础上, 筛选出最低薪资大于1500的职位
select job,min(sal) from emp group by job
having min(sal)>1500;

补充内容:where和having子句的区别:

相同点: 都是对查询的结果进行筛选过滤
不同点:
(1)where是在分组之前对数据进行筛选过滤; 而having是在分组之后对数据进行筛选过滤
(2)where子句中不能使用多行数据, 也不能使用列别名, 但可以使用表别名; 而having中可以使用多行函数, 也可以使用列别名和表别名;

– 51.列出在每个部门就职的员工数量、平均工资。显示部门编号、员工数量,平均薪资。

-- 根据部门编号对员工进行分组, 统计每个组(部门)中的员工人数、平均薪资
select dept_id 部门编号, count(*) 员工人数, avg(sal) 平均薪资
from emp group by dept_id;
-- 练习: 统计每个职位的员工数量、平均薪资,显示职位,员工数量和平均薪资
select job,count(*),avg(sal) from emp group by job;

– 52.查出至少有一个员工的部门,显示部门编号、部门名称、部门位置、部门人数。

-- 连接查询员工表和部门表
select d.id, d.name, d.loc, count(*)
from emp e, dept d
where e.dept_id=d.id;
-- 在上面查询的基础上,按照部门对员工进行分组,统计每个部门的人数
select d.id, d.name, d.loc, count(*)
from emp e, dept d
where e.dept_id=d.id
group by d.name
having count(*)>0;

– 53.列出受雇日期早于直接上级的所有员工,显示员工编号、员工姓名、部门名称。

/* emp e1(员工表) emp e2(上级表) dept d* 显示的列: e1.id, e1.name, d.name* 查询的表: emp e1, emp e2, dept d* 连接条件: e1.topid=e2.ide1.dept_id=d.id* 筛选条件: e1.hdate < e2.hdate*/
select e1.id, e1.name, d.name
from emp e1, emp e2, dept d
where e1.topid=e2.id and e1.dept_id=d.idand e1.hdate < e2.hdate;

– 补充:查询员工表中薪资最高的员工信息

-- 查询员工表欧中的最高薪资,显示员工姓名、最高薪资
select name, max(sal) from emp; -- name和max(sal)很可能不对应-- 方式一:对薪资进行降序排序,第一个薪资就是最高薪资,用分页查询第一条记录
select name,sal from emp order by sal desc limit 0,1;
-- 方式二:先求出最高薪资,拿着最高薪资到emp表中找对应的员工
select max(sal) from emp;
select name,sal from emp where sal=(select max(sal) from emp);

数据库备份与恢复

备份数据库

在cmd窗口中(未登录的状态下),可以通过如下命令对指定的数据库进行备份:

mysqldump -u用户名 -p 数据库的名字 > 备份文件的位置

示例1: 对db40库中的数据(表,表记录)进行备份,备份到 d:/db40.sql文件中

mysqldump -uroot -p db40 > d:/db40.sql

键入密码,如果没有提示,即表示备份成功!

也可以一次性备份所有库,例如:

对mysql服务器中所有的数据库进行备份,备份到 d:/all.sql文件中

mysqldump -uroot -p --all-database > d:/all.sql

键入密码,如果没有提示错误(警告信息不是错误,可以忽略),即表示备份成功!

恢复数据库

1、恢复数据库方式一:

在cmd窗口中(未登录的状态下),可以通过如下命令对指定的数据库进行恢复:

mysql -u用户名 -p 数据库的名字 < 备份文件的位置

示例:将d:/db40.sql文件中的数据恢复到db60库中

– 在cmd窗口中(已登录的状态下),先创建db60库:

create database db60 charset utf8;

– 在cmd窗口中(未登录的状态下)

mysql -uroot -p db60 < d:/db40.sql

2、恢复数据库方式二:

在cmd窗口中(已登录的状态下),可以通过source执行指定位置的SQL文件:

source sql文件的位置

示例:将d:/db40.sql文件中的数据恢复到db80库中

– 在cmd窗口中(已登录的状态下),先创建db80库,进入db80库:

create database db80 charset utf8;
use db80;

– 再通过source执行指定位置下的sql文件:

source d:/db40.sql

Navicat软件的使用

Navicat Premium是一套带图形用户界面的数据库管理工具,让你从单一应用程序中同时连接MySQL、MariaDB、MongoDB、SQL Server、Oracle、PostgreSQL 和 SQLite数据库。使用Navicat可以快速、轻松地创建、管理和维护数据库。

1、使用navicat连接mysql服务器(使用cmd连接mysql服务器)
2、查看所有库、进入数据库、创建数据库、删除数据库、修改数据库
3、创建表、查看表、修改表、删除表
4、新增表记录、查询表记录、修改表记录、删除表记录
5、使用navicat书写SQL语句操作数据库、表和表记录
...

哔哩哔哩视频链接:https://www.bilibili.com/video/BV1yA41147Vi/

扩展内容

现创建学生表:

use test; -- 进入test库
drop table if exists stu; -- 删除学生表(如果存在)
create table stu( -- 创建学生表id int, -- 学生idname varchar(20), -- 学生姓名gender char(1), -- 学生性别birthday date -- 出生年月
);

修改表—新增列

语法:ALTER TABLE tabname ADD col_name datatype [DEFAULT expr][,ADD col_name datatype…];

1、往stu表中添加score列,double类型

alter table stu add score double;

修改表—修改列

语法:ALTER TABLE tabname MODIFY (col_name datatype [DEFAULT expr][,MODIFY col_name datatype]…);

1、修改id列,将id设置为主键

alter table stu modify id int primary key;

2、修改id列,将id主键设置为自动增长

alter table stu modify id int auto_increment;

修改表—删除列

语法:ALTER TABLE tabname DROP [COLUMN] col_name;

1、删除stu表中的score列

alter table stu drop score;

添加或删除主键及自增

思考:a) 在建表时,如何为id指定主键约束和自增?

b) 建好的表,如何通过修改添加主键约束和自增?

c) 如何删除表中的主键约束和自增?

1、创建stu学生表,不添加主键自增, 查看表结果

use mydb1; -- 切换到mydb1库
drop table if exists stu; -- 删除stu学生表(如果存在)
create table stu( -- 重建stu学生表,没有主键自增id int,name varchar(20),gender char(1),birthday date
);
desc stu; -- 查看表结构

表结构如下: 没有主键约束和自增。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KJZL9jtg-1618403808162)(D:/1930200215/CGB2/第二阶段讲义02.assets/4f7cf015ff7829f26d326b6f443628cc-1595871301917.png)]

2、如果表没有创建,或者要删除重建,在创建时可以指定主键或主键自增

drop table if exists stu; -- 删除stu表
create table stu( -- 重新创建stu表时,指定主键自增id int primary key auto_increment,name varchar(20),gender char(1),birthday date
);
desc stu; -- 查看表结构

表结构如下: 已经添加了主键约束和自增。

3、如果不想删除重建表,也可以通过修改表添加主键或主键自增

再次执行第1步,创建stu学生表,不添加主键自增,查看表结果

– 例如: 将stu学生表中的id设置为主键和自动增长

alter table stu modify id int primary key auto_increment;
desc stu; -- 查看表结构

如果只添加主键约束,不设置自增

alter table stu modify id int **primary key**;

如果已经添加主键约束,仅仅设置自增,但需注意:

(1)如果没有设置主键,不可添加自增

(2)只有当主键是数值时,才可以添加自增

alter table stu modify id int **auto_increment**;

4、如果想删除主键自增

– 删除主键自增时,要先删除自增

alter table stu modify id int;

– 再删除主键约束

alter table stu drop primary key;
desc stu; -- 查看表结构

添加外键约束

1、添加外键方式一:建表时添加外键

现有部门表如下:

– 创建部门表

create table dept(id int primary key auto_increment, -- 部门编号name varchar(20) -- 部门名称
);

要求创建员工表,并在员工表中添加外键关联部门主键

– 创建员工表

create table emp(id int primary key auto_increment, -- 员工编号name varchar(20), -- 员工姓名dept_id int, -- 部门编号foreign key(dept_id) references dept(id) -- 指定dept_id为外键
);

2、添加外键方式二:建表后添加外键

现有部门表和员工表:

– 创建部门表

create table dept(id int primary key auto_increment, -- 部门编号name varchar(20) -- 部门名称
);

– 创建员工表

create table emp(id int primary key auto_increment, -- 员工编号name varchar(20), -- 员工姓名dept_id int -- 部门编号
);

– 如果表已存在,可以使用下面这种方式:

alter table emp add constraint fk_dept_id foreign key(dept_id) references dept(id);

其中 fk_dept_id (名字由自己定义),是指外键约束名称,也可以将【constraint fk_dept_id】省略,MySQL会自动分配一个外键名称,将来可以通过该名称删除外键。

foreign key(dept_id)中的dept_id为外键

删除外键约束

1、首先通过 “show create table 表名”语法,查询含有外键表的建表语句,例如:

show create table emp;

显示结果如下:

其中,emp_ibfk_1是在创建表时,数据库为外键约束指定的一个名字,删除这个名字即可删除外键关系,例如:

alter table emp drop foreign key emp_ibfk_1;

外键删除成功!

添加外键约束(多对多)

– 现有学生(stu)表和教师(tea)表:

– 创建学生表

create table stu(stu_id int primary key auto_increment, -- 学生编号name varchar(20) -- 学生姓名
);

– 创建教师表

create table tea(tea_id int primary key auto_increment, -- 教师编号name varchar(20) -- 教师姓名
);

– 添加第三方表(stu_tea)表示学生表和教师表关系

– 创建学生和教师关系表

create table stu_tea(stu_id int, -- 学生编号tea_id int, -- 教师编号primary key(stu_id,tea_id), -- 设置联合主键foreign key(stu_id) references stu(stu_id), -- 添加外键foreign key(tea_id) references tea(tea_id) -- 添加外键
);

其中为了防止重复数据,将stu_id和tea_id设置为联合主键。

将stu_id设置为外键,参考stu表中的stu_id列

并将tea_id设置为外键,参考tea表中的tea_id列

级联更新、级联删除

– 创建db20库、dept表、emp表并插入记录

– 删除db20库(如果存在),并重新创建db20库

drop database if exists db20;
create database db20 charset utf8;
use db20;

– 创建部门表, 要求id, name字段

create table dept(id int primary key auto_increment, -- 部门编号name varchar(20) -- 部门名称
);

– 往部门表中插入记录

insert into dept values(null, '财务部');
insert into dept values(null, '人事部');
insert into dept values(null, '科技部');
insert into dept values(null, '销售部');

– 创建员工表, 要求id, name, dept_id

create table emp(id int primary key auto_increment, -- 员工编号name varchar(20), -- 员工姓名dept_id int, -- 部门编号foreign key(dept_id) references dept(id) -- 指定外键on update cascade -- 级联更新on delete cascade -- 级联删除
);
insert into emp values(null, '张三', 1);
insert into emp values(null, '李四', 2);
insert into emp values(null, '老王', 3);
insert into emp values(null, '赵六', 4);
insert into emp values(null, '刘能', 4);

级联更新:主表(dept表)中的主键发生更新时(例如将销售部的id改为40),从表(emp表)中的记录的外键数据也会跟着该表(即赵六和刘能的部门编号也会更新为40)

级联删除:如果不添加级联删除,当删除部门表中的某一个部门时(例如删除4号部门),若该部门在员工表中有对应的员工(赵六和刘能),删除会失败!

若果添加了级联删除,当删除部门表中的某一个部门时,若该部门在员工表中有对应的员工,会在删除部门的同时,将员工表中对应的员工也删除!

where中不能使用列别名

SQL语句的书写顺序:

select * | 列名 -- 确定要查询的列有哪些
from 表名 -- 确定查询哪张表
where 条件 -- 通过筛选过滤,剔除不符合条件的记录
group by 分组的列 -- 指定根据哪一列进行分组
having 条件 -- 通过条件对分组后的数据进行筛选过滤
order by 排序的列 -- 指定根据哪一列进行排序
limit (countPage-1)*rowCount, rowCount -- 指定返回第几页记录以及每页显示多少条

SQL语句的执行顺序:

from 表名 -- 确定查询哪张表
where 条件 -- 通过筛选过滤,剔除不符合条件的记录
select * | 列名 列别名 -- 确定要查询的列有哪些,
group by 分组的列 -- 指定根据哪一列进行分组
having 条件 -- 通过条件对分组后的数据进行筛选过滤
order by 排序的列 -- 指定根据哪一列进行排序
limit (countPage-1)*rowCount, rowCount

**** 关于where中不能使用列别名但是可以使用表别名?**

database db20 charset utf8;
use db20;


-- 创建部门表, 要求id, name字段```sql
create table dept(id int primary key auto_increment, -- 部门编号name varchar(20) -- 部门名称
);

– 往部门表中插入记录

insert into dept values(null, '财务部');
insert into dept values(null, '人事部');
insert into dept values(null, '科技部');
insert into dept values(null, '销售部');

– 创建员工表, 要求id, name, dept_id

create table emp(id int primary key auto_increment, -- 员工编号name varchar(20), -- 员工姓名dept_id int, -- 部门编号foreign key(dept_id) references dept(id) -- 指定外键on update cascade -- 级联更新on delete cascade -- 级联删除
);
insert into emp values(null, '张三', 1);
insert into emp values(null, '李四', 2);
insert into emp values(null, '老王', 3);
insert into emp values(null, '赵六', 4);
insert into emp values(null, '刘能', 4);

级联更新:主表(dept表)中的主键发生更新时(例如将销售部的id改为40),从表(emp表)中的记录的外键数据也会跟着该表(即赵六和刘能的部门编号也会更新为40)

级联删除:如果不添加级联删除,当删除部门表中的某一个部门时(例如删除4号部门),若该部门在员工表中有对应的员工(赵六和刘能),删除会失败!

若果添加了级联删除,当删除部门表中的某一个部门时,若该部门在员工表中有对应的员工,会在删除部门的同时,将员工表中对应的员工也删除!

where中不能使用列别名

SQL语句的书写顺序:

select * | 列名 -- 确定要查询的列有哪些
from 表名 -- 确定查询哪张表
where 条件 -- 通过筛选过滤,剔除不符合条件的记录
group by 分组的列 -- 指定根据哪一列进行分组
having 条件 -- 通过条件对分组后的数据进行筛选过滤
order by 排序的列 -- 指定根据哪一列进行排序
limit (countPage-1)*rowCount, rowCount -- 指定返回第几页记录以及每页显示多少条

SQL语句的执行顺序:

from 表名 -- 确定查询哪张表
where 条件 -- 通过筛选过滤,剔除不符合条件的记录
select * | 列名 列别名 -- 确定要查询的列有哪些,
group by 分组的列 -- 指定根据哪一列进行分组
having 条件 -- 通过条件对分组后的数据进行筛选过滤
order by 排序的列 -- 指定根据哪一列进行排序
limit (countPage-1)*rowCount, rowCount

**** 关于where中不能使用列别名但是可以使用表别名?**

是因为,表别名是声明在from中,from先于where执行,先声明再使用没有问题,但是列别名是声明在select中,where先于select执行,如果先使用列别名,再声明,这样执行会报错!!

Servlet和MySQL基本语法概述相关推荐

  1. 浅析Mysql Join语法以及性能优化

    在讲MySQL的Join语法前还是先回顾一下联结的语法,呵呵,其实连我自己都忘得差不多了,那就大家一起温习吧,这里我有个比较简便的记忆方法,内外联结的区别是内联结将去除所有不符合条件的记录,而外联结则 ...

  2. [转]Mysql Join语法解析与性能分析

    转自:http://www.cnblogs.com/BeginMan/p/3754322.html 一.Join语法概述 join 用于多表中字段之间的联系,语法如下: ... FROM table1 ...

  3. mysql数据库语法_MySQL数据库语法(一)

    MySQL数据库语法 数据库管理系统(DBMS)的概述 什么是DBMS:数据的仓库 方便查询 可存储的数据量大 保证数据的完整.一致 安全可靠 DBMS的发展:今天主流数据库为关系型数据库管理系统(R ...

  4. mysql join 与 cross join 效率_浅析Mysql Join语法以及性能优化

    在讲MySQL的Join语法前还是先回顾一下联结的语法,呵呵,其实连我自己都忘得差不多了,那就大家一起温习吧,这里我有个比较简便的记忆方法,内外联结的区别是内联结将去除所有不符合条件的记录,而外联结则 ...

  5. MySql基础语法-java学习

    MySQL 目录结构 MySQL 的目录结构 描述 bin<目录> 所有 mysql 的可执行文件,如:mysql.exe MySQLInstanceConfig.exe 数据库的配置向导 ...

  6. MySQL基础语法总结

    MySQL基本语法 注:使用到的数据库表在文章末尾 文章目录 MySQL基本语法 基础查询 条件查询 排序查询 常见函数 分组查询 连接查询 基础查询补充 子查询 分页查询 联合查询 查询总结 插入 ...

  7. c3p0连接池配置_Maven+JSP+Servlet+C3P0+Mysql实现的音乐库管理系统

    项目简介 项目来源于:https://gitee.com/sunnyandgood/OnlineMusic 本系统基于Maven+JSP+Servlet+C3P0+Mysql实现的音乐库管理系统.简单 ...

  8. Servlet 3.0 新特性概述

    Servlet 3.0 新特性概述 Servlet 3.0 作为 Java EE 6 规范体系中一员,随着 Java EE 6 规范一起发布.该版本在前一版本(Servlet 2.5)的基础上提供了若 ...

  9. c3p0 mysql maven_Maven+JSP+Servlet+C3P0+Mysql实现的音乐库管理系统

    项目简介 本系统基于Maven+JSP+Servlet+C3P0+Mysql实现的音乐库管理系统.简单实现了充值.购买歌曲.poi数据导入导出.歌曲上传下载.歌曲播放.用户注册登录注销等功能. 难度等 ...

最新文章

  1. 通过web sql实现增删查改
  2. Windows10编译源码安装Aleth(Ethereum C++ client, tools and libraries)
  3. 如何系统性掌握深度学习模型设计和优化
  4. python语言的数字类型_基本数据类型数字
  5. 低配服务器装那个系统吗,用低配服务器好还是高配vps
  6. echart vue 图表大小_vue里echarts自适应窗口大小改变
  7. 小汤学编程之JavaEE学习day08——Maven
  8. MFC工作笔记0008---UDP协议通信_sendto 和 recvfrom 浅析与示例
  9. nmap 扫描工具 使用方法
  10. JSP变量和方法声明
  11. 计算机c语言实验心得与小结,C语言实验心得体会
  12. oracle怎么截取long类型,如何把long类型的值取出来
  13. 计算机思维是运用计算机科学的什么,计算思维是运用计算机科学的基础概念进行问题求解、系统设计以及人类行为理解等涵盖计算机科学之广度的一系列计算机的思维活动。...
  14. unity3D-游戏/AR/VR在线就业班 C#入门访问修饰符学习笔记
  15. CentOS虚拟机根分区磁盘扩容操作
  16. 交大研究生去日本签证攻略
  17. Java 接收返回json数据动态取data里的值
  18. 我们应当怎样做需求确认:快速原型法
  19. 【研究生】这六种引用参考文献的行为,居然属于学术不端?
  20. fiddler监控手机请求 1

热门文章

  1. Google 开发者网站常用网址
  2. 【JavaScript】try/catch和Promise.catch捕捉错误的区别?
  3. 关于HC04超声波模块测距的进一步思考(51版)
  4. 2022-2028中国高纯氘气市场现状研究分析与发展前景预测报告
  5. Java注解系列之5th(完结)
  6. 一文读懂TDengine的三种查询功能
  7. 67在C51语言表示16进制数,一级笔试试题考核的主要知识点及答案
  8. 路由器设置好竟能将wifi提速这么多!
  9. 基于pxe技术实现linux自动安装原理,网络安装Linux的技术原理分析及实现
  10. 使用Python对图像进行不同级别量化QP,使用RLE计算压缩比,并计算对应的PSNR