MariaDB - 左连接不返回所有行
Posted
技术标签:
【中文标题】MariaDB - 左连接不返回所有行【英文标题】:MariaDB - LEFT JOIN does not return all rows 【发布时间】:2020-12-17 16:30:24 【问题描述】:(在将其标记为重复之前,请阅读问题)。
我有两个不同的表,Bot
和 Switch
。
Bot
ID | Info | Active
------------------
0 | abc | 1
1 | def | 1
Switch
Date | Activated | BotID | User
-------------------------------------
2020-01-01 | 1 | 0 | John
2020-01-02 | 0 | 0 | John
对于每个机器人,我想检索其最新状态,这意味着:对于每个机器人,我想知道最新行的Activated
字段是1
还是0
。为了为那些在Switch
表中没有条目的机器人返回结果,我尝试使用LEFT JOIN
语句。这是查询:
SELECT IFNULL(x.Activated, 0) AS Following, b.ID, b.Info
FROM bot b
LEFT JOIN (
SELECT *
FROM switch s1
WHERE s1.Date IN (
SELECT MAX(s.Date)
FROM switch s
WHERE s.User = 'SomeUsername'
GROUP BY s.Bot
)
) AS x ON x.BotID = b.ID
WHERE b.Active = 1
我的预期结果是:
Following | ID | Info
---------------------
0 | 0 | abc
0 | 1 | def
重点是我没有得到所有行,而是这个查询只返回一行:
Following | ID | Info
---------------------
0 | 0 | abc
这很奇怪,因为我使用的是LEFT JOIN
。为了弄明白怎么回事,我分别跑了
SELECT *
FROM bot
和
SELECT *
FROM switch s1
WHERE s1.Date IN (
SELECT MAX(s.Date)
FROM switch s
WHERE s.User = 'SomeUsername'
GROUP BY s.Bot
)
当然,第一个查询返回:
ID | Info
---------
0 | abc
1 | def
当第二个返回时:
Date | Activated | BotID | User
-------------------------------------
2020-01-02 | 0 | 0 | John
我真的不明白为什么LEFT JOIN
没有同时保留Bot
行。我检查了this 问题,但我没有使用任何外部WHERE
所以这不是我的情况。
提前致谢!
【问题讨论】:
b.Active
不在您显示的架构中
@GarrGodfrey 抱歉,我试图尽可能简化架构。刚刚编辑了问题。谢谢你告诉我。
你的查询应该做你想做的事。你能设置一个db<>fiddle 来演示这个问题吗?
这里有问题。您声称此连接有效 AS x ON x.Bot = b.ID
但 x 不包含列 Bot
根据您的“而第二个返回”输出
旁注;如果您使用的是 MariaDB,请先声明您使用的是它,而不是 mysql。两个产品不一样
【参考方案1】:
您的查询应该做您想做的事,如您所见in this db fiddle(我修复了列名的问题并稍微更改了数据,因此很容易理解它正在工作)。所以问题要么出在你的数据上,要么出在你的查询过于简单。
不过,让我提出一个优化建议。由于您只需要表 switch
中的一列,因此您可以使用行限制相关子查询而不是连接:
select
coalesce(
(
select s.activated
from switch s
where s.user = 'SomeUsername' and s.botid = b.id
order by s.date desc limit 1
),
0
) as following,
b.*
from bot
我希望这比带有连接和过滤的原始版本更有效。
如果你运行的是 MySQL 8.0,你可以使用row_number()
:
select coalesce(s.activated, 0) as following, b.*
from bot b
left join (
select s.*, row_number() over(partition by botid order by date desc) rn
from switch s
where user = 'SomeUsername'
) s on s.botid = b.id and rn = 1
Demo on DB Fiddle.
如果您的 MySQL / MariaDB 版本不支持窗口函数,并且您需要来自switch
的多个列,那么您可以尝试对查询进行一些不同的表述,以查看问题是否仍然存在。考虑在left join
的on
子句中进行过滤,如下所示:
SELECT ...
FROM bot b
LEFT JOIN switch s
ON s.botid = b.id
AND s.date = (
SELECT MAX(s1.date)
FROM switch s1
WHERE s1.User = 'SomeUsername' AND s1.botid = s.botid
)
WHERE b.Active = 1
【讨论】:
正如 Garr 所建议的,我可能遇到了 MariaDB 错误,因为我的查询在小提琴中工作。非常感谢您的回答,当然我正在寻找更好的性能,您的查询实际上并没有引起任何问题。 还有一个问题,如果我还需要从switch
检索另一列怎么办?以前从未使用过COALESCE
,我不知道该怎么做,在互联网上找不到太多。
@DamiToma:如果您想要多个列,那么相关子查询是不够的(您需要为每一列重复该列)。我在答案中添加了另一个查询,它与您的原始查询执行相同的操作,但使用不同的技术 - 也许不会遇到错误。注意:COALESCE()
就像 IFNULL()
一样(但它是标准 SQL,而后者不是)。
非常感谢,这个答案非常有帮助。期待提高我的 SQL 技能!【参考方案2】:
在 Maria 10.5 上,您可以使用分析让您的生活更轻松:
WITH x AS (
SELECT
COALESCE(s.Activated, 0) AS Following,
b.ID,
b.Info,
ROW_NUMBER() OVER(PARTITION BY b.ID ORDER BY s.Date DESC) rn
FROM
bots b
LEFT JOIN switch s ON s.Bot = b.ID
WHERE b.Active = 1
)
SELECT * FROM x WHERE rn = 1
这对您来说应该很容易编辑 - 从本质上讲,它是一个标准的左连接 bots:switch。有时你得到一个开关,有时没有。当你得到一个开关时,你会从 row_number 中得到最近的日期。如果您删除WHERE rn = 1
,您会看到所有日期,但是有一个 rn 列,它是一个递增计数器,当机器人 ID 更改时从 1 重新开始。 rn = 1
行是您想要的,因为随着日期从最近到过去,计数器会递增
向这里添加更多列只是将它们添加到 WITH 块内的选择中的一种情况
COALESCE
没什么神奇的;它就像 IFNULL 但可以有很多参数。它从左到右返回第一个非空值。当与 2 个参数一起使用时,它就像 IFNULL,但它的好处是它适用于所有主要规范兼容的数据库,而 IFNULL/NVL/ISNULL 一直在更改名称
如果分析不可用,典型的“获取最新行”如下所示:
WITH switchlatest AS (
SELECT s.*
FROM
switch s
INNER JOIN (SELECT bot, MAX(date) maxd FROM switch GROUP BY bot) maxd
ON s.bot = maxd.bot AND s.date = maxd.date
)
你可以像switch
一样离开加入这个switchlatest
如果有多个日期与 true max 相同(它们都返回,rownum 路由只返回一个),它的行为与 rownum 有点不同
不支持 WITH 的数据库版本的一般模式
WITH alias AS
(
SELECT columns FROM ...
)
SELECT ...
FROM
table
JOIN
alias
ON ...
您将“别名”一词复制到右括号之后:
(
SELECT columns FROM ...
) alias
然后复制整个内容并将其粘贴到查询中的别名上:
SELECT ...
FROM
table
JOIN
(
SELECT columns FROM ...
) alias
ON
...
Or you can take the WITH and turn it into a view (Replace the WITH with CREATE VIEW) - might as well have it on hand without having to code it into every query, because you clearly have a business need for "the latest switch"
Starting to think it might be easier to just upgrade the db!
【讨论】:
非常感谢您的有用回答!请注意,我使用的是 MariaDB 10.1.25,而不是 10.5,请检查问题下方的 cmets。 哎呀。真可惜.. 我认为 10.2 引入了分析 我又补充了一点信息 @CaiusJard:with
clause 也是在 Maria DB 10.2.1 中引入的......不过,您可以将子查询直接放在 from
子句中。以上是关于MariaDB - 左连接不返回所有行的主要内容,如果未能解决你的问题,请参考以下文章