2009-06-28 14 views
1

Ich bin ziemlich grün auf mysql und ich brauche ein paar Tipps zum Aufräumen einer Abfrage. Es wird in mehreren Variationen auf einer Website verwendet. Es hat einige Subquerys abgeleitete Tabellen und Spaß weiter. Heres die Abfrage:Wie kann ich diese nachgefragte und verbundene MySQL-Abfrage optimieren?

# Query_time: 2 Lock_time: 0 Rows_sent: 0 Rows_examined: 0 
SELECT * 
    FROM (
    SELECT products . *, categories.category_name AS category, (
    SELECT COUNT(*) 
    FROM distros 
    WHERE distros.product_id = products.product_id) AS distro_count, 
    (SELECT COUNT(*) FROM downloads WHERE downloads.product_id = products.product_id AND WEEK(downloads.date) = WEEK(curdate())) AS true_downloads, 
    (SELECT COUNT(*) FROM views WHERE views.product_id = products.product_id AND WEEK(views.date) = WEEK(curdate())) AS true_views 
    FROM products 
    INNER JOIN categories ON products.category_id = categories.category_id ORDER BY created_date DESC, true_views DESC) AS count_table 
    WHERE count_table.distro_count > 0 
    AND count_table.status = 'published' 
    AND count_table.active = 1 LIMIT 0, 8 

Heres das erklären:

+----+--------------------+------------+-------+---------------+-------------+---------+------------------------------------+------+----------------------------------------------+ 
| id | select_type  | table  | type | possible_keys | key   | key_len | ref        | rows | Extra          | 
+----+--------------------+------------+-------+---------------+-------------+---------+------------------------------------+------+----------------------------------------------+ 
| 1 | PRIMARY   | <derived2> | ALL | NULL   | NULL  | NULL | NULL        | 232 | Using where         | 
| 2 | DERIVED   | categories | index | PRIMARY  | idx_name | 47  | NULL        | 13 | Using index; Using temporary; Using filesort | 
| 2 | DERIVED   | products | ref | category_id | category_id | 4  | digizald_db.categories.category_id | 9 |            | 
| 5 | DEPENDENT SUBQUERY | views  | ref | product_id | product_id | 4  | digizald_db.products.product_id | 46 | Using where         | 
| 4 | DEPENDENT SUBQUERY | downloads | ref | product_id | product_id | 4  | digizald_db.products.product_id | 14 | Using where         | 
| 3 | DEPENDENT SUBQUERY | distros | ref | product_id | product_id | 4  | digizald_db.products.product_id | 1 | Using index         | 
+----+--------------------+------------+-------+---------------+-------------+---------+------------------------------------+------+----------------------------------------------+ 
6 rows in set (0.04 sec) 

und die Tische:

mysql> describe products; 
+---------------+--------------------------------------------------+------+-----+-------------------+----------------+ 
| Field   | Type            | Null | Key | Default   | Extra   | 
+---------------+--------------------------------------------------+------+-----+-------------------+----------------+ 
| product_id | int(10) unsigned         | NO | PRI | NULL    | auto_increment | 
| product_key | char(32)           | NO |  | NULL    |    | 
| title   | varchar(150)          | NO |  | NULL    |    | 
| company  | varchar(150)          | NO |  | NULL    |    | 
| user_id  | int(10) unsigned         | NO | MUL | NULL    |    | 
| description | text            | NO |  | NULL    |    | 
| video_code | text            | NO |  | NULL    |    | 
| category_id | int(10) unsigned         | NO | MUL | NULL    |    | 
| price   | decimal(10,2)         | NO |  | NULL    |    | 
| quantity  | int(10) unsigned         | NO |  | NULL    |    | 
| downloads  | int(10) unsigned         | NO |  | NULL    |    | 
| views   | int(10) unsigned         | NO |  | NULL    |    | 
| status  | enum('pending','published','rejected','removed') | NO |  | NULL    |    | 
| active  | tinyint(1)          | NO |  | NULL    |    | 
| deleted  | tinyint(1)          | NO |  | NULL    |    | 
| created_date | datetime           | NO |  | NULL    |    | 
| modified_date | timestamp          | NO |  | CURRENT_TIMESTAMP |    | 
| scrape_source | varchar(215)          | YES |  | NULL    |    | 
+---------------+--------------------------------------------------+------+-----+-------------------+----------------+ 
18 rows in set (0.00 sec) 

