使用多对多相同类型关系表在SELECT上排除类似的行

时间:2016-01-04 03:10:39

标签: mysql sql

我有两个MySQL表:ArticlesSimilarArticles

CREATE TABLE `Articles` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `text` text,
  `priority` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
)

CREATE TABLE `SimilarArticles` (
  `article_id` int(11) unsigned NOT NULL,
  `similar_article_id` unsigned NOT NULL,
  PRIMARY KEY (`article_id`,`similar_article_id`)
)

SimilarArticles是一个简单的关系表,用于跟踪类似的文章记录。

this post中的用户totymedli有一个很好的解释如何在这种情况下选择类似的记录,这很好。

但是现在我必须创建一个SELECT语句,以确保结果中有没有类似的文章。因此,对于表中所有类似文章组,只有字段priority中具有最高值的记录才会进入结果集。

到目前为止,我还没有想出办法。有谁知道如何在MySQL中有效地做到这一点?

实施例

表格文章

id     text      priority

1      FooA      1
2      Bar       1
3      FooB      3
4      FooC      2
5      Baz       9

表格类似文章

article_id  similar_article_id

1           3
4           1
3           4

我正在尝试创建的SELECT语句应返回:

id     text      priority

2      Bar       1
3      FooB      3
5      Baz       9

因为FooA,FooB和FooC根据SimilarArticles表是相似的,并且FooB具有这三个记录中最高的priority,所以只有FooB应该在结果集中(与Bar和Baz一起,没有类似的记录)。

2 个答案:

答案 0 :(得分:0)

您需要执行左连接并过滤掉任何类似的文章。请参阅this great explanation排除使用SO创始人Jeff Atwood的LEFT JOIN。

enter image description here

react.js

如果您还希望获得与类似文章无关的最高优先级文章,那么您必须将此查询与该查询结合起来:

   SELECT Articles.*
     FROM Articles
LEFT JOIN SimilarArticles ON Articles.id = SimilarArticles.article_id
    WHERE SimilarArticles.article_id IS NULL
 ORDER BY priority DESC

注意:union之后的第二个查询将返回所有优先级最高的记录,而不仅仅是第一个。如果您真的只想返回第一个,那么您必须在该查询的末尾添加 SELECT * FROM Articles LEFT JOIN SimilarArticles ON Articles.id = SimilarArticles.article_id WHERE SimilarArticles.article_id IS NULL UNION ALL SELECT Articles.* FROM Articles INNER JOIN SimilarArticles ON Articles.id = SimilarArticles.article_id INNER JOIN (SELECT max(priority) AS priority FROM SimilarArticles) maxp ON maxp.priority = Articles.priority WHERE SimilarArticles.article_id IS NOT NULL

答案 1 :(得分:0)

我找到的一个工作解决方案是创建一个查询,该查询为我提供了每个文章的所有类似文章ID,这些文章在一个额外的列中以逗号分隔。

在我的服务器端语言中迭代结果时,我会爆炸类似的文章ID并构建一个包含所有类似ID的数组。这使我有可能跳过ID已经存储在该数组中的所有行。

虽然我更喜欢纯SQL的解决方案,但我觉得这在我的情况下运行得很好。

查询:

SELECT
  id,
  GROUP_CONCAT(
    IF (sa.article_id = a.id, similar_article_id, article_id)
    SEPARATOR ','
  ) AS similar_ids,
  text,
  priority
FROM Articles a

LEFT JOIN SimilarArticles sa
  ON a.id = sa.article_id OR a.id = sa.similar_article_id

GROUP BY id
ORDER BY priority DESC

结果(使用给定的样本数据):

id  similar_ids    text   priority
1   3,4            FooA   3
4   3,1            FooC   2         <- Will be skipped
2   NULL           Bar    1         
3   1,4            FooB   1         <- Will be skipped