我不是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,产品表在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 | |
+----+-------------+--------------------+-------+---------------+------+---------+------+---------+----------+--------------------------+
答案 0 :(得分:3)
要建立基线,我建议首先在两个表上运行OPTIMIZE TABLE
命令。请注意,这可能需要一些时间。来自docs:
如果删除了大部分内容,则应使用
OPTIMIZE TABLE
表或者如果您对具有可变长度的表进行了许多更改 行(具有VARCHAR, VARBINARY, BLOB
或TEXT
列的表)。 已删除的行将保留在链接列表中,并随后INSERT
操作重用旧行位置。您可以使用OPTIMIZE TABLE
回收未使用的空间并对数据文件进行碎片整理。后 对表格进行了大量更改,此声明也可能会有所改进 使用该表的语句的性能,有时是显着的。[...]
对于MyISAM表,
OPTIMIZE TABLE
的工作原理如下:
如果表已删除或拆分行,请修复该表。
如果未对索引页面进行排序,请对它们进行排序。
- 醇>
如果表的统计信息不是最新的(并且无法通过对索引进行排序来完成修复),请更新它们。
如果不考虑空间和索引管理,您可以尝试在
上添加composite indexproduct_categories.cat4, product_categories.cat3, product_categories.cat2, product_categories.cat1
如果您在查询中使用这些列的最左侧子集经常,则会建议您这样做。查询计划表明它可以使用cat1
的{{1}}索引。这很可能只包含product_categories
列。通过将所有四个类别列添加到索引,它可以更有效地寻找所需的行。来自docs:
MySQL可以对测试所有内容的查询使用多列索引 索引中的列,或仅测试第一列的查询, 前两列,前三列,依此类推。如果你指定 索引定义中的列顺序正确,单个 复合索引可以在同一个上加速多种查询 表
此外,鉴于您的表格 90列,您还应该知道a wider table can lead to slower query performance。您可能希望将您的表Vertically Partitioning考虑到多个表中:
拥有太多列可能会使您的记录大小膨胀 导致更多的内存块被读入和读出内存 更高的I / O.这可能会影响性能。解决这个问题的一种方法是 将表拆分为更小的更独立的表 基数比原来的要大。现在应该可以做得更好 阻塞因子(如上所述)意味着更少的I / O和更快 性能即可。像这样拆开桌子的过程是一个 称为垂直分区。
答案 1 :(得分: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列上创建单独的索引。复合唯一索引(“在所有列上组合”)不会对您有所帮助。它仍然可能不太好。
更好的解决方案可能是FULLTEXT在BOOLEAN模式下搜索。您正在使用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上的内部查询来调试这些内容。
答案 2 :(得分: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