mysql> describe categories 
    -> ; 
+------------------+------------------+------+-----+---------+----------------+ 
| Field   | Type    | Null | Key | Default | Extra   | 
+------------------+------------------+------+-----+---------+----------------+ 
| category_id  | int(10) unsigned | NO | PRI | NULL | auto_increment | 
| category_name | varchar(45)  | NO | MUL | NULL |    | 
| parent_id  | int(10) unsigned | YES | MUL | NULL |    | 
| category_type_id | int(10) unsigned | NO |  | NULL |    | 
+------------------+------------------+------+-----+---------+----------------+ 
4 rows in set (0.00 sec) 

mysql> describe compatibilities 
    -> ; 
+------------------+------------------+------+-----+---------+----------------+ 
| Field   | Type    | Null | Key | Default | Extra   | 
+------------------+------------------+------+-----+---------+----------------+ 
| compatibility_id | int(10) unsigned | NO | PRI | NULL | auto_increment | 
| name    | varchar(45)  | NO |  | NULL |    | 
| code_name  | varchar(45)  | NO |  | NULL |    | 
| description  | varchar(128)  | NO |  | NULL |    | 
| position   | int(10) unsigned | NO |  | NULL |    | 
+------------------+------------------+------+-----+---------+----------------+ 
5 rows in set (0.01 sec) 

mysql> describe distros 
    -> ; 
+------------------+--------------------------------------------------+------+-----+---------+----------------+ 
| Field   | Type            | Null | Key | Default | Extra   | 
+------------------+--------------------------------------------------+------+-----+---------+----------------+ 
| id    | int(10) unsigned         | NO | PRI | NULL | auto_increment | 
| product_id  | int(10) unsigned         | NO | MUL | NULL |    | 
| compatibility_id | int(10) unsigned         | NO | MUL | NULL |    | 
| user_id   | int(10) unsigned         | NO |  | NULL |    | 
| status   | enum('pending','published','rejected','removed') | NO |  | NULL |    | 
| distro_type  | enum('file','url')        | NO |  | NULL |    | 
| version   | varchar(150)          | NO |  | NULL |    | 
| filename   | varchar(50)          | YES |  | NULL |    | 
| url    | varchar(250)          | YES |  | NULL |    | 
| virus   | enum('READY','PASS','FAIL')      | YES |  | NULL |    | 
| downloads  | int(10) unsigned         | NO |  | 0  |    | 
+------------------+--------------------------------------------------+------+-----+---------+----------------+ 
11 rows in set (0.01 sec) 

mysql> describe downloads; 
+------------+------------------+------+-----+---------+----------------+ 
| Field  | Type    | Null | Key | Default | Extra   | 
+------------+------------------+------+-----+---------+----------------+ 
| id   | int(10) unsigned | NO | PRI | NULL | auto_increment | 
| product_id | int(10) unsigned | NO | MUL | NULL |    | 
| distro_id | int(10) unsigned | NO | MUL | NULL |    | 
| user_id | int(10) unsigned | NO | MUL | NULL |    | 
| ip_address | varchar(15)  | NO |  | NULL |    | 
| date  | datetime   | NO |  | NULL |    | 
+------------+------------------+------+-----+---------+----------------+ 
6 rows in set (0.01 sec) 

mysql> describe views 
    -> ; 
+------------+------------------+------+-----+---------+----------------+ 
| Field  | Type    | Null | Key | Default | Extra   | 
+------------+------------------+------+-----+---------+----------------+ 
| id   | int(10) unsigned | NO | PRI | NULL | auto_increment | 
| product_id | int(10) unsigned | NO | MUL | NULL |    | 
| user_id | int(10) unsigned | NO | MUL | NULL |    | 
| ip_address | varchar(15)  | NO |  | NULL |    | 
| date  | datetime   | NO |  | NULL |    | 
+------------+------------------+------+-----+---------+----------------+ 
5 rows in set (0.00 sec) 

Antwort

0

