2012-04-14 2 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

+0

@hakre 고마워 ... 그때, 쿼리의 크기는 중요하지 않습니다. – Ozzy

답변

3

MySQL의 JOINs 근처에는 아무 것도 없습니다. 조인 횟수가 나쁘지 않습니다. 그러나 파생 테이블에 인덱스가 없으므로 수행중인 파생 테이블 (내부 하위 쿼리)에 조인하면 성능 문제가 발생할 수 있습니다. 인덱스가없는 파생 테이블에 대해 조인을 수행하면 속도가 느려질 수 있습니다.

가입을위한 인덱스가있는 실제 임시 테이블을 만들거나 하위 쿼리를 피하는 방법을 찾아야합니다.

MySQL의 JOIN은 기본적으로 조인 된 각 행에 대한 검색 (탐색)과 같습니다. 따라서 많은 레코드를 합치면 MySQL은 많은 조회를 수행해야합니다. 참여하는 행 수가 문제가 될 수있는 행 수보다 적습니다.

어쨌든, MySQL은 많은 테이블을 포기하고 전체 테이블을 읽기 전에 만 수행합니다. 그것은 어느 것이 더 비싸지 않을지를 결정할 때 꽤 좋은 일을합니다.

아마도 가장 좋은 방법은 ANALYZE TABLE을 사용하여 인덱스 통계를 업데이트하는 것입니다.

SELECT 당 하나의 WHERE 절이있을 수 있습니다. 내부의 하위 쿼리는 WHERE 절을 가지며 외부 쿼리는 WHERE 절을 가지며 JOIN 이후에 적용됩니다 (적어도 논리적으로는 일반적으로 MySQL이 일반적으로 먼저 성능에 적용하지만).

또한이 모든 것은 인덱스를 올바르게 사용하는 방법을 알고 있다고 가정합니다.

+0

이 대답을 기꺼이 받아들입니다. 나는 성능 문제가 있다면 (당신이 제안한 것처럼) 다른 테이블을 만들지 만, 지금은 그대로 두겠습니다. 아직 색인 생성에 대한 지식이 없습니다 ... 몇 가지 독창적 인 내용을 읽어야합니다. @ marcus-adams에게 감사합니다. – Ozzy

1

나는 어떤 일반적인 조언, 그냥 내 자신의 expirience이없는 여기

는 쿼리입니다.

JOIN을 사용하여 테이블을 추가 할 때 성능이 매우 좋았던 문제가있었습니다. 약 20 명이 참여했습니다. 테스트에서 몇 개의 JOINS를 제거하면 속도가 다시 올라갔습니다. 단일 정보가 필요했기 때문에 대부분의 JOINS를 하위 선택으로 바꿀 수있었습니다. 내 질문에 대한 25 초에서 1 초 미만으로 내 문제가 해결되었습니다.

아마도 테이블 디자인, 조인 된 테이블의 열 수 및 조인 된 where 절의 인덱스가 사실 일 것입니다.

+0

그래서 큰 쿼리를 가질 수 있지만 하위 SELECT가 JOIN 구문보다 빠르다는 것을 말하는 것입니까? – Ozzy

+1

나는 쿼리 자체에 의존한다고 생각한다. 내 경우 엔 서브 인덱스가 올바른 인덱스 설정으로 더 빨리 선택됩니다. – YvesR

+0

나는 당신이 필요로하는 모든 것을 시작한 다음 그것을 단순화하기 시작해야한다고 생각한다. (서브 1 열만 데이터를 선택하고 동일한 데이터를 병합한다.) – YvesR

2

글쎄 한 번 시도해 보았습니다. 조인 횟수 제한을 보았습니다. 올바르게 호출하면 MySQL 5.0.4에서 100 개의 테이블로 조인을 테스트했습니다.

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

내가 MySQL을 4에 대한 제한은 31 개 테이블 생각 :

나는 다음과 같은 오류가 주어졌다.

쿼리에서 많은 테이블을 사용할 계획이라면 쿼리 디자인을 단순화하는 것에 대해 생각해보십시오.

+0

+1 (감사)에 대한 정보. 나는이 정보가 나를이 관점에 넣었다고 생각합니다. 가능한 한 8 개의 조인이 전혀 나쁜 것이 아닙니다. – Ozzy

2

나는 이것도 찾고 있었는데,이 다른 해결책을 발견하고, "AND"를 JOIN의 일부로 추가했습니다.

INNER JOIN kopplaMedlemIntresse 
ON intressen.id = kopplaMedlemIntresse.intresse_id AND kopplaMedlemIntresse.medlem_id = 3 
관련 문제