簡體   English   中英

優化mysql查詢-避免創建臨時表?

[英]Optimizing the mysql query - Avoid creation of temporary table?

這是我在表格上使用的查詢: productsreviewsrepliesreview_images

查詢:

SELECT products.id, reviews.*,
GROUP_CONCAT(DISTINCT CONCAT_WS('~',replies.reply, replies.time)) AS Replies,
GROUP_CONCAT(DISTINCT CONCAT_WS('~',review_images.image_title, review_images.image_location)) AS ReviewImages
FROM products
LEFT JOIN reviews on products.id = reviews.product_id
LEFT JOIN replies on reviews.id = replies.review_id
LEFT JOIN review_images on reviews.id = review_images.review_id
WHERE products.id = 1
GROUP BY products.id, reviews.id;

架構

產品:

id  |  name  |  product_details....

評論:

id  |  product_id  |  username  |  review  |  time  | ...

回覆:

id  |  review_id   |  username  |  reply  |  time  | ...

評論圖片:

id  |  review_id  |  image_title  |  image_location  | ...

指標

產品:

主鍵-ID

評論:

主鍵-ID

外鍵-product_id(產品表中的id)

外鍵-用戶名(“用戶名”為“用戶”表)

回覆:

主鍵-ID

外鍵-review_id(id為“評論”表)

外鍵-用戶名(“用戶名”為“用戶”表)

評論圖片:

主鍵-ID

外鍵-review_id(id為“評論”表)


說明查詢:

id | select_type | 桌子 | 類型 可能的鑰匙 | | 額外

1 | 簡單 產品| 索引| 空| 1 | 使用索引; 使用臨時; 使用文件排序

1 | 簡單 評論| 全部| product_id | 4 | 在哪里使用 使用聯接緩沖區(塊嵌套循環)

1 | 簡單 回復| 參考| review_id | 1 | 空值

1 | 簡單 review_images | 全部| review_id | 5 | 在哪里使用 使用聯接緩沖區(塊嵌套循環)

我不知道這有什么問題,它需要使用filesort並創建一個臨時表?

以下是一些分析結果:

開孔台140 µs

初始化139 µs

系統鎖定34 µs

優化21 µs

統計量106 µs

准備146 µs

創建Tmp表13.6毫秒

排序結果27 µs

執行11 µs

發送數據11.6毫秒

創建排序索引1.4毫秒

結束89 µs

刪除Tmp表8.9毫秒

結束34 µs

查詢結束25 µs

閉合台66 µs

釋放項目41 µs

刪除Tmp表1.4毫秒

釋放項目46 µs

刪除Tmp表1.2毫秒

釋放項目203 µs

清理55 µs


從解釋和概要分析結果來看,很明顯,已創建臨時表以產生結果。 如何優化此查詢以獲得相似的結果和更好的性能,並避免創建臨時表?

幫助將不勝感激。 提前致謝。

編輯

建立表格

CREATE TABLE `products` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(100) NOT NULL,
 `description` varchar(100) NOT NULL,
 `items` int(11) NOT NULL,
 `price` int(11) NOT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB

CREATE TABLE `reviews` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `username` varchar(30) NOT NULL,
 `product_id` int(11) NOT NULL,
 `review` text NOT NULL,
 `time` datetime NOT NULL,
 `ratings` int(11) NOT NULL,
 PRIMARY KEY (`id`),
 KEY `product_id` (`product_id`),
 KEY `username` (`username`)
) ENGINE=InnoDB

CREATE TABLE `replies` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `review_id` int(11) NOT NULL,
 `username` varchar(30) NOT NULL,
 `reply` text NOT NULL,
 `time` datetime NOT NULL,
 PRIMARY KEY (`id`),
 KEY `review_id` (`review_id`)
) ENGINE=InnoDB

CREATE TABLE `review_images` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `review_id` int(11) NOT NULL,
 `image_title` text NOT NULL,
 `image_location` text NOT NULL,
 PRIMARY KEY (`id`),
 KEY `review_id` (`review_id`)
) ENGINE=InnoDB

編輯

我簡化了上面的查詢,現在它不會創建臨時表。 @Bill Karwin提到的唯一原因是我在聯接的第二個表上使用了GROUP BY

簡化查詢:

SELECT reviews. * ,
GROUP_CONCAT( DISTINCT CONCAT_WS( '~', replies.reply, replies.time ) ) AS Replies,
GROUP_CONCAT( DISTINCT CONCAT_WS( '~', review_images.image_title, review_images.image_location ) ) AS ReviewImages
FROM reviews
LEFT JOIN replies ON reviews.id = replies.review_id
LEFT JOIN review_images ON reviews.id = review_images.review_id
WHERE reviews.product_id = 1
GROUP BY reviews.id

現在我面臨的問題是:

因為我使用的是GROUP_CONCAT,所以它可以保存的數據存在一個限制,該限制在變量GROUP_CONCAT_MAX_LEN ,因此當我串聯用戶給出的答復時,它可能會非常長,並且可能超出定義的內存。 我知道我可以更改當前會話的GROUP_CONCAT_MAX_LEN的值,但是它仍然有一個局限性,在某個時間點,查詢可能會失敗或無法獲取完整的結果。

如何修改查詢,以免使用GROUP_CONCAT並仍然得到預期的結果。

可能的解決方案:

簡單地使用LEFT JOINS,它為最后一列中的每個新結果創建重復的行,這使得在php難以遍歷? 有什么建議么?

我看到這個問題沒有從SO成員那里得到足夠的答復。 但是從上周到上周,我一直在尋找解決方案並搜索概念。 仍然沒有運氣。 希望你們中的一些專業人士能幫助我。 提前致謝。

當您的GROUP BY子句引用來自兩個不同表的列時,您將避免創建臨時表。

避免此查詢中的臨時表的唯一方法是在一個表中存儲數據的非規范化版本,並為要分組的兩列建立索引。


您可以簡化並以更易於在PHP中使用的格式獲取結果的另一種方法是,無需使用GROUP BY即可進行多個查詢。

首先獲得評論。 示例在PHP和PDO中,但是原理適用於任何語言。

$review_stmt = $pdo->query("
    SELECT reviews.*,
    FROM reviews
    WHERE reviews.product_id = 1");

將它們排列在一個由review_id鍵控的關聯數組中。

$reviews = array();
while ($row => $review_stmt->fetch(PDO::FETCH_ASSOC)) {
    $reviews[$row['d']] = $row;
}

然后獲取答復,並使用鍵“答復”將其附加到數組中。 使用INNER JOIN而不是LEFT JOIN,因為沒有回復就可以。

$reply_stmt = $pdo->query("
    SELECT replies.*
    FROM reviews
    INNER JOIN replies ON reviews.id = replies.review_id
    WHERE reviews.product_id = 1");
while ($row = $reply_stmt->fetch(PDO::FETCH_ASSOC)) {
    $reviews[$row['review_id']]['replies'][] = $row; 
}

並對review_images執行相同的操作。

$reply_stmt = $pdo->query("
    SELECT review_images.*
    FROM reviews
    INNER JOIN review_images ON reviews.id = review_images.review_id
    WHERE reviews.product_id = 1");
while ($row = $reply_stmt->fetch(PDO::FETCH_ASSOC)) {
    $reviews[$row['review_id']]['review_images'][] = $row; 
}

最終結果是一個評論數組,其中包含元素,它們是分別用於相關回復和圖像的嵌套數組。

運行簡單查詢的效率可以彌補運行三個查詢的額外工作。 另外,您不必編寫代碼即可explode()包含組的字符串。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM