【问题标题】:JOIN two tables, sort by field on second table, no duplicates加入两个表,按第二个表的字段排序,没有重复
【发布时间】:2019-11-16 12:18:23
【问题描述】:

我很抱歉,但我无法理解这一点(即使在搜索和尝试了一些东西之后也是如此)。我要做的就是连接两个表,然后在 article_translations 表中的 created_at 上对连接进行降序排序。但是,我需要唯一的条目。

我有两张桌子:

articles
--------
id
user_id
article_translations
--------
id
article_id (brings this table together with the other one)
locale
title
...
created_at
updated_at

执行mysql查询:

SELECT * from articles
JOIN article_translations as t on t.article_id = articles.id 
ORDER BY t.created_at desc

我得到了带有相应相关条目的连接表。

articles.id t.article_id created_at
1               1           ''
1               1           ''
2               2           ''

当我尝试不删除重复项时,在这种 id = 1 的文章的情况下,我得到一个严重的错误:

SELECT 列表的表达式 #3 不在 GROUP BY 子句中,并且包含非聚合列“blog.t.id”,它在功能上不依赖于 GROUP BY 子句中的列;这与 sql_mode=only_full_group_by 不兼容

期望的结果是:

articles.id t.article_id created_at
1               1           ''
2               2           ''

任何帮助请...谢谢!

【问题讨论】:

  • 继续你向我们展示的样本数据,你想保留两个重复的created_at 值吗?
  • 换句话说,即使一篇文章可能有多个翻译,您只想要最近的一个?

标签: mysql


【解决方案1】:

获取唯一行的唯一方法是,如果您想要每个 id 的最新(或最早?)日期,您可以通过 group by a.id, t.article_id 和聚合来做到这一点:

SELECT a.id, t.article_id, MAX(t.created_at) AS created_at 
FROM articles AS a INNER JOIN article_translations AS t 
ON t.article_id = a.id 
GROUP BY a.id, t.article_id
ORDER BY MAX(t.created_at) DESC 

如果您想要 2 个表的所有列,首先从 article_translationsNOT EXISTS 获取唯一行,然后加入到 articles

SELECT * 
FROM articles AS a INNER JOIN (
  SELECT t.*
  FROM article_translations t
  WHERE NOT EXISTS (
    SELECT 1 FROM article_translations
    WHERE article_id = t.article_id AND created_at > t.created_at
  )
) AS t 
ON t.article_id = a.id 
ORDER BY t.created_at DESC

如果article_translations 中的行数不超过 1 行,article_id 的最大行数与 created_at 相同。

对于 MySql 8.0+,您可以使用 ROW_NUMBER():

SELECT t.* FROM (
  SELECT *,  ROW_NUMBER() OVER (PARTITION BY a.id ORDER BY t.created_at DESC) rn 
  FROM articles AS a INNER JOIN article_translations AS t 
  ON t.article_id = a.id 
) AS t
WHERE t.rn = 1

【讨论】:

  • 是的,我想获得最早期/最新的文章,所以 MAX() 是正确的。当我排除 Join 并仅使用 article_translations 时,我也来到了这个解决方案。
  • 如果您真的不需要来自articles 的任何列,则不需要加入。
  • 并且只是为了理解整个事情......在 created_at 上需要 MAX() 是否正确,否则 mysql 不知道如何进一步“分组”结果,对吗?如果我在没有看到 id 的情况下进行查询,例如,我会得到两个具有两个不同日期的 id 2 条目。所以基本上我需要告诉 mysql 从这些剩余的那些中取出具有“最高”/最大日期的那个 - 是吗?
  • 对,我这样做的原因是我想结合使用 Laravel 的 Eloquent 模型和 Eager Loding 选项,我想仍然可以访问 Eloquent 方法。跨度>
  • MAX() 需要对所选组执行聚合并返回单个值。在任何其他情况下,即使不存在,如果一个组有 2 行具有相同的 max created_at,则将返回具有该 max created_at 的 2 行。
【解决方案2】:

问题中的查询几乎就在那里。您只需要添加 distinct 关键字:

SELECT distinct * from articles
JOIN article_translations as t on t.article_id = articles.id 
ORDER BY t.created_at desc`

【讨论】:

    【解决方案3】:

    感谢 forpas 提供正确答案。对于 Laravel Eloquent 模型和急切加载,我基本上需要这个查询。万一有人关心,这就是最终解决方案现在的样子:

    $articles = Article::join('article_translations as at', function ($join) {
                                $join->on('articles.id', '=', 'at.article_id');
                        })
                        ->select('articles.id', 
                        'articles.user_id',DB::raw('MAX(at.created_at) as created_at'))
                        ->groupBy('at.article_id')
                        ->orderBy('created_at', 'desc')
                        ->with('translations')
                        ->get();
    

    纯 SQL

    SELECT at.article_id, MAX(at.created_at) as created_at FROM articles as a
    INNER JOIN article_translations as at
    ON a.id = at.article_id
    GROUP BY at.article_id
    ORDER BY MAX(created_at) desc
    

    【讨论】:

      【解决方案4】:

      既然你确实想要所有列:

      如果您尝试仅使用最新创建日期保留文章翻译,则假设创建日期对于给定文章的翻译是唯一的,一种方法是创建一个子查询,计算每个 article_translation.article_id 的最大值 @ 987654322@列值:

      SELECT articles.*, t.* from articles
      JOIN article_translations as t on t.article_id = articles.id
      JOIN (
         SELECT article_id, max(created_at) as created_at from article_translations
         GROUP BY article_id
      ) a_t on t.article_id = a_t.article_id and t.created_at = a_t.created_at
      ORDER BY t.created_at desc
      

      如果创建日期不是唯一的,或者即使它们是唯一的,那么这也应该有效:

      SELECT articles.*, t.* from articles
      JOIN article_translations as t on t.article_id = articles.id
      AND t.article_id = (
          SELECT t2.article_id from article_translations t2
          WHERE t2.article_id = t.article_id
          ORDER BY created_date DESC
          LIMIT 1
      )
      ORDER BY t.created_at DESC
      

      【讨论】:

        猜你喜欢
        • 2011-07-01
        • 2018-11-23
        • 2019-08-05
        • 1970-01-01
        • 1970-01-01
        • 2017-09-05
        • 1970-01-01
        • 1970-01-01
        • 2013-03-09
        相关资源
        最近更新 更多