开发者社区> 问答> 正文

如何用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")

展开
收起
a123456678 2016-07-01 15:16:13 3819 0
1 条回答
写回答
取消 提交回答
  • 才开始学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也有它的关于合并行的函数,你可以去看看。如果这种“包含几个标记的书”这样的需求很多的话,可以搞个视图,就生成合并行的结果集,然后之后这样的需求就都到视图里面查,省事。

    2019-07-17 19:50:06
    赞同 展开评论 打赏
问答分类:
问答标签:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
SQL Server 2017 立即下载
GeoMesa on Spark SQL 立即下载
原生SQL on Hadoop引擎- Apache HAWQ 2.x最新技术解密malili 立即下载