我一直在寻找网络并向人们寻求指导,但似乎没有人知道解决问题的正确(相对快速)的解决方案:
我有三张桌子,经典的多对多解决方案:
entries
:id(int),title(varchar [255]),content(text)tags
:id(int),name(varchar [255]),slug(varchar [255])entries_tags
:id(int),entry_id(int),tag_id(int)到目前为止没有什么不寻常的事。现在让我们说我在标签中有测试数据(因为它们并不重要,所以我要保留slu ::)
ID | name
1. | one
2. | two
3. | three
4. | four
5. | five
我还有三个条目:
ID | title
1. | Something
2. | Blah blah blah
3. | Yay!
和关系:
ID | entry_id | tag_id
1. | 1 | 1
2. | 1 | 2
3. | 2 | 1
4. | 2 | 3
5. | 3 | 1
6. | 3 | 2
7. | 3 | 3
8. | 4 | 1
9. | 4 | 4
好的,我们有测试数据。我想知道如何获得所有标记为One的条目,但没有标记Three(即条目1和条目4)。
我知道如何用子查询来做,问题是,它需要花费很多时间(100k条目需要大约10-15秒)。有没有办法用JOIN做到这一点?或者我错过了什么?
编辑我想我应该提到我需要一个适用于数据集而不是单个标签的解决方案,所以在我的问题中将'One'替换为'One','Two'和“两个”,“三个”,“四个”
edit2 提供的答案是对的,但实际使用起来太慢了。我认为使其成功的唯一方法是使用像Lucene或ElasticSearch这样的第三方搜索引擎。
答案 0 :(得分:3)
以下脚本选择包含标记One
和Two
但没有标记Three
和Four
的条目:
SELECT DISTINCT
et.entry_id
FROM entries_tags et
INNER JOIN tags t1 ON et.tag_id = t1.id AND t1.name IN ('One', 'Two')
LEFT JOIN tags t2 ON et.tag_id = t2.id AND t2.name IN ('Three', 'Four')
WHERE t2.id IS NULL
替代方案:INNER JOIN
替换为WHERE EXISTS
,这样我们就可以摆脱(相当昂贵的)DISTINCT
:
SELECT
et.entry_id
FROM entries_tags et
LEFT JOIN tags t2 ON et.tag_id = t2.id AND t2.name IN ('Three', 'Four')
WHERE t2.id IS NULL
AND EXISTS (
SELECT *
FROM tags t1
WHERE t1.id = et.tag_id
AND t1.name IN ('One', 'Two')
)
答案 1 :(得分:1)
这应该做你想要的。
(它可能会或可能不会比子查询解决方案更快,我建议您比较查询计划)
SELECT DISTINCT e.*
FROM tags t1
INNER JOIN entries_tags et1 ON t1.id=et1.tag_id
INNER JOIN entries e ON e.entry_id=et1.entry_id
INNER JOIN tags t2 on t2.name='three'
INNER JOIN tags t3 on t3.name='four'
LEFT JOIN entries_tags et2 ON (et1.entryid=et2.entryid AND t2.id = et2.tag_id )
OR (et1.entryid=et2.entryid AND t3.id = et2.tag_id )
WHERE t1.name IN ('one','two') AND et2.name is NULL
通过LEFT加入entries_tags表et2(您不想要的数据),然后您只能选择et2.name IS NULL(其中et2记录不存在)的记录。
答案 2 :(得分:0)
你提到过尝试子查询。这是你试过的吗?
SELECT entries.id, entries.content
FROM entries
LEFT JOIN entries_tags ON entries.id=entries_tags.entries_id
LEFT JOIN tags ON entries_tags.tag_id=tags.id
WHERE tag.id=XX
and entries.id NOT IN (
SELECT entries.id
FROM entries
LEFT JOIN entries_tags ON entries.id=entries_tags.entries_id
LEFT JOIN tags ON entries_tags.tag_id=tags.id
WHERE tag.id=YY
)
(其中XX是您想要的标签,YY是您不想要的标签)
对于ID字段的索引,这不应该像你说的那么慢。它将取决于数据集,但它应该没有索引(并省略字符串比较)。