当前的实现是一个具有多个连接和临时表的复杂查询,但对我的 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, ...
单个公司可以在两个产品表之间拆分超过 10 万个项目。产品表按 line_name 合并和过滤,然后按 assembly_datetime 排序并根据分页进行限制。日期时间值也依赖于时区,这作为查询的一部分应用(另一个 JOIN + 临时表)。 line_name 也是返回的列之一。
我正在考虑将 line_name 过滤器从产品联合查询中分离出来。本质上,我会确定与过滤器对应的行的 ID,然后使用 WHERE 条件执行 UNION 查询 WHERE line_id IN (<results from previous query>)
.这将消除对连接和临时表的需要,我可以在 PHP 中将 line_name 应用于 line_id 和时区修改,但我不确定这是处理事情的最佳方法。
我也考虑过使用 Redis 的可能性,但是当通过 PHP 将所有数据推送到 Redis 时(20-30 秒),大量的单个产品会导致类似的等待时间,即使它只是被拉取直接来自产品表。
- 是否可以调整现有查询以提高效率?
- 我可以将一些处理推送给 PHP 以减少 SQL 服务器上的负载吗? 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 已编入索引;主键是每列的第一个键。
最佳答案
让我们从它的组成部分构建这个查询,看看我们可以优化什么。
观察:您正在从两个大型产品表的联合中获取最近的 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+,所以它会快得多。
最后将此查询包含在您的外部查询 Material 中。加入table_manufacture_line
信息,并修正时区。
如果您更早地进行索引和 ORDER BY ... LIMIT
操作,这个查询应该会变得非常快。
您问题中的评论对话框向我表明您可能有多种产品类型,而不仅仅是两种,并且您的分页显示有复杂的选择标准。对大量行使用 UNION ALL
会严重影响性能:它将多个索引表转换为一个内部行列表,根本无法有效地进行搜索。
您真的应该考虑将两种产品数据放在一个表中,而不必 UNION ALL
多个产品表。您现在拥有的设置不灵活,不会轻易扩展。如果您使用主产品表和一些产品特定信息的属性表来构建您的模式,那么两年后您会发现自己会更快乐。严重地。请考虑进行更改。
关于php - 在不严重影响数据库的情况下在分页表中显示大量数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26169567/