MySql 面试题

170 阅读19分钟

什么是jdbc

如何通过 Java 程序来访问数据库呢?可以使用 JDBC(Java DataBase Connectivity)。JDBC 由一组使用 Java 语言编写的类和接口组成,可以为 Oracle、MySQL 等多种关系数据库提供统一的访问方式,从而实现用 Java 代码来操作数据库。

JDBC连接数据库的七个步骤

1、  加载JDBC驱动程序:

在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM。

这个通过java.lang.Class类的静态方法forname(String ClassName)实现的

2、  提供JDBC连接的URL

连接URL定义了连接数据库时的协议,子协议,数据源标示

"jdbc:mysql://localhost:3306/jdbctest?useUnicode=true&characterEncoding=UTF-8","root","123"

其中useUnicode=true表示使用Unicode字符集。如果characterEncoding设置为GBK或者是UTF-8,该参数必须设置为TRUE。characterEncoding=UTF-8字符编码方式。

3、  创建数据库的连接

要想连接数据库,需要向java.sql.DriverManager请求并获取Connection对象,获取的对象就代表着一个数据库的链接。数据库链接时使用的是DriverManager的getConnectin(String url , String username , String password )方法传入指定的欲连接的数据库的路径、数据库的用户名和密码来获得。 

JDBC 使用 DriverManager 类来管理驱动程序,并通过其 getConnection() 获取连接对象,代码如下:

Connection connection = DriverManager.getConnection("连接字符串", "数据库用户名", "数据库密码");

Connection 接口的常用方法如下表所示:

方 法简 介
Statement createStatement() throws SQLException创建 Statement 对象
PreparedStatement prepareStatement(String sql)创建 PreparedStatement 对象
获得了 Connection 对象后,就可以通过 Connection 对象来获得 Statement 或 PreparedStatement 对象,并通过该对象向数据库发送 SQL 语句。

4、  创建一个statement

要执行sql语句,必须获得statement实例,statement实例分为以下3中类型:

  • 执行静态sql语句时,通常通过Statement实例实现。
// 创建 Statement 对象
Statement stmt = connection.createStatement();
  • 执行动态sql语句时,通常通过PreparedStatement实例实现。
// 用占位符来代替参数值 
String deleteSql = "DELETE FROM student WHERE stuName = ? AND stuAge = ?" ;

// 创建 PreparedStatement 对象
PreparedStatement pstmt = connection.prepareStatement(deleteSql("增、删、改、查的 SQL 语句"));

// 将第一个占位符?的值替换为 `张三` (占位符的位置是从 1 开始的) 
pstmt.setString(1, "张三"); 

// 将第二个占位符?的值替换为 23 
pstmt.setInt(2, 23);
  • 执行数据库存储过程,通常通过CallableStatement实例实现。

5、执行sql语句

Statement接口提供了三种执行sql语句的方法:executeQuery、executeUpdate和execute

1、ResultSet executeQuery(String sql):执行查询数据库的sql语句,返回一个结果集(ResultSet)对象。

2、int executeUpdate(String sql):用于执行insert、update或Delete语句以及sql DDL语句。

3、execute(sql):用于执行返回多个结果集、多个更新计数或二者组合的语句。

  • Statement 发送“增、删、改”类型的 SQL 语句:
int count = stmt.executeUpdate("增、删、改的 SQL 语句")

发送“查询”类型的 SQL 语句:

ResultSet rs = stmt.executeQuery("查询的 SQL 语句");
  • PreparedStatement(在创建PreparedStatement对象时,已经将Sql语句传入) 发送“增、删、改”类型的 SQL 语句:
int count = pstmt.executeUpdate();

发送“查询”类型的 SQL 语句:

ResultSet rs = stmt.executeQuery();

6、处理结果

两种情况:

1、  执行查询返回的结果是一个ResultSet对象。

2、  执行更新返回的是本次操作影响的行数。

