【问题标题】:Extremely slow select query from 3 left joins来自 3 个左连接的极慢选择查询
【发布时间】:2013-02-06 22:41:45
【问题描述】:

我有一个非常慢的选择查询,我尝试了很多方法来加快它,但它仍然需要 分钟 来执行。我将解释这些表格以及我尝试过的内容。我想知道是否有更有效的方法来组织这个相当简单的查询。

大约有 18000 行。

查询解释

基本上头寸经纪人拥有的客户所有,因此要将头寸与经纪人联系起来,我们通过clients 表。我正在显示与相应经纪人的头寸,以及它们是否通过covered_positions 链接到另一个头寸,以及关联的头寸(如果有)。

表格说明

  • positions 包含客户完成的所有交易(股票/期权)。它有一个 PK positionsID (AI),但 MySQL 似乎没有将它识别为 可能的索引。在下面解释结果。
  • clients 包含所有客户信息(客户)。它有 PK clientsID (AI),这是 positions 中的外键。
  • login_users 包含经纪人(员工)。它的 PK user_id (AI) 是 clients 中的外键 broker_id
  • covered_positions 包含 2-5 个位置之间的链接(不是超链接)。它有 PK covered_id (AI)。 linked_id* 列是链接在一起的positionsIDs。

查询

delimiter $$

CREATE VIEW `main_join3` AS 
select `positions`.`id` AS `positionsID`,
`positions`.`order_list` AS `order_list`,
`positions`.`client_id` AS `client_id`,
`positions`.`client_name` AS `client_name`,
`positions`.`broker_name` AS `pos_broker_name`,
`positions`.`account_status` AS `pos_account_status`,
`positions`.`cost_basis` AS `cost_basis`,
`positions`.`cost_per_share` AS `cost_per_share`,
`positions`.`security` AS `security`,
`positions`.`security_type` AS `security_type`,
`positions`.`strike_price` AS `strike_price`,
`positions`.`option_type` AS `option_type`,
`positions`.`exp_month` AS `exp_month`,
`positions`.`exp_year` AS `exp_year`,
`positions`.`exp_date` AS `exp_date`,
`positions`.`buy_shares` AS `buy_shares`,
`positions`.`buy_date` AS `buy_date`,
`positions`.`buy_price` AS `buy_price`,
`positions`.`buy_commission` AS `buy_commission`,
`positions`.`buy_misc` AS `buy_misc`,
`positions`.`buy_cost` AS `buy_cost`,
`positions`.`sell_shares` AS `sell_shares`,
`positions`.`sell_date` AS `sell_date`,
`positions`.`sell_price` AS `sell_price`,
`positions`.`sell_commission` AS `sell_commission`,
`positions`.`sell_misc` AS `sell_misc`,
`positions`.`sell_cost` AS `sell_cost`,
`positions`.`loss` AS `loss`,
`positions`.`profit` AS `profit`,
`positions`.`funds_in_out` AS `funds_in_out`,
`positions`.`funds_in` AS `funds_in`,
`positions`.`funds_out` AS `funds_out`,
`positions`.`stop_order` AS `stop_order`,
`positions`.`notes` AS `notes`,
`positions`.`orange_highlight` AS `orange_highlight`,
`positions`.`date_created` AS `pos_date_created`,
`positions`.`date_updated` AS `pos_date_updated`,
`positions`.`created_by_id` AS `pos_cb_id`,
`positions`.`created_by_name` AS `pos_cb_name`,
`clients`.`id` AS `clientsID`,
`clients`.`broker_id` AS `broker_id`,
`clients`.`account_status` AS `clients_account_status`,
`login_users`.`user_id` AS `user_id`,
`login_users`.`user_level` AS `user_level`,
`login_users`.`username` AS `username`,
`login_users`.`name` AS `name`,
`covered_positions`.`id` AS `covered_id`,
`covered_positions`.`linked_id1` AS `linked_id1`,
`covered_positions`.`linked_id2` AS `linked_id2`,
`covered_positions`.`linked_id3` AS `linked_id3`,
`covered_positions`.`linked_id4` AS `linked_id4`,
`covered_positions`.`linked_id5` AS `linked_id5` 
from (((`positions` 
left join `clients` 
on((`positions`.`client_id` = `clients`.`id`))) 
left join `login_users` 
on((`clients`.`broker_id` = `login_users`.`user_id`))) 
left join `covered_positions` 
on(((`positions`.`id` = `covered_positions`.`linked_id1`) 
or (`positions`.`id` = `covered_positions`.`linked_id2`) 
or (`positions`.`id` = `covered_positions`.`linked_id3`) 
or (`positions`.`id` = `covered_positions`.`linked_id4`) 
or (`positions`.`id` = `covered_positions`.`linked_id5`))))$$

