5

単一の注文に関する情報を収集するためのこのクエリがあり、それは非常に複雑になります。

テストするデータがないので、大小のデータセットでこれを経験したことがある人がいる場合、1回のクエリで実行できる結合の数に制限はありますか?大きなクエリを小さな部分に分割することをお勧めしますか、それとも大きな違いはありませんか?

WHEREまた、それぞれの後に条項を設けることは合法INNER JOINですか?

アドバイスありがとうございます。

クエリは次のとおりです。

# 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;
4

4 に答える 4

3

MySQLのJOINの制限に近いところはありません。参加数は悪くありません。ただし、派生テーブルにはインデックスがないため、実行中に派生テーブル(内部サブクエリ)に結合すると、パフォーマンスの問題が発生する可能性があります。インデックスのない派生テーブルでの結合の実行は遅くなる可能性があります。

結合するためのインデックスを使用して実際の一時テーブルを作成することを検討するか、サブクエリを回避する方法を考え出す必要があります。

MySQLのJOINは、基本的に、結合された各行のルックアップ(シーク)を実行するようなものです。したがって、多くのレコードを結合する場合、MySQLは多くのルックアップを実行する必要があります。問題になる可能性があるのは、結合する行の数よりも、結合するテーブルの数の方が重要です。

とにかく、MySQLは、あきらめてテーブル全体を読み取る前に、非常に多くのシークを実行するだけです。どちらがより安価になるかを決定するのにかなり良い仕事をします。

おそらくあなたができる最善のことは、ANALYZETABLEでインデックス統計を更新することによって推測を助けることです。

SELECTごとに1つのWHERE句を持つことができます。したがって、内部サブクエリにはWHERE句があり、外部クエリにはWHERE句があり、これらはJOINの後に適用されます(少なくとも論理的には、MySQLはパフォーマンスのために最初に適用します)。

また、これはすべて、インデックスを適切に使用する方法を知っていることを前提としています。

于 2012-04-14T15:29:28.387 に答える
2

私もこれを探していましたが、JOINの一部として「AND」を追加して、この別の解決策を見つけました。

INNER JOIN kopplaMedlemIntresse 
ON intressen.id = kopplaMedlemIntresse.intresse_id AND kopplaMedlemIntresse.medlem_id = 3
于 2012-06-26T23:15:34.337 に答える
2

さて、私はかつて自分で結合数の制限を確認しようとしましたが、正しく思い出せば、mysql5.0.4で100個のテーブルを使用して結合をテストしました。

次のエラーが発生しました。

テーブルが多すぎます。MySQLは結合で61個のテーブルしか使用できません。

MySql4の制限は31テーブルだったと思います。

クエリでこれだけ多くのテーブルを使用する場合は、クエリの設計を簡素化することを検討してください。

于 2012-04-14T15:13:40.673 に答える
1

一般的なアドバイスはありません。自分の経験だけです。

かつて、JOINを使用して大量のテーブルを追加するという非常に悪いパフォーマンスの問題がありました。私がやった約20のJOINでした。テストでいくつかのJOINSを削除すると、速度が再び上がりました。単一の情報が必要だったため、ほとんどのJOINSをサブ選択に置き換えることができました。これにより、クエリの25秒から1秒未満まで問題が解決しました。

これはおそらく、テーブルの設計、結合されたテーブルの列の数、および結合されたwhere句のインデックスの事実です。

于 2012-04-14T14:52:54.247 に答える