MariaDB-LEFT JOIN不会返回所有行

问题描述

(在将其标记为重复之前,请先阅读问题)。

我有两个不同的表,BotSwitch

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,所以不是我的情况。
预先感谢!

解决方法

您的查询应该执行您想要的操作,如您所见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中的多个列,则可以尝试用不同的方式对查询进行措辞,以查看问题是否仍然存在。考虑在on的{​​{1}}子句中进行过滤,如下所示:

left join
,

在使用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列是一个递增计数器,当bot ID更改时,该计数器从1重新开始。 rn = 1行是您想要的行,因为计数器会随着日期从最近到过去而递增

为此添加更多列只是将它们添加到WITH块内的select中的一种情况

COALESCE并不神奇。就像IFNULL一样,但是可以包含很多参数。它以从左到右的方式返回第一个不为null的值。当与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

)

您可以像switchlatest那样离开此switch

如果有多个与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!

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...