7

次のような単純なタグレコード構造に似た 2 つのテーブルがあります (実際にはもっと複雑ですが、これが問題の本質です)。

tag (A.a) | recordId (A.b)
1         | 1
2         | 1
2         | 2
3         | 2
....

recordId (B.b) | recordData (B.c)
1              | 123
2              | 666
3              | 1246

問題は、特定のタグを持つ順序付けられたレコードをフェッチすることです。それを行う明白な方法は、単純な結合と (PK)(Aa, Ab), (Ab), (PK)(Bb), (Bb,Bc) のインデックスを使用することです。

select A.a, A.b, B.c from A join B on A.b = B.b where a = 44 order by c;

ただし、これにより、ファイルソートの不快な結果が得られます。

+----+-------------+-------+------+---------------+---------+---------+-----------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key     | key_len | ref       | rows | Extra                                        |
+----+-------------+-------+------+---------------+---------+---------+-----------+------+----------------------------------------------+
|  1 | SIMPLE      | A     | ref  | PRIMARY,b     | PRIMARY | 4       | const     |   94 | Using index; Using temporary; Using filesort | 
|  1 | SIMPLE      | B     | ref  | PRIMARY,b     | b       | 4       | booli.A.b |    1 | Using index                                  | 
+----+-------------+-------+------+---------------+---------+---------+-----------+------+----------------------------------------------+

巨大で非常に冗長な「具体化されたビュー」を使用すると、かなりまともなパフォーマンスを得ることができますが、これはビジネス ロジックを複雑にするという代償を伴います。特に、A テーブルと B テーブルが既に MV:s (および他のクエリに必要であり、実際には UNION を使用して同じクエリを実行します)。

create temporary table C engine=innodb as (select A.a, A.b, B.c from A join B on A.b = B.b);
explain select a, b, c from C where a = 44 order by c;

状況をさらに複雑にしているのは、B テーブルに範囲フィルターなどの条件があるという事実です。

select A.a, A.b, B.c from A join B on A.b = B.b where a = 44 AND B.c > 678 order by c;

しかし、ファイルソートの問題が解消されれば、これを処理できると確信しています。

上記のコードブロック 3 の単純な結合が並べ替えにインデックスを使用しない理由と、新しい MV を作成せずに何らかの方法で問題を回避できるかどうかを知っている人はいますか?

以下は、テストに使用している完全な SQL リストです。

DROP TABLE IF EXISTS A;
DROP TABLE IF EXISTS B;
DROP TABLE IF EXISTS C;
CREATE TEMPORARY TABLE A (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY(a, b), INDEX idx_A_b (b)) ENGINE=INNODB;
CREATE TEMPORARY TABLE B (b INT NOT NULL, c INT NOT NULL, d VARCHAR(5000) NOT NULL DEFAULT '', PRIMARY KEY(b), INDEX idx_B_c (c), INDEX idx_B_b (b, c)) ENGINE=INNODB;

DELIMITER $$
CREATE PROCEDURE prc_filler(cnt INT)
BEGIN
        DECLARE _cnt INT;
        SET _cnt = 1;
        WHILE _cnt <= cnt DO
                INSERT IGNORE INTO A SELECT RAND()*100, RAND()*10000;
                INSERT IGNORE INTO B SELECT RAND()*10000, RAND()*1000, '';
                SET _cnt = _cnt + 1;
        END WHILE;
END
$$
DELIMITER ;

START TRANSACTION;
CALL prc_filler(100000);
COMMIT;
DROP PROCEDURE prc_filler;

CREATE TEMPORARY TABLE C ENGINE=INNODB AS (SELECT A.a, A.b, B.c FROM A JOIN B ON A.b = B.b);
ALTER TABLE C ADD (PRIMARY KEY(a, b), INDEX idx_C_a_c (a, c));

EXPLAIN EXTENDED SELECT A.a, A.b, B.c FROM A JOIN B ON A.b = B.b WHERE A.a = 44;
EXPLAIN EXTENDED SELECT A.a, A.b, B.c FROM A JOIN B ON A.b = B.b WHERE 1 ORDER BY B.c;
EXPLAIN EXTENDED SELECT A.a, A.b, B.c FROM A JOIN B ON A.b = B.b where A.a = 44 ORDER BY B.c;
EXPLAIN EXTENDED SELECT a, b, c FROM C WHERE a = 44 ORDER BY c;
-- Added after Quassnois comments
EXPLAIN EXTENDED SELECT A.a, A.b, B.c FROM  B FORCE INDEX (idx_B_c) JOIN A ON A.b = B.b WHERE A.a = 44 ORDER BY B.c;
EXPLAIN EXTENDED SELECT A.a, A.b, B.c FROM A JOIN B ON A.b = B.b WHERE A.a = 44 ORDER BY B.c LIMIT 10;
EXPLAIN EXTENDED SELECT A.a, A.b, B.c FROM  B FORCE INDEX (idx_B_c) JOIN A ON A.b = B.b WHERE A.a = 44 ORDER BY B.c LIMIT 10;
4

2 に答える 2

1

select A.a, A.b, B.c from A join B on A.b = B.b where a = 44 order by c;

列にエイリアスを付ける場合、それは役に立ちますか?例:

 SELECT 
 T1.a AS colA, 
 T2.b AS colB, 
 T2.c AS colC 
 FROM A AS T1 
 JOIN B AS T2 
 ON (T1.b = T2.b) 
 WHERE 
 T1.a = 44 
 ORDER BY colC;

私が行った唯一の変更は次のとおりです。

  • 結合条件を括弧で囲みます
  • 結合条件と条件がテーブル列に基づく場所
  • ORDER BY条件は、結果のテーブル列に基づいています
  • 結果テーブルの列とクエリされたテーブルのエイリアスを作成して、どちらか一方を使用しているときに(うまくいけば)より明確にしました(サーバーにとってより明確になりました。元のクエリの2か所で列を参照することを怠っています)。

実際のデータはもっと複雑ですが、問題はその単純なレベルにあるため、単純なバージョンのクエリを提供したと思います。

于 2009-08-04T14:15:33.773 に答える