数据库小技能:内外连接

简介: 在一个表中能够找到在另一个表中与之对应的记录,来组成新的记录显示出来,舍弃表中在另一个表中没有对应记录的记录。inner join (inner可以省略): 查出的是两张表的交集,两张表都有的才查出来

引言

理解SQL查询的过程是进行SQL优化的理论依据。

SQL查询的基本原理:

  • 单表查询:根据WHERE条件过滤表中的记录,形成中间表(这个中间表对用户是不可见的);然后根据SELECT的选择列选择相应的列进行返回最终结果。
  • 两表连接查询:对两表求积(笛卡尔积)并用ON条件和连接类型进行过滤形成中间表;然后根据WHERE条件过滤中间表的记录,并根据SELECT指定的列返回查询结果。
  • 多表连接查询:先对第一个和第二个表按照两表连接做查询,然后用查询结果和第三个表做连接查询,以此类推,直到所有的表都连接上为止,最终形成一个中间的结果表,然后根据WHERE条件过滤中间表的记录,并根据SELECT指定的列返回查询结果。

I 内连接 (inner join)

在一个表中能够找到在另一个表中与之对应的记录,来组成新的记录显示出来,舍弃表中在另一个表中没有对应记录的记录。

inner join (inner可以省略): 查出的是两张表的交集,两张表都有的才查出来

select * from 表A inner join 表B on 判断条件;
select r.*,m.merchant_name,m.facilitator_id,m.facilitator_name,m.facilitator_top_id,m.facilitator_top_name  from risk_equipment_position r inner join mer_merchant m on r.merchant_id = m.merchant_id ORDER BY create_time DESC LIMIT 15 ;

1.1 等值连接

一个表中的某个字段是另一个表的外键。

例如: emp表和dept表存在多对一的关联关系,由外键字段deptno来维护,即emp表来主动维护这一关系。

1.2 非等值连接

between and 来实现非等值连接;

select e.ename,e.sal,s.grade from emp e join salgrades on e.sal between s.losal and s.hisal;

1.3 自连接( nature join)

表中的字段之间存在引用关系,通过同一个表取不同别名来实现自身连接。

select e.empno,m.name from emp e join emp m on m.empno=e.mgr;

II 外连接

外连接,将驱动表中所有记录显示。

说明:

  • 左表就是在“(LEFT OUTER JOIN)”关键字左边的表。右表是右边的。
  • 在三种类型的外连接中,OUTER 关键字是可省略的。

2.1 左外连接 (left join)

左外连接会将主表对应的所有信息显示,从表与之匹配的记录显示。

以左表为主表(查询全部), 右表为辅表(没有的显示null)

案例1:

select * from emp join dept on emp.deptno=dept.deptno(+); --加号在join 右边的表的属性上为左连接

select *  from emp left join dept on emp.deptno=dept.deptno;

案例2: 查询商户终端定位限制

select r.*,m.merchant_name,m.facilitator_id,m.facilitator_name,m.facilitator_top_id,m.facilitator_top_name  from risk_equipment_position r left join mer_merchant m on r.merchant_id = m.merchant_id ORDER BY create_time DESC LIMIT 15 ;

2.2 右外连接 (right join)

加号在join 左边的表的属性上为右连接

`以右表为主表(查询全部记录), 左表为辅表(没有的显示null)
`
select r.* ,m.merchant_name,m.facilitator_id,m.facilitator_name,m.facilitator_top_id,m.facilitator_top_name from risk_equipment_position r right join mer_merchant m on r.merchant_id = m.merchant_id ORDER BY create_time DESC LIMIT 15

2.3 全连接 ( full join)

将两表中所有匹配和不匹配记录显示出来: 两个表的所有数据都展示出来

MySQL 不识别 FULL join,所以可以通过 union 来实现。

2.4 联合(合并)查询(union)

案例1:

select a.*, ad.* from test_a as a left join test_a_description as ad on a.id=ad.parent_id 
union
select a.*, ad.* from test_a as a right join test_a_description as ad on a.id=ad.parent_id;

案例2:

