web-dev-qa-db-ja.com

サブクエリは個別に非常に高速に実行されますが、結合すると非常に低速になります

ypercubeが問題を解決しました。サブクエリは完全に不要であり、すべてが単純な結合で機能します。しかし、MySQLのオプティマイザが私の元のクエリを利用できなかったのはまだ奇妙です。質問と詳細については、以下を参照してください。さらに、私の質問の最後にある完全なソリューション。それはypercubeの答えに基づいています。

各サブクエリは非常に高速で、1秒未満です。 5〜6個のサブクエリが結合され(一部はLEFT、一部はINNER)、時間はキノコが400秒になります。

テストに使用しているクエリ全体では、441行しか返されません。

各サブクエリを "CREATE TABLE"クエリに入れてみました。それぞれ1秒未満で完了しました。次に、新しく作成されたテーブルを使用して外部クエリを再実行しましたが、1秒未満で実行されました。したがって、結合には実際の問題はありません。作成したテーブルのidにインデックスを付けました。すべてのテーブルは、一致するid = idで結合されます。

MySQLにクエリを効率的に実行させるにはどうすればよいですか?一時テーブルを使用する必要がありますか?私はすでに複数のPHPコードを記述して、複数のサブクエリ結合をまとめるコードを書いているので、可能であればその方法を理解するだけです。

「STRAIGHT_JOIN」キーワードを使用して、外側のORDER BYを削除してみました。これにより、クエリ時間が90秒に短縮されました。しかし、私は最大1秒を取得する必要があります。

STRAIGHT_JOINORDER BYで試したところ、235秒かかりました。したがって、外側のORDER BYはパフォーマンスの大きな問題のようです。

編集:

一時テーブルを使用してテストされています。クエリは非常に高速に実行されます。しかし、mysqlにJOINSを使用して高速で実行させる方法が必要です。

また、スロークエリログには次のように表示されます。

Rows_examined: 484006914

4億8400万行は、デカルト積のように見えます。なぜそれほど多くの行を検査するのですか?

クエリの構造は次のとおりです。

SELECT t0.`id`, t1.`length`, t2.`height`, t3.`family`
FROM
`products` t0
INNER JOIN
(
SELECT t1.`id`, t2.`value` AS `length`
FROM `products` t1
INNER JOIN `product_eav_decimal` t2
ON t1.`id` = t2.`product_id`
WHERE t2.`attribute_id` = 91
AND t2.`value` BETWEEN 15 AND 35
) t1

ON t0.`id` = t1.`id`

LEFT JOIN
(
SELECT t1.`id`, t2.`value` AS `height`
FROM `products` t1
INNER JOIN `product_eav_decimal` t2
ON t1.`id` = t2.`product_id`
WHERE t2.`attribute_id` = 80
# no other conditions
) t2
ON t0.`id` = t2.`id`

INNER JOIN
(
.
.
.
) t6
ON t0.`id` = t6.`id`
ORDER BY t0.`id` ASC

... etc LEFT JOINSは、attribute_id以外のサブクエリに他の条件がない場合に使用されます。他の条件がある場合に使用されるINNER JOIN。これにより、有効な検索結果が作成されます。クエリは機能し、0.04ではなく400秒しかかかりません。

JOIN構文を機能させる方法が誰にもわからない場合は、一時テーブルを使用します。

テーブル:

1.)製品

CREATE TABLE `products` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `sku` varchar(127) NOT NULL COMMENT '3char vencode + model',
 `model` varchar(127) NOT NULL,
 `vendor_id` int(11) DEFAULT NULL,
 `updated` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 PRIMARY KEY (`id`),
 UNIQUE KEY `sku` (`sku`),
 KEY `model` (`model`),
 KEY `vendor_id` (`vendor_id`),
 CONSTRAINT `FK1` FOREIGN KEY (`vendor_id`) REFERENCES `vendors` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=153282 DEFAULT CHARSET=utf8

2.)小数

CREATE TABLE `product_eav_decimal` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `product_id` int(11) NOT NULL,
 `attribute_id` int(11) DEFAULT NULL,
 `value` decimal(11,3) DEFAULT NULL,
 `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 PRIMARY KEY (`id`),
 UNIQUE KEY `natural_key` (`product_id`,`attribute_id`,`value`),
 UNIQUE KEY `product_id_2` (`product_id`,`attribute_id`),
 KEY `last_update` (`last_update`),
 KEY `product_id` (`product_id`),
 KEY `attribute_id` (`attribute_id`),
 KEY `value` (`value`),
 CONSTRAINT `FK1` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
 CONSTRAINT `FK2` FOREIGN KEY (`attribute_id`) REFERENCES `attributes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=370772 DEFAULT CHARSET=utf8 COLLATE=utf8_bin

