如何查询特征table(AND条件)的多对多关系

How to query many-to-many relation with features table (AND condition)

我想这是一个常见的设置,但由于我没有做那么多 SQL 工作,所以我无法理解这个......所以,我有一堆具有某些特征(音乐风格、情绪等)的歌曲,我想 select 具有这些特征的歌曲(例如快乐和欣快的歌曲)。

SONG
+----+----------+
| id |    title |
+----+----------+
|  1 |  song #1 |
+----+----------+
|  2 |  song #2 |
+----+----------+

FEATURE
+----+-------+----------+
| id |  name |    value |
+----+-------+----------+
|  1 |  mood |      sad |
+----+-------+----------+
|  2 |  mood |    happy |
+----+-------+----------+
|  3 |  mood | euphoric |
+----+-------+----------+
|  4 | style |     rock |
+----+-------+----------+
|  5 | style |     jazz |
+----+-------+----------+

SONG_FEATURE
+---------+------------+
| song_id | feature_id |
+---------+------------+
|       1 |          1 |
+---------+------------+
|       2 |          1 |
+---------+------------+
|       2 |          2 |
+---------+------------+

我想 select 所有具有 AND 条件的特定功能的歌曲。我会将此查询用于 OR-案例。

SELECT 
    s.*,
    f.*
FROM
    song_feature sf
        LEFT JOIN song s ON s.id = sf.song_id
        LEFT JOIN feature f ON f.id = sf.feature_id
WHERE
    (
        f.name = 'style'
        AND f.value = 'pop'
    )
    OR /* <-- this works, but I would like an AND condition */
    (
        f.name = 'style'
        AND f.value = 'pop'
    )
GROUP BY sf.song_id;

但这显然不适用于 AND 条件,所以我想我走错路了...任何提示将不胜感激。

如果过滤连接的结果集并在 HAVING 子句中设置条件,则可以使用聚合来实现:

SELECT s.id, s.title
FROM SONG s
INNER JOIN SONG_FEATURE sf ON sf.song_id = s.id
INNER JOIN FEATURE f ON f.id = sf.feature_id
WHERE (f.name, f.value) IN (('mood', 'sad'), ('mood', 'happy'))
GROUP BY s.id, s.title
HAVING COUNT(DISTINCT f.name, f.value) = 2

参见demo
结果:

> id | title  
> -: | :------
>  2 | song #2