Sql MariaDB-LEFT JOIN不会返回所有行

Sql MariaDB-LEFT JOIN不会返回所有行,sql,mariadb,left-join,greatest-n-per-group,mariadb-10.1,Sql,Mariadb,Left Join,Greatest N Per Group,Mariadb 10.1,(在将此标记为重复之前,请阅读问题)。 我有两个不同的表,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

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

我有两个不同的表,
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
对于每个bot,我想检索它的最新状态,这意味着:对于每个bot,我想知道最近一行的
激活的
字段是
1
还是
0
。为了返回那些在
开关
表中没有条目的机器人程序的结果,我尝试使用
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
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!
我的预期结果是:

Following | ID | Info
---------------------
        0 | 0  | abc
        0 | 1  | def
关键是我并没有得到所有的行,相反,这个查询只返回一行:

Following | ID | Info
---------------------
        0 | 0  | abc
这很奇怪,因为我使用的是
左连接
。为了了解发生了什么,我分别跑了

SELECT * 
FROM bot

当然,第一个查询返回:

ID | Info
---------
0  | abc
1  | def
当第二个返回时:

Date       | Activated | BotID | User
-------------------------------------
2020-01-02 |         0 |     0 | John
我真的不明白为什么
左连接
没有同时保留
Bot
行。我检查了问题,但我没有使用任何外部
WHERE
,所以这不是我的情况。

提前谢谢

正如您所见,您的查询应该做您想做的事情(我修复了列名的问题,并稍微更改了数据,因此很容易理解它的工作原理)。所以问题不是在于你的数据,就是你过于简化了查询

不过,让我提出一个优化建议。因为您只需要表
开关
中的一列,所以可以使用行限制相关子查询而不是联接:

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\u number()


如果您的MySQL/MariaDB版本不支持窗口功能,并且您需要来自
开关的多个列,那么您可以尝试使用稍微不同的语句来表示查询,以查看问题是否仍然存在。考虑<代码> < <代码> >子句>左连接< /代码>中的筛选,如下所示:

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

正如您所看到的,您的查询应该做您想做的事情(我修复了列名的问题,并稍微更改了数据,因此很容易理解它的工作原理)。所以问题不是在于你的数据,就是你过于简化了查询

不过,让我提出一个优化建议。因为您只需要表
开关
中的一列,所以可以使用行限制相关子查询而不是联接:

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\u number()


如果您的MySQL/MariaDB版本不支持窗口功能,并且您需要来自
开关的多个列,那么您可以尝试使用稍微不同的语句来表示查询,以查看问题是否仍然存在。考虑<代码> < <代码> >子句>左连接< /代码>中的筛选,如下所示:

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

当您使用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
这应该是相当容易编辑-在它的核心是一个标准的左加入机器人:开关。有时你会有一个开关,有时不会。当你得到一个开关时,你会从行号中得到最近的日期。如果删除
,其中rn=1
,您将看到获得所有日期,但有一个rn列,它是一个递增计数器,当bot id更改时,它将从1重新启动。
rn=1
行是您想要的,因为计数器随着日期从最近到过去而递增

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

联合
并不是什么神奇的东西;它类似于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

)
您可以在中加入此
switchlatest
,就像您将
switch

如果有多个日期与true max相同,则其行为与rownum稍有不同(它们都返回,rownum route仅返回一个)

不支持的数据库版本的常规模式

WITH alias AS 
(
   SELECT columns FROM ...
)
SELECT ... 
FROM 
  table 
  JOIN 
  alias 
  ON ...
将“别名”复制到右括号后:

(
   SELECT columns FROM ...
) alias
然后复制整个内容并将其粘贴到查询中的别名上:

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
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!

当您使用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
这应该是相当容易编辑-在它的核心是一个标准的左加入机器人:开关。有时你会有一个开关,有时不会。当你得到一个开关时,你会从行号中得到最近的日期。如果删除
,其中rn=1
,您将看到获得所有日期,但有一个rn列,它是一个递增计数器,当bot id更改时,它将从1重新启动。
rn=1
行是您想要的,因为计数器随着日期从最近到过去而递增

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

联合
并不是什么神奇的东西;它类似于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

)
您可以在中加入此
switchlatest
,就像您将
switch

如果有多个日期与true max相同(它们都返回),则其行为与rownum稍有不同