【问题标题】:Query to search for the songs to contain all the requested tags查询以搜索包含所有请求标签的歌曲
【发布时间】:2025-12-06 12:15:01
【问题描述】:

我正在为使用标签的歌曲创建搜索引擎,但在构建列出所有与标签匹配的歌曲的 SQL 查询时遇到了麻烦。

数据库如下所示: http://i.imgur.com/5zmfAz8.png

歌曲通过一个中间表(SongTags)有很多标签。

我们以人口为例:

标签: 电子、器乐、活力、忧郁、声乐、摇滚

歌曲: SongA(电音、忧郁、声乐) SongB(器乐、忧郁、摇滚) SongC(充满活力,声乐)


搜索应该返回包含所有请求标签的歌曲。

搜索1:

“Vocal”返回:SongA、SongB

搜索2:

“Vocal”、“Energetic”回归:SongC

搜索3:

“人声”、“精力充沛”、“电”返回:什么都没有


我知道如何在 1 个标签上进行搜索,而不是在多个标签上进行搜索。 以 Search1 为例,我知道这会起作用:

SELECT * FROM "songs"
INNER JOIN "song_tags" ON "song_tags"."song_id" = "songs"."id"
INNER JOIN "tags" ON "tags"."id" = "song_tags"."tag_id"
WHERE "tags"."name" = 'Vocal'

但是我不知道如何执行 Search2,因为我需要歌曲包含“Vocal”和“Energetic”。

编辑:

我正在使用 PostgreSQL

【问题讨论】:

    标签: sql ruby-on-rails postgresql subquery nested-queries


    【解决方案1】:
    SELECT songs.*, COUNT(songs.id) AS total FROM "songs"
    INNER JOIN "song_tags" ON "song_tags"."song_id" = "songs"."id"
    INNER JOIN "tags" ON "tags"."id" = "song_tags"."tag_id"
    WHERE "tags"."name" IN ('Vocal', 'Energetic')
    GROUP BY songs.id
    HAVING total = 2
    

    【讨论】:

      【解决方案2】:

      如果您想获得与您提供的标签所有匹配的歌曲,我会使用子选择。它们会有点慢,但它会给你一个万能的解决方案:

      SELECT songs.* FROM songs
        WHERE songs.song_id IN
          (SELECT song_tags.song_id FROM song_tags st JOIN tags t ON st.tag_id = t.id AND t.name = 'VOCAL')
         AND songs.song_id IN (SELECT song_tags.song_id FROM song_tags st JOIN tags t ON st.tag_id = t.id AND t.name = 'ENERGETIC')
      

      如果您的 SQL 服务器支持 INTERSECT 命令,您可以这样做:

      SELECT songs.* FROM songs
        WHERE songs.song_id IN
          (SELECT song_tags.song_id FROM song_tags st JOIN tags t ON st.tag_id = t.id AND t.name = 'VOCAL' 
          INTERSECT
         SELECT song_tags.song_id FROM song_tags st JOIN tags t ON st.tag_id = t.id AND t.name = 'ENERGETIC')
      

      我认为这是一种更酷的方式。

      希望对您有所帮助。 :)

      【讨论】:

      • 谢谢你的回答,但archana的回答更有效率:)干杯!
      • 嗯...我认为它也是...但据我了解,IN 不是完全匹配查询 - 我认为它将匹配 either标签,这意味着您将获得一个歌曲列表,这些歌曲是声乐充满活力,但不一定都是。
      • 最后的 'HAVING total = 2' 语句使其成为 AND 语句,请再考虑一下。
      • 对这两种方法进行比较,看看执行计划和效率如何。