解释结果

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  positions   ALL NULL    NULL    NULL    NULL    18070   
1   SIMPLE  clients eq_ref  PRIMARY PRIMARY 4   blackri_posting.positions.client_id 1   
1   SIMPLE  login_users eq_ref  PRIMARY,user_id PRIMARY 4   blackri_posting.clients.broker_id   1   
1   SIMPLE  covered_positions   ALL NULL    NULL    NULL    NULL    214

我尝试过的

  • 从查询中删除所有未使用的列
  • 将查询创建为视图
  • 分析和优化数据库
  • 更换服务器

非常感谢任何支持。

【问题讨论】:

  • 你能描述一下你想用这个查询完成什么吗?即,在现实世界中,您想要的输出是什么?这将帮助我们解决问题,而不是破译查询。
  • 您是否尝试过为正在使用的列创建索引?查看之前的答案:stackoverflow.com/questions/2580859/…

标签: mysql sql optimization left-join


【解决方案1】:

我设法使用触发器解决了这个问题。我没有使用那些昂贵的 OR,而是简单地在 positions 中添加了一个名为 link_id 的列,其中来自 covered_positionsid 是通过以下方式添加的:

DELIMITER $$
CREATE TRIGGER covereds_after_insert
    AFTER INSERT ON covered_positions
    FOR EACH ROW
BEGIN
    UPDATE positions
    SET link_id = NEW.id 
    WHERE positions.id 
    IN (NEW.linked_id1, NEW.linked_id2, NEW.linked_id3, NEW.linked_id4, NEW.linked_id5);
END
$$

DELIMITER $$
CREATE TRIGGER covereds_after_delete
    AFTER DELETE ON covered_positions
    FOR EACH ROW
BEGIN
    UPDATE positions
    SET link_id = NULL
    WHERE positions.id 
    IN (OLD.linked_id1, OLD.linked_id2, OLD.linked_id3, OLD.linked_id4, OLD.linked_id5);
END
$$

这样查询不会将 18,000 行变成大约 400 万行。

【讨论】:

    【解决方案2】:

    虽然次要,但你的括号用得太多了...这里是从/join修改的

    from 
       positions
          left join clients
             ON positions.client_id = clients.id 
             left join login_users
                ON clients.broker_id = login_users.user_id
          left join covered_positions
             ON  positions.id = covered_positions.linked_id1
             or  positions.id = covered_positions.linked_id2  
             or  positions.id = covered_positions.linked_id3 
             or  positions.id = covered_positions.linked_id4
             or  positions.id = covered_positions.linked_id5
    

    其次,只是为了笑,尝试将 MySQL 关键字“STRAIGHT_JOIN”添加到您的查询中......

    SELECT STRAIGHT_JOIN(查询的其余部分)

    第三,您的表“Covered_positions” 您是否对每个linked_id 列都有索引。如果无法在连接上使用索引,这将使您的查询爬网。

    最后,如果您的覆盖头寸表有客户 ID,我绝对会将其添加为索引中的第一个字段,并在连接中包含客户 ID...这样,您至少会加入客户首先,然后是该客户中的职位。

    【讨论】:

    • DRapp 感谢您的回复。请注意:括号是由 MySQL Workbench 生成的,不在原始查询中。如何为每个linked_id 列添加索引?
    • 在covered_positions(client_id(如果可用)、linked_id1、linked_id2、linked_id3、linked_id4、linked_id5)上创建索引name_of_index。如果您确实有客户ID(如此处所示),请将您的查询调整为加入on position.clientid =covered_position.clientid AND (位置...或位置...或位置...)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-21
    • 1970-01-01
    • 1970-01-01
    • 2014-11-16
    • 2013-01-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多