3.)varchar(別のテーブルを参照、実際のvarchar値はvalues_varcharテーブル)

CREATE TABLE `product_eav_varchar` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `product_id` int(11) DEFAULT NULL,
 `attribute_id` int(11) DEFAULT NULL,
 `value_id` int(11) DEFAULT NULL,
 `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 PRIMARY KEY (`id`),
 UNIQUE KEY `natural_key` (`product_id`,`attribute_id`,`value_id`),
 KEY `last_update` (`last_update`),
 KEY `product_id` (`product_id`),
 KEY `value_id` (`value_id`),
 KEY `attribute_id` (`attribute_id`),
 CONSTRAINT `FK1` FOREIGN KEY (`value_id`) REFERENCES `values_varchar` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
 CONSTRAINT `FK2` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
 CONSTRAINT `FK3` FOREIGN KEY (`attribute_id`) REFERENCES `attributes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=86049 DEFAULT CHARSET=utf8 COLLATE=utf8_bin

Ypercubeの回答からの抜粋:

SELECT t0.id, 
       t1.`value` AS length, 
       t2.`value` AS height, 
       t3.`value` AS family,
       t5.`value` AS type
FROM
  products t0

INNER JOIN # INNER used when search criteria
# length (only searched values)
  product_eav_decimal t1
    ON  t1.product_id = t0.id  
    AND t1.attribute_id = 91
    AND t1.`value` BETWEEN 15 AND 35 # search criteria

LEFT JOIN # LEFT used when no search criteria
# height (all, including blank/null)
  product_eav_decimal t2
    ON  t2.product_id = t0.id  
    AND t2.attribute_id = 80  

LEFT JOIN  # LEFT - no search critera
# family - varchar type requires extra join to values table
  product_eav_varchar t3
    ON  t3.product_id = t0.id  
    AND t3.attribute_id = 77
LEFT JOIN # LEFT join to values table matches eav table join
values_varchar t4
    ON t3.value_id = t4.id
# search criteria would be here. see next

INNER JOIN # INNER - search criteria below
# type - varchar requires extra join, see below
  product_eav_varchar t5
    ON t5.product_id = t0.id
    AND t5.attribute_id = 76
INNER JOIN # INNER join to values table matches eav table join
values_varchar t6
    ON t5.value_id = t6.id
    # search criteria
    AND (t6.value LIKE "%sofa%" COLLATE utf8_general_ci OR t6.value LIKE "%chair%" COLLATE utf8_general_ci)

ORDER BY t0.id ASC;

クエリは機能します。数ミリ秒で実行されます。検索語句または範囲の制限が指定されている場合、一致する結果のみが返され、INNER JOINが使用されます。基準がない場合、LEFT JOINを使用して値(NULL /空白を含む)を返します。

2014年8月の更新-productsテーブルに400〜50万行が含まれるようになり、上記で使用されたクエリスタイルは依然として高速で実行されます。結合はMySQLのサブクエリよりもはるかに高速であるようです。

7
Buttle Butkus

すべての派生テーブルが必要なわけではありません。基本(product)に何度も参加しています。結合するクエリは1回だけ記述できます。

EAV設計では、複合インデックスは必須です。 (attribute_id, product_id, value)にインデックスを追加してから、クエリを追加してください:

SELECT t0.id, 
       t1.`value` AS length, 
       t2.`value` AS height, 
       t3.`value` AS family
FROM
  products t0

INNER JOIN 
  product_eav_decimal t1
    ON  t1.product_id = t0.id  
    AND t1.attribute_id = 91
    AND t1.`value` BETWEEN 15 AND 35

LEFT JOIN
  product_eav_decimal t2
    ON  t2.product_id = t0.id  
    AND t2.attribute_id = 80  
-- 
-- 
--

LEFT JOIN                              -- LEFT or INNER join
  product_eav_decimal t6
    ON  t6.product_id = t0.id  
 -- AND t6.attribute_id = 

ORDER BY t0.id ASC ;
6
ypercubeᵀᴹ