簡體   English   中英

如何簡化/改進此MySQL查詢的性能?

[英]How can I simplify/improve the performance of this MySQL query?

我是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

編輯:這是查詢正在做什么的描述: -

我有三張桌子:

  • $ sTable =歌曲表(songid,mp3link,artwork,useruploadid等)

  • $ sTable2 =與其相關聯的歌曲的項目表(項目,歌曲,項目名稱等)

  • $ sTable3 =歌曲收視率表(songid,userid,rating)

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

查詢本身按此順序執行以下操作: -

  1. 從$ sTable收集所有行
  2. 在songsID上連接到$ sTable2並計算此表中具有相同songsID的行數(項目)
  3. 在songsID上加入$ stable3並計算出此表中具有相同songsID的列'rating'的平均值
  4. 此時,它還計算$ sTable3中具有相同songID的總行數,以提供總投票數。
  5. 最后,它對所有這些行執行檢查,以查看$ userid(包含登錄用戶ID的變量)是否與$ sTable3中每行的'userid'存儲匹配,以便檢查用戶是否已經存在是否對某個歌曲進行了投票。 如果它匹配則返回“User Voted”,否則返回“Not Voted”。 它將它作為一個單獨的列輸出到我的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 

顯然現在“投票檢查”功能已丟失。

此外,更重要的是,它不會返回我的數組中定義的所有列,只返回songsID。 我的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。*而不是$ sTable.songsID(記住$ sTable是歌曲表)

然后......查詢顯然有效,但當然表現糟糕。 但只返回了5000首歌曲測試數據集中的24首歌曲。 因此,我將您的第一個'JOIN'更改為'LEFT JOIN',以便返回所有5000首歌曲。 為了澄清查詢需要返回歌曲表中的所有行,但是每個歌曲的項目和評級表中都有各種額外的數據。

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

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);

您將需要以下索引:

  • $ sTable2上的(songs_id)
  • $ sTable3上的復合(songid,rating,userid)

查詢背后的想法:

  • 子查詢與INT一起運行,因此子查詢的結果很容易適合內存
  • 左連接分別分組以減少笛卡爾積
  • 用戶投票在與其他評級相同的子查詢中計算,以避免昂貴的相關子查詢
  • 在最終加入時檢索所有其他信息

讓我嘗試根據您的描述,而不是查詢。 為了清楚起見,我將僅使用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,如果用戶標識為空 - 您知道他沒有投票。

現在,我沒有仔細檢查你的查詢,因為它看起來很復雜。 可能是我錯過了一些東西 - 如果是這樣的話,請更新說明,我可以再試一次:)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM