SQL查询运行速度极慢

时间:2015-11-26 03:12:29

标签: mysql

我有一个SQL查询,用于收集清单中所有产品的年初至今总计。此查询运行得很快,在一秒钟内返回大约5000个结果。

使用的查询是

SELECT `ITINCODE` as Code, `IT_product_id` as ID,
SUM(IF(YEAR(`ITDATE`) = YEAR(CURDATE() ), IF(ITTYPE = "O",`ITVAL`, -`ITVAL` ), 0) ) as 'YTD_VAL'
FROM `FITEMS`
WHERE (ITTYPE = 'O' OR `ITTYPE` = 'R') AND ITBACKORDER = 'N' AND ITAMNT > 0 AND YEAR(`ITDATE`) >= YEAR(CURDATE() ) -1
GROUP BY `ITINCODE`
ORDER BY YTD_VAL DESC

我想获取YTD_VAL中的值并将它们存储在实际的products表中,以便它们可以用作其他查询的ORDER BY。我在products表中添加了一个名为ytd_val的新字段,然后运行

UPDATE products p SET p.ytd_val = 
(SELECT SUM(IF(YEAR(`ITDATE`) = YEAR(CURDATE() ), IF(ITTYPE = 'O',`ITVAL`, -`ITVAL` ), 0) ) as 'YTD_VAL'
FROM `FITEMS`
WHERE ITINCODE  = p.products_model AND (ITTYPE = 'O' OR `ITTYPE` = 'R') AND ITBACKORDER = 'N' AND ITAMNT > 0 AND YEAR(`ITDATE`) >= YEAR(CURDATE() ) -1
GROUP BY `ITINCODE`
ORDER BY YTD_VAL DESC)

我的想法是每晚通过cron作业运行,以便更新值以反映前几天的销售情况。

但是,运行此查询需要10多分钟,但仍未完成。

FITEMS表中的ITINCODE与products表中的products_model相同。 FITEMS表中的IT_product_id与products表中的products_id相同。

我该怎么做才能加快查询速度?我认为,因为原始结果查询返回得足够快,只需更新另一个表上的值就需要几秒钟!

表结构如下:

show create table fitems\G;

