Mysql 基于两表关系的SQL top记录

Mysql 基于两表关系的SQL top记录,mysql,sql,foreign-key-relationship,has-many,Mysql,Sql,Foreign Key Relationship,Has Many,我存储三个主要项目:文章、实体和关键字。这包括5个表: article { id } entity {id, name} article_entity {id, article_id, entity_id} keyword {id, name} article_keyword {id, article_id, keyword_id} 我想得到所有的文章,包含前X关键字+实体。我可以通过实体id/keyword\u id上的一个简单的分组获得前X个关键字或实体 SELECT [entity|ke

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

article { id }
entity {id, name}
article_entity {id, article_id, entity_id}
keyword {id, name}
article_keyword {id, article_id, keyword_id}
我想得到所有的文章,包含前X关键字+实体。我可以通过
实体id/keyword\u id
上的一个简单的分组获得前X个关键字实体

SELECT [entity|keyword]_id, count(*) as num FROM article_entity
GROUP BY entity_id ORDER BY num DESC LIMIT 10
如何获取与顶级实体和关键词相关的所有文章?

这是我想象的,但我知道这不起作用,因为逐实体分组限制了文章id的返回

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

我采取了几个步骤

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

这里有一个

说明:

首先要努力找到最上面的X个实体。(4似乎对我想在小提琴中建立的联想数量起作用)

我不想在这里选择文章,因为它会使组倾斜,因为您希望只关注顶级实体

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

显然,关键字也需要同样的逻辑。然后将查询
union
ed放在一起(),并从union中提取不同的项目ID


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

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


如果我理解你的意思,那么查询的目的是找到与前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;
在顶级实体/关键字的两个子查询中使用简单的
限制10
的缺点是它不会处理关系,因此,如果第11个关键字与第10个关键字一样流行,它仍然不会被选中。这可以通过使用ranking函数来修复,但是afaik-MySQL没有任何内置功能(比如Oracle或MSSQL中的RANK()窗口函数)


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

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

现在,也就是说,其他的建议与我发布的内容有些相似,但它们似乎都限制了每套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及其计数。按项目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

实体
关键字
表没有重复项。每个实体和关键字只使用一次,因此子查询中的
COUNT(*)
没有意义。也许您打算对
实体文章
关键字文章
表进行全部计数?这些可能会有多个结果。我根据您的反馈更新了我的答案。在子查询中,您可以只使用映射表,因为这是真正的计数,但我将它们加入到相应的表中,以防您想验证您的查询是否正确。此外,我相信你只会给出这个查询中关键词最多的10篇文章。而不是前10个关键词中的文章/entities@Xeoncross我已经更新了我的查询并给出了一些解释,包括SQLFIDLE链接。我相信这就是你需要的答案。查询可能会被缩短,我会考虑,但希望你对结果作出反应。如果你愿意,考虑下面这个简单的两步行动:1。如果您还没有这样做,请提供适当的DDL(和/或SQLFIDLE),以便我们可以更轻松地复制问题。2.如果您还没有这样做,请提供一个与步骤1中提供的信息相对应的所需结果集。@草莓,请您避免一般性的复制/粘贴,即告诉某人放置DLL并转到SQL FIDLE。有了所提供的信息,那些流利的人可以理解,我知道我已经看到了太多的这些粘贴评论。我提供了一个答案,另一个注意到我可能误解了你想要的,但不相信我这么做了。让我知道,如果我的目标与你正在寻找的。。。谢谢,我认为你提到了一个正确的观点,那就是消极选择适合一套但不适合另一套的文章
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
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
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;
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