select r.*,m.merchant_name,m.facilitator_id,m.facilitator_name,m.facilitator_top_id,m.facilitator_top_name  from risk_equipment_position r left join mer_merchant m on r.merchant_id = m.merchant_id  union select r.*,m.merchant_name,m.facilitator_id,m.facilitator_name,m.facilitator_top_id,m.facilitator_top_name  from t_risk_equipment_position r right join t_mer_merchant m on r.merchant_id = m.merchant_id ORDER BY create_time DESC LIMIT 15;

III 案例:mybatis-plus 多表查询

3.1 多表查询:在mapper.xml 写语句和拼接查询条件

在mapper.xml 写语句,对应方法写到mapper里,service可以注入对应的mapper进行调用。

.xml

    <select id="selectProfitTradeActivityList"
            resultType="com.dto.TradeActivityDto"
            parameterType="com.dto.GenerateTradeQuery">
        select a.id,a.trade_no,a.activity_type,a.activity_code
        from trans_flow t
        inner join trans_flow_activity a on t.trade_no = a.trade_no
        <where>
            t.trade_state='S' and a.pay_channel_id='XYD'
            <if test="ps.startTradeTime != null">
                and t.trade_time &gt;= #{ps.startTradeTime}
            </if>

        </where>
--         order by t.update_time desc
    </select>
@Mapper
public interface TransFlowActivityMapper extends BaseMapper<TransFlowActivity> {

    List<GenerateTradeActivityDto> selectProfitTradeActivityList(@Param("ps") GenerateTradeQuery input);

}

3.2 多表关联:Java代码中书写语句和拼接查询条件

实现思路:

  • 使用 Select注解书写sql语句
  • 采用apply方法自定义sql拼接条件, 使用条件构造器作为参数。
  1. 驱动表表和辅表的参数拼接

  1. 书写SQL

3.3 案例:左外连接

实现思路:

  • 使用 Select注解书写sql语句/xml书写语句。
  • 采用apply方法自定义sql拼接条件, 使用wrapper条件构造器作为参数。

实现步骤:

  1. 定义查询接口: mapper.java/Service.java
  • Service层
@Override
    public List<RiskEquipmentPositionDto> getlist(LambdaQueryWrapper<RiskEquipmentPosition> input) {
        return baseMapper.getList(input);
    }
  1. Mapper层:采用 ${ew.customSqlSegment}@Param(Constants.WRAPPER)实现,将条件构造器作为参数。
  2. 书写SQL

方式一:使用Select注解书写sql语句

@Select("select r.* ,m.merchant_name,m.facilitator_id,m.facilitator_name,m.facilitator_top_id,m.facilitator_top_name from risk_equipment_position r left join mer_merchant m on r.merchant_id = m.merchant_id ${ew.customSqlSegment} ")
    List<RiskEquipmentPositionDto> getList(@Param(Constants.WRAPPER) LambdaQueryWrapper<RiskEquipmentPosition> input);
where后面的内容就是条件构造器生成的部分

方式2: 使用xml书写语句

  1. 拼接查询条件
