2011-11-29 12 views
7

Jestem nowicjuszem w MySQL i dzięki wsparciu ze strony bardziej doświadczonych użytkowników jestem w stanie sobie z tym poradzić, jednocześnie dużo się ucząc.Jak mogę uprościć/poprawić wydajność tego zapytania MySQL?

Mam zapytanie, które robi dokładnie to, co chcę. Jednak dla mnie wygląda to bardzo niechlujnie i jestem pewien, że musi istnieć sposób, aby to uprościć.

W jaki sposób można poprawić i zoptymalizować to zapytanie pod kątem wydajności?

Dziękujemy

  $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 

EDIT: Tutaj znajduje się opis tego, co kwerenda robi: -

Mam trzy tabele:

  • $ stabilny = stół piosenek (songid, mp3link, grafika, useruploadid itp.)

  • $ sTable2 = tabelę projektów z piosenkami z nimi związanych (ProjectID, songid, nazwa projektu itd.)

  • $ sTable3 = tabelę ocen Song (songid, userid, ocena)

Wszystkie te dane są wysyłane do tablicy JSON i wyświetlane w tabeli w mojej aplikacji, aby zapewnić listę utworów w połączeniu z danymi projektów i ocen.

Sama kwerenda wykonuje następujące czynności w następującej kolejności: -

  1. Zbiera wszystkie wiersze z $ stabilny
  2. dołącza do $ sTable2 na songsID i zlicza liczbę wierszy (projektów) w tej tabeli, które mają samo songsID
  3. dołącza do $ stable3 na songsID i pracuje się średnio w kolumnie „Ocena” w tej tabeli, które mają taką samą songsID
  4. w tym momencie również zlicza całkowitą liczbę wierszy w $ sTable3 które ten sam identyfikator utworu, aby zapewnić całkowitą liczbę głosów .
  5. W końcu sprawdza wszystkie te wiersze, aby sprawdzić, czy identyfikator $ userid (który jest zmienną zawierającą identyfikator zalogowanego użytkownika) odpowiada sklepom "userid" w $ sTable3 dla każdego wiersza, aby sprawdzić, czy użytkownik już głosował na dany songID lub nie. Jeśli się zgadza, zwraca "Użytkownik zagłosował", jeśli nie zwraca "Nie zagłosowano". Wyświetla to jako oddzielną kolumnę w mojej tablicy JSON, którą następnie sprawdzam w aplikacji i dodam klasę do klienta.

Jeśli potrzebujesz więcej informacji, daj mi znać. Dziękuje wszystkim.

EDIT:

Dzięki Aurimis' doskonałej pierwszej próbie jestem zbliża się o wiele bardziej proste rozwiązanie.

To jest kod, który próbowałem na podstawie tej sugestii.

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 

Istnieje jednak kilka problemów. Musiałem usunąć pierwszą linię odpowiedź gdyż spowodował błąd 500 serwera wewnętrznego:

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

Oczywiście teraz „głosowało check” funkcjonalność jest stracone.

Co więcej, nie zwraca wszystkich kolumn zdefiniowanych w mojej tablicy, tylko identyfikator utworu. Mój JSON zwraca nieznaną kolumnę "nazwa_piosenki" w "liście pól" - jeśli usuniemy ją z mojej tablicy $ aColumns, przejdzie ona oczywiście do następnej.

Definiuję moje kolumny na początku mojego skryptu, ponieważ ta tablica służy do filtrowania i zestawiania danych wyjściowych dla kodowania JSON. Jest to definicja $ 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'); 

W celu szybkiego sprawdzenia resztę zapytania I zmodyfikowaną pierwszą linię w podzapytaniu wybierz $ stabilny * zamiast $ sTable.songsID (pamiętaj $ stabilny jest tabelą piosenek)

Następnie ... Zapytanie oczywiście zadziałało, ale z fatalną wydajnością oczywiście. Ale tylko zwróciło 24 utwory z zestawu 5000 utworów testowych. Dlatego zmieniłem twoje pierwsze "JOIN" na "LEFT JOIN", aby wszystkie 5000 piosenek zostało zwróconych. Aby wyjaśnić zapytanie, należy zwrócić WSZYSTKIE wiersze w tabeli utworów, ale z różnymi dodatkowymi bitami danych z tabel projektów i ocen dla każdej piosenki.

