2

我不是 MySQL 高手,但我明白了,我刚刚继承了一个非常大的表(600,000 行和大约 90 列(请杀了我......))并且我创建了一个较小的表来链接它一个类别表。

我正在尝试使用左连接查询所述表,因此我将两组数据都放在一个对象中,但运行速度非常慢,而且我还不够热,无法对其进行排序;对于它为什么这么慢,我真的很感激一些指导和解释。

SELECT 
    `products`.`Product_number`,
    `products`.`Price`,
    `products`.`Previous_Price_1`,
    `products`.`Previous_Price_2`,
    `products`.`Product_number`,
    `products`.`AverageOverallRating`,
    `products`.`Name`,
    `products`.`Brand_description`
FROM `product_categories`
LEFT OUTER JOIN `products`
ON `products`.`product_id`= `product_categories`.`product_id`
WHERE COALESCE(product_categories.cat4, product_categories.cat3,
product_categories.cat2, product_categories.cat1) = '123456'
AND `product_categories`.`product_id` != 0

这两个表是 MyISAM,products 表在 Product_number 和 Brand_Description 上有索引,product_categories 表在所有组合列上都有唯一索引;如果此信息有任何帮助。

继承了这个系统后,我需要尽快让这个系统正常工作,然后才能正确地操作它,所以现在的任何帮助都会为您赢得我最大的尊重!

[编辑] 这是解释扩展的输出:

+----+-------------+--------------------+-------+---------------+------+---------+------+---------+----------+--------------------------+
| id | select_type | table              | type  | possible_keys | key  | key_len | ref  | rows    | filtered | Extra                    |
+----+-------------+--------------------+-------+---------------+------+---------+------+---------+----------+--------------------------+
|  1 | SIMPLE      | product_categories | index | NULL          | cat1 | 23      | NULL | 1224419 |   100.00 | Using where; Using index |
|  1 | SIMPLE      | products           | ALL   | Product_id    | NULL | NULL    | NULL |  512376 |   100.00 |                          |
+----+-------------+--------------------+-------+---------------+------+---------+------+---------+----------+--------------------------+
4

3 回答 3

3

优化表

要建立基线,我首先建议OPTIMIZE TABLE在两个表上运行一个命令。请注意,这可能需要一些时间。从文档

OPTIMIZE TABLE如果您删除了表的大部分,或者您对具有可变长度行的表(具有VARCHAR, VARBINARY, BLOBTEXT列的表)进行了许多更改,则应该使用。删除的行保存在链表中,后续INSERT 操作重用旧的行位置。您可以使用它OPTIMIZE TABLE来回收未使用的空间并对数据文件进行碎片整理。在对表进行大量更改后,此语句还可以提高使用该表的语句的性能,有时会显着提高。

[...]

对于 MyISAM 表,OPTIMIZE TABLE工作方式如下:

  1. 如果表已删除或拆分行,请修复表。

  2. 如果索引页未排序,请对其进行排序。

  3. 如果表的统计信息不是最新的(并且无法通过对索引进行排序来完成修复),请更新它们。

索引

如果空间和索引管理不是问题,您可以尝试在

product_categories.cat4, product_categories.cat3, product_categories.cat2, product_categories.cat1

如果您在查询中经常使用这些列的最左侧子集,则会建议您这样做。查询计划表明它可以使用 的cat1索引product_categories。这很可能只包括该cat1列。通过将所有四个类别列添加到索引中,它可以更有效地查找所需的行。从文档

MySQL 可以将多列索引用于测试索引中所有列的查询,或者只测试第一列、前两列、前三列等的查询。如果在索引定义中以正确的顺序指定列,则单个复合索引可以加快对同 一张表的多种查询。

结构

此外,鉴于您的表有90 列,您还应该知道更宽的表会导致查询性能变慢。您可能需要考虑将表垂直分区为多个表:

列过多会使您的记录大小膨胀,进而导致更多的内存块被读入和读出内存,从而导致更高的 I/O。这可能会损害性能。解决此问题的一种方法是将您的表拆分为更小、更独立的表,其基数比原始表更小。这现在应该允许更好的阻塞因子(如上定义),这意味着更少的 I/O 和更快的性能。像这样拆分表的过程称为Vertical Partition

于 2012-10-14T17:04:10.550 回答
1

您查询的意思似乎是“查找所有类别为 '123456' 的产品”。那是对的吗?

COALESCE是一个在WHERE语句中使用的非常昂贵的函数,因为它对索引敌对的 NULL 值进行操作。您的解释结果表明您的查询对您的 product_categories 表不是很有选择性。在 MySQL 中,如果您想利用索引来加快查询速度,则需要完全避免 WHERE 语句中的函数。

其他人所说的关于 90 列表有害的事情也是正确的。但是你被它困住了,所以让我们处理它。

我们可以修改您的查询以摆脱基于函数的查询WHERE吗?让我们试试这个。

SELECT  /* some columns from the products table */
  FROM products
 WHERE product_id IN 
 (
     SELECT DISTINCT product_id 
                FROM product_categories
               WHERE product_id <> 0
                 AND (   cat1='123456'
                      OR cat2='123456'
                      OR cat3='123456'
                      OR cat4='123456')
 )

为此,您需要在四个 cat 列上创建单独的索引。复合唯一索引(“在所有列上组合”)对您没有帮助。它仍然可能不是那么好。

更好的解决方案可能是在布尔模式下进行全文搜索。您正在使用 MyISAM 访问方法,因此这是可能的。这绝对值得一试。它确实可能非常快。

SELECT  /* some columns from the products table */
  FROM products
 WHERE product_id IN 
 (
     SELECT product_id 
       FROM product_categories
      WHERE MATCH(cat1,cat2,cat3,cat4) 
            AGAINST('123456' IN BOOLEAN MODE)
        AND product_id <> 0

 )

为此,您需要像这样创建一个 FULLTEXT 索引。

 CREATE FULLTEXT INDEX cat_lookup 
                    ON product_categories (cat1, cat2, cat3, cat4)

请注意,这些建议的查询都不会产生与您的查询完全相同的结果COALESCE。您的COALESCE查询设置方式,某些组合将不匹配将匹配这些查询。例如。

    cat1     cat2     cat3     cat4   
  123451   123453   123455   123456      matches your and my queries
  123456   123455   123454   123452      matches my queries but not yours

但我的查询很可能会产生有用的产品列表,即使它在您的产品中有更多项目。

您可以通过使用 product_categories 上的内部查询来调试这些东西。

于 2012-10-14T19:39:22.677 回答
0

有什么奇怪的。该表是否product_categories确实有一product_id列?from和子句不应该where是这样的:

FROM `product_categories` pc 
LEFT OUTER JOIN `products` p ON p.category_id = pc.id
WHERE 
    COALESCE(product_categories.cat4, product_categories.cat3,product_categories.cat2, product_categories.cat1) = '123456'
    AND pc.id != 0
于 2012-10-14T17:08:17.797 回答