Create Table: CREATE TABLE `fitems` (
  `ITUNIQUEREF` int(11) unsigned NOT NULL,
  `ITAMNT` int(11) NOT NULL,
  `ITREF` int(11) unsigned NOT NULL,
  `ITTYPE` char(1) NOT NULL,
  `ITVAL` decimal(10,4) NOT NULL,
  `ITVAT` decimal(10,4) NOT NULL,
  `ITPRICE` decimal(10,4) NOT NULL,
  `ITDATE` date NOT NULL,
  `ITBACKORDER` char(1) NOT NULL,
  `ITDISC` decimal(10,2) NOT NULL,
  `ITORDERREF` int(11) NOT NULL,
  `ITTREF` int(11) unsigned NOT NULL,
  `ITDATEDLY` date NOT NULL,
  `ITINCODE` char(20) NOT NULL,
  `IT_product_id` int(11) unsigned NOT NULL,
  `ITBUILT` int(11) NOT NULL,
  PRIMARY KEY (`ITUNIQUEREF`),
  KEY `ITREF` (`ITREF`,`ITTYPE`,`ITDATE`,`ITBACKORDER`,`ITORDERREF`,`ITDATEDLY`,`ITINCODE`,`IT_product_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

show create table products\G;

CREATE TABLE `products` (
  `products_id` int(11) NOT NULL,
  `products_type` int(11) NOT NULL DEFAULT '1',
  `products_quantity` float NOT NULL DEFAULT '0',
  `products_model` varchar(32) CHARACTER SET utf8 DEFAULT NULL,
  `products_image` varchar(64) CHARACTER SET utf8 DEFAULT NULL,
  `products_price` decimal(15,4) NOT NULL DEFAULT '0.0000',
  `products_group_a_price` decimal(15,4) NOT NULL,
  `products_group_b_price` decimal(15,4) NOT NULL,
  `products_group_c_price` decimal(15,4) NOT NULL,
  `products_group_d_price` decimal(15,4) NOT NULL,
  `products_group_e_price` decimal(15,4) NOT NULL,
  `products_group_f_price` decimal(15,4) NOT NULL,
  `products_group_g_price` decimal(15,4) NOT NULL,
  `products_virtual` tinyint(1) NOT NULL DEFAULT '0',
  `products_date_added` datetime NOT NULL DEFAULT '0001-01-01 00:00:00',
  `products_last_modified` datetime DEFAULT NULL,
  `products_date_available` datetime DEFAULT NULL,
  `products_weight` float NOT NULL DEFAULT '0',
  `products_status` tinyint(1) NOT NULL DEFAULT '0',
  `products_tax_class_id` int(11) NOT NULL DEFAULT '0',
  `manufacturers_id` int(11) DEFAULT NULL,
  `products_ordered` float NOT NULL DEFAULT '0',
  `products_quantity_order_min` float NOT NULL DEFAULT '1',
  `products_quantity_order_units` float NOT NULL DEFAULT '1',
  `products_priced_by_attribute` tinyint(1) NOT NULL DEFAULT '0',
  `product_is_free` tinyint(1) NOT NULL DEFAULT '0',
  `product_is_call` tinyint(1) NOT NULL DEFAULT '0',
  `products_quantity_mixed` tinyint(1) NOT NULL DEFAULT '0',
  `product_is_always_free_shipping` tinyint(1) NOT NULL DEFAULT '0',
  `products_qty_box_status` tinyint(1) NOT NULL DEFAULT '1',
  `products_quantity_order_max` float NOT NULL DEFAULT '0',
  `products_sort_order` int(11) NOT NULL DEFAULT '0',
  `products_canonical` text COLLATE utf8_unicode_ci NOT NULL,
  `products_discount_type` tinyint(1) NOT NULL DEFAULT '0',
  `products_discount_type_from` tinyint(1) NOT NULL DEFAULT '0',
  `products_price_sorter` decimal(15,4) NOT NULL DEFAULT '0.0000',
  `master_categories_id` int(11) NOT NULL DEFAULT '0',
  `products_mixed_discount_quantity` tinyint(1) NOT NULL DEFAULT '1',
  `metatags_title_status` tinyint(1) NOT NULL DEFAULT '0',
  `metatags_products_name_status` tinyint(1) NOT NULL DEFAULT '0',
  `metatags_model_status` tinyint(1) NOT NULL DEFAULT '0',
  `metatags_price_status` tinyint(1) NOT NULL DEFAULT '0',
  `metatags_title_tagline_status` tinyint(1) NOT NULL DEFAULT '0',
  `pricing_group` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL,
  `ytd_val` int(20) NOT NULL,
  PRIMARY KEY (`products_id`),
  KEY `idx_products_date_added_zen` (`products_date_added`),
  KEY `idx_products_status_zen` (`products_status`),
  KEY `idx_products_date_available_zen` (`products_date_available`),
  KEY `idx_products_ordered_zen` (`products_ordered`),
  KEY `idx_products_model_zen` (`products_model`),
  KEY `idx_products_price_sorter_zen` (`products_price_sorter`),
  KEY `idx_master_categories_id_zen` (`master_categories_id`),
  KEY `idx_products_sort_order_zen` (`products_sort_order`),
  KEY `idx_manufacturers_id_zen` (`manufacturers_id`),
  KEY `products_price` (`products_price`),
  KEY `products_status_products_price` (`products_status`,`products_price`),
  FULLTEXT KEY `idx_enhanced_products_model` (`products_model`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

4 个答案:

答案 0 :(得分:2)

SELECT总是比UPDATE快一点。

加快更新速度:

  • 查看要更新的表上的索引:它们都需要吗?如果没有,请删除不需要的内容(我至少会删除idx_products_status_zen,因为products_status_products_price也包含此内容)

  • 查看数据模型:您可以对要更新的表进行分区吗?如果是这样,那将加快更新速度,因为您要更新的索引会更小,因此更新更快;

  • 使用InnoDB。它更快;

  • 您需要符合ACID标准吗?如果没有,则更改InnoDB的设置以加速系统。

  • 如果您真的使用MySQL:切换到MariaDB:速度提高约8%;

  • 安装监控以查看瓶颈所在:IO或CPU:如果读取时为IO,则尝试压缩。

答案 1 :(得分:1)

如果您对第一次查询性能感到满意。

您可以将第二个查询转换为使用INNER JOIN,例如:

http://sqlfiddle.com/#!9/1028a/1

UPDATE products p 
INNER JOIN (
  SELECT SUM(IF(YEAR(`ITDATE`) = YEAR(CURDATE() ), IF(ITTYPE = 'O',`ITVAL`, -`ITVAL` ), 0) ) as 'YTD_VAL',
  ITINCODE
FROM `FITEMS`
WHERE (ITTYPE = 'O' OR `ITTYPE` = 'R') 
  AND ITBACKORDER = 'N' AND ITAMNT > 0 AND YEAR(`ITDATE`) >= YEAR(CURDATE() ) -1
GROUP BY `ITINCODE`
  ) t
ON t.ITINCODE  = p.products_model
SET p.ytd_val = t.YTD_VAL

更新顺便说一句,你不需要那个ORDER BY YTD_VAL DESC我认为在这个特殊情况下没有任何意义。

更新2

UPDATE products p 
    INNER JOIN (
      SELECT SUM(IF(YEAR(`ITDATE`) = YEAR(CURDATE() ), IF(ITTYPE = 'O',`ITVAL`, -`ITVAL` ), 0) ) as 'YTD_VAL',
      IT_product_id 
    FROM `FITEMS`
    WHERE (ITTYPE = 'O' OR `ITTYPE` = 'R') 
      AND ITBACKORDER = 'N' AND ITAMNT > 0 AND YEAR(`ITDATE`) >= YEAR(CURDATE() ) -1
    GROUP BY `IT_product_id`
      ) t
    ON t.IT_product_id = p.products_id 
    SET p.ytd_val = t.YTD_VAL

答案 2 :(得分:1)

我不是一个可以这么做的人,而是考虑覆盖索引的情况。特别是在表fitems上。对于该查询,这些列相对较小。我会惊慌失措地尝试特定的专栏。我们已经看到了在很快的时候可以完成非常长的查询的情况。但没有承诺。啊,我看到你有一些。正在使用下面的alter table查看第一个编辑。我会继续寻找。

覆盖指数

覆盖索引是一种可以通过索引b树快速漫步来解析查询的索引,需要查找实际表数据页面。这些是速度的终极天堂。 Percona快速撰写文章。

解释

通过Explain运行查询(更新一次)并检查输出。另见文章Using Explain to Write Better Mysql Queries

请注意,其中一些评论适用于以下内容,不一定是此操作。他好像有他的房子。

答案 3 :(得分:0)

在您的子查询中,order bygroup by都不是必需的,因此update可以写成:

UPDATE products p
    SET p.ytd_val = (SELECT SUM(IF(YEAR(`ITDATE`) = YEAR(CURDATE() ), IF(ITTYPE = 'O',`ITVAL`, -`ITVAL` ), 0) )
                     FROM `FITEMS`
                     WHERE FITEMS.ITINCODE = p.products_model AND
                           ITTYPE IN ('O', 'R') AND
                           ITBACKORDER = 'N' AND
                           ITAMNT > 0 AND
                           YEAR(`ITDATE`) >= YEAR(CURDATE() ) - 1
                    );

为此,您需要FITEMS(ITINCODE, ITBACKORDER, ITTYPE, ITAMNT, ITVAL)上的索引。这可能会显着加快您的查询速度。