5

これはあなたのためのきちんとしたものです(明らかにMySQLです):

# セットアップ
DROP DATABASE IF EXISTS index_test_gutza;
CREATE DATABASE index_test_gutza;
index_test_gutza を使用します。

CREATE TABLE customer_order (
    ID MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT、
    請求書 MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
    主キー(ID)
);
INSERT INTO customer_order
    (ID、請求書)
    値
    (1, 1),
    (2, 2),
    (3, 3),
    (4, 4),
    (5,5);

CREATE TABLE customer_invoice (
    ID MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT、
    Invoice_no MEDIUMINT UNSIGNED DEFAULT NULL,
    Invoice_pdf LONGBLOB、
    主キー(ID)
);
INSERT INTO customer_invoice
    (id、invoice_no)
    値
    (1, 1),
    (2, 2),
    (3, 3),
    (4, 4),
    (5,5);

# よし、これがビーフだ
説明
    co.id を選択
    FROM customer_order AS co;

説明
    co.id を選択
    FROM customer_order AS co
    ORDER BY co.id;

説明
    co.id、ci.invoice_no を選択
    FROM customer_order AS co
    左 JOIN customer_invoice AS ci ON ci.id=co.invoice;

説明
    co.id、ci.invoice_no を選択
    FROM customer_order AS co
    ci ON ci.id=co.invoice AS LEFT JOIN customer_invoice
    ORDER BY co.id;

下部に 4 つの EXPLAIN ステートメントがあります。最初の 2 つは、まさに期待どおりの結果になります。

+----+-------------+-------+-------+-------------- ----------+---------+------+------+-------------+
| | ID | select_type | テーブル | タイプ | 可能な_キー | キー | key_len | 参照 | 行 | 行 エクストラ |
+----+-------------+-------+-------+-------------- ----------+---------+------+------+-------------+
| | 1 | シンプル | コ | インデックス | ヌル | プライマリ | 3 | ヌル | 5 | インデックスの使用 |
+----+-------------+-------+-------+-------------- ----------+---------+------+------+-------------+

3 つ目はすでに興味深いものです。customer_order の主キーが使用されなくなったことに注目してください。

+----+-------------+-------+--------+------------- --+---------+---------+--------------------------- --+-----+-------------+
| | ID | select_type | テーブル | タイプ | 可能な_キー | キー | key_len | 参照 | 行 | 行 エクストラ |
+----+-------------+-------+--------+------------- --+---------+---------+--------------------------- --+-----+-------------+
| | 1 | シンプル | コ | すべて | ヌル | ヌル | ヌル | ヌル | 5 | | |
| | 1 | シンプル | ci | eq_ref | プライマリ | プライマリ | 3 | index_test_gutza.co.invoice | 1 | インデックスの使用 |
+----+-------------+-------+--------+------------- --+---------+---------+--------------------------- --+-----+-------------+

ただし、4 つ目はzinger です。主キーに ORDER BY を追加するだけで、customer_order でファイルソートが行われます (上記で既に当惑していることを考えると、これは予想されることです)。

+----+-------------+-------+--------+------------- --+---------+---------+--------------------------- --+--------------------+----------------+
| | ID | select_type | テーブル | タイプ | 可能な_キー | キー | key_len | 参照 | 行 | 行 エクストラ |
+----+-------------+-------+--------+------------- --+---------+---------+--------------------------- --+--------------------+----------------+
| | 1 | シンプル | コ | すべて | ヌル | ヌル | ヌル | ヌル | 5 | ファイルソートの使用 |
| | 1 | シンプル | ci | eq_ref | プライマリ | プライマリ | 3 | index_test_gutza.co.invoice | 1 | インデックスの使用 |
+----+-------------+-------+--------+------------- --+---------+---------+--------------------------- --+--------------------+----------------+

ファイルソート!また、注文用の customer_order テーブルの主キーと、JOIN 用の customer_invoice テーブルの主キー以外は何も使用していません。では、すべてが正しいという名目で、なぜ突然ファイルソートに切り替わるのでしょうか?! さらに重要なことに、これを回避するにはどうすればよいですか? 記録として、これが避けられない理由を説明する文書化された回答を喜んで受け入れます(その場合)。

ご想像のとおり、これは実際に本番環境で発生しています。テーブルは決して巨大ではありませんが (数百のレコードしかありません)、invoice テーブル (PDF ファイルを含む) のファイルソートを実行すると、サーバーが強制終了されます。上記と同様のクエリ (どの注文が請求書を発行され、どの注文が発行されていないかを知るために必要です)。

あなたが尋ねる前に、私はデータベースを設計しました.PDFファイルをそのテーブルに安全に保存できると思いました。なぜなら、検索クエリが必要になることは決してないからです.私は常に主キーを手元に持っています.

更新(コメント概要)

