如何用SQL语句包含同时包含多个标签?
现在有三张数据表, 一张是书籍表, 一张是书籍的标签表, 由于数据与标签是多对多的关系, 所以还有一张中间表。 大概的结构如下:
书籍表 books
|id|name|
|001|BOOK001|
|002|BOOK002|
标签表 labels
|id|name|
|001|LABEL001|
|002|LABEL002|
中间表 book_label_maps
|id|book_id|label_id|
|001|001|001|
|002|001|002|
|003|002|001|
那么问题来了, 我怎么才能找出标记为LABEL001和LABEL002的呢? 这里也就是找出BOOK001.
PS. 由于在真实查询过程中标签的数量不固定, 所以, 最好能够将查询的LABEL id全都放到一个地方。
例如:
select * from books where otherquery(laebl001, label002)
目前的解决办法(PHP):
$query = 'SELECT * FROM books WHERE ';
foreach( $labels as $index => $laeblID ) {
$laebls[$index] => 'EXISTS (SELECT * FROM book_label_maps WHERE book_id = book.id AND laebl_id='.$laeblID.' )'
}
$laebls = implode(' AND ', $laebls);
$query .= $labels;
结果大概是这样的:
SELECT * FROM books
WHERE
EXISTS (SELECT * FROM book_label_maps WHERE book_id = book.id AND label_id="001" )
AND
EXISTS (SELECT * FROM book_label_maps WHERE book_id = book.id AND label_id="002")
〃博丽灵梦¨
9 years, 10 months ago
Answers
才开始学SQL不久,只不过前几天恰好遇到另外的问题,可能相比题主的方案那边的解决方案能用,也更好一些。 在我看来,题主的方法即使能行,但我感觉属于效率不高的,举个例子:100本书,每本书10个标签,你的WHERE下面的子查询都得查个1000次吧,接下来再说我之前遇到的问题。 我之前遇到的是合并行的问题,如果在你的例子中,我们就需要根据BookID把labels合并,就是需要下面的效果
book_id|label_id
001|001,002
002|001
然后在对这个结果集进行查询的时候就可以根据“001,002”这样的字符串匹配进行查询了。 我用的是oracle,贴上oracle可用的解决方案的SQL:
SELECT bookID,LTRIM(MAX(SYS_CONNECT_BY_PATH(labelID,','))KEEP(DENSE_RANK LAST ORDER BY curr),',') AS labels
FROM (
SELECT labelID ,bookID
ROW_NUMBER() OVER (ORDER BY bookID) curr ,
ROW_NUMBER() OVER (ORDER BY bookID) -1 prev
FROM book_label_maps)
GROUP BY bookID
CONNECT BY prev = PRIOR curr AND bookID=bookID START WITH curr=1
以上SQL语句可以通过你的MAPS表生成根据bookID聚合的表(我没有实际测试)。之前我搜索的时候也看到过MYSQL也有它的关于合并行的函数,你可以去看看。如果这种“包含几个标记的书”这样的需求很多的话,可以搞个视图,就生成合并行的结果集,然后之后这样的需求就都到视图里面查,省事。
隔壁的包子君
answered 9 years, 10 months ago