Mysql 如何在3个表之间约束标记
我正在开发一个标签系统,它应该做的是你可以有一个查询和选择的标签,例如Mysql 如何在3个表之间约束标记,mysql,tags,constraints,Mysql,Tags,Constraints,我正在开发一个标签系统,它应该做的是你可以有一个查询和选择的标签,例如jquery和javascript,库的标签。它应该只显示与查询相关的脚本,并且只显示具有标记的脚本。这是数据库布局: 脚本表: +-----------+---------------+ | script_id | name | +-----------+---------------+ | 1 | jQuery | | 2 | Sencha Touch |
jquery
和javascript
,库的标签。它应该只显示与查询相关的脚本,并且只显示具有标记的脚本。这是数据库布局:
脚本表:
+-----------+---------------+
| script_id | name |
+-----------+---------------+
| 1 | jQuery |
| 2 | Sencha Touch |
| 3 | Codeigniter |
| 4 | Google Chrome |
| 5 | memcached |
| 6 | PHP |
| 7 | MooTools |
| 8 | node.js |
| 9 | jQuery Mobile |
+-----------+---------------+
标签表:
+--------+-------------+-------------+
| tag_id | name | url_name |
+--------+-------------+-------------+
| 1 | JavaScript | javascript |
| 2 | Library | library |
| 3 | PHP | php |
| 4 | MySQL | mysql |
| 5 | Cache | cache |
| 6 | HTML | html |
| 7 | Open source | open-source |
+--------+-------------+-------------+
标记表:
+-----------+--------+-----------+
| tagged_id | tag_id | script_id |
+-----------+--------+-----------+
| 1 | 1 | 1 | # javascript -- jQuery
| 2 | 2 | 1 | # library -- jQuery
| 3 | 1 | 9 | # javascript -- jQuery mobile
+-----------+--------+-----------+
当我运行SQL时,它仍然会选择jQuery Mobile
,但它不应该选择,因为它不包含library
标记,而jQuery
包含该标记,我需要它来约束必须满足所选标记的结果
这是我的SQL:
SELECT scripts.script_id,
scripts.name
FROM
(
scripts scripts
LEFT OUTER JOIN tagged tagged ON tagged.script_id = scripts.script_id
LEFT OUTER JOIN tags tags ON tags.tag_id = tagged.tag_id
)
WHERE MATCH(scripts.name) AGAINST ('jquery*' IN BOOLEAN MODE) AND ( tags.url_name = 'javascript' OR tags.url_name = 'library' )
GROUP BY script_id
ORDER BY scripts.name
LIMIT 0, 25
它返回:
+-----------+---------------+
| script_id | name |
+-----------+---------------+
| 1 | jQuery |
| 9 | jQuery Mobile |
+-----------+---------------+
如果我将或更改为和,它将不会返回任何内容,或者如果我从标记(
和)
中删除括号,它也不会返回任何内容
如何使查询约束标记?我的尝试:
SELECT s.script_id,
s.name
FROM
scripts AS s
INNER JOIN
tagged AS st1
ON st1.script_id = s.script_id
INNER JOIN tags AS tag1
ON tag1.tag_id = st1.tag_id
AND tag1.url_name = 'javascript'
INNER JOIN
tagged AS st2
ON st2.script_id = s.script_id
INNER JOIN tags AS tag2
ON tag2.tag_id = st2.tag_id
AND tag2.url_name = 'library'
ORDER BY s.name
LIMIT 0, 25
SELECT s.script_id, s.name, so_tags.url_name
FROM so_scripts s
LEFT JOIN so_tagged USING (script_id)
LEFT JOIN so_tags USING (tag_id)
WHERE s.script_id IN
(
SELECT td.script_id FROM so_tagged td
LEFT JOIN so_tags t USING (tag_id)
WHERE t.url_name = "javascript" OR t.url_name = "library"
)
GROUP BY url_name HAVING COUNT(s.name) = 2
这有点像黑客,它首先查找与任何标记匹配的所有条目,然后按名称分组,只返回那些匹配数等于提供的标记数的条目:)
不确定3个、4个或更多标记的速度有多快,但至少它可以工作:)您的解决方案的主要问题是,当连接在一起时,它会创建多行,但每行只有一个标记值。如果使用OR条件,则任何匹配的标记都足以返回。有和无行可以有两个不同的值
在将它们分组之前,将生成三个单独的行:
+-----------+-----------+---------------+
| tag_id| script_id | name |
+-----------+-----------+---------------+
| 1 | 1 | jQuery |
| 2 | 1 | jQuery |
| 1 | 9 | jQuery Mobile |
+-----------+-----------+---------------+
要解决此问题,必须使用OR条件,但在组之后,使用
HAVING count(tag_id) = 2
这告诉sql,根据每个组有多少个标记来评估每个组,只留下那些正好有2个标记的组
因此,基于您的代码的解决方案:
SELECT scripts.script_id,
scripts.name
FROM
(
scripts scripts
LEFT OUTER JOIN tagged tagged ON tagged.script_id = scripts.script_id
LEFT OUTER JOIN tags tags ON tags.tag_id = tagged.tag_id
)
WHERE MATCH(scripts.name) AGAINST ('jquery*' IN BOOLEAN MODE) AND ( tags.url_name = 'javascript' OR tags.url_name = 'library' )
GROUP BY script_id
HAVING count(tag_id) = 2
ORDER BY scripts.name
LIMIT 0, 25
尝试一下:
select s.script_id, s.name from scripts s
join tagged tj on s.script_id = tj.script_id
join tags t on t.tag_id = tj.tag_id
where s.name like 'jQuery%' and t.url_name in ('javascript', 'library')
group by s.script_id, s.name
having count(*) = 2
order by s.name
limit 25
唯一需要考虑的是,必须将2
替换为in
子句中in的元素数量
以下是。您是否尝试了内部联接
而不是左侧外部联接
?但并非所有脚本都已标记。结果看起来正常。您要求脚本匹配“jQuery”和“javascript”或“library”的标记url。这就是结果。顺便说一句,不需要使用与表名相同的别名来为表别名(“从脚本中选择[…]);您可以省略别名。如果要缩短表名或多次引用同一表,您只需要的别名。请查看以下标记解决方案: