2015-08-18 33 views
11

首先:我不太确定要在问题标题中放置什么,我不知道如何调用这样的查询,也许这就是为什么我找不到任何回答。按首选值列表进行的SQL分组

我有一个广播电台表和一个流表。每个无线电台可以有多个数据流,用于不同的格式,比特率等。 我想获得所有工作站的列表,并给出一个给定应用程序首选格式的数据流。

现在,这是它得到棘手,我想首选格式是list,和我的数据库应该返回第一个合适的流。

,所以我可能有这样的名单:(“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'); 

我由SQLFiddle here

如果优选媒体类型列表是('MP3', 'AAC'),使用上述例子的数据应然后期望的结果:

station stream type 
1  1  MP3 
2  4  AAC 
  • 站1应该有流1 o f类型的MP3(AAC也支持,但MP3比AAC更受欢迎)
  • 2号台应该有4类AAC(MP3不是由2号台提供,但是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) 

但仅返回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 

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

B.T.W.,我运行MySQL 5.6

所以,我应该使用什么样的查询与首选属性的列表来工作?

+1

非常好的问题。感谢您创建一个SQL小提琴! – SQLCurious

+0

您提出的解决方案使用非聚合列,这是MySQL GROUP BY GROUP BY的扩展。根据[MySQL手册](https://dev.mysql.com/doc/refman/5.6/en/group-by-handling.html):服务器可以自由选择任何值(来自非聚合列)每个组,除非它们是相同的,*所选的值是不确定的*。因此,您提出的解决方案不能*保证*产生正确的结果。 –

+0

如果只有每一个问题都如此完善,那么! – Strawberry

回答

5

如果您只想返回'MP3''AAC'存在的行,则不需要外连接。

这是一个标准的SQL解决方案,将工作,是在MySQL中,看到fiddle

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 

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

+0

谢谢,这是我寻找的解决方案。我添加了'HAVING stream_id IS NOT NULL',它消除了WHERE子句,似乎没有执行时间的改变。这使查询更容易生成。 –

+0

@LéonMelis:如果有很多其他媒体类型的行,WHERE会更有效,因为它减少了GROUP BY中的行数, – dnoeth

0

编辑

得到每电台最高优先流,你可以使用一个变量根据其media_type价值排名每电台每个流只有1级选择行:

select * from (
    select *, 
    @rn := if(@prevStationId = station_id, @rn+1, 1) rn, 
    @prevStationId := station_id 
    from streams 
    where media_type in ('MP3','AAC') 
    order by station_id, FIELD(media_type,'MP3','AAC') 
) t1 where rn = 1