【问题标题】:SQL top records based on two tables relations基于两表关系的 SQL 最高记录
【发布时间】:2014-11-04 17:06:07
【问题描述】:

我要存储三个主要项目:文章、实体和关键字。这有 5 个表:

article { id }
entity {id, name}
article_entity {id, article_id, entity_id}
keyword {id, name}
article_keyword {id, article_id, keyword_id}

我想获取所有包含前 X 个关键字 + 实体的文章。我可以通过entity_id/keyword_id 上的简单分组获得前 X 个关键字 实体。

SELECT [entity|keyword]_id, count(*) as num FROM article_entity
GROUP BY entity_id ORDER BY num DESC LIMIT 10

如何获取与热门实体和关键字相关的所有文章?

这是我的想象,但我知道它不起作用,因为实体分组限制了 article_id 的返回。

SELECT * FROM article
WHERE EXISTS (
    [... where article is mentioned in top X entities.. ]
) AND EXISTS (
    [... where article is mentioned in top X keywords.. ]
);

【问题讨论】:

  • 如果您愿意,请考虑遵循以下简单的两步操作: 1. 如果您还没有这样做,请提供适当的 DDL(和/或 sqlfiddle),以便我们可以更轻松地复制问题。 2. 如果您尚未这样做,请提供与步骤 1 中提供的信息相对应的所需结果集。
  • @Strawberry,你能不能不要告诉一个人把 DLL 放到 SQL Fiddle 的通用复制/粘贴。有了提供的信息,那些流利的人可以弄清楚,我知道我见过太多这样的粘贴 cmets。
  • 我提供了一个答案,另一个指出我可能误解了你想要的,但不要相信我做了。让我知道我是否符合您正在寻找的目标...谢谢

标签: mysql sql foreign-key-relationship has-many


【解决方案1】:

如果我理解您正确,查询的目标是查找与前 10 个实体之一以及前 10 个关键字之一相关的文章。如果是这种情况,下面的查询应该做到这一点,要求返回的文章在前 10 个实体集和前 10 个关键字集中都匹配。

请试一试。

SELECT a.id 
FROM article a
INNER JOIN article_entity  ae ON a.id = ae.article_id
INNER JOIN article_keyword ak ON a.id = ak.article_id
INNER JOIN (
  SELECT entity_id, COUNT(article_id) AS article_entity_count
  FROM article_entity
  GROUP BY entity_id 
  ORDER BY article_entity_count DESC LIMIT 10
) top_ae ON ae.entity_id = top_ae.entity_id
INNER JOIN (
  SELECT keyword_id, COUNT(article_id) AS article_keyword_count 
  FROM article_keyword
  GROUP BY keyword_id 
  ORDER BY article_keyword_count DESC LIMIT 10
) top_ak ON ak.keyword_id = top_ak.keyword_id
GROUP BY a.id;

在顶级实体/关键字的两个子查询中使用 simplelimit 10 的缺点是它不会处理关系,因此如果第 11 个关键字与第 10 个关键字一样受欢迎,它仍然不会被选中。这可以通过使用排名函数来解决,但是 afaik MySQL 没有内置任何东西(如 Oracle 或 MSSQL 中的 RANK() 窗口函数)。

我设置了一个示例SQL Fiddle(但使用较少的数据点和limit 2因为我很懒)。

【讨论】:

  • 这看起来像我想要做的。我想唯一的另一件事是基于最多匹配的某种排序,以防有大量文档。例如,在您的查询中,我只需要添加a.id, COUNT(*) num FROM,然后添加ORDER BY num DESCSQL Fiddle
  • @Xeoncross 这似乎是一个聪明的修改,您可能还想使用LIMIT来减少返回的行数。
【解决方案2】:

不知道您正在处理的数据量,我首先建议您在文章表上设置两个存储列,分别用于实体和关键字的计数。然后通过添加/删除每个触发器,更新相应的计数器列。这样,您不必每次都需要进行刻录查询,尤其是在基于 Web 的界面中。然后,您可以只从按 E+K 计数降序排列的文章表中选择并完成它,而不是不断地子查询基础表。

现在,也就是说,其他建议与我发布的内容有些相似,但它们似乎都限制了每组 10 条记录。让我们把这个场景放到图片中。假设您的文章 1-20 包含 10、9 和 8 个实体以及 1-2 个关键字。然后文章 21-50 有相反的... 10、9、8 个关键字和 1-2 个实体。现在,您的文章 51-58 有 7 个实体和 7 个关键字,总共 14 个组合点。由于实体只会返回符合条件的 1-20 记录和关键字记录 21-50,因此没有任何查询会捕捉到这一点。第 51 至 58 条在列表中的位置太靠后了,即使它的总数是 14 条,它也不会被考虑。

为了处理这个问题,每个子查询都是专门针对文章 ID 及其计数的完整查询。 article_ID 的简单排序是连接到主文章表的基础。

现在,如果可用,则 coalesce() 将获取计数,否则为 0 并将两个值相加。因此,当应用限制时,结果会按照计数最高的顺序排列(从而获得场景示例文章 51-58 以及其他一些文章)。

