left join关联查询一对多数据重复问题解决方案

50 篇文章 0 订阅
写在前面:

使用准则:

在使用左右连接时,一定要保障主表与关联表的on条件是1:1的关系,以保障正常查询主表数据。

实例
# 车主表
create table owner
(
    id         int(10) auto_increment
        primary key,
    owner_name varchar(10) null,
    brand_id   int(10)     null
);

# 车辆表
create table vehicle
(
    id       int(10) auto_increment
        primary key,
    brand_id int(10) null
);

# 车辆品牌表
create table brand
(
    id         int(10) auto_increment
        primary key,
    brand_name varchar(10) null
);


INSERT INTO `halodb`.`owner` (`id`, `owner_name`, `brand_id`) VALUES (1, 'debug', 1);
INSERT INTO `halodb`.`owner` (`id`, `owner_name`, `brand_id`) VALUES (2, 'Ltx', 2);
INSERT INTO `halodb`.`owner` (`id`, `owner_name`, `brand_id`) VALUES (3, 'Ltx', 1);

INSERT INTO `halodb`.`brand` (`id`, `brand_name`) VALUES (1, '比亚迪');
INSERT INTO `halodb`.`brand` (`id`, `brand_name`) VALUES (2, '大众');

INSERT INTO `halodb`.`vehicle` (`id`, `brand_id`) VALUES (1, 1);
INSERT INTO `halodb`.`vehicle` (`id`, `brand_id`) VALUES (2, 2);
INSERT INTO `halodb`.`vehicle` (`id`, `brand_id`) VALUES (3, 1);

image-20220115215310713

image-20220115215610068

image-20220115215630849

需求1:展示列表车辆品牌信息、车主信息。

根据当前的表结构,那么很自然的我们可以使用brand表关联owner表。

select b.brand_id, b.brand_name, o.owner_name
from brand b
         left join owner o on b.id = o.brand_id;

执行结果长这样:

image-20220115215914205

需求2:展示列表车辆信息、车辆品牌信息、车主信息。

以车辆为主表,分别关联车辆品牌表和车主表

select v.id as vehicleId, b.id as brandId, b.brand_name as brandName, o.owner_name as ownerName
from vehicle v
         left join brand b on v.brand_id = b.id
         left join owner o on b.id = o.brand_id;

执行结果长这样:

image-20220115220556365

可问题来了,主表brand只有两条数据,但是查出来了三条数据,vehicle表有三条数据,却查出来五条,并不能正确展示主表数据

分析SQL

Q1:如果规定没人只有辆车的话,该SQL并没有问题,可实际业务中可能会出现一个人拥有多辆车的情况,也就是车牌与车主的关系是1:n,那么我们使用品牌id去关联车主表的brand_id则违反了我写在最前面的使用准则。

Q2:一辆车只有一个品牌,vehicle表与brand表是1:1关系,那么没有问题,但是使用车辆品牌表去关联车主表时,车辆品牌与车主是n:1的关系,当使用左右连接时,会显示主表全部数据和符合条件的关联表数据,所以第二条关联数据会导致主表数据重复。

解决方案
  1. 根据实际业务场景,可以更换关联条件
  2. 分别查询数据,在Java中循环补充另一个SQL中查询的字段,即分别使用vehicle表关联brand表,再使用vehicle关联owner表

举例中表设计并不允许这样做,方案仅提供思路。

分两次查询的数据是两个List,两层for循环赋值时间复杂度高,那么可以将其中一个List根据vehicleId转换成Map,循环第一个List,在循环中使用list中的vehicleId做key去getMap的value.

由于Map本身的数据结构,会导致占用内存比List大,那么这种方案就是用空间去换时间,若数据量比较大,需权衡时间复杂度与空间复杂度。

怪味道的方案

使用group by对重复数据进行过滤

select v.id as vehicleId,
       b.id as brandId, b.brand_name as brandName,
       o.owner_name as ownerName
from vehicle v
         left join brand b on v.brand_id = b.id
         left join owner o on b.id = o.brand_id
group by vehicleId;

❌这样显然是不对的。

报错: this is incompatible with sql_mode=only_full_group_by

原因就是在MySQL 5.7.5以上版本后,要求group by 的字段需要查询的字段与group by的字段满足唯一性。也就是vehicleId有多个,MySQL不知道用哪个。所以在写SQL时保持一个习惯:唯一性的数据需要写在首位。

SQL 标准中不允许 SELECT 列表,HAVING 条件语句,或 ORDER BY 语句中出现 GROUP BY 中未列表的可聚合列。

建议

在使用group by时也需要注意,group by的该列一定是唯一的,如果group列出现数据重复数据时,仅会显示一条数据。

为测试该问题,在数据库新增一条重复数据

image-20220115230309202

select brand_name from brand group by brand_name;

执行结果长这样:

image-20220115230506532

