Mysql 多对多单查询优化
我有3个多对多关系的表Mysql 多对多单查询优化,mysql,sql,database,Mysql,Sql,Database,我有3个多对多关系的表 CREATE TABLE news(id int, content varchar(64)); CREATE TABLE tags(id int, name varchar(64)); CREATE TABLE news_tags(id int, tag_id int, news_id int); INSERT INTO news VALUES (1, "Hello, world!"), (2, "Test news"), (3
CREATE TABLE news(id int, content varchar(64));
CREATE TABLE tags(id int, name varchar(64));
CREATE TABLE news_tags(id int, tag_id int, news_id int);
INSERT INTO news VALUES
(1, "Hello, world!"),
(2, "Test news"),
(3, "test news 2"),
(4, "test news 3"),
(5, "test news 4");
INSERT INTO tags VALUES
(1, "general tag"),
(2, "sub tag 1"),
(3, "sub tag 2"),
(4, "normal tag");
INSERT INTO news_tags VALUES
(1, 1, 1),
(2, 2, 1),
(3, 3, 1);
INSERT INTO news_tags VALUES
(4, 1, 2),
(5, 2, 2),
(6, 1, 3),
(7, 4, 3),
(7, 2, 4),
(8, 3, 4),
(9, 1, 5);
我想选择新闻\u id什么
SELECT news_id FROM news_tags WHERE tag_id = 1 OR tag_id = 2
GROUP BY news_id
HAVING COUNT(news_id) = 2
UNION
SELECT news_id FROM news_tags WHERE tag_id = 1 AND news_id not in (SELECT news_id FROM news_tags WHERE tag_id in (2,3));
但是有两个问题
实例您的问题不清楚,因为“子”标记和“一般”标记的概念没有定义 但是,如果您想同时处理多个条件,您仍然可以使用一个
groupby
和HAVING
子句
例如,如果您想要满足以下任一条件的news\u id
s:
=1tag\u id
- 或者
=2和tag\u id
=3tag\u id
SELECT nt.news_id
FROM news_tags nt
GROUP BY nt.news_id
HAVING (COUNT(*) = 1 AND MIN(nt.tag_id) = 1) OR
SUM( nt.tag_id IN (2, 3) ) = 2;
您可以很容易地将这一想法扩展到标签的描述中(但是您需要加入
标签表中进行说明。您的问题不清楚,因为“子”标签和“一般”标签的概念没有定义
但是,如果您想同时处理多个条件,您仍然可以使用一个groupby
和HAVING
子句
例如,如果您想要满足以下任一条件的news\u id
s:
tag\u id
=1
- 或者
tag\u id
=2和tag\u id
=3
然后您可以使用:
SELECT nt.news_id
FROM news_tags nt
GROUP BY nt.news_id
HAVING (COUNT(*) = 1 AND MIN(nt.tag_id) = 1) OR
SUM( nt.tag_id IN (2, 3) ) = 2;
你可以很容易地将这个想法扩展到标签的描述中(但是你需要加入标签表来实现这一点。我建议根据关于阻止标签的额外注释,重新设计标签
为新闻项分配标记是好的,但是您的表应该看起来像news\u标记(news\u id,tag\u id)
,主键位于news\u id
和tag\u id
字段上方
如果要使标记阻塞,一种方法是添加另一个多对多关系,称为news\u blocking\u标记(news\u id,tag\u id)
。或者您可以定义news\u标记(news\u id,tag\u id,is\u blocking)
,这样您就知道哪些标记正在阻塞,哪些只是标记
优化从设计数据库开始。我们这里只能给出一般性的指示。很好,你知道结果需要是什么,这已经是设计的一半了!我建议根据关于阻塞标记的额外评论,重新设计
为新闻项分配标记是好的,但是您的表应该看起来像news\u标记(news\u id,tag\u id)
,主键位于news\u id
和tag\u id
字段上方
如果要使标记阻塞,一种方法是添加另一个多对多关系,称为news\u blocking\u标记(news\u id,tag\u id)
。或者您可以定义news\u标记(news\u id,tag\u id,is\u blocking)
,这样您就知道哪些标记正在阻塞,哪些只是标记
优化从设计数据库开始。我们这里只能给出一般性的指针。很好,您知道结果需要是什么,这已经是设计的一半了!看起来我找到了解决方案,没有对架构表进行任何更改
SELECT news_id, tag_id
FROM news_tags
WHERE tag_id in (1,2,3)
GROUP BY news_id
HAVING (COUNT(news_id) = 1 AND tag_id = 1) OR (count(news_id) = 2 and tag_id in (1,2));
首先,我们找到所有带有阻止标记的新闻,而不是带有过滤结果的新闻
(COUNT(news_id) = 1 AND tag_id = 1)
查找所有国家/地区仅带有“阻止”标记的所有记录(其部分可用于多对多查找所有记录仅使用单个标记的内容)
查找成对的“阻止”标记和国家/地区标记
如果我们需要更多的国家,我们需要添加或
(count(news_id) = 2 and tag_id in (1,3))
我需要做一些测试,但看起来它的工作很好,而且我的第一个查询更像是在没有对模式表进行更改的情况下找到了解决方案
SELECT news_id, tag_id
FROM news_tags
WHERE tag_id in (1,2,3)
GROUP BY news_id
HAVING (COUNT(news_id) = 1 AND tag_id = 1) OR (count(news_id) = 2 and tag_id in (1,2));
首先,我们找到所有带有阻止标记的新闻,而不是带有过滤结果的新闻
(COUNT(news_id) = 1 AND tag_id = 1)
查找所有国家/地区仅带有“阻止”标记的所有记录(其部分可用于多对多查找所有记录仅使用单个标记的内容)
查找成对的“阻止”标记和国家/地区标记
如果我们需要更多的国家,我们需要添加或
(count(news_id) = 2 and tag_id in (1,3))
我需要做一些测试,但看起来效果不错,而且比我的第一个查询更好。我已经做了很多次标记。我建议不要使用多对多映射表到标记表。而是将它们组合到
CREATE TABLE tags (
news_id MEDIUMINT UNSIGNED NOT NULL, -- Assuming you don't need more than 16M
tag VARCHAR(...) NOT NULL,
PRIMARY KEY(news_id, tag), -- For going one way
INDEX(tag, news_id) -- For going the other way
) ENGINE=InnoDB; -- Important due to the way indexes are handled
添加auto_inc是对空间和速度的浪费
是的,有些查询非常复杂。但是任何技术都会导致混乱的代码;我相信这个模式是最好的。我已经做了很多次标记。我建议不要使用多对多映射表到标记表。而是将它们合并到
CREATE TABLE tags (
news_id MEDIUMINT UNSIGNED NOT NULL, -- Assuming you don't need more than 16M
tag VARCHAR(...) NOT NULL,
PRIMARY KEY(news_id, tag), -- For going one way
INDEX(tag, news_id) -- For going the other way
) ENGINE=InnoDB; -- Important due to the way indexes are handled
添加auto_inc是对空间和速度的浪费
是的,有些查询非常复杂。但是任何技术都会导致混乱的代码;我相信这个模式是最好的。你能根据你的小提琴给出你的预期结果吗?@18Man on fiddle是正确的结果“子标签”的概念不清楚,从你显示的预期结果来看,很难理解它。没有它就无法优化了解您想要实现的目标。此外,您的news_标记必须使用“复合键”(而不是额外的id字段),并且您的所有表必须至少有主键(也称为索引)。@geertjanvdk例如,您有一些带有一些标记的新闻,现在您需要为某些国家阻止一些新闻,快速方法是添加一些标记,如标记“block”它的meen to block news for search for all country或add tags“block”“usa”它的meen to block news only forms usa country,not for the other country它的解释很清楚?@geertjanvdk所以当你按国家搜索新闻时,你需要获得新闻ID而不是(ID)ID中的新闻-包含新闻ID接受2个条件1)新闻标签表中的新闻“阻止”标签和没有其他国家标签(阻止所有国家)2)新闻标签表上的新闻有“阻止”和“国家标签”(阻止搜索计数