2011-11-29 58 views
7

我對MySQL非常陌生,也感謝來自您的更有經驗的人士提供的大力支持,這裏我一直在努力奮鬥,同時在此過程中學習了很多。如何簡化/提高此MySQL查詢的性能?

我有一個查詢,正是我想要的。但是,它看起來非常混亂,我確信必須有一種方法來簡化它。

該查詢如何針對性能進行改進和優化?

非常感謝

  $sQuery = " 
     SELECT SQL_CALC_FOUND_ROWS ".str_replace(" , ", " ", implode(", ", $aColumns))." 

    FROM $sTable b 
    LEFT JOIN (
    SELECT COUNT(*) AS projects_count, a.songs_id 

    FROM $sTable2 a 
    GROUP BY a.songs_id 
) bb ON bb.songs_id = b.songsID 

LEFT JOIN (
    SELECT AVG(rating) AS rating, COUNT(rating) AS ratings_count, c.songid 

FROM $sTable3 c 

    GROUP BY c.songid 
) bbb ON bbb.songid = b.songsID 

LEFT JOIN (
    SELECT c.songid, c.userid, 

    CASE WHEN EXISTS 
    ( 
     SELECT songid 
     FROM $sTable3 
     WHERE songid = c.songid 
    ) Then 'User Voted' 
    else 
    (
     'Not Voted' 
    ) 
    end 
    AS voted 
FROM $sTable3 c 
WHERE c.userid = $userid 


    GROUP BY c.songid 
) bbbb ON bbbb.songid = b.songsID 

編輯:這是一個什麼樣的查詢做了說明: -

我有三個表:

  • $穩定=歌曲表(songid,mp3link,作品,useruploadid 等)

  • $ sTable2 =的項目鏈接到他們(專案編號, songid,項目名稱等)的歌曲表

  • $ sTable3 =歌曲排行榜(songid,用戶ID,等級)

的表

所有這些數據都會輸出到JSON數組中,並顯示在我的應用程序的表中,以提供歌曲列表以及項目和評分數據。

查詢本身並按此順序如下: -

  1. 收集來自$穩定的所有行
  2. 加入到$ sTable2上songsID並在此表中具有計數的行(項目)的數量同樣songsID
  3. 加入到$ stable3上songsID並在此表中具有相同songsID
  4. 此時的作品出列「評級」的平均同時也計算在$ sTable3行具有總數相同的歌曲ID來提供總票數。
  5. 最後,它會對所有這些行執行檢查,以查看每個行的$ userid(它是包含登錄用戶的ID的變量)是否與$ sTable3中的'userid'存儲匹配,以檢查用戶已經對給定的歌曲ID進行了投票。如果匹配,則返回「用戶投票」,否則返回「未投票」。它將它作爲一個單獨的列輸出到我的JSON數組中,然後在我的應用程序中檢查客戶端並添加一個類。

如果有任何更多的細節需要任何人,請讓我知道。謝謝大家。

編輯:

由於Aurimis'優第一次嘗試我將結束在一個更簡單的解決方案。

這是我嘗試基於該建議的代碼。

SELECT SQL_CALC_FOUND_ROWS ".str_replace(" , ", " ", implode(", ", $aColumns))." 

    FROM 
     (SELECT 
     $sTable.songsID, COUNT(rating) AS ratings_count, 
     AVG(rating) AS ratings 
     FROM $sTable 
     LEFT JOIN $sTable2 ON $sTable.songsID = $sTable2.songs_id 
     LEFT JOIN $sTable3 ON $sTable.songsID = $sTable3.songid 
     GROUP BY $sTable.songsID) AS A 
    LEFT JOIN $sTable3 AS B ON A.songsID = B.songid AND B.userid = $userid 

但是有幾個問題。我不得不刪除你的答案的第一行,因爲它造成了500內部服務器錯誤:

IF(B.userid = NULL, "Not voted", "User Voted") AS voted 

顯然,現在的「投檢查」功能將丟失。

此外,更重要的是它不返回在我的數組中定義的所有列,只有歌曲ID。我的JSON在'字段列表'中返回未知列'song_name' - 如果我從$ aColumns數組中刪除它,它當然會移動到下一個。

我在我的腳本的開頭定義了我的列,因爲此數組用於篩選和放置JSON編碼的輸出。這是$ aColumns的定義: - 。

$aColumns = array('songsID', 'song_name', 'artist_band_name', 'author', 'song_artwork', 'song_file', 'genre', 'song_description', 'uploaded_time', 'emotion', 'tempo', 'user', 'happiness', 'instruments', 'similar_artists', 'play_count', 'projects_count', 'rating', 'ratings_count', 'voted'); 

爲了快速測試我修改了子查詢中的第一行查詢的其餘部分選擇$穩定*而不是$ sTable.songsID(記得$穩定是歌曲表)