LambdaQueryWrapper<RiskEquipmentPosition> lambda = new LambdaQueryWrapper<>();
        lambda.orderByDesc(TRiskEquipmentPosition::getCreateTime);
        if (input.getStartTime() != null) {
            lambda.ge(RiskEquipmentPosition::getCreateTime, input.getStartTime());
        }
        if (input.getEndTime() != null) {
            lambda.lt(RiskEquipmentPosition::getCreateTime, input.getEndTime());
        }
        if (input.getState() != null) {
            lambda.eq(RiskEquipmentPosition::getState, input.getState());
        }
        // 终端SN、商户号本表查询,其他关键词需要关联商户表进行查询。
        if (null != input.getKeywordType() && StringUtils.isNotBlank(input.getKeyword())) {

            var keyWordTypes = new ArrayList<String>();
            keyWordTypes.add(ERiskEquipmentPositionKeyWord.MER_ID.getCode());
            keyWordTypes.add(ERiskEquipmentPositionKeyWord.FAC_ID.getCode());
            keyWordTypes.add(ERiskEquipmentPositionKeyWord.TOP_ID.getCode());
            Long longKeyWord = 0L;
            if (keyWordTypes.contains(input.getKeywordType())) {
                try {
                    longKeyWord = Long.parseLong(input.getKeyword());
                } catch (Exception ex) {
                    throw CommonException.create(ServerResponse.createByError("无效的编号"));
                }
            }
            switch (BaseEnum.getEnum(ERiskEquipmentPositionKeyWord.class, input.getKeywordType())) {
                case SN:
                    lambda.eq(RiskEquipmentPosition::getEquipmentSn, input.getKeyword());
                    break;
                case MER_ID:
                    lambda.apply("r.merchant_id = {0}",input.getKeyword());
                    break;
                case MER_NAME:
                    lambda.apply("m.merchant_name = {0}",input.getKeyword());
                    break;
                case FAC_ID:
                    //apply方法用于拼接自定义的条件判断,如果自定义的条件判断是需要独立的结果,就必须记得加括号。
                    lambda.apply("m.facilitator_id = {0}",input.getKeyword());
                    break;
                case FAC_NAME:
                    lambda.apply("m.facilitator_name = {0}",input.getKeyword());
                    break;
                case TOP_ID:
                    lambda.apply("m.faclitator_top_id= {0}",input.getKeyword());
                    break;
                case TOP_NAME:
                    lambda.apply("m.facilitator_top_name= {0}",input.getKeyword());
                    break;
            }

        }
        // 分页查询
        PageHelper.startPage(input.getPageNum(), input.getPageSize());
        List<RiskEquipmentPositionDto> positionList = tRiskEquipmentPositionService.getlist(lambda);
        PageInfo pageInfo = new PageInfo(positionList);
        PageHelper.clearPage();
        return pageInfo;
  1. 调用查询接口
        List<RiskEquipmentPositionDto> positionList = RiskEquipmentPositionService.getlist(lambda);

IV 笛卡尔积 (cross join)

交差集: 把表A和表B的数据进行一个N*M的组合,即笛卡尔积。
SELECT * FROM TableA CROSS JOIN TableB

V 小结:

5.1 外连接的区别分析

外连接:外连不但返回符合连接和查询条件的数据行,还返回不符合条件的一些行。

外连接分三类:左外连接(LEFT OUTER JOIN)、右外连接(RIGHT OUTER JOIN)和全外连接(FULL OUTER JOIN)

  1. 三者的共同点是都返回符合连接条件和查询条件(即:内连接)的数据行。
  2. 不同点如下:
  • 左外连接:还返回左表中不符合连接条件但符合查询条件的数据行。
  • 右外连接:还返回右表中不符合连接条件但符合查询条件的数据行。
  • 全外连接:还返回左表中不符合连接条件但符合查询条件的数据行,并且还返回右表中不符合连接条件但符合查询条件的数据行。
全外连接实际是上左外连接和右外连接的数学合集(去掉重复),即“全外 = 左外 UNION 右外”。

5.2 Column 'merchant_id' in where clause is ambiguous

错误代码:SELECT count(0) FROM risk_equipment_position r LEFT JOIN mer_merchant m ON r.merchant_id = m.merchant_id WHERE (merchant_id = ?)

case MER_ID:
                    lambda.eq(TRiskEquipmentPosition::getMerchantId, input.getKeyword());
                    break;

原因:多表查询的时候几个表中同时出现了某个相同的列名,而在查询条件WHERE后面又没有指定是那个表,而引起的。

解决方案:where 条件语句后查询某个字段要加上表名。可以使用mybatis的apply方法来拼接自定义的条件判断。

                case MER_ID:
                    lambda.apply("r.merchant_id = {0}",input.getKeyword());
                    break;
                case MER_NAME:
                    lambda.apply("m.merchant_name = {0}",input.getKeyword());
                    break;
                case FAC_ID:
                    //apply方法用于拼接自定义的条件判断,如果自定义的条件判断是需要独立的结果,就必须记得加括号。
                    lambda.apply("m.facilitator_id = {0}",input.getKeyword());
                    break;

相关问题:查询结果里面有两个相同的列名,而没有指定是哪个表使用的时候,sql查询前面加表名可避免出现错误。

VI 集合

集合查询中的字段个数和类型要一致,这称为select的同构

select  job from emp deptno=10 union all select job  from emp deptno=20;
  • union all 并集,并且不去除重复
  • union 并集,去除重复
  • intersect 交集
  • minus 差集

