2011-07-18 17 views
0

私は注文を選択するためのクエリを持っており、各注文について、各製品のサマリ数量に含まれる製品のリストを取得する必要があります。クエリが遅い2つのテーブルフィールドでSELECT ... GROUP BY(MySQL/InnoDB)

現在、非常に遅いです(セットアップでは10秒です)。スピードアップする必要があります。

アップデート:MyISAMテーブルに切り替えることがオプションではありません、私はトランザクションを必要とする

クエリは、現在そのようなものです:

SELECT `Order`.*, `Product`.`id`, `Product`.`msrp`, SUM(`OrderItem`.`quantity`) AS sum 
FROM `orders` AS `Order` 
LEFT JOIN `order_items` AS `OrderItem` ON (`OrderItem`.`order_id` = `Order`.`id`) 
LEFT JOIN `product_variations` AS `ProductVariation` ON (`OrderItem`.`product_variation_id` = `ProductVariation`.`id`) 
LEFT JOIN `products` AS `Product` ON (`ProductVariation`.`product_id` = `Product`.`id`) 
WHERE 1 = 1 
GROUP BY `Order`.`id`, `Product`.`id` 
ORDER BY `Order`.`created` DESC 
LIMIT 20; 

は説明:

​​

をスキーマ(切り捨てられ、必須フィールドのみが残っています):

CREATE TABLE `orders` (
    `id` int(11) NOT NULL auto_increment, 
    `created` timestamp NOT NULL default CURRENT_TIMESTAMP, 
    `customer_comments` text NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `created` (`created`), 
    KEY `id_created` (`id`,`created`), 
) ENGINE=InnoDB 

CREATE TABLE `order_items` (
    `id` int(11) NOT NULL auto_increment, 
    `order_id` int(11) NOT NULL, 
    `product_variation_id` int(11) NOT NULL, 
    `type` enum('First','Second') default NULL, 
    `quantity` int(11) NOT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `item_UNIQUE` (`order_id`,`product_variation_id`,`type`), 
    KEY `fk_order_items_product_variations1` (`product_variation_id`), 
    CONSTRAINT `fk_order_items_orders1` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 
    CONSTRAINT `fk_order_items_product_variations1` FOREIGN KEY (`product_variation_id`) REFERENCES `product_variations` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION 
) ENGINE=InnoDB 

CREATE TABLE `product_variations` (
    `id` int(11) NOT NULL auto_increment, 
    `product_id` int(11) NOT NULL, 
    PRIMARY KEY (`id`), 
    CONSTRAINT `fk_product_variations_products1` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 
) ENGINE=InnoDB 

CREATE TABLE `products` (
    `id` int(11) NOT NULL auto_increment, 
    `msrp` decimal(5,2) NOT NULL, 
    PRIMARY KEY (`id`), 
) ENGINE=InnoDB 

サーバはMySQL 5.0.77です。テーブルはInnoDBです。 ordersは約75kレコード、order_items 160kレコード、product_variations 140kレコード、product - 300レコードです。

何か助けていただければ幸いです。

答えて

1

まず、SUMをprecomputed product_sumテーブルに置き換えることを試みます。 InnoDBは、集約関数ではより遅くなることが知られています(特に75k +行で!)。データが頻繁に変更される場合を除き、すべてのクエリでこの合計を計算することは実際には望ましくありません。

オーダーの挿入/更新後にproduct_sumテーブルを再計算するトリガーを使用すると便利ですが、このクエリのコンテキスト(エンドユーザーが実行しているかどうかは実際にはわかりません。ウェブリクエストやレポートなど)

もう一つの選択肢は、エンジンをMyISAMに切り替えることです(これは集約ではるかに高速です)。ただし、トランザクションと外部キーの制約がなくなります。

+0

私は、最後の手段として事前計算合計および他の集計を開催しました。私の理解では、RDBMSが何かに優れているならば、それはフィルタリング、グループ化、集約です。私はもちろん、すべての集合体を事前に計算することができます。なぜRDBMSを使うのですか? – lxa

+0

コンテキストについて - これはWebインターフェイスのページングクエリです。 (実際にはその一部です - 各注文のためにもう一度時間を要する必要があります - これを行う方法はまだわかりませんが、WITH ROLLUPはこのクエリでは機能しません)。 – lxa

+0

ここでの問題はRDBMSではなく、テーブルエンジンです。集約が確かにボトルネックであるかどうかを確かめるためのテストとしてMyISAMに切り替えます。 InnoDBは集約時にすべての行を読み込みますが、MyISAMは集計しません。残念ながら、InnoDBのトランザクション/制約機能が必要な場合は、集約を自分でキャッシュする必要があります。 – Matt

1

クエリがうまく構成されて結合されているように見えますが、レコード量が問題になることがあります。 1つの句を追加します。

SELECT STAIGHT_JOIN ...残りのクエリ。

STRAIGHT_JOINは、小さなテーブルをプライマリとして最適化しようとするのではなく、指定された順序でエンジンを実行するよう指示します。逆の順序で結合します。私はSTRAIGHT_JOINが他のいくつかの分野で素晴らしいパフォーマンスを発揮しているのを見てきました。

+0

残念ながら、これではありません。それを試しました - 大きな違いはありません。 – lxa