【问题标题】:How would I make this query run faster?如何使此查询运行得更快?
【发布时间】:2011-09-25 19:57:02
【问题描述】:

如何让这个查询运行得更快...?

选择帐户 ID, 帐户名称, 帐户更新, account_sold, account_mds, ftp_url, ftp_livestatus, number_digits, number_cw, 客户名称, ppc_状态, 用户名 从 账户, FTP详细信息, 站点编号, 客户, 每次点击付费, 用户 WHERE Accounts.account_id = FTPDetails.ftp_accountid AND Accounts.account_id = SiteNumbers.number_accountid AND Accounts.account_client = Clients.client_id AND Accounts.account_id = PPC.ppc_accountid AND Accounts.account_designer = Users.user_id AND Accounts.account_active = '活跃' AND FTPDetails.ftp_active = '活动' AND SiteNumbers.number_active = '活跃' AND Clients.client_active = '活跃' 和 PPC.ppc_active = '活跃' AND Users.user_active = '活跃' 订购方式 Accounts.account_update DESC

提前致谢:)

EXPLAIN 查询结果:

我实际上并没有设置任何外键...我试图避免对数据库进行更改,因为很快就必须进行全面检修。

只有主键是每个表的 id,例如account_id, ftp_id, ppc_id ...

【问题讨论】:

  • 你的表有什么索引?
  • 发布您的索引和 EXPLAIN 输出供人们评估。否则他们只会猜测。
  • ...以及 EXPLAIN 计划,以及每个表的行数和索引的基数
  • 如果您有理由不添加外键,则不必添加。您可以将简单的 KEY(不是 FOREIGN KEY)添加到 JOIN 中使用的字段中,并且很可能会提高性能。
  • @Pickup,请不要使用隐式 where 连接语法,将其埋在它所属的 1989 年并使用显式连接。

标签: mysql sql performance


【解决方案1】:

使用EXPLAIN 找出可以使用的索引以及实际使用的索引。如有必要,创建适当的索引。

如果FTPDetails.ftp_active 只有两个有效条目'active''inactive',则使用BOOL 作为数据类型。

附带说明:我强烈建议使用显式连接而不是隐式连接:

SELECT
  account_id, account_name, account_update, account_sold, account_mds, 
  ftp_url, ftp_livestatus, 
  number_digits, number_cw,
  client_name, 
  ppc_status, 
  user_name 
FROM Accounts 
INNER JOIN FTPDetails
  ON  Accounts.account_id = FTPDetails.ftp_accountid
  AND FTPDetails.ftp_active = 'active'
INNER JOIN SiteNumbers
  ON  Accounts.account_id = SiteNumbers.number_accountid 
  AND SiteNumbers.number_active = 'active'
INNER JOIN Clients
  ON  Accounts.account_client = Clients.client_id
  AND Clients.client_active = 'active'
INNER JOIN PPC
  ON  Accounts.account_id = PPC.ppc_accountid
  AND PPC.ppc_active = 'active'
INNER JOIN Users
  ON  Accounts.account_designer = Users.user_id
  AND Users.user_active = 'active'
WHERE Accounts.account_active = 'active' 
ORDER BY Accounts.account_update DESC

这使得查询更具可读性,因为连接条件接近正在连接的表的名称。

【讨论】:

  • 谢谢,这快了 2 秒 :) 我已经运行了解释,但不确定信息实际上告诉了我什么?或者如何使用它...
  • @JPickup:在这里发布解释计划,添加到问题中。
【解决方案2】:

解释,对不同的选项进行基准测试。对于初学者,我确信有几个查询会比这个怪物更快。首先,因为查询优化器将花费大量时间检查最佳连接顺序(5!=120 种可能性)。其次,像SELECT ... WHERE ....active = 'active' 这样的查询将被缓存(尽管它取决于数据更改的数量)。

【讨论】:

  • 到目前为止,我一直在使用多个语句,但我被要求添加功能以供用户按任何值过滤结果...并且不知道该怎么做没有在一个选择语句中选择所有数据?
  • 好的优化器不会检查所有可能性——对于这个查询来说,这将远远超过 120 个(这不仅是执行 JOIN 的顺序,而且是每个 JOIN 使用的方法以及使用的索引以及检查条件的顺序)。
  • @ypercube 好的优化器是configurable
  • 可配置与否,检查所有可能性的选项对于复杂查询来说是一个非常糟糕的选择。
