MySQL多对多的关系问题

时间:2015-10-07 18:16:29

标签: mysql

我有三张桌子:文章,标签和articles_tags。可以想象,每篇文章都可以有多个标签,每个标签都可以分配给多篇文章。我有所谓的主要文章(由唯一的URL表示),并希望得到它的相关文章,基于它们之间的共享标签,如:如果主要文章和第2条有一个共同的标签,显示两篇文章(理想情况下,它不会在结果中显示/包含主要文章)。主要文章的唯一URL在SQL查询中传递。

预期的结果超出了我的意愿,所以任何帮助都会受到赞赏。

SQLFiddle

如果以上网站脱机,则复制代码:

数据库和内容:

CREATE TABLE `articles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `url` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `status` tinyint(4) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE `tags` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `tag` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `url` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE `articles_tags` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `article_id` int(11) NOT NULL,
  `tag_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `articles` (`url`, `title`, `status`) VALUES
('test-article-1',  'Test Article #1',  1),
('test-article-2',  'Test Article #2',  1),
('test-article-3',  'Test Article #3',  0),
('test-article-4',  'Test Article #4',  0),
('test-article-5',  'Test Article #5',  1);

INSERT INTO `tags` (`tag`, `url`) VALUES
('Test',    'test'),
('City',    'city'),
('Nature',  'nature');

INSERT INTO `articles_tags` (`article_id`, `tag_id`) VALUES
(1, 1),
(1, 2),
(1, 3),
(2, 2),
(3, 1),
(3, 2),
(4, 2),
(5, 1);

最新(不正常)SQL查询:

SELECT
tags.tag,
articles.url,
articles.title
FROM articles
LEFT JOIN articles_tags ON articles_tags.article_id=articles.id
LEFT JOIN tags ON articles_tags.tag_id=tags.id
WHERE (articles.url='test-article-1'
    OR tags.id IN (articles_tags.tag_id))
    AND articles.status=1
GROUP BY articles.id

结果: 正如你在SQLFiddle上看到的,它显示了第1,2和5条,但在我看来它应该只显示1和5

预期成果:第1条和第5条,理想情况下只有5条(不包括第1条,因为它是主要条款)。

2 个答案:

答案 0 :(得分:0)

我不太清楚我理解你为什么不期望你的结果中的第2条,因为它和第1条都有标记2.这下面仍然应该返回第2条,所以它可能不是你想要的,但这是最直接的#34;同样标记的排名"查询我能想到:

SELECT b.*, COUNT(1) AS tagMatches
FROM articles AS a
INNER JOIN articles_tags AS aTags ON a.id=aTags.article_id
INNER JOIN articles_tags AS bTags 
   ON aTags.article_id<>bTags.article_id 
   AND aTags.tag_id = bTags.tag_id
INNER JOIN articles AS b ON bTags.article_id
WHERE a.url = ?
GROUP BY b.url
ORDER BY tagMatches DESC, b.title
;

编辑:这假设文章不能多次使用相同的标签。如果不是这种情况,它会使排名偏差(但如果重复的标签应该有更多的权重,这可能是有利的。)

Edit2:值得注意的是,*可能不应该用于最终结果;为了简单起见,我在这里使用它。

答案 1 :(得分:0)

OR条件OR tags.id IN (articles_tags.tag_id))会触发这些行:

INSERT INTO `articles_tags` (`article_id`, `tag_id`) VALUES
(1, 1),
 ...
(3, 1),
 ...,
(5, 1);

所以,对我来说结果看起来很好

相关问题