Tak więc ... Docieramy i jestem pewien, że jest to o wiele lepsze podejście, które wymaga jedynie pewnych modyfikacji. Dzięki za pomoc do tej pory Aurimis.

+0

wydaje się, że osoba, która napisała zapytanie nie był nowy do MySQL – newtover

+0

napisałem go samodzielnie przez „googlowania”, pytając różne pytania tutaj i czytania dużo. Mam jednak w istocie powiązanie kilku elementów w jedno "działające" zapytanie, aby osiągnąć to, czego chcę. Chociaż rozumiem, co robi każda część zapytania i dlaczego działa, nie mam doświadczenia, aby wiedzieć, czy robię to w skuteczny sposób, czy też nie. Mój instynkt z mojego doświadczenia z innymi językami mówi mi, że nie jestem. Dlatego z radością powitam rady bardziej doświadczonych programistów MySQL. :) – gordyr

+0

Czy możesz opisać oczekiwany rezultat? – Peter

Odpowiedz

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

Potrzebne będą następujące indeksy:

  • (songs_id) na $ sTable2
  • kompozyt (songid, ocenił, userid) na $ sTable3

idee zapytania:

  • podzapytania działać z INT tak, że wynik podzapytania będzie łatwo zmieścić w pamięci
  • lewej łączy są zgrupowane osobno, aby zmniejszyć iloczyn kartezjański
  • głosów użytkowników są liczone w tym samym podzapytania jak inne wskaźniki, aby uniknąć drogie skorelowane podzapytanie
  • wszystko othe informacja jest pobierana ib ostateczna dołączyć
+0

Dzięki za sugestię od nowa. Nie będę w stanie sprawdzić twojego rozwiązania aż do weekendu. Od szybkiego spojrzenia wszystko wydaje mi się dobre, dam ci znać w poniedziałek. :-) – gordyr

+0

Fantastyczne ... Działa idealnie. Jest o wiele prostszy do zrozumienia i nieskończenie bardziej elegancki pod względem wykonania. Nie zrobiłem jeszcze żadnych testów wydajności, ale od początkowego wrażenia z pewnością nie jest gorsze, a użycie zapytania sprawi, że wszelkie przyszłe dopasowania będą dużo łatwiejsze w zarządzaniu, ponieważ jest ono o wiele lepsze pod względem strukturalnym. Wielkie dzięki newtover, prawdziwie wspaniała odpowiedź! – gordyr

+0

@gordyr, zapraszam =) – newtover

1

Pozwól mi spróbować na podstawie Twojego opisu, a nie zapytania. Po prostu użyję Songs, aby wskazać , Projects, aby wskazać Table2 i , aby wskazać Table3 - dla jasności.

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 */ 

Jak widać, można uzyskać wszystkie informacje na temat piosenek w jednym, stosunkowo proste zapytanie (tylko przy użyciu przez Grupę i liczyć()/avg() funkcji). Uzyskanie informacji, czy utwór został oceniony przez określonego użytkownika, wymaga podkwerendy - gdzie można wykonać LEWY DOŁĄCZ, a jeśli identyfikator użytkownika jest pusty - wiesz, że nie głosował.

Nie przeszedłem dokładnie tego zapytania, ponieważ wygląda naprawdę skomplikowanie.Może być, że coś mi umknęło - jeśli o to chodzi, proszę zaktualizować opis i mogę spróbować ponownie :)

+0

To wygląda świetnie Aurimas, wielkie dzięki za sugestię. Nie będę mógł przetestować tego przez następną godzinę. Dam ci znać, jak to działa. – gordyr

+0

Dzięki Aurimas ... Zaktualizowałem moje pytanie z moimi odkryciami odkąd próbowałem twojej sugestii. Twoje zdrowie. :) – gordyr

Powiązane problemy