方法简介
int executeUpdate()用于执行 INSERT、UPDATE、DELETE 以及 DDL(数据定义语言)语句(如 CREATE TABLE… 和 DROP TABLE…)。对于 INSERT、UPDATE、DELETE 语句,会返回一个 int 型结果,表示多少行受到了影响,即增、删、改了几条数据对于 CREATE TABLE 或 DROP TABLE 等 DDL 类型的语句,executeUpdate 的返回值总为零
ResultSet executeQuery()用于执行 SELECT 查询语句,返回值是一个 ResultSet 类型的结果集。
void close()关闭 Statement 对象。

7、  关闭JDBC对象

操作完成以后要把所有使用的JDBC对象全都关闭,以释放JDBC资源,关闭顺序和声明顺序相反:  

1、  关闭记录集  

2、  关闭声明  

3、 关闭连接对象

源码如下:

  • 使用 Statement 实现查操作

查询数据库和增、删、改操作的步骤基本相同

import java.sql.*;
public class JDBCQueryByStatement{
    final static String DRIVER = "com.mysql.cj.jdbc.Driver";
    // 数据库的实例名是 shiyan
    final static String URL = "jdbc:mysql://localhost:3306/shiyan?useUnicode=true&characterEncoding=utf8";
    final static String USERNAME = "root";
    final static String PASSWORD = "";
    static Connection connection = null;
    static Statement stmt = null;
    static ResultSet rs = null;
    public static void executeQuery() {
        try{
            // 1 加载数据库驱动
            Class.forName(DRIVER);
            // 2 获取数据库连接
            connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            // 3 通过连接,获取一个操作数据库 Statement 的对象
            stmt = connection.createStatement();
            // 4 通过 executeQuery()实现对数据库的查询,并返回一个结果集(结果集中包含了所有查询到的数据)
            String querySql = "SELECT stuNo, stuName, stuAge FROM student";
            rs = stmt.executeQuery(querySql);
            // 5 通过循环读取结果集中的数据
            while(rs.next()) {
                // 等价于 rs.getInt(1);
                int stuNo = rs.getInt("stuNo");
                // rs.getString(2);
                String stuName = rs.getString("stuName");
                // rs.getString(3);
                int stuAge = rs.getInt("stuAge");
                System.out.println(stuNo+"\t"+stuName+"\t"+stuAge);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            try {
                // 注意 rs、stmt、connection 三个对象的关闭顺序
                if(rs != null)rs.close();
                if(stmt != null)stmt.close();
                if(connection != null)connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args){
        executeQuery();
    }
}

执行 executeQuery() 方法,即可查询出 student 表中所有的 stuNo、stuName 和 stuAge 字段值。

  • 使用 PreparedStatement 实现查操作
import java.sql.*;
import java.util.Scanner;
public class JDBCQueryByPreparedStatement{
final static String DRIVER = "com.mysql.cj.jdbc.Driver";
    // 数据库的实例名是 shiyan
    final static String URL = "jdbc:mysql://localhost:3306/shiyan?useUnicode=true&characterEncoding = utf8";
    final static String USERNAME = "root";
    final static String PASSWORD = "";
    static Connection connection = null;
    static Statement stmt = null;
    static ResultSet rs = null;
    public static void executeQuery() {
        Scanner input = new Scanner(System.in);
        try{
            Class.forName(DRIVER);
            connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            System.out.println("请输入用户名:");
            String name = input.nextLine();
            System.out.println("请输入密码:");
            String pwd = input.nextLine();
            // 如果用户输入的 username 和 password 在表中有对应的数据(count(1) > 0),
            // 则说明存在此用户
            String querySql = "SELECT COUNT(1) FROM login WHERE username = ? AND password = ?" ;
            pstmt = connection.preparedStatement(querySql);
            pstmt.setString(1, name);
            pstmt.setString(2, pwd);
            rs = pstmt.executeQuery();
            if (rs.next()){
                // 获取 SQL 语句中 count(1)的值
                int count = rs.getInt(1);
                if (count > 0)
                    System.out.println("登录成功");
                else {
                    System.out.println("登录失败");
                }
            }
        }
        catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            try {
                // 注意 rs、stmt、connection 三个对象的关闭顺序
                if(rs != null)rs.close();
                if(stmt != null)stmt.close();
                if(connection != null)connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args){
        executeQuery();
    }
}

如果使用 PreparedStatement 进行模糊查询,可以在 setxxx() 方法的第二个参数中加入通配符(如 % )。例如,根据 name 模糊查询代码如下。

PreparedStatement pstmt = ... ;
ResultSet rs = ... ;
...
String querySql = "SELECT *  FROM book WHERE name LIKE ?" ;
pstmt.setString(1, "%" +name +"%");
rs = pstmt.executeQuery();

需要注意的是,如果使用的是 Statement ,当需要给 SQL 语句拼接 String 类型变量时,必须加上单引号,例如 SELECT … FROM … WHERE stuName LIKE'%"+name + "%';但如果使用的是 PreparedStatement ,则不需要加,例如: pstmt.setString(1, "%" +name +"%") 。

  • 使用 Statement 提供的的 executeUpdate() 方法实现 ’增‘ 操作
import java.sql.*;
public class JDBCUpdateByStatement{ 
final static String DRIVER = "com.mysql.cj.jdbc.Driver";
// 数据库的实例名是 shiyan
final static String URL = "jdbc:mysql://localhost:3306/shiyan?useUnicode=true&characterEncoding=utf8"; 
final static String USERNAME = "root"; 
final static String PASSWORD = "";
static Connection connection = null; 
static Statement stmt = null; 
static ResultSet rs = null; 
// 执行 `插入` 的方法
public static boolean executeUpdate() {
boolean flag = false ; try{ 
// 1 加载数据库驱动
Class.forName(DRIVER); 
// 2 获取数据库连接
connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
// 3 通过连接,获取一个 Statement 的对象,用来操作数据库 
stmt = connection.createStatement();
// 4 通过 executeUpdate()实现插入操作
String addSql = "INSERT INTO student(stuNo, stuName, stuAge) VALUES(5, '王五', 25)" ; 
int count = stmt.executeUpdate(addSql); 
System.out.println("受影响的行数是:"+count);
flag = true ; 
// 如果一切正常,没有发生异常,则将 flag 设置为 true 
}
catch (ClassNotFoundException e) { 
e.printStackTrace(); 
} catch (SQLException e) {
e.printStackTrace(); 
} catch (Exception e) {
e.printStackTrace();
} finally{ 
try {
if(stmt != null)stmt.close(); 
if(connection != null)connection.close();
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) { 
e.printStackTrace(); 
} 
}
return flag ; 
} 
public static void main(String[] args){ executeUpdate();
} 
}
  • 使用 PreparedStatement 提供的的 executeUpdate() 方法实现 ’删‘ 操作
import java.sql.*;
public class JDBCUpdateByPreparedStatement{
    final static String DRIVER = "com.mysql.cj.jdbc.Driver";
    //数据库的实例名是 shiyan
    final static String URL = "jdbc:mysql://localhost:3306/shiyan?useUnicode=true&characterEncoding=utf8";
    final static String USERNAME = "root";
    final static String PASSWORD = "";
    static Connection connection = null;
    static Statement stmt = null;
    static ResultSet rs = null;
    static PreparedStatement pstmt = null;
    public static boolean executeUpdate() {
        boolean flag = false;
        try {
            Class.forName(DRIVER);
            connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            // 用占位符来代替参数值
            String deleteSql = "DELETE FROM student WHERE stuName = ? AND stuAge = ?" ;
            pstmt = connection.prepareStatement(deleteSql);
            // 将第一个占位符?的值替换为 `张三` (占位符的位置是从 1 开始的)
            pstmt.setString(1, "张三");
            // 将第二个占位符?的值替换为 23
            pstmt.setInt(2, 23);
            int count = pstmt.executeUpdate();
            System.out.println("受影响的行数是:" + count);
            flag = true;
        }
        catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            try {
                // 注意 rs、stmt、connection 三个对象的关闭顺序
                if(rs != null)rs.close();
                if(stmt != null)stmt.close();
                if(connection != null)connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return flag;
    }
    public static void main(String[] args){
        executeUpdate();
    }
}

可见,与 Statement 相比,本次使用 PreparedStatement 执行增、删、改操作的不同之处如下:

  1. SQL 语句提前写在了 prepareStatement() 方法参数中。
  2. 先在 SQL 语句中使用了占位符 ? ,然后使用 setxxx() 方法对占位符进行了替换。

Statement 和 PreparedStatement 的比较

Statement 和 PreparedStatement 都可以实现数据库的增删改查等操作。但在实际开发中,一般推荐使用 PreparedStatement 。因为两者相比,PreparedStatement 有如下优势。

提高了代码的可读性和可维护性

PreparedStatement 可以避免烦琐的 SQL 语句拼接操作。例如,SQL 语句 insert into student(stuNo,stuName,stuAge,course) values(5,'王五',25) ,如果将其中的字段值用变量来表示(int stuNo=5;String stuName="王五";int stuAge=23;),用 Statement 方式执行时,需要写成:

stmt.executeUpdate("insert into student(stuNo,stuName,stuAge ) values("+stuNo+",'"+stuName+"',"+stuAge+")");

而如果用 PreparedStatement 方式执行时,就可以先用 ? 充当参数值的占位符,然后再用 setXxx() 方法设置 ? 的具体值,从而避免 SQL 语句的拼接操作。

提高了 SQL 语句的性能

在使用 Statement 和 PreparedStatement 向数据库发送 SQL 语句时,数据库都会解析并编译该 SQL 语句,并将解析和编译的结果缓存起来。但在使用 Statement 时,这些缓存结果仅仅适用于那些完全相同的 SQL 语句(SQL 主体和拼接的 SQL 参数均相同)。换个角度讲,如果某条 SQL 的 SQL 主体相同,但拼接的参数不同,也仍然不会使用之前缓存起来的结果,这就严重影响了缓存的使用效率。

而 PreparedStatement 就不会像 Statement 那样将 SQL 语句完整的编译起来,而是采用了预编译机制:只编译 SQL 主体,不编译 SQL 参数。因此,在使用 PreparedStatement 时,只要多条 SQL 语句的 SQL 主体相同(与 SQL 语句中的参数无关),就可以复用同一份缓存。这点就类似于 Java 中方法调用的流程:Java 编译器会预先将定义的方法编译好(但不会编译方法的参数值),之后在多次调用这个方法时,即使输入参数值不同,也可以复用同一个方法。因此,如果某个业务需要重复执行主体相同的 SQL 语句(无论 SQL 中的参数是否相同),就可以利用 PreparedStatement 这种预编译 SQL 的特性来提高数据库缓存的利用率,进而提升性能。

但要注意的是,PreparedStatement 虽然在执行重复的 SQL 语句时具有较高的性能,但如果某个 SQL 语句仅仅会被执行一次或者少数几次,Statement 的性能是高于 PreparedStatement 的。

提高了安全性,能有效防止 SQL 注入

在使用 Statement 时,可能会用以下代码来进行登录验证。

stmt = connection.createStatement();
String querySql = "select count(_) from login where username = '"+uname+"' and password = '"+upwd+"'" ;
rs = stmt.executeQuery(querySql);
…
if(rs.next()){
    int result = rs.getInt("count(_)");
    if(result>0) { //登录成功}
    else{//登录失败}
}

上述代码看起来没有问题,但试想如果用户输入的 uname 值是 任意值 or 1=1-- 、upwd 的值是 任意值 ,则 SQL 语句拼接后的结果如下:

select count(*) from login where username = '任意值' or 1=1-- and password = '任意值';

在这条 SQL 语句中,用 or 1=1 使 where 条件永远成立,并且用 -- 将后面的 SQL 语句注释掉,这样就造成了安全隐患(SQL 注入),使得并不存在的用户名和密码也能登录成功。

而 PreparedStatement 方式传入的任何数据都不会和已经编译的 SQL 语句进行拼接,因此可以避免 SQL 注入攻击。综上所述,在实际开发中推荐使用 PreparedStatement 操作数据库。

以上内容取自以下链接:

JDBC 连接数据库的七个步骤

JDBC 数据库连接

数据库三大范式是什么

  • 第一范式:每个列都不可以再拆分。
  • 第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。
  • 第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。

在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有足够的理由。比如性能。事实上我们经常会为了性能而妥协数据库的设计。

什么是索引?

  • 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。
  • 索引是一种数据结构。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。
  • 更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。

索引有哪几种类型?

主键索引: 数据列不允许重复,不允许为NULL,一个表只能有一个主键。

唯一索引: 数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。

  • 可以通过 ALTER TABLE table_name ADD UNIQUE (column); 创建唯一索引
  • 可以通过 ALTER TABLE table_name ADD UNIQUE (column1,column2); 创建唯一组合索引

普通索引: 基本的索引类型,没有唯一性的限制,允许为NULL值。

  • 可以通过ALTER TABLE table_name ADD INDEX index_name (column);创建普通索引
  • 可以通过ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);创建组合索引

全文索引: 是目前搜索引擎使用的一种关键技术。

  • 可以通过ALTER TABLE table_name ADD FULLTEXT (column);创建全文索引

什么是数据库事务?

  • 事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。
  • 事务最经典也经常被拿出来说例子就是转账了。
  • 假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。

事物的四大特性(ACID)介绍一下?

  • 关系性数据库需要遵循ACID规则,具体内容如下:

在这里插入图片描述

  1. 原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
  2. 一致性: 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
  3. 隔离性: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
  4. 持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。

什么是脏读?幻读?不可重复读?

  • 脏读

1、在事务A执行过程中,事务A对数据资源进行了修改,事务B读取了事务A修改后的数据。

2、由于某些原因,事务A并没有完成提交,发生了RollBack操作,则事务B读取的数据就是脏数据。

一个事务读取到另一个事务未提交的数据的现象就是脏读(Dirty Read)。

  • 不可重复读

事务B读取了两次数据资源,在这两次读取的过程中事务A修改了数据,导致事务B在这两次读取出来的数据不一致。

这种在同一个事务中,前后两次读取的数据不一致的现象就是不可重复读(Nonrepeatable Read)。

  • 幻读

事务B前后两次读取同一个范围的数据,在事务B两次读取的过程中事务A新增了数据,导致事务B后一次读取到前一次查询没有看到的行。

幻读和不可重复读有些类似,但是幻读强调的是集合的增减,而不是单条数据的更新。

什么是事务的隔离级别?MySQL的默认隔离级别是什么?

为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。

隔离级别脏读不可重复读幻读
READ-UNCOMMITTED
READ-COMMITTED×
REPEATABLE-READ××
SERIALIZABLE×××

SQL 标准定义了四个隔离级别:

读未提交(Read Uncommitted),是最低的隔离级别,所有的事务都可以看到其他未提交的事务的执行结果。只能防止第一类更新丢失,不能解决脏读,可重复读,幻读,所以很少应用于实际项目。

读已提交(Read Committed), 在该隔离级别下,一个事务的更新操作结果只有在该事务提交之后,另一个事务才可能读取到同一笔数据更新后的结果。可以防止脏读和第一类更新丢失,但是不能解决可重复读和幻读的问题。

可重复读(Repeatable Read),MySQL默认的隔离级别。在该隔离级别下,一个事务多次读同一个数据,在这个事务还没结束时,其他事务不能访问该数据(包括了读写),这样就可以在同一个事务内两次读到的数据是一样的。可以防止脏读、不可重复读、第一类更新丢失、第二类更新丢失的问题,不过还是会出现幻读。

串行化(Serializable),这是最高的隔离级别。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。在这个级别,可以解决上面提到的所有并发问题,但可能导致大量的超时现象和锁竞争,通常不会用这个隔离级别。

注意:

  • 这里需要注意的是:Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别
  • 事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVVC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。
  • 因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容): ,但是你要知道的是InnoDB 存储引擎默认使用 REPEATABLE-READ(可重读) 并不会有任何性能损失。
  • InnoDB 存储引擎在 分布式事务 的情况下一般会用到 SERIALIZABLE(可串行化) 隔离级别。

以上内容取自以下链接:

什么是脏读、不可重复读、幻读?

一文详解脏读、不可重复读、幻读

SQL语句主要分为哪几类

  • 数据定义语言DDL(Data Ddefinition Language) CREATE,DROP,ALTER主要为以上操作 即对逻辑结构等有操作的,其中包括表结构,视图和索引。

  • 数据查询语言DQL(Data Query Language) SELECT这个较为好理解 即查询操作,以select关键字。各种简单查询,连接查询等 都属于DQL。

  • 数据操纵语言DML(Data Manipulation Language) INSERT,UPDATE,DELETE主要为以上操作 即对数据进行操作的,对应上面所说的查询操作 DQL与DML共同构建了多数初级程序员常用的增删改查操作。而查询是较为特殊的一种 被划分到DQL中。

  • 数据控制功能语言DCL(Data Control Language) GRANT,REVOKE,COMMIT,ROLLBACK主要为以上操作 即对数据库安全性完整性等有操作的,可以简单的理解为权限控制等。

六种关联查询

  • 交叉连接(CROSS JOIN)

  • 内连接(INNER JOIN)

  • 外连接(LEFT JOIN/RIGHT JOIN)

  • 联合查询(UNION与UNION ALL)

  • 全连接(FULL JOIN)

  • 交叉连接(CROSS JOIN)

    SELECT * FROM A,B(,C)或者SELECT * FROM A CROSS JOIN B (CROSS JOIN C)
    #没有任何关联条件,结果是笛卡尔积,结果集会很大,没有意义,很少使用内连接(INNER JOINSELECT * FROM A,B WHERE A.id=B.id或者SELECT * FROM A INNER JOIN B ON A.id=B.id
    多表中同时符合某种条件的数据记录的集合,INNER JOIN可以缩写为JOIN
    

内连接分为三类

  • 等值连接:ON A.id=B.id
  • 不等值连接:ON A.id > B.id
  • 自连接:SELECT * FROM A T1 INNER JOIN A T2 ON T1.id=T2.pid

外连接(LEFT JOIN/RIGHT JOIN)

  • 左外连接:LEFT OUTER JOIN, 以左表为主,先查询出左表,按照ON后的关联条件匹配右表,没有匹配到的用NULL填充,可以简写成LEFT JOIN
  • 右外连接:RIGHT OUTER JOIN, 以右表为主,先查询出右表,按照ON后的关联条件匹配左表,没有匹配到的用NULL填充,可以简写成RIGHT JOIN

联合查询(UNION与UNION ALL)

SELECT * FROM A UNION SELECT * FROM B UNION ...
  • 就是把多个结果集集中在一起,UNION前的结果为基准,需要注意的是联合查询的列数要相等,相同的记录行会合并
  • 如果使用UNION ALL,不会合并重复的记录行
  • 效率 UNION 高于 UNION ALL

全连接(FULL JOIN)

SELECT * FROM A LEFT JOIN B ON A.id=B.id UNION SELECT * FROM A RIGHT JOIN B ON A.id=B.id
  • MySQL不支持全连接
  • 可以使用LEFT JOIN 和UNION和RIGHT JOIN联合使用

以上内容取自以下链接:

SQL的多表关联查询常用的几种方式

六种关联查询

drop、delete与truncate的区别

  • 三者都表示删除,但是三者有一些差别:
比较DeleteTruncateDrop
类型属于DML属于DDL属于DDL
回滚可回滚不可回滚不可回滚
删除内容表结构还在,删除表的全部或者一部分数据行表结构还在,删除表中的所有数据从数据库中删除表,所有的数据行,索引和权限也会被删除
删除速度删除速度慢,需要逐行删除删除速度快删除速度最快
  • 因此,在不再需要一张表的时候,用drop;在想删除部分数据行时候,用delete;在保留表而删除所有数据的时候用truncate。

以上内容取自以下链接:

drop、delete与truncate的区别