SELECT
      a.id,
      coalesce( JustE.ECount, 0 ) ECount,
      coalesce( JustK.KCount, 0 ) KCount,
      coalesce( JustE.ECount, 0 ) + coalesce( JustK.KCount, 0 ) TotalCnt
   from
      article a
         LEFT JOIN ( select article_id, COUNT(*) as ECount
                        from article_entity
                        group by article_id
                        order by article_id ) JustE
            on a.id = JustE.article_id
         LEFT JOIN ( select article_id, COUNT(*) as KCount
                        from article_keyword
                        group by article_id
                        order by article_id ) JustK
            on a.id = JustK.article_id
   order by
      coalesce( JustE.ECount, 0 ) + coalesce( JustK.KCount, 0 ) DESC
   limit 10

【讨论】:

  • 我认为您在否定选择适合一组但不适合另一组的文章的问题上触及了一个有效点,但我也认为您可能误解了其他查询(或至少我的)工作。子查询返回 10 个最常见的实体/文章,整个查询选择两组中至少有一个匹配项的文章,因此只要满足条件,它就不会限制返回的文章数量。我认为这就是 OP 想要的(获得在关键字和实体中均排名靠前的文章),尽管我可能误解了。
  • @jpw,我恭敬地不同意对 OP 请求的解释。希望他们会回答我们各自的问题以进行任何澄清,但我确实理解您在两个类别中的最高点......您的限制过程可能仍然会导致错误。 10 ent + 1 kw vs 5 ent + 11 kw ... 11kw 不会出现在您的 ent 结果集中。
  • 让我们同意尊重地不同意:) 如果10 ent + 1 kw 是指排名第 10 的实体和排名第一的关键字,它将被选中,而第 5 最常见的 ent + 11 最常见的 kw 的组合不会因为第 11 个 kw 不在前 10 组中,但我不知何故我觉得这不是你所说的,而是应该是匹配的 ent/kw 的总数应该是限制(这样总匹配数为 16 的文章应优先于总匹配数为 11 的文章),这就是我的解释不同的地方。
  • @jpw,不。我的意思是 10 个实体和 1 个关键字与 5 个实体和 10 个关键字。这 5 个实体对于实体计数的前 10 篇文章可能太少,因此永远不会在关键字文件中找到匹配的关键字,其中 11 个关键字将位于顶部关键字列表中。
  • 希望 OP 能够澄清意图,因为根据所需结果,两种解释都可能有效。
【解决方案3】:

我分几个步骤完成了这个

tl;dr 这显示了前 (4) 个关键字和实体中的所有文章:

这是fiddle

select
  distinct article_id
from
(
select
  article_id
from
  article_entity ae
  inner join 
    (select
      entity_id, count(*)
    from
      article_entity
    group by
      entity_id
    order by 
      count(*) desc
    limit 4) top_entities on ae.entity_id = top_entities.entity_id
union all
select
  article_id
from
  article_keyword ak
  inner join 
    (select
      keyword_id, count(*)
    from
      article_keyword
    group by
      keyword_id
    order by 
      count(*) desc
    limit 4) top_keywords on ak.keyword_id = top_keywords.keyword_id) as articles

解释:

首先要努力寻找前 X 个实体。 (4 似乎适用于我想在小提琴中建立的关联数量)

我不想在这里选择文章,因为它会扭曲分组,您只想专注于顶级实体。 Fiddle

select
  entity_id, count(*)
from
  article_entity
group by
  entity_id
order by 
  count(*) desc
limit 4

然后我从这些顶级实体中选择了所有文章。 Fiddle

select
  *
from
  article_entity ae
  inner join 
    (select
      entity_id, count(*)
    from
      article_entity
    group by
      entity_id
    order by 
      count(*) desc
    limit 4) top_entities on ae.entity_id = top_entities.entity_id

显然,关键字需要发生相同的逻辑。然后将这些查询 unioned 一起 (fiddle) 并从联合中提取不同的文章 ID。

这将为您提供与前 (x) 个实体和关键字相关的所有文章。

【讨论】:

  • @Xeoncross 我已经更新了我的查询并给出了一些解释,包括 sql fiddle 链接。我相信这是您需要的答案。查询可能会缩短,我会考虑这一点,但希望您对结果做出反应。
【解决方案4】:

这将获得前 10 个关键字文章,它们也是前 10 个实体。您可能无法返回 10 条记录,因为一篇文章可能只满足其中一个条件(顶级实体但不是顶级关键字或顶级关键字但不是顶级实体)

select *
from article a
inner join
                (select count(*),ae.article_id
                 from article_entity ae
                group by ae.article_id
                order by count(*) Desc limit 10) e
on a.id = e.article_id
inner join
                 (select count(*),ak.article_id
                from article_keyword ak
                group by ak.article_id
                order by count(*) Desc limit 10) k
on a.id = k.article_id

【讨论】:

  • entitykeyword 表没有重复项。它们每个实体和关键字只使用一次,因此子查询中的COUNT(*) 没有意义。也许您打算对entity_articlekeyword_article 表进行计数?这些可能有多个结果要计算。
  • 我根据您的反馈更新了我的答案。在子查询中,您可以只使用映射表,因为这是真正的计数所在,但我将它们加入到相应的表中,以防您想要验证
  • 您的查询似乎是doesnt compile。此外,我相信您只会使用此查询给出 10 篇关键字最多的文章。而不是来自前 10 个关键字/实体的文章
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-13
  • 1970-01-01
相关资源
最近更新 更多