然後...查詢顯然有效,但當然表現糟糕。但是,只能從5000首歌曲測試數據集中返回24首歌曲。因此我將你的第一個'JOIN'改爲'LEFT JOIN',以便所有5000首歌曲都被返回。爲了說明查詢需要返回歌曲表中的所有行,但需要爲每首歌曲的項目和評分表添加各種額外的數據位。

所以......我們到了那裏,我確信這是一個更好的方法,它只是需要一些修改。感謝你的幫助到目前爲止Aurimis。

+0

似乎誰寫的查詢的人是不是新來的MySQL – newtover

+0

我通過「谷歌搜索」,問各種問題在這裏,並讀了自己寫的很多。然而,我本質上已經將幾件作品拼湊成一個「工作」查詢來實現我想要的。雖然我理解查詢的每個部分都做了什麼以及它爲什麼起作用,但我沒有經驗來知道我是否以有效的方式執行此操作。我從其他語言的經驗中得出的結論告訴我,我不是。因此,我會歡迎更有經驗的MySQL開發人員的建議。 :) – gordyr

+0

你能描述一下預期的結果嗎? – Peter

回答

3
SELECT SQL_CALC_FOUND_ROWS 
    songsID, song_name, artist_band_name, author, song_artwork, song_file, 
    genre, song_description, uploaded_time, emotion, tempo, 
    `user`, happiness, instruments, similar_artists, play_count, 
    projects_count, 
    rating, ratings_count, 
    IF(user_ratings_count, 'User Voted', 'Not Voted') as voted 
FROM (
    SELECT 
     sp.songsID, projects_count, 
     AVG(rating) as rating, 
     COUNT(rating) AS ratings_count, 
     COUNT(IF(userid=$userid, 1, NULL)) as user_ratings_count 
    FROM (
     SELECT songsID, COUNT(*) as projects_count 
     FROM $sTable s 
     LEFT JOIN $sTable2 p ON s.songsID = p.songs_id 
     GROUP BY songsID) as sp 
    LEFT JOIN $sTable3 r ON sp.songsID = r.songid 
    GROUP BY sp.songsID) as spr 
JOIN $sTable s USING (songsID); 

您將需要以下指標:

  • (songs_id)在$ sTable2
  • 在$ sTable3

查詢背後的想法複合(songid,等級,用戶ID):

  • 子查詢與INT的操作,使得所述子查詢的結果將容易地裝配在存儲器
  • 左聯接單獨分組,以減少笛卡爾積
  • 用戶投票以相同的子查詢算作其它的評分,以避免昂貴的相關子查詢
  • 所有的行吟詩人信息IB最終檢索到的加入
+0

感謝您的建議newtover。我將無法在週末之後檢查您的解決方案。從簡單的看看,對我來說一切似乎都很好,我會在星期一讓你知道。 :-) – gordyr

+0

太棒了......它完美的工作。理解起來要簡單得多,而且在執行方面要優雅得多。我還沒有做任何性能測試,但從最初的印象來看,它肯定不會更糟,使用您的查詢將使任何未來的調整更容易管理,因爲它在結構上也遠遠優越。非常感謝newtover,一個真正偉大的答案! – gordyr

+0

@gordyr,不客氣=) – newtover

1

讓我試着根據你的描述,而不是查詢。我就用Songs指示Table1Projects指示Table2Ratings指示Table3 - 爲清楚起見。

SELECT 
    /* [column list again] */, 
    IF(B.userid = NULL, "Not voted", "Voted") as voted 
FROM 
    (SELECT 
    Songs.SongID, count(rating) as total_votes, 
    avg(rating) as average_rating /*[,.. other columns as you need them] */ 
    FROM Songs 
    JOIN Projects ON Songs.SongID = Projects.SongID 
    LEFT JOIN Ratings ON Songs.SongID = Ratings.SongID 
    GROUP BY Songs.SongID) as A 
LEFT JOIN Ratings as B ON A.SongID = B.SongID AND B.userid = ? /* your user id */ 

正如你看到的,你可以得到歌曲的所有信息在一個相對簡單的查詢(只使用GROUP BY和COUNT()/ AVG()函數)。要獲得某首歌曲是否被特定用戶評分的信息,需要一個子查詢 - 您可以在其中進行LEFT JOIN,如果用戶ID爲空 - 則表明他沒有投票。現在

,我沒經過深入查詢,因爲它確實看起來比較複雜。可能是因爲我錯過了一些東西 - 如果是這樣的話,請及時通知說明,我可以再次嘗試:)

+0

這看起來很棒Aurimas,非常感謝你的建議。我不準備再測試一個小時左右。我會讓你知道它是怎麼回事。 – gordyr

+0

感謝Aurimas ...自從嘗試您的建議以來,我已經用我的發現更新了我的問題。乾杯。 :) – gordyr