当前的实现是一个包含多个连接和临时表的复杂查询,但是对我的MySQL施加了太大的压力,并且加载表需要超过30秒。 PHP通过JavaScript Ajax调用检索数据并显示在网页上。以下是涉及的表格:
Table: table_companies
Columns: company_id, ...
Table: table_manufacture_line
Columns: line_id, line_name, ...
Table: table_product_stereo
Columns: product_id, line_id, company_id, assembly_datetime, serial_number, ...
Table: table_product_television
Columns: product_id, line_id, company_id, assembly_datetime, serial_number, warranty_expiry, ...
单个公司可以在两个产品表之间分配100k +项目。产品表由line_name联合和过滤,然后按assembly_datetime排序,并根据分页进行限制。日期时间值也依赖于时区,这将作为查询的一部分应用(另一个JOIN +临时表)。 line_name也是返回列之一。
我在考虑将line_name过滤器从产品联合查询中分离出来。本质上,我确定与过滤器对应的行的ID,然后使用WHERE条件WHERE line_id IN (<results from previous query>)
执行UNION查询。这将减少对连接和临时表的需求,我可以将line_name应用于PHP中的line_id和时区修改,但我不确定这是解决问题的最佳方法。
我也看过可能使用Redis,但是大量的单个产品在通过PHP(20-30秒)将所有数据推送到Redis时导致类似的长等待时间,即使它刚被拉直接来自产品表。
感谢您提供的任何意见。
修改
现有查询:
SELECT line_name,CONVERT_TZ(datetime,'UTC',timezone) datetime,... FROM (SELECT line_name,datetime,... FROM ((SELECT line_id,assembly_datetime datetime,... FROM table_product_stereos WHERE company_id=# ) UNION (SELECT line_id,assembly_datetime datetime,... FROM table_product_televisions WHERE company_id=# )) AS union_products INNER JOIN table_manufacture_line USING (line_id)) AS products INNER JOIN (SELECT timezone FROM table_companies WHERE company_id=# ) AS tz ORDER BY datetime DESC LIMIT 0,100
这里的格式是为了一些可读性。
SELECT line_name,CONVERT_TZ(datetime,'UTC',tz.timezone) datetime,...
FROM (SELECT line_name,datetime,...
FROM (SELECT line_id,assembly_datetime datetime,...
FROM table_product_stereos WHERE company_id=#
UNION
SELECT line_id,assembly_datetime datetime,...
FROM table_product_televisions
WHERE company_id=#
) AS union_products
INNER JOIN table_manufacture_line USING (line_id)
) AS products
INNER JOIN (SELECT timezone
FROM table_companies
WHERE company_id=#
) AS tz
ORDER BY datetime DESC LIMIT 0,100
ID已编入索引;主键是每列的第一个键。
答案 0 :(得分:2)
让我们从其组成部分构建此查询,以查看我们可以优化的内容。
观察:您从两个大型产品表的联合中获取100个最新行。
因此,让我们首先尝试优化从产品表中提取东西的子查询。这是其中之一。
SELECT line_id,assembly_datetime datetime,...
FROM table_product_stereos
WHERE company_id=#
但是看,你这里只需要100个最新的条目。所以,让我们添加
ORDER BY assembly_datetime DESC
LIMIT 100
此查询。此外,您应该在此表上放置一个复合索引,如下所示。这将允许索引满足WHERE和ORDER BY查找。
CREATE INDEX id_date ON table_product_stereos (company_id, assembly_datetime)
所有相同的注意事项都适用于table_product_televisions
的查询。按时间排序,将其限制为100,并将其编入索引。
如果您需要应用其他选择条件,可以将它们放在这些内部查询中。例如,在评论中,您提到了基于子字符串搜索的选择。您可以按照以下方式执行此操作
SELECT t.line_id,t.assembly_datetime datetime,...
FROM table_product_stereos AS t
JOIN table_manufacture_line AS m ON m.line_id = t.line_id
AND m.line_name LIKE '%test'
WHERE company_id=#
ORDER BY assembly_datetime DESC
LIMIT 100
接下来,您使用UNION
将这两个查询结果集合并为一个。 UNION
具有消除重复的功能,这非常耗时。 (你知道你没有重复,但MySQL没有。)请改用UNION ALL
。
将这一切放在一起,最里面的子查询就变成了这个。我们必须包装子查询,因为SQL在同一查询级别被UNION
和ORDER BY
子句混淆。
SELECT * FROM (
SELECT line_id,assembly_datetime datetime,...
FROM table_product_stereos
WHERE company_id=#
ORDER BY assembly_datetime DESC
LIMIT 100
) AS st
UNION ALL
SELECT * FROM (
SELECT line_id,assembly_datetime datetime,...
FROM table_product_televisions
WHERE company_id=#
ORDER BY assembly_datetime DESC
LIMIT 100
) AS tv
这会让你获得200行。它应该很快得到那些行。
确保200行足以在您执行外部ORDER BY ... LIMIT
操作后为您提供100个最新项目。但是这个操作只需要处理200行,而不是100K +,所以它会快得多。
最后在您的外部查询材料中包装此查询。加入table_manufacture_line
信息,然后修复时区。
如果您之前执行索引编制和ORDER BY ... LIMIT
操作,则此查询应该变得非常快。
您问题中的评论对话框向我表明您可能有多种产品类型,而不仅仅是两种产品类型,并且您的分页显示有复杂的选择条件。在大量行上使用UNION ALL
可以获得性能:它将多个索引表转换为无法有效搜索的内部行列表。
您真的应该考虑将两种产品数据放在一个表中,而不是必须使用UNION ALL
多个产品表。您现在拥有的设置不够灵活,不容易扩展。如果使用主产品表构建模式,并且可能使用某些属性表来构建特定于产品的信息,那么两年后您会发现自己更快乐。认真。请考虑进行更改。
答案 1 :(得分:1)
记住:索引速度快,数据速度慢。在嵌套查询上使用连接。嵌套查询返回所有数据字段,而连接只考虑过滤器(应该全部编入索引 - 确保在table_product _ *。line_id上有唯一索引)。已经有一段时间了,但我很确定你可以加入&#34; ON company_id =#&#34;这应该会在早期削减结果。
在这种情况下,所有结果都指向同一家公司(或更小的子集),因此单独运行该查询是有意义的(并且它使查询更易于维护)。
所以你的数据源是:
(table_product_stereos as prod
INNER JOIN table_manufacture_line AS ml ON prod.line_id = ml.line_id and prod.company_id=#
UNION
table_product_televisions as prod
INNER JOIN table_manufacture_line as ml on prod.line_id = ml.line_id and prod.company_id=#)
您可以从中选择产品。或ml。字段根据需要。
答案 2 :(得分:0)
PHP根本不是解决方案...... Redis可以成为一种解决方案。
但我要改变的主要是表的索引创建(添加缺少索引)...如果你正在运行临时表,你没有为表创建好索引。而且几乎没有100k行。
但是,如果没有任何表创建语句以及您运行的查询,我无法帮助您。
确保你的&#34;在哪里?&#34;是从左到右的youf btree索引的一部分。