2012-08-04 21 views
1

我有2個表:優化MySQL查詢:獎牌榜

  1. olympic_medalists與列gold_country,silver_country,bronze_country

  2. 標誌與國家列

我要列出奧運獎牌表相應。我有這個查詢,它的工作原理,但它似乎殺死MySQL。希望有人可以幫助我優化查詢。

SELECT DISTINCT country AS sc, 
    IFNULL(
     (SELECT COUNT(silver_country) 
      FROM olympic_medalists 
     WHERE silver_country = sc AND silver_country != '' 
     GROUP BY silver_country),0) AS silver_medals, 
    IFNULL(
     (SELECT COUNT(gold_country) 
      FROM olympic_medalists 
     WHERE gold_country = sc AND gold_country != '' 
     GROUP BY gold_country),0) AS gold_medals, 
    IFNULL(
     (SELECT COUNT(bronze_country) 
      FROM olympic_medalists 
     WHERE bronze_country = sc AND bronze_country != '' 
     GROUP BY bronze_country),0) AS bronze_medals 
FROM olympic_medalists, flags 
GROUP BY country, gold_medals, silver_country, bronze_medals HAVING (
    silver_medals >= 1 || gold_medals >= 1 || bronze_medals >= 1) 
ORDER BY gold_medals DESC, silver_medals DESC, bronze_medals DESC, 
SUM(gold_medals+silver_medals+bronze_medals) 

結果會是這樣的:

country | g | s | b | tot 
--------------------------------- 
country1 | 9 | 5 | 2 | 16 
country2 | 5 | 5 | 5 | 15 

謝謝!

olympic medalists: 

    `id` int(8) NOT NULL auto_increment, 
    `gold_country` varchar(64) collate utf8_unicode_ci default NULL, 
    `silver_country` varchar(64) collate utf8_unicode_ci default NULL, 
    `bronze_country` varchar(64) collate utf8_unicode_ci default NULL, PRIMARY KEY (`id`) 

flags 

    `id` int(11) NOT NULL auto_increment, 
    `country` varchar(128) default NULL, 
    PRIMARY KEY (`id`) 
+0

你可以發佈您的架構?我也不太清楚你的意思是通過殺死mysql嗎? – 2012-08-04 05:53:36

+0

'殺死mysql' - 當我運行這個時,頁面加載需要一段時間,並且在本地服務器上。當我在運行的服務器上運行它時,基本上需要永久加載和mysql死掉。我會將模式添加到後 – 2012-08-04 05:59:08

+0

抱歉問這個問題,但是有可能修改您的模式嗎?我假設標誌中的varchar是國家代碼或國家代碼,奧運獎牌獲得者在干涉類別中會重複這一點。如果不重複,IT部門可能會稍微乾淨一些,以便更新/插入記錄。也許將國旗改爲國家,併爲黃金,白銀和銅牌製作連接表,並在其中放置諸如國家ID,事件,動作名稱等的具體信息(遺憾地建議更改您的架構,我知道它並不總是可行的),這會使事情變得更簡單但從長遠來看。這些指標也會有所幫助。 – 2012-08-04 06:22:30

回答

0

這將是比你目前的執行三種不同的SELECT子查詢在交叉連接關係,各行的解決方案更有效(和你想知道爲什麼它攤上了!):

SELECT a.country, 
      COALESCE(b.cnt,0)  AS g, 
      COALESCE(c.cnt,0)  AS s, 
      COALESCE(d.cnt,0)  AS b, 
      COALESCE(b.cnt,0) + 
      COALESCE(c.cnt,0) + 
      COALESCE(d.cnt,0)  AS tot 
FROM  flags a 
LEFT JOIN (
      SELECT gold_country, COUNT(*) AS cnt 
      FROM  olympic_medalists 
      GROUP BY gold_country 
     ) b ON a.country = b.gold_country 
LEFT JOIN (
      SELECT silver_country, COUNT(*) AS cnt 
      FROM  olympic_medalists 
      GROUP BY silver_country 
     ) c ON a.country = c.silver_country 
LEFT JOIN (
      SELECT bronze_country, COUNT(*) AS cnt 
      FROM  olympic_medalists 
      GROUP BY bronze_country 
     ) d ON a.country = d.bronze_country 

甚至更​​快的是將實際的文本國家名稱存儲在每個黃金,白銀和古銅色列中,而不是存儲基於整數的國家id。整數比較總是比字符串比較快。

此外,一旦用對應的ID替換olympic_medalists表中的每個國家/地區名稱,您都需要在每列(黃金,白銀和銅牌)上創建索引。

更新文本名稱是他們相應的id的,而不是一個簡單的任務,可以用一個單一的UPDATE聲明與一些ALTER TABLE命令共同完成。

+0

這真的很快。正是我所期待的。謝謝!我會盡量按照您的建議儲存國家ID而不是國家本身。 – 2012-08-04 08:55:59

0

試試這個:

SELECT F.COUNTRY,IFNULL(B.G,0) AS G,IFNULL(B.S,0) AS S, 
IFNULL(B.B,0) AS B,IFNULL(B.G+B.S+B.B,0) AS TOTAL 
FROM FLAGS F LEFT OUTER JOIN 
     (SELECT A.COUNTRY, 
     SUM(CASE WHEN MEDAL ='G' THEN 1 ELSE 0 END) AS G, 
     SUM(CASE WHEN MEDAL ='S' THEN 1 ELSE 0 END) AS S, 
     SUM(CASE WHEN MEDAL ='B' THEN 1 ELSE 0 END) AS B 
     FROM 
      (SELECT GOLD_COUNTRY AS COUNTRY,'G' AS MEDAL 
      FROM OLYMPIC_MEDALISTS WHERE GOLD_COUNTRY IS NOT NULL 
      UNION ALL 
      SELECT SILVER_COUNTRY AS COUNTRY,'S' AS MEDAL 
      FROM OLYMPIC_MEDALISTS WHERE SILVER_COUNTRY IS NOT NULL 
      UNION ALL 
      SELECT BRONZE_COUNTRY AS COUNTRY,'B' AS MEDAL 
      FROM OLYMPIC_MEDALISTS WHERE BRONZE_COUNTRY IS NOT NULL)A 
     GROUP BY A.COUNTRY)B 
ON F.COUNTRY=B.COUNTRY 
ORDER BY IFNULL(B.G,0) DESC,IFNULL(B.S,0) DESC, 
      IFNULL(B.B,0) DESC,IFNULL(B.G+B.S+B.B,0) DESC,F.COUNTRY