【解决方案3】:

索引

  • 您需要 - 至少 - 在 JOIN 条件中使用的每个字段都有一个索引。

  • 出现在WHEREGROUP BYORDER BY 子句中的字段索引在大多数情况下也很有用。

  • 当在一个表中,两个或多个字段用于 JOIn(或 WHERE 或 GROUP BY 或 ORDER BY)时,这些(两个或多个)字段的复合(组合)索引可能比单独的索引更好.例如在SiteNumbers 表中,可能的索引是复合(number_accountid, number_active)(number_active, number_accountid)

  • 布尔字段(ON/OFF,活动/非活动)中的条件有时会减慢查询速度(因为索引没有选择性,因此不是很有帮助)。在这种情况下,重组(父亲规范化)表是一种选择,但您可能可以避免增加的复杂性。


除了通常的建议(检查 EXPLAIN 计划,在需要的地方添加索引,测试查询的变体),

我注意到在您的查询中存在部分笛卡尔积。表Accounts 与三个表FTPDetailsSiteNumbersPPC 具有一对多关系。例如,如果您有 1000 个帐户,并且每个帐户都与 10 个 FTPDetails、20 个 SiteNumbers 和 3 个 PPC 相关,则该查询将为每个帐户返回 600 行(10x20x3 的乘积)。总共有 600K 行,其中有许多数据重复。

您可以改为将查询拆分为三加一的基础数据(帐户和其余表)。这样,只会传输 34K 行数据(长度更短):

Accounts JOIN Clients JOIN Users 
  (with all fields needed from these tables)
  1K rows

Accounts JOIN FTPDetails
  (with Accounts.account_id and all fields from FTPDetails)
  10K rows

Accounts JOIN SiteNumbers
  (with Accounts.account_id and all fields from SiteNumbers)
  20K rows

Accounts JOIN PPC
  (with Accounts.account_id and all fields from PPC)
  3K rows

然后在客户端使用来自 4 个查询的数据来显示组合信息。



我会添加以下索引:

Table Accounts
  index on (account_designer)
  index on (account_client)
  index on (account_active, account_id)
  index on (account_update)

Table FTPDetails
  index on (ftp_active, ftp_accountid)

Table SiteNumbers
  index on (number_active, number_accountid)

Table PPC
  index on (ppc_active, ppc_accountid)

【讨论】:

  • 为什么不Table Client index on (client_id, client_active)Table Users index on (user_id, user_active)
  • @sunrong 我不记得为什么我没有为ClientsUsers 添加任何索引建议。重读我的答案和问题,我认为我对(客户和用户)的处理方式有所不同,因为他们似乎与帐户具有一对一的关系,而不是一对多的关系。索引也应该有所帮助。
【解决方案4】:

您的主要问题之一在这里:x.y_active = 'active'

问题:基数低
活动字段是具有 2 个可能值的布尔字段,因此它的基数非常低。 MySQL(或者当 30% 或更多的行具有相同值时,任何 SQL 都不会使用索引)。
强制索引是没有用的,因为它会使你的查询变慢,而不是更快。

解决方案:对表进行分区
一种解决方案是在 active 列上对表进行分区。
这将排除所有非活动字段,并使select 表现得好像您实际上在xxx-active 字段上有一个工作索引。

旁注
请永远不要使用隐式的 where 连接,它太容易出错并且无法使用。
请改用Oswald's answer 之类的语法。

链接:
基数:http://en.wikipedia.org/wiki/Cardinality_(SQL_statements)
基数和索引:http://www.bennadel.com/blog/1424-Exploring-The-Cardinality-And-Selectivity-Of-SQL-Conditions.htm
MySQL 分区:http://dev.mysql.com/doc/refman/5.5/en/partitioning.html

【讨论】:

  • 有没有关于数据库设计和sql语句的书籍/阅读材料推荐?
猜你喜欢
  • 2014-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-27
  • 1970-01-01
  • 2016-02-01
  • 2021-11-11
  • 1970-01-01
相关资源
最近更新 更多