Eine Sache habe ich bemerkt, dass ein schöner Tipp ist (vielleicht durch neuere Optimizern veraltet) für Die allgemeine Leistungsverbesserung besteht darin, COUNT (1) zu verwenden, wenn Sie insgesamt möchten. COUNT (*) muss Spalten verarbeiten, während COUNT (1) nur über Zeilen nachdenken muss.

Wenn das nicht mehr wahr ist, bitte hier einen Kommentar hinterlassen und ich werde den Beitrag löschen.

Ein weiterer Tipp ist, niemals SELECT * zu verwenden. Im Allgemeinen sollten Sie die Spalten, die Sie auswählen, immer aufzählen, um sich gegen das Brechen von Code zu isolieren, wenn sich Tabellen ändern.

Eine Sache, die Sie wirklich tun wollen, ist, die Unterabfragen aus dem SELECT-Block zu entfernen. Joins sind normalerweise viel schneller als diese verschachtelten Abfragen. Denken Sie jedoch daran, dass Optimizer unglaublich viel Arbeit aufwenden müssen, um unsere Anfragen für uns zu beheben. Wenn es korrekt geschrieben wird, zeigt es möglicherweise keine merkliche Verbesserung, wenn der Optimierer es bereits für uns tut!

Hier ist ein link auf Unterabfragen in Ihrer Auswahl.

+0

Danke Brian, das ist der ein bisschen Tipp, ich suche hoffentlich jemand kann dies überprüfen. Es macht Sinn und würde dieser Abfrage wahrscheinlich ein wenig helfen. – kevzettler

+0

auf weiteren Blick MySQL intern analysiert count (*) als count (1) oder count (0) – kevzettler

+0

Nicht wirklich eine große Menge, das ist/war eine dieser "freien" Optimierungen, die mir in den Kopf gedrillt wurde tun, weil es keine Gedanken oder Kompromisse erforderte. Jetzt einen Höhepunkt beim Rest der Abfrage nehmen – Brian

0

Beginnen Sie zunächst mit der Formatierung und Einrückung der Abfrage. So ist es schwierig, genau zu sehen, was vor sich geht.

Diese Seite hat eine zumutbare Arbeit, die Formatierung zu beheben: http://www.dpriver.com/pp/sqlformat.htm

Wie auch immer, es sieht aus wie die Abfrage im Grunde die Anzahl der Elemente in mehreren Tabellen zu zeigen versucht.

Statt es wie folgt tun:

select 
    (select count(*) from myothertable1 where myothertableid=myothertable.id), 
    (select count(*) from myothertable2 where myothertableid=myothertable.id), 
    (select count(*) from myothertable3 where myothertableid=myothertable.id) 
from 
    myothertable 

sollten Sie etwas tun:

select 
    count(myothertable1.id), 
    count(myothertable2.id), 
    count(myothertable3.id) 
from 
    mytable, 
    myothertable1, 
    myothertable2, 
    myothertable3 
where 
    myothertableid1=mytable.id and 
    myothertableid2=mytable.id and 
    myothertableid3=mytable.id 
+0

Beitritt der Geschichten wird wahrscheinlich bessere Zeiten –

+0

Sie sind verbunden. Wenn Sie die Syntax "join .. on" verwenden möchten, versuchen Sie dies mit "explain extended (sqlquery) and show warnings" auszuführen. um zu sehen, wie MySQL die Abfrage tatsächlich durchführt. MySQL konvertiert dies intern in die Syntax "join .. on". Ich habe diese Syntax für die Lesbarkeit verwendet. –

+0

Es gibt ein Problem mit Ihrer Lösung.Wenn einer von MyothertableN Datensätze für Mytable.id nicht enthält, wird die Abfrage es nicht auflisten. Und sollte eine Liste sein und Null als 'count()' setzen. Eine Lösung ist die Verwendung von "linker äußerer Verbindung". –

0

Nur um zu zeigen, was ich im Kommentar von Wouter van Nifterick´s anwswer ment. IMO, sollte es sein:

select 
    count(myothertable1.id), 
    count(myothertable2.id), 
    count(myothertable3.id) 
from mytable 
left outer join myothertable1 on (myothertableid1=mytable.id) 
left outer join myothertable2 on (myothertableid2=mytable.id) 
left outer join myothertable3 on (myothertableid3=mytable.id) 
where mytable.field = 'value'