2012-04-14 79 views
5

我有這個查詢收集關於單個訂單的信息,並且它變得相當複雜。MySQL對InnerJoin有限制嗎?

我沒有任何數據可以測試,因此我問,如果有人在小型和大型數據集中有這方面的經驗,是否有限制您可以或應該在單個查詢中進行多少次連接?將大型查詢拆分成更小的部分是明智的,還是這不會產生重大影響?

另外,在每個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; 
+2

將其推至最大。只要你沒有得到任何語法錯誤或諸如「太多連接」之類的東西,那麼繼續。 – hakre 2012-04-14 14:52:44

+0

@hakre謝謝......我接下來,查詢的大小並不重要。 – Ozzy 2012-04-14 15:05:42

回答

3

您沒有任何接近MySQL JOIN的限制的地方。你的加入數量不錯。但是,在進行派生表(內部子查詢)時,可能會導致性能問題,因爲派生表沒有索引。在沒有索引的派生表上執行連接可能會很慢。

你應該考慮製作一個真正的帶索引的臨時表來加入,或者找出一種避免子查詢的方法。

在MySQL中的JOIN基本上就像爲每個連接的行查找(seek)。因此,如果您要加入許多記錄,MySQL將不得不執行很多查找。與加入的行數相比,您加入多少個表的次數更少。

無論如何,MySQL只會執行如此多的查找操作,纔會放棄查看整個表。它在確定哪個更便宜的方面做得非常好。

也許你可以做的最好的事情是通過使用ANALYZE TABLE更新索引統計數據來幫助猜測。

每個SELECT可以有一個WHERE子句。所以你的內部子查詢將有一個WHERE子句,你的外部查詢將有一個WHERE子句,並且這些在JOIN之後得到應用(至少在邏輯上,儘管MySQL通常會首先將它們應用於性能)。

此外,所有這一切都假設你知道如何正確使用索引。

+0

我很高興接受這個答案。如果我有性能問題,我可能會創建另一個表(如您所建議的那樣),但我會暫時保留它。我還沒有編制索引的知識......我將不得不閱讀一些內容。謝謝@ marcus-adams – Ozzy 2012-04-14 18:09:38

1

我沒有任何一般性的建議,只是我自己的expirience。

我曾經有一個非常糟糕的表現,我用JOIN添加了tonz表。 約20加入我做到了。 當我在測試中刪除了幾個JOINS時,速度再次上升。 由於我只需要單個信息,因此我可以用子選擇替換大部分JOINS。這解決了我的問題從25秒,我的查詢下降到1秒以內。

這可能是您的表設計的事實,您連接表的列數和連接where子句的索引。

+0

所以你說我可以有很大的查詢,但是子查詢的速度比JOIN語法快嗎? – Ozzy 2012-04-14 14:56:48

+1

我認爲這取決於查詢本身。在我的情況下,sub以更快的方式選擇正確的索引設置。 – YvesR 2012-04-14 15:19:15

+0

我認爲你應該加入所有你需要的,然後開始簡化它(子選擇第1列數據,併合並相同的數據) – YvesR 2012-04-14 15:19:47

2

嗯,我曾經自己試過看到連接數量的限制,並且如果我正確記得它,我在mysql 5.0.4上測試了一個包含100個表的連接。

我得到了以下錯誤:

Too many tables. MySQL can only use 61 tables in a join.

我認爲MySQL的4限爲31桌。

如果您打算在查詢中使用多個表格,建議您簡化查詢設計。

+0

+1的限制信息(謝謝)。我認爲這個信息爲我提供了這個角度,我的8個加入61可能不會是壞的。 – Ozzy 2012-04-14 18:02:27

2

我也在尋找這個,然後我找到了這個其他的解決方案,加入了「AND」作爲JOIN的一部分。

INNER JOIN kopplaMedlemIntresse 
ON intressen.id = kopplaMedlemIntresse.intresse_id AND kopplaMedlemIntresse.medlem_id = 3