2015-08-18 32 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