从 LEFT JOIN 列获取最新结果

Get most recent result from a LEFT JOIN column

我正在从头开始创建一个自定义论坛,我正在尝试使用一些 LEFT JOIN 查询来获取信息,例如 total poststotal threads 和大多数 recent thread.我已设法获取数据,但 recent thread 一直返回随机值而不是最近的线程。

CREATE TABLE forum_categories
    (`name` varchar(18), `label` varchar(52), `id` int)
;
    
INSERT INTO forum_categories
    (`name`, `label`, `id`)
VALUES
    ('General Discussion', 'Talk about anything and everything Digimon!', 1),
    ('Deck Discussion', 'Talk about Digimon TCG Decks and Strategies!', 2),
    ('Card Discussion', 'Talk about Digimon TCG Cards!', 3),
    ('Website Feedback', 'A place to discuss and offer feedback on the website', 4)
;

CREATE TABLE forum_topics
    (`name` varchar(18), `id` int, `parent_id` int, `author_id` int, date date)
;
    
INSERT INTO forum_topics
    (`name`, `id`, `parent_id`, `author_id`, `date`)
VALUES
    ('My First Topic', 1, 1, 16, '2021-03-29'),
    ('My Second Topic', 2, 1, 16, '2021-03-30')
;

CREATE TABLE forum_topics_content
    (`id` int, `topic_id` int, `author_id` int, date datetime, `content` varchar(300))
;
    
INSERT INTO forum_topics_content
    (`id`, `topic_id`, `author_id`, `date`, `content`)
VALUES
    (1, 1, 16, '2021-03-29 15:46:55', 'Hey guys! This is my first post!'),
    (2, 1, 16, '2021-03-30 08:05:13', 'This is my first topic reply!')
;

我的查询:

SELECT forum_categories.name, label, forum_categories.id, COUNT(DISTINCT(forum_topics.id)) as 'topics', COUNT(DISTINCT(forum_topics_content.id)) as 'posts', SUBSTRING(forum_topics.name,1, 32) as 'thread'
FROM forum_categories 
LEFT JOIN forum_topics ON forum_categories.id = forum_topics.parent_id
LEFT JOIN forum_topics_content ON forum_topics.id = forum_topics_content.topic_id
GROUP BY forum_categories.id
ORDER BY forum_categories.id, forum_topics.date DESC

我认为 ORDER BYforum_topics.date DESC 对我有用并输出最新的线程 "My Second Topic" 但它没有。 我有点难过,尝试了 ORDER BY 的不同变体但无济于事。 thread 从两个可能的结果中不断返回一个随机结果。

此 fiddle 上提供了包含数据的完整示例:https://www.db-fiddle.com/f/auDzUABaEpYzLKDkRqE7ok/0

期望的结果'thread' 始终是最新线程,在本例中为 "My Second Topic"。然而,它似乎总是在 "My First Topic""My Second Topic" 之间随机选择。

第一行的输出应始终为:

'General Discussion' , 'Talk about anything and everything Digimon!' 1, 2, 2, 'My Second Topic'

你 fiddle 你有:

SET SESSION sql_mode = '';

您应该将其更改为:

SET SESSION sql_mode = 'ONLY_FULL_GROUP_BY';

你会得到这样的错误:

Query Error: Error: ER_WRONG_FIELD_WITH_GROUP: Expression #1 of 
SELECT list is not in GROUP BY clause and contains nonaggregated 
column 'test.forum_categories.name' which is not functionally 
dependent on columns in GROUP BY clause; this is incompatible with
 sql_mode=only_full_group_by

文档中指出:

ONLY_FULL_GROUP_BY

Reject queries for which the select list, HAVING condition, or ORDER BY list refer to nonaggregated columns that are neither named in the GROUP BY clause nor are functionally dependent on (uniquely determined by) GROUP BY columns.

As of MySQL 5.7.5, the default SQL mode includes ONLY_FULL_GROUP_BY. (Before 5.7.5, MySQL does not detect functional dependency and ONLY_FULL_GROUP_BY is not enabled by default. For a description of pre-5.7.5 behavior, see the MySQL 5.6 Reference Manual.)

他们这样做是有充分理由的。 参见:

thread keeps returning a random result from the two possible results.

提供的查询只是不确定的,等同于:

SELECT forum_categories.name, 
  forum_categories.label, 
  forum_categories.id,
  COUNT(DISTINCT(forum_topics.id)) as 'topics',
  COUNT(DISTINCT(forum_topics_content.id)) as 'posts',
  SUBSTRING(ANY_VALUE(forum_topics.name),1, 32) as 'thread'
FROM forum_categories 
LEFT JOIN forum_topics ON forum_categories.id = forum_topics.parent_id
LEFT JOIN forum_topics_content ON forum_topics.id = forum_topics_content.topic_id
GROUP BY forum_categories.id,forum_categories.name,forum_categories.label
ORDER BY forum_categories.id, ANY_VALUE(forum_topics.date) DESC;

假设 forum_categories.id 是 PRIMARY KEY,name/label 在功能上是依赖的,但列的其余部分只是 ANY_VALUE.

如果 SELECT 列表中的列在功能上不相关或不包含聚合函数,则查询 不正确 。在 MySQL 8.0 或启用 ONLY_FULL_GROUP_BY 时,结果是错误的。

相关:


有不同的方法可以达到预期的结果(相关子查询、窗口函数、限制)等等。

此处使用GROUP_CONCAT:

SELECT forum_categories.name, 
  forum_categories.label, 
  forum_categories.id,
  COUNT(DISTINCT(forum_topics.id)) as `topics`,
  COUNT(DISTINCT(forum_topics_content.id)) as `posts`,
  SUBSTRING_INDEX(GROUP_CONCAT(SUBSTRING(forum_topics.name,1,32)
                 ORDER BY forum_topics.`date` DESC 
                 SEPARATOR '~'),
                 '~',1) AS `thread`
FROM forum_categories 
LEFT JOIN forum_topics ON forum_categories.id = forum_topics.parent_id
LEFT JOIN forum_topics_content ON forum_topics.id = forum_topics_content.topic_id
GROUP BY forum_categories.id,forum_categories.name,forum_categories.label
ORDER BY forum_categories.id;

工作原理:

GROUP_CONCAT 是允许连接字符串保留顺序的聚合函数。

My Second Topic~My First Topic~My First Topic

然后 SUBSTRING_INDEX returns 字符串的一部分,直到第一次出现分隔符 ~

db<>fiddle demo