この記事では、MySQL の接続原理を理解します。

序文

データベースに取り組むときに避けては通れない概念の 1 つは连接( join) です。接続のセマンティクスを理解した後、各テーブルのレコードがどのように接続されているかが理解できず、接続を使用するときに次の 2 つの誤解に陥ることがよくあります。後でデータベース:

  • 误区一: ビジネスが第一。クエリがどれほど複雑であっても、1 つの接続ステートメントで実行できます。
  • 误区二: 近づかないでください。クエリの速度が遅いのは、接続の使用が原因である可能性があります。

そこでこの記事では、接続の原理を体系的に学びます。友人の中には初心者もいることを考慮して、まず MySQL でサポートされている接続構文をいくつか紹介しましょう。

【数据库原理 • 二】关系数据库理论興味のあるお友達は、 「電車で行くもご覧ください。

1. 接続の概要

1.1 接続の性質

通常の学習を行うために、ここで 2 つの単純なテーブルを作成し、そこにデータを挿入します。

mysql> create table demo9 (m1 int, n1 char(1));
Query OK, 0 rows affected (0.01 sec)

mysql> insert into demo9 values(1, 'a'), (2, 'b'), (3, 'c');
Query OK, 3 rows affected (0.02 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> create table demo10 (m2 int, n2 char(1));
Query OK, 0 rows affected (0.03 sec)

mysql> insert into demo10 values(2, 'b'), (3, 'c'), (4, 'd');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

両方とも1 つのタイプと 1 つのタイプを持つdemo92 つのテーブルが正常に作成されました。これら 2 つのテーブルのデータは次のとおりです。demo10两个列intchar(1)

mysql> select * from demo9;
+------+------+
| m1   | n1   |
+------+------+
|    1 | a    |
|    2 | b    |
|    3 | c    |
+------+------+
3 rows in set (0.00 sec)

mysql> select * from demo10;
+------+------+
| m2   | n2   |
+------+------+
|    2 | b    |
|    3 | c    |
|    4 | d    |
+------+------+
3 rows in set (0.00 sec)

连接的本质を入れるだけです各个连接表中的记录都取出来依次匹配的组合加⼊结果集并返回给用户demo92 つのdemo10テーブルを接続するプロセスを次の図に示します

ここに画像の説明を挿入
この処理は、demo9テーブルのレコードとdemo10テーブルのレコードを接続して、より大きな新しいレコードを形成するため、このクエリ処理は と呼ばれます连接查询結合クエリの結果セットには、一方のテーブルのすべてのレコードともう一方のテーブルのすべてのレコードの一致する組み合わせが含まれます。このような結果セットは と呼ばれます笛卡尔积テーブル内にレコードがdemo9あり、テーブル内にもレコードがあるため、 2 つのテーブルを接続した後にもレコードが存在します。では、ステートメントの後に複数のテーブル名が続く限り、接続クエリの構文も非常にランダムになります。たとえば、テーブルとテーブルを接続するクエリ ステートメントは次のように記述できます。3demo103笛卡尔积3×3=9MySQLfromdemo9demo10

mysql> select * from demo9,demo10;
+------+------+------+------+
| m1   | n1   | m2   | n2   |
+------+------+------+------+
|    1 | a    |    2 | b    |
|    2 | b    |    2 | b    |
|    3 | c    |    2 | b    |
|    1 | a    |    3 | c    |
|    2 | b    |    3 | c    |
|    3 | c    |    3 | c    |
|    1 | a    |    4 | d    |
|    2 | b    |    4 | d    |
|    3 | c    |    4 | d    |
+------+------+------+------+
9 rows in set (0.00 sec)

1.2 接続プロセスの概要

もしよろしければ、私たちは可以连接任意数量张表,但是如果没有任何限制条件的话,这些表连接起来产生的笛卡尔积可能是非常巨大. たとえば、100 レコードのテーブルを 3 つ接続して生成されるデカルト積には、100×100×100=1,000,000 個のデータがあります。したがって、接続時に过滤掉特定记录组合必要となり、接続クエリは过滤条件次の 2 種類に分類できます。

  • 単一のテーブルに関連する条件
    単一のテーブルのみを設計するというフィルター条件についてはこれまでに何千回も言及しており、以前から常に呼び出してきました。搜索条件たとえば、demo9.m1 > 1これはテーブルのみのフィルター条件でありdemo9、テーブルdemo10.n2 < 'd'のみのフィルター条件です。demo10テーブル。

  • 2 つのテーブルに関係する条件これらの条件に関係する などのフィルター
    条件についてはこれまで触れていませんが、このフィルター条件がどのように使用されるかを後ほど注意深く分析します。demo9.m1 = demo10.m2demo9.n1 > demo10.n2两个表

过滤条件的连接查询次に、次のクエリ ステートメントなど、実行される一般的な実行プロセスを見ていきます。

mysql> select * from demo9, demo10 where demo9.m1 > 1 and demo9.m1 = demo10.m2 and demo10.n2 < 'd';

このクエリでは、次のように指定します三个过滤条件

  • demo9.m1 > 1
  • demo9.m1 = demo10.m2
  • demo10.n2 < 'd'

このクエリの一般的な実行プロセスは次のようになります。

第一歩:

まず确定第一个需要查询的表、テーブルが呼び出されます驱动表単一のテーブルでクエリ ステートメントを実行する方法についてはすでに説明しました。実行するものを選択MySQLするだけです(つまり、クエリを実行するために、から最もコストの低い実行方法を選択します) ここで使用すると仮定するとテーブル内のデータが小さすぎるため、テーブル内で満足のいくレコードを見つける必要があり、テーブルにセカンダリ インデックスを確立していないため、ここでのクエリ テーブルのアクセス方法は次のようになります。に設定すると、そのように実行されます接続クエリのパフォーマンスを向上させる方法については後ほど説明しますが、最初に基本的な概念を明確にしましょう。したがって、クエリ プロセスは次の図に示すとおりです。代价最小访问方法单表查询语句constrefref_or_nullrangeindexalldemo9驱动表demo9demo9.m1>1demo9all全表扫描单表查询

ここに画像の説明を挿入

demo9テーブル内にdemo9.m1 > 1一致するレコードがあることがわかります两条

ステップ2:

前のステップのレコードの場合驱动表产生的结果集中的每一条记录,分别需要到demo10表中查找匹配的记录、いわゆる一致するレコードは を参照します符合过滤条件的记录demo9テーブル内のレコードはdemo10テーブル内のレコードに基づいて検索されるため、demo10テーブルを と呼ぶこともできます被驱动表前の手順で、ドライバー テーブルから2レコードが取得されたため、クエリを実行する必要があります2次demo10表この時点で、2 つのテーブルの列を含むフィルター条件がdemo9.m1=demo10.m2役に立ちます。

  • その際demo9.m1 = 2、条件をフィルタリングするdemo9.m1 = demo10.m2就相当于demo10.m2 = 2ので、この時点ではdemo10テーブルは have demo10.m2 = 2demo10.n2 < 'd'this两个过滤条件と同等になり、demo10テーブル内で実行されます。单表查询

  • その際にdemo9.m1 = 3demo9.m1 = demo10.m2就相当于demo10.m2 = 3この時点でdemo10テーブルがdemo10.m2 = 3、demo10.n2<'d'thisになっているのと同じになるように条件をフィルタリングし两个过滤条件demo10そのテーブルに移動して実行します单表查询

したがって、実行プロセス全体连接查询を次の図に示します。

ここに画像の説明を挿入

つまり整个连接查询最后的结果只有两条符合过滤条件的记录:

mysql> select * from demo9, demo10 where demo9.m1 > 1 and demo9.m1 = demo10.m2 and demo10.n2 < 'd';
+------+------+------+------+
| m1   | n1   | m2   | n2   |
+------+------+------+------+
|    2 | b    |    2 | b    |
|    3 | c    |    3 | c    |
+------+------+------+------+
2 rows in set (0.00 sec)

上記の 2 つの手順からわかるように、上記の 2 つのテーブル結合クエリには合計 が必要です查询1次demo9表2次demo10表もちろん、これは特定のフィルター条件の結果です。demo9.m1 > 1この条件を削除すると、demo9table からレコードが 1 つになり、テーブルに対して3クエリを実行する必要があります。3次demo10つまり、2 つのテーブル間の結合クエリでは、驱动表只需要访问⼀次,被驱动表可能被访问多次.

1.3 内部結合と外部結合

次の内容をよりよく学ぶために、まず 2 つの現実的な表を作成します。

mysql> create table student (
    number int not null auto_increment comment '学号',
    name varchar(5) comment '姓名',
    major varchar(30) comment '专业',
    primary key (number)
) comment '学生信息表';
Query OK, 0 rows affected (0.02 sec)

mysql> create table score (
    number int comment '学号',
    subject varchar(30) comment '科目',
    score tinyint comment '成绩',
    primary key (number, score)
) comment '学生成绩表';
Query OK, 0 rows affected (0.02 sec)

mysql> insert into student values(1,'张三','软件学院'),(2,'李四','计算机科学与工程'),(3,'王五','计算机科学与工程');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> insert into score values(1,'MySQL是怎样运行的',78),(1,'MySQL实战45讲',88),(2,'MySQL是怎样运行的',78),(2,'MySQL实战45讲',100);
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

新しいテーブルを作成し学⽣信息表一个学生成绩表上の 2 つのテーブルにデータを挿入しました。後の 2 つのテーブルのデータは次のとおりです。

mysql> select * from student;
+--------+--------+--------------------------+
| number | name   | major                    |
+--------+--------+--------------------------+
|      1 | 张三   | 软件学院                 |
|      2 | 李四   | 计算机科学与工程         |
|      3 | 王五   | 计算机科学与工程         |
+--------+--------+--------------------------+
3 rows in set (0.00 sec)

mysql> select * from score;
+--------+-------------------------+-------+
| number | subject                 | score |
+--------+-------------------------+-------+
|      1 | MySQL是怎样运行的       |    78 |
|      1 | MySQL实战45|    88 |
|      2 | MySQL是怎样运行的       |    98 |
|      2 | MySQL实战45|   100 |
+--------+-------------------------+-------+
4 rows in set (0.00 sec)

ここで、各生徒のテストのスコアをクエリしたい場合は、2 つのテーブルを接続する必要があります (score名前情報が含まれていないため、scoreテーブルをクエリするだけでは済みません)。接続プロセスでは、studentテーブルからレコードを取得し、テーブル内で同じグレードのレコードscoreを検索しますnumber。そのため、フィルター条件は でありstudent.number =socre.number、クエリ ステートメント全体は次のようになります。

mysql> select * from student,score where student.number=score.number;
+--------+--------+--------------------------+--------+-------------------------+-------+
| number | name   | major                    | number | subject                 | score |
+--------+--------+--------------------------+--------+-------------------------+-------+
|      1 | 张三   | 软件学院                 |      1 | MySQL是怎样运行的       |    78 |
|      1 | 张三   | 软件学院                 |      1 | MySQL实战45|    88 |
|      2 | 李四   | 计算机科学与工程         |      2 | MySQL是怎样运行的       |    98 |
|      2 | 李四   | 计算机科学与工程         |      2 | MySQL实战45|   100 |
+--------+--------+--------------------------+--------+-------------------------+-------+
4 rows in set (0.00 sec)

フィールドがたくさんありますが、クエリできるフィールドは少数です。

mysql> select s1.number,s1.name,s2.subject,s2.score from student s1 ,score s2  where s1.number=s2.number;
+--------+--------+-------------------------+-------+
| number | name   | subject                 | score |
+--------+--------+-------------------------+-------+
|      1 | 张三   | MySQL是怎样运行的       |    78 |
|      1 | 张三   | MySQL实战45|    88 |
|      2 | 李四   | MySQL是怎样运行的       |    98 |
|      2 | 李四   | MySQL实战45|   100 |
+--------+--------+-------------------------+-------+
4 rows in set (0.00 sec)

上記のクエリ結果から、各生徒に対応する各科目のスコアが判明したことがわかりますが、問題があります。Wang Wu、つまり、生徒番号 3 の生徒が試験を受けていないということです。そのため、scoreテーブル内に対応する成績レコードがありません そこで、教師が生徒全員のテストの成績を確認したい場合には、テストを欠席した生徒も表示する必要がありますが、これまで紹介した接続クエリではそのような要件を満たすことができません。この要件について少し考えてみましょう。本質的には、駆動テーブル内のレコードに一致するレコードがない場合でも、それらのレコードを結果セットに追加する必要があるということです。内连接この問題を解決するために、 sumの概念があります外连接

  • 駆動テーブルのレコードの場合内连接的两个表、駆動テーブルには一致するレコードが見つからず、レコードは最終的な結果セットに追加されません。上記の接続はすべて、いわゆる内部接続です。
  • 駆動テーブル内のレコードの場合外连接的两个表、駆動テーブルに一致するレコードがない場合でも、結果セットに追加する必要があります。

MySQL では、駆動テーブルの選択に応じて、外部接続はさらに 2 つのタイプに分類できます。

  • 左外连接: ドライバーテーブルとして左側のテーブルを選択します
  • 右外连接: ドライバーテーブルとして右側のテーブルを選択します

外连接しかし、私たちにとっても、時には問題が解決しないこともあります不想把驱动表的全部记录都加入到最后的结果集これは難しいです。マッチングが結果セットに追加されない場合と、結果セットに追加されない場合があります。どうすればよいですか? フィルター条件を 2 種類に分けるとこの問題は解決するため、フィルター条件を別の場所に配置しますセマンティクスが異なります:

  • where子句中的过滤条件:where句内のフィルター条件は、通常目にするものです。内部接続か外部接続かに関係なく、where句内のフィルター条件を満たさないすべてのレコードは、最終的な結果セットに追加されません。

  • ON子句中的过滤条件: 外部接続の駆動テーブルのレコードの場合、 の場合、レコードは引き続き結果セットに追加され、対応する駆動テーブル レコードの各フィールドに无法在被驱动表中找到匹配ON子句中的过滤条件的记录が入力されます。NULL

    この句は、外部結合ドライバ テーブルのレコードに対して特に提案されていることに注意してくださいON。駆動テーブルで一致するレコードが見つからない場合、レコードは結果セットに追加される必要があります。したがって、句が In に配置されている場合ON、内部接続では、句と同じようにMySQL扱われますwhere。つまり、句と句内连接同等です。whereON

通常の状況では、関与する のみを指し单表的过滤条件放到where子句中、関与する を指します两表的过滤条件都放到ON子句。また、通常、ON節に配置されるフィルター条件も指します连接条件

小提示:
左外部結合および右外部結合は、左結合および右結合と呼ばれます。

1.4 左外部結合

左外部結合の構文は非常に単純で、たとえば2 つのテーブルdemo9を結合する場合は次のように記述できます。demo10左外连接

select * from demo9 left [outer] join demo10 on 连接条件 [where 普通过滤条件]

括弧内の単語はouter省略できます。タイプの接続の場合、と呼ばれるものleft joinを入れてと呼びますしたがって、上の例では、それは ですそれは です左外部結合と右外部結合の場合、. 左外部結合の基本的な文法を理解したら、上記の実際の問題に戻り、すべての学生の成績情報をクエリするクエリ ステートメントの書き方を見てみましょう。試験を欠席した受験者も結果セットに含まれるはずです。左边的表外表或者驱动表右边的表内表或者被驱动表demo9外表或者驱动表demo10内表或者被驱动表必须使用on子句来指出连接条件

mysql> select s1.number,s1.name,s2.subject,s2.score from student s1 left join score s2 on s1.number=s2.number;
+--------+--------+-------------------------+-------+
| number | name   | subject                 | score |
+--------+--------+-------------------------+-------+
|      1 | 张三   | MySQL是怎样运行的       |    78 |
|      1 | 张三   | MySQL实战45|    88 |
|      2 | 李四   | MySQL是怎样运行的       |    98 |
|      2 | 李四   | MySQL实战45|   100 |
|      3 | 王五   | NULL                    |  NULL |
+--------+--------+-------------------------+-------+
5 rows in set (0.01 sec)

結果セットから、Wang Wu には対応するスコア レコードがないにもかかわらず、 を使用しているため結果セットに配置されていることがわかります。ただし、连接类型为左外连接対応するスコア レコードの列にはNULL値が入力されています。

1.5 右外部結合

右外部結合と左外部結合の原則は同じで、構文が次のように置き換えられるだけleftですright

select * from demo9 right [outer] join demo10 on 连接条件 [where 普通过滤条件]

右側のテーブルが駆動テーブル、左側のテーブルが被駆動テーブルというだけなので詳しくは説明しません。

1.6 内部結合

内连接和外连接的根本区别就是在驱动表中的记录不符合on子句中的连接条件时不会把该记录加入到最后的结果集、最初に学んだ結合クエリの種類はすべて です内连接ただし、以前は最も単純な内部結合構文についてのみ説明しました。これは、結合する必要がある複数のテーブルを from 句の後ろに直接置くものです。実際に、時計をに挙げてみ针对内连接,mysql提供了好多不同的语法ましょう。demo9demo10

select * from demo9 [inner|cross] join demo10 [on 连接条件] [where 普通过滤条件];

つまり、 ではMySQL、次の内部接続の書き込みメソッドは同等です。

select * from demo9 join demo10;
select * from demo9 inner join demo10;
select * from demo9 cross join demo10;

上記の記述方法は、fromステートメントの後に接続するテーブルの名前を直接記述するのと逗号,分隔开同じです。

select * from demo9,demo10;

内部リンクを記述する多くの方法を紹介しましたが、その 1 つに慣れておくと良いでしょう。ここでは、inner join主にinner joinセマンティクスが非常に明確であり、 やleft joinright join簡単に区別できるため、内部リンクを記述することをお勧めします。ここで、文節と文節は内连接途中で等価であるため、内部接続では必要ないことに注意してくださいonwhere强制写明on子句

先ほども述べたように、连接的本质就是把各个连接表中的记录都取出来依次匹配的组合加入结果集并返回给用户どのテーブルが駆動テーブルとして使用されるかに関係なく、2 つのテーブル間の接続は笛卡尔积同じである必要があります。については内连接来说,由于凡是不符合on子句或where子句中的条件的记录都会被过滤掉、実際には、2 つのテーブル間の接続のデカルト積からフィルター条件を満たさないレコードを追い出すことと同じなので、 になります对于内连接来说,驱动表和被驱动表是可以互换的,并不会影响最后的查询结果しかし外连接由于驱动表中的记录即使在被驱动表中找不到符合ON子句条件的记录时也要将其加入到结果集の場合、現時点では駆動テーブルと被駆動テーブルの関係、つまり が非常に重要です左外连接和右外连接的驱动表和被驱动表不能轻易互换

まとめ

上記では多くのことを述べてきましたが、誰もが簡単に理解できるように、テーブルdemo9demo10合計の 3 つの接続方法を直接書きます。

mysql> select * from demo9 inner join demo10 on demo9.m1 = demo10.m2;
+------+------+------+------+
| m1   | n1   | m2   | n2   |
+------+------+------+------+
|    2 | b    |    2 | b    |
|    3 | c    |    3 | c    |
+------+------+------+------+
2 rows inset (0.00 sec)

mysql> select * from demo9 left join demo10 on demo9.m1 = demo10.m2;
+------+------+------+------+
| m1   | n1   | m2   | n2   |
+------+------+------+------+
|    2 | b    |    2 | b    |
|    3 | c    |    3 | c    |
|    1 | a    | null | null |
+------+------+------+------+
3 rows inset (0.00 sec)

mysql> select * from demo9 right join demo10 on demo9.m1 = demo10.m2;
+------+------+------+------+
| m1   | n1   | m2   | n2   |
+------+------+------+------+
|    2 | b    |    2 | b    |
|    3 | c    |    3 | c    |
| null | null |    4 | d    |
+------+------+------+------+
3 rows inset (0.00 sec)

2. 接続原理

连接上記の紹介は、 、 、内连接外连接概念についての皆さんの記憶を呼び覚ますためのものであり、これらの基本概念は、この章のトピックへの実際の入り口への道を開くものです。本当のポイントは、MySQLテーブルの結合にどのようなアルゴリズムが使用されるかということであり、これを理解すると、結合クエリによっては電光石火のように速く実行されるものと、カタツムリのように遅いものがある理由が理解できるでしょう。

2.1 ネストループ結合

前に述べたように、2 つのテーブル間の接続の場合、驱动表只会被访问一遍,但被驱动表却要被访问到好多遍具体的な訪問数はペアによって異なります驱动表执行单表查询后的结果集中的记录条数对于内连接来说,选取哪个表为驱动表都没关系而外连接的驱动表是固定的,也就是说左外连接的驱动表就是左边的那个表,右外连接的驱动表就是右边的那个表demo9テーブルとテーブルに対して内部結合クエリを実行する一般的なプロセスをすでに簡単に紹介しましたdemo10

  • 选取驱动表、ドライバー テーブルに関連付けられたフィルター基準を使用して、 を選択します代价最低的单表访问方法来执行对驱动表的单表查询
  • 上記の手順でドライブ テーブルにクエリを実行して取得された結果セット内の各レコードについて分别到被驱动表中查找匹配的记录

2 つのテーブルを結合するプロセスを次の図に示します。

ここに画像の説明を挿入

その場合は3个表进行连接的话,那么步骤2中得到的结果集就像是新的驱动表,然后第三个表就成为了被驱动表、上記のプロセスを繰り返します。つまり、手順 2 で取得した結果セット内の各レコードについて、demo11テーブル内に一致するレコードがあるかどうかを確認する必要があります。疑似コードを使用して、このプロセスを次のように表現します。

for each row in demo9 {   #此处表示遍历满足对demo9单表查询结果集中的每一条记录
    for each row in demo10 {   #此处表示对于某条demo9表的记录来说,遍历满足对demo10单表查询结果集中的每一条记
        for each row in demo11 {   #此处表示对于某条demo9和demo10表的记录组合来说,对demo11表进行单表查询
            if row satisfies join conditions, send to client
        }
    }
}

このプロセスは 1 つのようなものである嵌套的循环ため、この種の駆動テーブルには 1 回しかアクセスされませんが、駆動テーブルには複数回アクセスされる可能性があります。アクセス数は、駆動テーブルで 1 つのテーブル クエリを実行した結果のレコード数によって異なります。これを嵌套循环连接( Nested-Loop Join) と呼びますが、これは最も単純で不器用な結合クエリ アルゴリズムです。

2.2 インデックスを使用して接続を高速化する

步骤2中可能需要访问多次被驱动表ネストされたループ接続では、アクセスされると何度もスキャンする必要があることがわかっています~しかし、クエリテーブルは実際には 1 回に相当し、それを使用できること被驱动表的方式都是全表扫描を忘れないでください冒頭で紹介したテーブル間の内部結合の例を見てみましょう。demo10单表扫描索引来加快查询速度demo9demo10

mysql> select * from demo9, demo10 where demo9.m1 > 1 and demo9.m1 = demo10.m2 and demo10.n2 < 'd';

実際には、アルゴリズムによって実行される接続クエリを使用しており嵌套循坏连接、上記のクエリ実行プロセス テーブルをプルダウンすると、次のことがわかります。

ここに画像の説明を挿入
駆動テーブルをクエリしたdemo9後の結果セットには 2 つのレコードがあり、嵌套循坏连接アルゴリズムには次のものが必要です对被驱动表查询两次

初め:

このときdemo9.m1 = 2demo10テーブルを再度クエリするには、demo10クエリ ステートメントは次と同等です。

select * from demo10 where demo10.m2 = 2 and demo10.m2 < 'd';

2回目:

このときdemo9.m1 =3demo10テーブルを再度クエリするには、demo10クエリ ステートメントは次と同等です。

select * from demo10 where demo10.m2 = 3 and demo10.m2 < 'd';

demo9.m1 = demo10.m22 つのテーブルに関係する元のフィルター条件は、demo10table をクエリするときにdemo9テーブルに関する条件をすでに決定していることがわかります。そのためdemo10、テーブルに対するクエリを最適化するだけで済みます。demo10テーブルに対する上記の 2 つのクエリ ステートメントでは、使用される列は次のとおりです。と列をm2使用するとn2、次のことができます。

  • 在m2列上建立索引、などm2列的条件是等值查找、はいdemo10.m2 = 2demo10.m2 = 3つまり、使用される可能性のあるアクセスメソッドであるため、アクセスメソッドがテーブルでクエリを実行するために使用されるrefと仮定すると、この条件が真であるかどうかを判断する前にテーブルに戻る必要があります。refdemo10demo10.n2 < d

    ここには特殊なケースがあります。つまり、m2列がテーブルの主キーであるか、または唯一の副インデックス列であると仮定すると、そのような条件を使用してテーブルからレコードを検索するdemo10コストは ですMySQL では、接続クエリ内の駆動テーブルの実行方法を次のように参照していることがわかりますdemo10.m2 = 常数值demo10常数级别在单表中使用主键值或者唯一二级索引列的值进行等值查找的方式称之为const使用主键值或者唯一二级索引列的值进行等值查找的查询eq_ref

  • n2にインデックスを作成するには、demo10.n2 < 'd'使用できるrangeアクセス方法が条件となります。使用したアクセス方法が、demo10 テーブルのクエリに使用されると仮定すると、列の条件が満たされているかどうかをrange判断する前に、テーブルに戻る必要があります。m2それは本当です。

m2と のn2両方の列にインデックスがあると仮定すると、これら 2 つのいずれかを選択する必要があります代价更低的去执行对demo10表的查询もちろん、インデックスは必ずしもインデックスを使用する必要はなく、インデックスのみを使用します在二级索引 +回表的代价比全表扫描的代价更低时才会使用索引

また、場合によっては连接查询的查询列表和过滤条件中可能只涉及被驱动表的部分列,而这些列都是某个索引的一部分,这种情况下即使不能使用eq_ref、ref、ref_or_null或者range这些访问方法执行对被驱动表的查询的话,也可以使用索引扫描indexアクセス方法をクエリすることもあります被驱动表したがって、実際の作業では最好不要使用*作为查询列表、 を使用するのが最善であることをお勧めします真实用到的列作为查询列表

2.3 ブロックの入れ子ループ結合

テーブルをスキャンするプロセスでは、実際にはこれが最初に行われます表从磁盘上加载到内存中,然后从内存中比较匹配条件是否满足実際のテーブルは、レコードが 3 つしかないテーブルとは異なり、数万のレコードがあることはまれで、数百万、数千万、さらには数億のレコードを持つテーブルがどこにでも存在しますdemo9demo10以下のテーブルのすべてのレコードをメモリに完全に保存できない可能性があるため、テーブルの前のレコードをスキャンするときに後者のレコードがまだディスク上にある可能性があり、後者のレコードをスキャンするとメモリが不足する可能性があります。 , そのため、以前のレコードをメモリ内で解放された状態から移動する必要があります。前に述べたように、ネストされたループ結合アルゴリズムを使用して 2 つのテーブルを結合するプロセスでは、駆動テーブルに何度もアクセスする必要があります。駆動テーブル内のデータが大きすぎてインデックスを使用してアクセスできない場合、それは同等です。このテーブルをディスクから何度も読み取るコストはI/O非常に高いため、駆動テーブルへのアクセス数を最小限に抑える方法を見つける必要があります。

被驱动表大量のデータが含まれている場合被驱动表レコード被驱动表がメモリにロードされます内存中的每一条记录只会和驱动表结果集的一条记录做匹配,之后就会被从内存中清除掉次に、驱动表結果セットから別のレコードを取得し、再度被驱动表記録し加载到内存中驱动表結果セットにあるレコードの数だけ被驱动表ディスクからメモリにロードすることを繰り返します。被驱动表したがって、メモリにレコードをロードするときにレコード多条驱动表内のレコードを一度に照合できるので、ディスクから駆動テーブルを繰り返しロードするコストを大幅に削減できます。したがって、接続クエリを実行する前に固定サイズのメモリを適用し、最初に駆動テーブルの結果セットにいくつかのレコードをインストールし、次に駆動テーブルと駆動テーブルの各レコードのスキャンを開始するという概念が提案されてますMySQLtable複数のドライバー テーブル レコードを 1 回の合計で照合します。照合プロセスはすべてメモリ内で行われるため、これが当てはまります使用されるプロセスを以下の図に示します。join bufferjoin bufferjoin bufferjoin buffer可以显著减少被驱动表的I/O代价join buffer

ここに画像の説明を挿入
最良の場合join buffer、駆動テーブルの結果セット内のすべてのレコードを収容できる十分な大きさであるため、結合操作を完了するには駆動テーブルへの 1 回のアクセスのみが必要になります。MySQL はこれを join とjoin buffer的嵌套循环连接算法呼びます基于块的嵌套连接(Block Nested-Loop Join)算法

このサイズは起動パラメータまたはシステム変数を通じて構成join bufferできますjoin_buffer_size。デフォルトのサイズは262144字节(つまり256KB)、最小値は に設定できます128字节もちろん、对于优化被驱动表的查询来说,最好是为被驱动表加上效率高的索引,如果实在不能使用索引,并且自己的机器的内存也比较大可以尝试调大join_buffer_size的值来对连接查询进行优化

mysql> show variables like 'join_buffer_size';
+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| join_buffer_size | 262144 |
+------------------+--------+
1 row in set (0.01 sec)

mysql> set persist join_buffer_size=524288;
Query OK, 0 rows affected (0.01 sec)

ヒント:
システム レベルでこの値を大きすぎる値に設定することはお勧めできません。最終的な解は依然としてそれを解くためのインデックスに依存するため、通常は 512K 以内に設定できます。もちろん、場合によっては 2テーブルは関連付けられていますが、確かに使用可能なインデックスはありません

もう 1 つ注意すべき点は、より多くのレコードをクエリ リストに追加できるように、クエリ リストに関心のある列だけを含めるのが驱动表的记录并不是所有列都会被放到join buffer中,只有查询列表中的列和过滤条件中的列才会被放到join buffer中最善であることをもう一度思い出してください不要把*作为查询列表join buffer

要約する

今日はつながりについて学びました。接続の本質、接続のプロセス、内部接続と外部接続の使用方法、接続の原理を理解します。元のアルゴリズムNLJに基づいて、より良いアルゴリズムMySQLが設計されています。駆動テーブルを渡すことができます。インデックスが使用できない場合は、値を増やすことができます( )。内部結合を使用する場合は、次の点に注意する必要があります。BNL添加关联字段索引的方式来提高查询效率Join Bufferjoin_buffer_size

  • ON子句和where子句是等价的したがって、内部接続では ON 句を必須にする必要はありません。

  • 内部結合の場合、不符合on子句或where子句中的条件的记录都会被过滤掉実際にはすべてが 2 つのテーブル間の結合のデカルト積からフィルター条件を満たさないレコードを追い出すことと同じであるため、内部結合の場合、驱动表和被驱动表是可以互换,并不会影响最后的查询结果.

今日の勉強はこれで終わりです、あなたが壊れない自分になれることを願っています
~~~

先を見据えて点と点を結ぶことはできません。過去を振り返って接続することしかできません。したがって、点と点が何らかの形であなたの将来につながると信じなければなりません。あなたは何かを信頼しなければなりません - 自分の直感、運命、人生、カルマ、何でも。このアプローチは私を決して失望させず、私の人生に大きな変化をもたらしました

私のコンテンツがお役に立てましたら、どうぞ点赞、、、、創作は簡単ではありません。皆さんのサポートが私が頑張れる原動力です评论!收藏

この記事は以下を参照しています: 子供たち「MySQL はどのように機能するか」
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/liang921119/article/details/130740613