3.4.3.4
right join on条件为非主键
right join 在on中添加了右表.tenant_id=xxx
right join是通过where将右表筛选之后的数据全部查询出来, on中的条件只是筛选左表的数据, 这里无法做到筛选条件
代码为你们提供的用例: https://gitee.com/baomidou/mybatis-plus-samples/tree/master/mybatis-plus-sample-tenant
修改 src/main/java/com/baomidou/mybatisplus/samples/tenant/mapper/UserMapper.xml文件如下图所示
执行junit单测 com.baomidou.mybatisplus.samples.tenant.TenantTest#I5LQU2:创建行存压缩表,对表并发执行insert/update/delete/vacuum/analyze/vacuum full,出现报错
生成的sql为
SELECT a.name AS addr_name, u.id, u.name FROM user_addr a RIGHT JOIN user u ON u.name = a.name AND u.tenant_id = 1
期望的sql应该是
SELECT a.name AS addr_name, u.id, u.name FROM user_addr a RIGHT JOIN user u ON u.name = a.name AND a.tenant_id = 1 where u.tenant_id=1
期望查询的数据是3条, 实际查询了5条
无
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
你的例子里面的sql也是有问题的
SELECT * FROM entity e right join entity1 e1 on e1.id = e.id
应该改写为
SELECT * FROM entity e right join entity1 e1 on e1.id = e.id and e .tenant_id = 1 where e1 .tenant_id = 1
但是mybatis-plus改写成
SELECT * FROM entity e RIGHT JOIN entity1 e1 ON e1.id = e.id AND e1 .tenant_id = 1 WHERE e .tenant_id = 1
mybatis-plus改写的这个sql on 里面的e1.tenant_id=1并不能起到筛选的作用, 右表里面的所有的租户都会被筛选出来, 如果结果能对的原因是这里是id关联的, where中的条件会筛选出来正确的结果, 如果on中的条件是普通字段, 而且有不同租户有相同的值, 这个sql就有问题了
为什么是“期望查询的数据是3条, 实际查询了5条”?
本来就是5个用户,其中1个用户2个地址
from user_addr
right join user --right join 本意 就是拿所有用户
经过验证,该问题确实是存在的。我们的处理方案是接下来可能会对所有使用租户的表进行降级处理。即:所有需要进行租户过滤的条件中,原表名称都会被替换为过滤租户后的子查询:
SELECT * FROM demo
中的 demo
将在需要进行租户过滤时被转换为:
SELECT * FROM (SELECT * FROM demo WHERE tenant_id = 1) as demo
我们认为租户本来可被视作一个前置的过滤条件,因此提前进行过滤;这种方式也可以避免处理各种 OUTER JOIN
的情况。
@SirMin https://github.com/baomidou/mybatis-plus/pull/4035
@h825944942 提前过滤,当表数据量大时,性能会有问题。
已在 github 提交了一个 pr 尝试修复,可以 review 下。
你的处理还是有点不对, 你在单测中的断言如下图所示
处理的原始sql
SELECT * FROM with_as_1 e right join entity1 e1 on e1.id = e.id
多租户处理之后的sql
SELECT * FROM with_as_1 e RIGHT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 WHERE e1.tenant_id = 1
因为right join on中的条件不能筛选e1, 所以此处on中的e1.tenant_id = 1
没有作用, 但是e并没有筛选条件,假如e中有其他租户的数据满足e1.id = e.id
, 那么也会join。
所以期望的sql应该是
SELECT * FROM with_as_1 e RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 WHERE e1.tenant_id = 1
通过on中的e.tenant_id = 1
筛选出e表到租户数据, where中的e1.tenant_id = 1
筛选出e1表的数据
不好意思, 没注意看到租户表的配置
但是上面那个还是有问题, 如下是代码
原始sql
SELECT * FROM entity e right join entity1 e1 on e1.id = e.id
多租户处理之后的sql
SELECT * FROM entity e RIGHT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 WHERE e.tenant_id = 1 AND e1.tenant_id = 1
如果left join的时候, 如果有entity表不满足on条件会出现,会出现有entity1表字段,但是entity表字段为空的情况
在where中加入e.tenant_id=1
会导致这些数据被排除,所以在where中不能含有e.tenant_id=1
这个条件
例如entity表数据如下
id | tenant_id |
---|---|
1 | 1 |
2 | 1 |
entity1表数据如下 | |
id | tenant_id |
---- | ----------- |
1 | 1 |
2 | 1 |
3 | 1 |
多租户处理之后的sql |
SELECT * FROM entity e RIGHT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 WHERE e.tenant_id = 1 AND e1.tenant_id = 1;
查询结果
将过滤放到表中的sql
SELECT * FROM (select * from entity where tenant_id=1) e RIGHT JOIN (select * from entity1 where tenant_id=1) e1 ON e1.id = e.id;
的查询结果
最后是期望sql应该是将entity表的筛选条件放到on中, entity1表的筛选条件放到where中
如下
SELECT * FROM entity e RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 WHERE e1.tenant_id = 1;
查询结果
希望你们整出连表查询的方法来。写SQL是有一点折磨了。
测试案例,需要在postgresql中测试, mysql不支持full outer join
CREATE TABLE People
(
ID bigint NOT NULL,
TENANT_ID int4 DEFAULT NULL,
NAME varchar(255) DEFAULT NULL,
PRIMARY KEY (ID)
);
CREATE TABLE Cars
(
ID bigint NOT NULL,
TENANT_ID int4 DEFAULT NULL,
NAME varchar(255) DEFAULT NULL,
PRICE int DEFAULT NULL,
people_id bigint DEFAULT NULL,
PRIMARY KEY (ID),
FOREIGN KEY (people_id) REFERENCES People (ID)
);
insert into people (id, tenant_id, name)
values (1, 1, 'Audi');
insert into people (id, tenant_id, name)
values (2, 1, 'Mercedes');
insert into people (id, tenant_id, name)
values (3, 2, 'Skoda');
INSERT INTO Cars (ID, TENANT_ID, NAME, PRICE, people_id)
VALUES (1, 1, 'Audi', 52642, 1);
INSERT INTO Cars (ID, TENANT_ID, NAME, PRICE, people_id)
VALUES (2, 2, 'Mercedes', 57127, 1);
INSERT INTO Cars (ID, TENANT_ID, NAME, PRICE, people_id)
VALUES (3, 1, 'Skoda', 9000, 1);
INSERT INTO Cars (ID, TENANT_ID, NAME, PRICE, people_id)
VALUES (4, 1, 'Volvo', 29000, null);
INSERT INTO Cars (ID, TENANT_ID, NAME, PRICE, people_id)
VALUES (5, 2, 'Bentley', 350000, 1);
INSERT INTO Cars (ID, TENANT_ID, NAME, PRICE, people_id)
VALUES (6, 2, 'Citroen', 21000, 1);
INSERT INTO Cars (ID, TENANT_ID, NAME, PRICE, people_id)
VALUES (7, 1, 'Hummer', 41400, 1);
INSERT INTO Cars (ID, TENANT_ID, NAME, PRICE, people_id)
VALUES (8, 1, 'Volkswagen', 21600, 1);
-- 正确但是效率可能不太好的sql
select t1.*, t2.*
from (select * from cars where TENANT_ID = 1) t1
full outer join (select * from people where People.TENANT_ID = 1) t2 on (t1.people_id = t2.ID);
-- 原始sql
select c.*, p.*
from cars c
full outer join people p on c.people_id = p.id;
-- tenant插件处理的sql
SELECT c.*, p.*
FROM cars c
FULL OUTER JOIN people p ON c.people_id = p.id
WHERE c.tenant_id = 1;
感觉full outer join需要写成子查询的方式
测试了下,full outer join 不好处理,只能重构为子查询,改写起来非常复杂。
目前可以把 full outer join 的原始语句改写为
select * from
(select * from cars) t1
full outer join
(select * from people) t2
on t1.people_id = t2.ID;
这种形式,插件处理后会变为
SELECT * FROM
(SELECT * FROM cars WHERE cars.tenant_id = 1) t1
FULL OUTER JOIN
(SELECT * FROM people WHERE people.tenant_id = 1) t2
ON t1.people_id = t2.ID
复杂多表查询 自动加上租户过滤条件的确不太靠谱,坑太多
发现这种处理方式有点像一个伪需求,可能在数据源或数据库层处理更为合适
请测试 使用新版本 3.5.3
登录 后才可以发表评论