按首选值列表进行SQL分组

Léo*_*lis 11 mysql sql

首先:我不太清楚在问题标题中放什么,我不知道如何调用这样的查询,也许这就是为什么我找不到任何答案.

我有一张无线电台和一张溪流表.每个无线电台可以有多个流,用于不同的格式,比特率等.我想得到一个所有电台的列表,其中一个流是给定应用的首选格式.

现在这是棘手的,我希望首选格式是一个列表,我的数据库应该返回第一个合适的流.

所以我可能会有这样的列表:('MP3','AAC','OGG')

然后我希望MySQL为每个站点返回"MP3"类型的流,但是如果它不存在,它应该返回该站的"AAC"流,依此类推.如果找不到合适的流,则不应该返回该站.

例:

CREATE TABLE `stations` (
  `id` INT(11),
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

CREATE TABLE `streams` (
  `id` INT(11),
  `station` INT(11),
  `media_type` ENUM('MP3', 'OGG', 'AAC', 'Flash'),
  PRIMARY KEY (`id`),
  KEY (`station`),
  CONSTRAINT `fk_1` FOREIGN KEY (`station`) REFERENCES `stations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;

INSERT INTO `stations` (`id`) VALUES (1), (2), (3);
INSERT INTO `streams` (`id`, `station`, `media_type`) VALUES (1, 1, 'MP3'), (2, 1, 'AAC'), (3, 2, 'Flash'), (4, 2, 'AAC'), (5, 3, 'Flash');
Run Code Online (Sandbox Code Playgroud)

我在这里做了一个SQLFiddle

如果是首选媒体类型列表('MP3', 'AAC'),则使用上述示例数据的所需结果应为:

station stream  type
1       1       MP3
2       4       AAC
Run Code Online (Sandbox Code Playgroud)
  • 站1应该有MP3类型的流1(也支持AAC,但MP3优于AAC)
  • 站2应该有AAC类型的流4(站2不提供MP3,但是AAC是)
  • 站3不应该在结果中,因为它只通过Flash提供流媒体

我试过这个:

SELECT
    st.id AS station_id,
    str.id AS stream_id,
    str.media_type,
    FIELD(str.media_type, 'MP3', 'AAC') AS preference
FROM
    stations st
LEFT JOIN
    streams str ON str.station = st.id
GROUP BY 
    st.id
HAVING
    MIN(preference)
Run Code Online (Sandbox Code Playgroud)

但是,只返回1或0条记录,这取决于流表中的第一条记录是首选媒体类型,我不明白为什么.

我能找到的唯一解决方案是使用子查询对流进行排序,然后按station_id进行分组,如下所示:

SELECT sub.* FROM
    (SELECT
        st.id AS station_id,
        str.id AS stream_id,
        str.media_type
    FROM
        stations st
    LEFT JOIN
        streams str ON str.station = st.id
    WHERE
        str.media_type IN ('MP3', 'AAC')
    ORDER BY
        FIELD(str.media_type, 'MP3', 'AAC')
    ) AS sub
GROUP BY sub.station_id
Run Code Online (Sandbox Code Playgroud)

但这会导致子查询创建的临时表的全表扫描,性能是不可接受的.由于我们不能限制内部查询(因为它尚未分组),临时表将变得非常大.

顺便说一下,我正在运行MySQL 5.6

那么,我应该使用什么样的查询来处理首选属性列表?

dno*_*eth 5

如果要仅返回其中'MP3''AAC'存在的行,则不需要外部联接.

这是一个标准的SQL解决方案,它将在mysql中按原样运行,请参阅小提琴:

SELECT
   st.id AS station_id,
   COALESCE(MAX(CASE WHEN str.media_type = 'MP3' THEN str.id END)
           ,MAX(CASE WHEN str.media_type = 'AAC' THEN str.id END)
           ) AS stream_id,
   COALESCE(MAX(CASE WHEN str.media_type = 'MP3' THEN str.media_type END)
           ,MAX(CASE WHEN str.media_type = 'AAC' THEN str.media_type END)
           ) AS media_type
FROM stations st
JOIN streams str 
  ON str.station = st.id
WHERE -- only stations with the requested media types
   str.media_type IN ('MP3', 'AAC')
GROUP BY st.id
Run Code Online (Sandbox Code Playgroud)

添加更多媒体类型很容易,主要是剪切和粘贴.COALESCE根据CASE的顺序返回第一个匹配的媒体类型.