see also

公z号:iOS逆向

performance-tips: http://postgres.cn/docs/9.3/performance-tips.html

目录
相关文章
|
9天前
|
关系型数据库 MySQL 数据库连接
Unity连接Mysql数据库 增 删 改 查
在 Unity 中连接 MySQL 数据库,需使用 MySQL Connector/NET 作为数据库连接驱动,通过提供服务器地址、端口、用户名和密码等信息建立 TCP/IP 连接。代码示例展示了如何创建连接对象并执行增删改查操作,确保数据交互的实现。测试代码中,通过 `MySqlConnection` 类连接数据库,并使用 `MySqlCommand` 执行 SQL 语句,实现数据的查询、插入、删除和更新功能。
|
24天前
|
关系型数据库 MySQL 数据库连接
数据库连接工具连接mysql提示:“Host ‘172.23.0.1‘ is not allowed to connect to this MySQL server“
docker-compose部署mysql8服务后,连接时提示不允许连接问题解决
|
1天前
|
关系型数据库 MySQL 网络安全
如何排查和解决PHP连接数据库MYSQL失败写锁的问题
通过本文的介绍,您可以系统地了解如何排查和解决PHP连接MySQL数据库失败及写锁问题。通过检查配置、确保服务启动、调整防火墙设置和用户权限,以及识别和解决长时间运行的事务和死锁问题,可以有效地保障应用的稳定运行。
40 25
|
4月前
|
SQL 开发框架 .NET
ASP.NET连接SQL数据库:详细步骤与最佳实践指南ali01n.xinmi1009fan.com
随着Web开发技术的不断进步,ASP.NET已成为一种非常流行的Web应用程序开发框架。在ASP.NET项目中,我们经常需要与数据库进行交互,特别是SQL数据库。本文将详细介绍如何在ASP.NET项目中连接SQL数据库,并提供最佳实践指南以确保开发过程的稳定性和效率。一、准备工作在开始之前,请确保您
383 3
|
28天前
|
前端开发 Java 数据库连接
Java后端开发-使用springboot进行Mybatis连接数据库步骤
本文介绍了使用Java和IDEA进行数据库操作的详细步骤,涵盖从数据库准备到测试类编写及运行的全过程。主要内容包括: 1. **数据库准备**:创建数据库和表。 2. **查询数据库**:验证数据库是否可用。 3. **IDEA代码配置**:构建实体类并配置数据库连接。 4. **测试类编写**:编写并运行测试类以确保一切正常。
52 2
|
3月前
|
关系型数据库 MySQL 数据库连接
python脚本:连接数据库,检查直播流是否可用
【10月更文挑战第13天】本脚本使用 `mysql-connector-python` 连接MySQL数据库,检查 `live_streams` 表中每个直播流URL的可用性。通过 `requests` 库发送HTTP请求,输出每个URL的检查结果。需安装 `mysql-connector-python` 和 `requests` 库,并配置数据库连接参数。
147 68
|
3月前
|
数据库 C# 开发者
ADO.NET连接到南大通用GBase 8s数据库
ADO.NET连接到南大通用GBase 8s数据库
|
3月前
|
数据库连接 Linux Shell
Linux下ODBC与 南大通用GBase 8s数据库的无缝连接配置指南
本文详细介绍在Linux系统下配置GBase 8s数据库ODBC的过程,涵盖环境变量设置、ODBC配置文件编辑及连接测试等步骤。首先配置数据库环境变量如GBASEDBTDIR、PATH等,接着修改odbcinst.ini和odbc.ini文件,指定驱动路径、数据库名称等信息,最后通过catalog.c工具或isql命令验证ODBC连接是否成功。
|
3月前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
85 3
|
4月前
|
关系型数据库 MySQL 数据库连接
DBeaver如何连接一个数据库
【10月更文挑战第27天】DBeaver 是一款功能强大的通用数据库管理工具,支持多种主流数据库。本文介绍了使用 DBeaver 连接数据库的基本步骤,包括下载安装、创建新连接、选择数据库类型、配置连接参数、测试连接以及最终连接到数据库。详细的操作指南帮助用户轻松管理和操作数据库。
853 9

热门文章

最新文章