2012-12-17 16 views
5

Ho questa query sql in esecuzione su tabella non normalizzata MySQL 5.1. Funziona nel modo in cui lo voglio, ma può essere piuttosto lento. Ho aggiunto un indice sulla colonna del giorno, ma deve ancora essere più veloce. Qualche suggerimento su come ottenerlo più velocemente? (Magari con un join, invece?)Ottimizza selezione nidificata MySQL con operazione aritmetica

SELECT DISTINCT(bucket) AS b, 
     (possible_free_slots - 
      (SELECT COUNT(availability) 
      FROM ip_bucket_list 
      WHERE bucket = b 
      AND availability = 'used' 
      AND tday = 'evening' 
      AND day LIKE '2012-12-14%' 
      AND network = '10_83_mh1_bucket')) AS free_slots 
FROM ip_bucket_list 
ORDER BY free_slots DESC; 

Le singole domande sono veloci:

SELECT DISTINCT(bucket) FROM ip_bucket_list; 
1024 rows in set (0.05 sec) 

SELECT COUNT(availability) from ip_bucket_list WHERE bucket = 0 AND availability = 'used' AND tday = 'evening' AND day LIKE '2012-12-14%' AND network = '10_83_mh1_bucket'; 
1 row in set (0.00 sec) 

Tabella:

mysql> describe ip_bucket_list; 
+---------------------+--------------+------+-----+-------------------+----------------+ 
| Field    | Type   | Null | Key | Default   | Extra   | 
+---------------------+--------------+------+-----+-------------------+----------------+ 
| id     | int(11)  | NO | PRI | NULL    | auto_increment | 
| ip     | varchar(50) | YES |  | NULL    |    | 
| bucket    | int(11)  | NO | MUL | NULL    |    | 
| availability  | varchar(20) | YES |  | NULL    |    | 
| network    | varchar(100) | NO | MUL | NULL    |    | 
| possible_free_slots | int(11)  | NO |  | NULL    |    | 
| tday    | varchar(20) | YES |  | NULL    |    | 
| day     | timestamp | NO | MUL | CURRENT_TIMESTAMP |    | 
+---------------------+--------------+------+-----+-------------------+----------------+ 

e la DESC:

DESC SELECT DISTINCT(bucket) as b,(possible_free_slots - (SELECT COUNT(availability) from ip_bucket_list WHERE bucket = b AND availability = 'used' AND tday = 'evening' AND day LIKE '2012-12-14%' AND network = '10_83_mh1_bucket')) as free_slots FROM ip_bucket_list ORDER BY free_slots DESC; 
+----+--------------------+----------------+------+-----------------------------------------+--------+---------+------+--------+---------------------------------+ 
| id | select_type  | table   | type | possible_keys       | key | key_len | ref | rows | Extra       | 
+----+--------------------+----------------+------+-----------------------------------------+--------+---------+------+--------+---------------------------------+ 
| 1 | PRIMARY   | ip_bucket_list | ALL | NULL         | NULL | NULL | NULL | 328354 | Using temporary; Using filesort | 
| 2 | DEPENDENT SUBQUERY | ip_bucket_list | ref | bucket,network,ip_bucket_list_day_index | bucket | 4  | func | 161 | Using where      | 
+----+--------------------+----------------+------+-----------------------------------------+--------+---------+------+--------+---------------------------------+ 
+0

potete inserire alcune righe di esempio e risultati attesi? Questo potrebbe aiutare. –

risposta

3

lo farei sposta la sottoquery correlata da SELECT clausola nella clausola di FROM, utilizzando un join:

SELECT distinct bucket as b, 
     (possible_free_slots - a.avail) as free_slots 
FROM ip_bucket_list ipbl left outer join 
    (SELECT bucket COUNT(availability) as avail 
     from ip_bucket_list 
     WHERE availability = 'used' AND tday = 'evening' AND 
      day LIKE '2012-12-14%' AND network = '10_83_mh1_bucket' 
    ) on a 
    on ipbl.bucket = avail.bucket 
ORDER BY free_slots DESC; 

La versione nella clausola SELECT viene probabilmente re-run per ogni riga (anche prima della distinct è in esecuzione). Inserendolo nella clausola from, la tabella ip_bucket_list verrà scansionata una sola volta.

Inoltre, se si prevede che ogni bucket venga visualizzato solo una volta, quindi si consiglia di utilizzare group by anziché distinct. Chiarirebbe lo scopo della query. Si può essere in grado di eliminare il secondo riferimento alla tabella del tutto, con qualcosa come:

SELECT bucket as b, 
     max(possible_free_slots - 
      (case when availability = 'used' AND tday = 'evening' AND 
         day LIKE '2012-12-14%' AND network = '10_83_mh1_bucket' 
       then 1 else 0 
      end) 
      ) as free_slots 
FROM ip_bucket_list 
group by bucket 
ORDER BY free_slots DESC; 

Per velocizzare la vostra versione della query, è necessario un indice su bucket, perché questo viene utilizzato per la subquery correlata.

+0

Grazie per la risposta. L'unica cosa è che avevo bisogno di filtrare il mio secondo SELECT con solo risultati per il bucket corrente. – Andrew

+0

Alla fine ho semplicemente sostituito il DISTINCT con una clausola GROUP BY e questo ha velocizzato la ricerca. – Andrew

0

provare a spostare la sottoquery nella query principale - in questo modo:

SELECT b.bucket AS b, 
     b.possible_free_slots - COUNT(l.availability) AS free_slots 
FROM ip_bucket_list b 
LEFT JOIN ip_bucket_list l 
     ON l.bucket = b.bucket 
     AND l.availability = 'used' 
     AND l.tday = 'evening' 
     AND l.day LIKE '2012-12-14%' 
     AND l.network = '10_83_mh1_bucket' 
GROUP BY b.bucket, b.possible_free_slots 
ORDER BY 2 DESC