Converting fast query with subquery to joined query for mysql-4.0.x

Converting fast query with subquery to joined query for mysql-4.0.x

am 15.10.2006 21:17:28 von Henri Schomaecker

Hallo alle zusammen,

um aus einem oscommerce Shopsystem aus tausenden von Kunden diejenigen
herauszusuchen, die sich zwar angemeldet, aber noch keinerlei Bestellung
aufgegeben haben, hab´ ich eine Abfrage geschrieben die sehr schnell ist
und eine Subquery enthält.

Dummerweise hab´ ich hierbei aber nicht bedacht, dass der Shop des
Kunden, der dies bestellt hat, noch auf einem unserer älteren Server
liegt der noch, allerdings aus gutem Grund, mit MySql-4.0.x bestückt ist.
Hier funktionieren Subqueries ja aber leider noch nicht, also hab ich
begonnen, die Query in eine umzuschreiben, die Joins verwendet.
Leider ist diese, LEFT JOINs verwendende Abfrage extreeeeem langsam und
ausserdem benötigt der Prozess dann ca 94% der CPU und ca. 40 MB im
Speicher. - Das war aber natürlich nicht Sinn der Übung ;-)
Ausserdem werden auch ein paar komplett leere Datenzeilen ausgegeben,
was auch nicht so schön ist.
Oft hilft bei JOINs beinhaltenden Queries schon eine Umstellung der
JOINs, was aber hier nicht geholfen hat.

Ich würde mich sehr freuen, wenn mir jemand bei einer besseren Umsetzung
helfen könnte und mir erklärt, warum die zweite Abfrage soooo
Ressourcenaufwändig ist und wie's besser ginge.

Hier einmal Beispiele (Extrem gekürzt!) für beide Abfragen...

Query mit Sub-Select:
8<--------8<--------8<--------8<--------8<--------8<--------
select DISTINCT
`c`.`customers_id`,
`c`.`customers_firstname`,
`c`.`customers_lastname`,
`address_book`.`entry_company`,
`countries`.`countries_name`
FROM `customers` `c`, `address_book`, `countries`
WHERE `address_book`.`customers_id` = `c`.`customers_id`
AND `address_book`.`address_book_id` = `c`.`customers_default_address_id`
AND `countries`.`countries_id` = `address_book`.`entry_country_id`
AND `c`.`customers_id` not in (SELECT DISTINCT `orders`.`customers_id`
FROM `orders`)
ORDER BY `c`.`customers_lastname` desc
8<--------8<--------8<--------8<--------8<--------8<--------


Query mit Left-Joins (Klappt, ist aber extrem langsam!)
8<--------8<--------8<--------8<--------8<--------8<--------
SELECT DISTINCT
`c`.`customers_id`,
`c`.`customers_firstname`,
`c`.`customers_lastname`,
`address_book`.`entry_company`,
`countries`.`countries_name`
FROM `customers` `c`
LEFT JOIN `address_book` on
`c`.`customers_default_address_id`=`address_book`.`address_b ook_id`
LEFT JOIN `countries` on
`address_book`.`entry_country_id`=`countries`.`countries_id`
LEFT JOIN `orders` on `c`.`customers_id`=`orders`.`customers_id`
WHERE `orders`.`customers_id` IS NULL
ORDER BY `c`.`customers_lastname` asc
8<--------8<--------8<--------8<--------8<--------8<--------

vielen herzlichen Dank schon einmal im Voraus,
Gruss Henri

Re: Converting fast query with subquery to joined query for mysql-4.0.x

am 15.10.2006 21:47:19 von Axel Schwenke

"news.t-online.de" wrote:

> Query mit Sub-Select:
> 8<--------8<--------8<--------8<--------8<--------8<--------
> select DISTINCT
> `c`.`customers_id`,
> `c`.`customers_firstname`,
> `c`.`customers_lastname`,
> `address_book`.`entry_company`,
> `countries`.`countries_name`
> FROM `customers` `c`, `address_book`, `countries`
> WHERE `address_book`.`customers_id` = `c`.`customers_id`
> AND `address_book`.`address_book_id` = `c`.`customers_default_address_id`
> AND `countries`.`countries_id` = `address_book`.`entry_country_id`
> AND `c`.`customers_id` not in (SELECT DISTINCT `orders`.`customers_id`
> FROM `orders`)
> ORDER BY `c`.`customers_lastname` desc
> 8<--------8<--------8<--------8<--------8<--------8<--------

vs.

> Query mit Left-Joins (Klappt, ist aber extrem langsam!)
> 8<--------8<--------8<--------8<--------8<--------8<--------
> SELECT DISTINCT
> `c`.`customers_id`,
> `c`.`customers_firstname`,
> `c`.`customers_lastname`,
> `address_book`.`entry_company`,
> `countries`.`countries_name`
> FROM `customers` `c`
> LEFT JOIN `address_book` on
> `c`.`customers_default_address_id`=`address_book`.`address_b ook_id`
> LEFT JOIN `countries` on
> `address_book`.`entry_country_id`=`countries`.`countries_id`
> LEFT JOIN `orders` on `c`.`customers_id`=`orders`.`customers_id`
> WHERE `orders`.`customers_id` IS NULL
> ORDER BY `c`.`customers_lastname` asc
> 8<--------8<--------8<--------8<--------8<--------8<--------

Was mir hier auffällt, ist daß du *nur* LEFT JOINs verwendest, während
deine Original-Query *nur* INNER JOINs hatte ("," == INNER JOIN).

Schreib also alle LEFT JOINs - außer dem mit `orders` als INNER JOINs
und versuch das nochmal.

Vermutlich ist der Lookup nach `orders`.`customers_id` langsam. Es
würde helfen, eine temporäre Tabelle als SELECT DISTINCT customers_id
FROM orders (mit UNIQUE Index auf customers_id) zu bauen und den LEFT
JOIN gegen diese laufen zu lassen. Vermutlich tut die Subquery-Variante
etwas ähnliches.


XL