MySQL Inner Join 有限制吗?
Posted
技术标签:
【中文标题】MySQL Inner Join 有限制吗?【英文标题】:MySQL Is there a limit to InnerJoin? 【发布时间】:2012-04-26 14:56:24 【问题描述】:我有这个查询来收集有关单个订单的信息,它变得非常复杂。
我没有任何数据可以测试,所以我想问,如果有人在小型和大型数据集中有这方面的经验,那么在单个查询中可以或应该进行多少个连接是否有限制?是否建议将大型查询拆分为较小的部分,或者这不会产生显着差异?
另外,在每个INNER JOIN
之后有一个WHERE
子句是否合法?
感谢您的建议。
这里是查询:
# Order: Get Order
function getOrder($order_id)
$sql = "SELECT (order.id, order.created, o_status.status,
/* payment info */
order.total, p_status.status,
/* ordered by */
cust_title.title, cust.forename, cust.surname,
customer.phone, customer.email,
cust.door_name, cust.street1,
cust.street2, cust.town,
cust.city, cust.postcode,
/* deliver to */
recip_title.title, recipient.forename, recipient.surname,
recipient.door_name, recipient.street1,
recipient.street2, recipient.town,
recipient.city, recipient.postcode,
/* deliver info */
shipping.name, order.memo,
/* meta data */
order.last_update)
FROM tbl_order AS order
INNER JOIN tbl_order_st AS o_status
ON order.order_status_id = o_status.id
INNER JOIN tbl_payment_st AS p_status
ON order.payment_status_id = p_status.id
INNER JOIN (SELECT (cust_title.title, cust.forename, cust.surname,
customer.phone, customer.email,
/* ordered by */ cust.door_name, cust.street1,
cust.street2, cust.town,
cust.city, cust.postcode)
FROM tbl_customer AS customer
INNER JOIN tbl_contact AS cust
ON customer.contact_id = cust.id
INNER JOIN tbl_contact_title AS cust_title
ON cust.contact_title_id = cust_title.id
WHERE order.customer_id = customer.id)
ON order.customer_id = customer.id
INNER JOIN (SELECT (recip_title.title, recipient.forename, recipient.surname,
/* deliver to */ recipient.door_name, recipient.street1,
recipient.street2, recipient.town,
recipient.city, recipient.postcode)
FROM tbl_contact AS recipient
INNER JOIN tbl_contact_title AS recip_title
ON recipient.contact_title_id = recip_title.id
WHERE order.contact_id = recipient.id)
ON order.contact_id = recipient.id
INNER JOIN tbl_shipping_opt AS shipping
ON order.shipping_option_id = shipping.id
WHERE order.id = '?';";
dbQuery($sql, array((int)$order_id));
$rows = dbRowsAffected();
if ($rows == 1)
return dbFetchAll();
else
return null;
由于有人为此查询请求了架构,所以这里是:
# TBL_CONTACT_TITLE
DROP TABLE IF EXISTS tbl_contact_title;
CREATE TABLE tbl_contact_title(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
title CHAR(3)
) ENGINE = InnoDB;
INSERT INTO tbl_contact_title
(title)
VALUES ('MR'),
('MRS'),
('MS');
# TBL_CONTACT
DROP TABLE IF EXISTS tbl_contact;
CREATE TABLE tbl_contact(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
contact_title_id INT,
FOREIGN KEY(contact_title_id) REFERENCES tbl_contact_title(id) ON DELETE SET NULL,
forename VARCHAR(50),
surname VARCHAR(50),
door_name VARCHAR(25),
street1 VARCHAR(40),
street2 VARCHAR(40),
town VARCHAR(40),
city VARCHAR(40),
postcode VARCHAR(10),
currency_id INT,
FOREIGN KEY(currency_id) REFERENCES tbl_currency(id) ON DELETE SET NULL
) ENGINE = InnoDB;
# TBL_CUSTOMER
DROP TABLE IF EXISTS tbl_customer;
CREATE TABLE tbl_customer(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
contact_id INT,
FOREIGN KEY(contact_id) REFERENCES tbl_contact(id) ON DELETE SET NULL,
birthday DATE,
is_male TINYINT,
phone VARCHAR(20),
email VARCHAR(50) NOT NULL
) ENGINE = InnoDB, AUTO_INCREMENT = 1000;
# TBL_ORDER_ST
DROP TABLE IF EXISTS tbl_order_st;
CREATE TABLE tbl_order_st(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
status VARCHAR(25)
) ENGINE = InnoDB;
INSERT INTO tbl_order_st
(status)
VALUES
('NEW'),
('PROCESSING'),
('SHIPPED'),
('COMPLETED'),
('CANCELLED');
# TBL_SHIPPING_OPT
DROP TABLE IF EXISTS tbl_shipping_opt;
CREATE TABLE tbl_shipping_opt(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
name VARCHAR(50),
description VARCHAR(255),
cost DECIMAL(6,3)
) ENGINE = InnoDB;
INSERT INTO tbl_shipping_opt
(name, description, cost)
VALUES
('UK Premier', 'U.K. Mainland upto 30KG, Next Working Day', 8.00),
('Europe Standard', 'Most European Destinations* upto 30KG, 2 to 5 Working Days *please check before purchase', 15.00);
# TBL_PAYMENT_ST
DROP TABLE IF EXISTS tbl_payment_st;
CREATE TABLE tbl_payment_st(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
status VARCHAR(25)
) ENGINE = InnoDB;
INSERT INTO tbl_payment_st
(status)
VALUES
('UNPAID'),
('PAID');
# TBL_ORDER
DROP TABLE IF EXISTS tbl_order;
CREATE TABLE tbl_order(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
customer_id INT,
FOREIGN KEY(customer_id) REFERENCES tbl_customer(id) ON DELETE SET NULL,
contact_id INT,
FOREIGN KEY(contact_id) REFERENCES tbl_contact(id) ON DELETE SET NULL,
created DATETIME,
last_update TIMESTAMP,
memo VARCHAR(255),
order_status_id INT,
FOREIGN KEY(order_status_id) REFERENCES tbl_order_st(id),
shipping_option_id INT,
FOREIGN KEY(shipping_option_id) REFERENCES tbl_shipping_opt(id),
coupon_id INT,
FOREIGN KEY(coupon_id) REFERENCES tbl_coupon(id) ON DELETE SET NULL,
total DECIMAL(9,3),
payment_status_id INT,
FOREIGN KEY(payment_status_id) REFERENCES tbl_payment_st(id)
) ENGINE = InnoDB, AUTO_INCREMENT = 1000;
【问题讨论】:
把它推到最大。只要您没有收到任何语法错误或“连接过多”之类的问题,请继续。 @hakre 谢谢...我接受它,查询的大小无关紧要。 【参考方案1】:我也在寻找这个,然后我找到了另一个解决方案,将“AND”添加为 JOIN 的一部分。
INNER JOIN kopplaMedlemIntresse
ON intressen.id = kopplaMedlemIntresse.intresse_id AND kopplaMedlemIntresse.medlem_id = 3
【讨论】:
【参考方案2】:您没有任何地方接近 mysql 的 JOIN 限制。你的加入数量还不错。但是,在您这样做时加入派生表(您的内部子查询)可能会导致性能问题,因为派生表没有索引。在没有索引的派生表上执行连接可能会很慢。
你应该考虑创建一个真正的带有索引的临时表来连接,或者想办法避免子查询。
MySQL 中的 JOIN 基本上就像对每个连接的行进行查找(查找)。因此,如果您要连接许多记录,MySQL 将不得不执行许多查找。与您加入的行数相比,您加入的表数量不是问题。
无论如何,MySQL 在放弃并读取整个表之前只会执行这么多次查找。它在决定哪个更便宜方面做得很好。
也许您能做的最好的事情是通过使用 ANALYZE TABLE 更新索引统计信息来帮助它猜测。
每个 SELECT 可以有一个 WHERE 子句。因此,您的内部子查询将有一个 WHERE 子句,而您的外部查询将有一个 WHERE 子句,并且在 JOIN 之后应用它们(至少在逻辑上,尽管 MySQL 通常会首先应用它们以提高性能)。
此外,所有这些都假设您知道如何正确使用索引。
【讨论】:
我很高兴接受这个答案。如果我遇到性能问题,我可能会创建另一个表(如您所建议的那样),但我会暂时保留它。我还没有索引知识...我将不得不阅读一些tuts。谢谢@marcus-adams【参考方案3】:我曾经亲自尝试过查看连接数的限制,如果我没记错的话,我在 mysql 5.0.4 上测试了一个包含 100 个表的连接。
我收到以下错误:
表太多。 MySQL 在一个连接中只能使用 61 个表。
我认为 MySql 4 的限制是 31 个表。
如果您打算在一个查询中使用这么多表,那么是时候开始考虑简化您的查询设计了。
【讨论】:
+1 获取有关限制的信息(谢谢)。我认为这些信息对我来说是正确的,我的 8 个可能加入了 61 个,一点也不差。【参考方案4】:我没有任何一般性的建议,只是我自己的经验。
我曾经遇到过性能非常差的问题,我使用 JOIN 添加了大量表。 我做了大约 20 JOIN。 当我在测试中删除了一些 JOINS 时,速度又提高了。 由于我只需要单个信息,因此我能够用子选择替换大部分 JOINS。这解决了我的问题,从查询的 25 秒缩短到不到 1 秒。
这可能与您的表设计、连接表的列数以及连接的 where 子句上的索引有关。
【讨论】:
所以你是说我可以进行大型查询,但 sub-SELECT 比 JOIN 语法快得多? 我认为这取决于查询本身。在我的情况下 sub 选择正确的索引设置更快的方式。 我认为你应该加入所有你需要的然后开始简化它(子选择仅用于第 1 列数据并合并相同数据) 我不同意。联合通常比相关子查询执行得更好。 我在correlated subqueries 上引用了 MySQL 文档:“......它们效率低下并且可能很慢。将查询重写为连接可能会提高性能。”文档确实同意您的观点,在某些情况下,它们可以进行优化。试一试不会有伤害的,对吧? :)以上是关于MySQL Inner Join 有限制吗?的主要内容,如果未能解决你的问题,请参考以下文章
限制 GROUP_CONCAT() 或 INNER JOIN 的结果
MySQL 双表 INNER JOIN , LEFT JOINED 到第三张表,只有一行具有最低值