首頁  >  問答  >  主體

mysql - 如何用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")
ringa_leeringa_lee2743 天前809

全部回覆(1)我來回復

  • 高洛峰

    高洛峰2017-04-17 13:10:41

    才開始學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也有它的關於合併行的函數,你可以去看看。如果這種「包含幾個標記的書」這樣的需求很多的話,可以搞個視圖,就產生合併行的結果集,然後之後這樣的需求就都到視圖裡面查,省事。

    回覆
    0
  • 取消回覆