以下のコメントで提案された内容の概要を以下に示します。そのすべてを読む必要はありません。

  • *customer_order.invoice* にキーを追加する必要があります-実際に本番環境で試してみましたが、違いはありません (そうすべきではないため)。
  • 使用する必要がありますUSE INDEX-- 試してみましたが、うまくいきませんでした。私も試しましたFORCE INDEX-結果もありません(まったく変更はありません)
  • ユース ケースを単純化しすぎました。実際の本番クエリが必要です。最初の反復で少し削除しすぎた可能性があるため、更新しました (最後の 2 つのクエリに を追加, ci.invoice_noしただけです)。SELECT記録として、誰かが本当に興味を持っている場合は、生産クエリをそのまま示します (これにより、注文の最後のページが取得されます)。
選択する
    コーダー.id、
    corder.public_id、
    CONCAT(buyer.fname," ",buyer.lname) AS バイヤー名,
    コーダーステータス、
    コーダー支払い、
    corder.reserved AS R,
    corder.tracking_id!="" A として、
    corder.payment_received を pay_date として、
    Invoice.invoice_no AS inv,
    Invoice.receipt_no AS rec,
    請求書.public AS pub_inv,
    proforma.proforma_no AS 教授、
    proforma.public AS pub_pf,
    corder.rating、
    corder.rating_comments!="" AS got_comment
から
    コーダー
buy.id=corder.buyer のバイヤーとして LEFT JOIN ユーザー
請求書を請求書として LEFT JOIN invoice.id=corder.invoice
LEFT JOIN 請求書をプロフォーマとして ON proforma.id=corder.proforma
オーダーバイ
    ID DESC
リミット 400、20;

上記のクエリ (これも、私が実稼働環境で実行したものとまったく同じです) の実行には約 14 秒かかります。上記のユース ケースで示したように、本番環境で実行される簡略化されたクエリを次に示します。

選択する
    コーダー.id、
    請求書.invoice_no
から
    コーダー
LEFT JOIN 請求書の請求書.id=corder.invoice
オーダーバイ
    corder.id DESC
リミット 400、20;

この実行には 13 秒かかります。結果の最後のページについて話している限り、LIMIT は何の違いもありません。つまり、ファイルソートが関係している場合、最後の 12 件の結果を取得する場合と 412 件すべての結果を取得する場合とでは、まったく大きな違いはありません。

結論

ypercube の回答は正しいだけでなく、残念ながら唯一の正当なもののようです。SELECT * FROM corderコーダー自体にLONGBLOBが含まれている場合(サブクエリでメインクエリからフィールドを複製することはエレガントではありません)、サブクエリが大量のデータを含む可能性があるため、フィールドから条件をさらに分離しようとしましたが、残念ながらそうではないようです仕事:

選択する
    コーダー.id、
    corder.public_id、
    CONCAT(buyer.fname," ",buyer.lname) AS バイヤー名,
    コーダーステータス、
    コーダー支払い、
    corder.reserved AS R,
    corder.tracking_id != "" AS A,
    corder.payment_received AS pay_date,
    Invoice.invoice_no AS inv,
    Invoice.receipt_no AS rec,
    請求書.public AS pub_inv,
    proforma.proforma_no AS 教授、
    proforma.public AS pub_pf,
    corder.rating、
    corder.rating_comments!="" AS got_comment
から
    コーダー
LEFT JOIN ユーザーをバイヤーとして ON buy.id = corder.buyer
LEFT JOIN 請求書を請求書として、invoice.id = corder.invoice
LEFT JOIN 請求書 AS proforma ON proforma.id = corder.proforma
WHERE corder.id IN (
    IDを選択
    フロムコーダー
    ID DESC で注文
    リミット 400,20
)
オーダーバイ
    corder.id DESC;

これは失敗し、次のエラー メッセージが表示されます。

エラー 1235 (42000): このバージョンの MySQL はまだ「LIMIT & IN/ALL/ANY/SOME サブクエリ」をサポートしていません

I'm using MySQL 5.1.61, which is reasonably recent in the 5.1 family (and apparently this is not supported in 5.5.x either).

4

1 に答える 1

5

このバージョンを試すことができますか (基本的に、最初にcorderテーブルの 420 行を取得し、そのうちの 20 行を保持してから、3 つの外部結合を実行します):

SELECT
    corder.id,
    corder.public_id,
    CONCAT(buyer.fname," ",buyer.lname) AS buyer_name,
    corder.status,
    corder.payment,
    corder.reserved AS R,
    corder.tracking_id != "" AS A,
    corder.payment_received AS pay_date,
    invoice.invoice_no AS inv,
    invoice.receipt_no AS rec,
    invoice.public AS pub_inv,
    proforma.proforma_no AS prof,
    proforma.public AS pub_pf,
    corder.rating,
    corder.rating_comments!="" AS got_comment
FROM
    ( SELECT * 
      FROM corder
      ORDER BY
        id DESC 
      LIMIT 400, 20
    )
    AS corder
LEFT JOIN user as buyer ON buyer.id = corder.buyer
LEFT JOIN invoice AS invoice ON invoice.id = corder.invoice
LEFT JOIN invoice AS proforma ON proforma.id = corder.proforma
ORDER BY
    corder.id DESC ;
于 2012-11-01T17:20:04.497 に答える