MySQL选择太大(加入三个表)

时间:2014-04-23 17:52:52

标签: mysql sql

我有一个查询,我正在选择一个大型数据集,由于连接表的一对多关系,它需要花费大量时间。我有三张桌子 - 简化如下:

客户

| customers | CREATE TABLE `customers` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `login` varchar(128) NOT NULL DEFAULT '',
  `username` varchar(128) NOT NULL DEFAULT '',
  `usertype` char(1) NOT NULL DEFAULT '',
  `password` varchar(255) NOT NULL DEFAULT '',
  `membershipid` int(11) NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`),
  KEY `membershipid` (`membershipid`)
) ENGINE=MyISAM AUTO_INCREMENT=980 DEFAULT CHARSET=latin1 |

成员

| memberships | CREATE TABLE `memberships` (
  `membershipid` int(11) NOT NULL AUTO_INCREMENT,
  `membership` varchar(255) NOT NULL DEFAULT '',
  `flag` char(2) NOT NULL DEFAULT '',
  PRIMARY KEY (`membershipid`),
) ENGINE=MyISAM AUTO_INCREMENT=316 DEFAULT CHARSET=latin1 |

membership_contracts

| membership_contracts | CREATE TABLE `membership_contracts` (
   `bond_id` int(11) NOT NULL AUTO_INCREMENT,
   `membership_id` int(11) DEFAULT NULL,
   `contract_id` int(11) NOT NULL,
  PRIMARY KEY (`bond_id`),
  UNIQUE KEY `membership_id` (`membership_id`,`contract_id`)
) ENGINE=MyISAM AUTO_INCREMENT=1623 DEFAULT CHARSET=utf8 |

contract_items

| contract_items | CREATE TABLE `contract_items` (
  `contract_id` int(11) DEFAULT NULL,
  `product_id` int(11) DEFAULT NULL,
  `price` decimal(12,2) DEFAULT NULL,
  `start` int(11) DEFAULT NULL,
  `end` int(11) DEFAULT NULL,
  `contract_item_id` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`contract_item_id`)
) ENGINE=MyISAM AUTO_INCREMENT=27398 DEFAULT CHARSET=utf8 |

会员资格与合同,合同和合同项目之间存在一对多的关系。

我当前的查询是:

SELECT customers.username, contract_items.contract_id, so_on_and_so_forth
FROM   customers, memberships, membership_contracts, contract_items
WHERE  customers.membershipid=memberships.membershipid
AND    memberships.`membershipid=membership_contracts.membership_id
AND    membership_contracts.contract_id=contract_items.contract_id
AND    contract_items.item_id=user.item_id

所以我有一个小的数据集(用户和项目),增加了600,000行(每个成员有大约25个合同,每个合同可以有超过1000个项目),只选择用户对其成员的合同上的项目

有更快的方法吗?我是否使用了不应该加入的联盟?我有一个关于ids的membership_contracts的唯一索引,所以至少这是最小的。                     |

| id | select_type | table                | type   | possible_keys            | key           | key_len | ref                                                                                             | rows  | Extra                           |
+----+-------------+----------------------+--------+--------------------------+---------------+---------+-------------------------------------------------------------------------------------------------+-------+---------------------------------+
 |  1 | SIMPLE      | contract_items       | ALL    | NULL                     | NULL          | NULL    | NULL                                                                                            | 24691 | Using temporary; Using filesort |
 |  1 | SIMPLE      | order_details        | ref    | orderid,productid        | productid     | 4       | database123456789.contract_items.product_id                                                   |     2 |                                 |
 |  1 | SIMPLE      | orders               | eq_ref | PRIMARY,odsp,ospd,userid | PRIMARY       | 4       | database123456789.order_details.orderid                                                       |     1 |                                 |
 |  1 | SIMPLE      | customers            | eq_ref | PRIMARY,membershipid     | PRIMARY       | 4       | database123456789.orders.userid                                                               |     1 |                                 |
 |  1 | SIMPLE      | membership_contracts | ref    | membership_id            | membership_id | 9       | database123456789.customers.membershipid,database123456789.contract_items.contract_id       |    12 | Using where; Using index        |
 |  1 | SIMPLE      | memberships          | eq_ref | PRIMARY                  | PRIMARY       | 4       | database123456789.membership_contracts.membership_id                                          |     1 | Using where                     |

2 个答案:

答案 0 :(得分:1)

“contract_items”表需要contract_id字段的索引。

答案 1 :(得分:1)

SELECT customers.username, contract_items.contract_id, so_on_and_so_forth
FROM   customers
join memberships on customers.membershipid=memberships.membershipid
join membership_contracts on  memberships.membershipid=membership_contracts.membership_id
join  contract_items on  membership_contracts.contract_id=contract_items.contract_id
WHERE      contract_items.item_id=user.item_id

当您将查询放入连接时,您可以立即看到where子句没有意义,因为连接中没有用户表。您的查询根本不应运行。

导致您的真实查询出现问题的原因(您显然没有给我们解释,因为您的解释计划显然不适用于您提供给我们的查询以及您提供给我们的内容根本不应该运行))很可能一个或多个缺失的索引(所有FK应该被索引)或意外的交叉连接(这是隐式连接是反模式的一个原因,使用它们是非常糟糕的做法)。