### 回答1: Git是一种版本控制系统,用于记录代码的改动,协作开发,和代码管理。Git有很多功能和优势,但一开始学习可能会感到困难,因为它的术语和操作需要一些时间来理解。下面是一个简单的教程,用于介绍Git的基本概念和用法: 1.安装Git:首先你需要下载Git,并按照安装向导进行安装。在Windows系统中,你将看到有一个新的Git Bash窗口,用来输入Git命令。 2.创建仓库:如果你想要将一个项目加入Git进行版本控制,那么你首先需要在你的本地计算机上创建一个仓库。打开Git Bash窗口,输入如下命令来创建一个名为MyProject的仓库: mkdir MyProject cd MyProject git init 3.添加文件:现在,你已经有一个新的仓库,但它是空的。你可以使用命令添加项目中的文件到仓库中。 git add . 4.提交代码:当你改动了文件并想要将它们保存到Git仓库时,你需要使用提交指令。 git commit -m "这里输入你的提交信息" 5.推送至远程仓库:一旦你的本地仓库中的代码得到了提交,并想要在分支中共享它们,你可以使用如下命令将提交的代码推送至远程仓库。 git push origin master 通过以上的5个简单步骤,就可以将你的项目加入到Git中进行版本控制,管理和协作开发了。这是最简单、最清晰易懂的Git使用教程。这里面还有许多更多的高级含义,例如Git的分支,标签,拉取,合并等等,掌握这些功能,需要更进一步的学习和实践。 ### 回答2: Git是一个强大的版本控制系统,它是程序员必须掌握的技能之一。但是,学习Git可能会令人感到有些困难,因为它有其特定的术语和工作流程。下面将为大家提供一份最详细最傻瓜的Git使用教程。 一. Git的安装 首先,你需要安装Git客户端。在Windows系统上可以使用Git Bash或Git GUI, 在Mac或Linux系统上可以使用Git命令行工具。 Git官方网站提供了Git客户端的下载或者直接在命令行使用安装命令进行安装。 二. Git配置 在安装Git之后,你需要对Git进行配置。 通过运行以下两个命令,你可以设置你的用户名和电子邮件地址,这将用于你提交的每个代码的作者身份标记。 git config --global user.name "Your Name" git config --global user.email "youremail@yourdomain.com" 三. 创建和克隆仓库 在Git中,你可以使用init命令创建一个新的仓库。 mkdir mynewproject cd mynewproject git init 你也可以使用clone命令从一个现有仓库进行克隆。 git clone https://github.com/youruser/yourproject.git 四. Git基本的工作流程 在Git中,你需要使用工作区,暂存区和版本库来管理代码。 首先,你需要将代码添加到暂存区中以进行跟踪。 git add myfile.py 然后,你需要将更改提交到版本库中。 git commit -m "Added new feature to myfile.py" 在有多个开发人员协同工作的项目中,每个人都应该在开始工作之前使用pull命令获取最新的代码版本。 git pull 然后,进行开发和更改后,如果你想将更改推送到远程仓库并与团队共享,请使用push命令。 git push origin master 五. Git常用命令 在Git中,你需要掌握以下常用命令。 - git init - 初始化仓库 - git clone - 克隆一个现有仓库 - git add - 添加文件或文件夹 - git commit - 将更改提交到版本库中 - git push - 推送更改到远程仓库 - git pull - 拉取最新的代码版本 - git status - 显示当前代码的状态 - git branch - 显示所有分支 - git checkout - 切换到另一个分支 - git merge - 合并两个分支 - git diff - 显示两个版本之间的差异 六. Git的分支管理 分支是Git最重要的特性之一。在开发过程中,团队中的每个成员都应该使用自己的分支。下面是一些有用的分支管理命令。 - git branch - 列出所有分支 - git branch newbranch - 创建一个新分支 - git checkout branchname - 切换到另一个分支 - git merge branchname - 将分支的更改合并到当前分支中 - git branch -d branchname - 删除特定的分支 七. 总结 本篇文章提供了Git使用教程最详细最傻瓜的步骤和命令列表。如果你是初学者,建议先从一些简单的Git使用场景入手,然后扩展到更复杂的方法。如果你在使用Git时遇到问题,请在Stack Overflow或其他技术社区寻求帮助。总之,使用Git来管理你的代码将大大提高你的编程效率。 ### 回答3: Git 是一款非常流行的源代码管理工具,它具有分布式、速度快、支持大型项目等优点。但对于初学者来说,可能会觉得 Git 使用起来有些困难和复杂。 以下是 Git 使用教程最详细最傻瓜的步骤: 第一步:安装 Git 工具 需要在官网上下载并安装 Git 工具。macOS 和 Linux 系统已经内置了 Git,只需在终端中输入 git --version 即可查看是否已安装 Git。 第二步:创建本地仓库 首先在本地新建一个文件夹,然后通过终端进入该文件夹。在终端输入 git init,该文件夹就会成为一个本地的 Git 仓库。 第三步:添加文件 在该文件夹中添加需要版本管理的文件,然后在终端中输入 git add .,用来把文件添加到暂存区。 如果只想提交某个文件,则可以使用 git add 文件名 的形式。 第四步:提交文件 在终端中输入 git commit -m "描述信息",用来将文件提交到本地 Git 仓库中。其中描述信息是对此次提交的说明,可以写明本次提交的内容、修改的文件以及其他需要说明的信息。 第五步:创建远程仓库 在 Github 等代码托管平台中创建一个仓库,用于同步本地 Git 仓库中的代码。 然后通过 git remote add origin 远程仓库地址 的方式将本地仓库与远程仓库关联起来。 第六步:推送到远程仓库 在终端中输入 git push -u origin master 将本地仓库的代码上传到远程仓库中。这里 origin 是远程仓库的别名,master 表示上传到主分支中。 之后每次提交代码时,只需执行 git push 命令即可将修改的代码推送到远程仓库中。 以上就是 Git 使用教程最详细最傻瓜的步骤,尽管 Git 使用起来可能不是那么容易,但只要跟着这些简单的步骤操作,就能够轻松地做到版本管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值