序文
データベースに取り組むときに避けては通れない概念の 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 つのタイプを持つdemo9
2 つのテーブルが正常に作成されました。これら 2 つのテーブルのデータは次のとおりです。demo10
两个列
int
char(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)
连接的本质
を入れるだけです各个连接表中的记录都取出来依次匹配的组合加⼊结果集并返回给用户
。demo9
2 つのdemo10
テーブルを接続するプロセスを次の図に示します。
この処理は、demo9
テーブルのレコードとdemo10
テーブルのレコードを接続して、より大きな新しいレコードを形成するため、このクエリ処理は と呼ばれます连接查询
。結合クエリの結果セットには、一方のテーブルのすべてのレコードともう一方のテーブルのすべてのレコードの一致する組み合わせが含まれます。このような結果セットは と呼ばれます笛卡尔积
。テーブル内にレコードがdemo9
あり、テーブル内にもレコードがあるため、 2 つのテーブルを接続した後にもレコードが存在します。では、ステートメントの後に複数のテーブル名が続く限り、接続クエリの構文も非常にランダムになります。たとえば、テーブルとテーブルを接続するクエリ ステートメントは次のように記述できます。3
demo10
3
笛卡尔积
3×3=9
MySQL
from
demo9
demo10
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.m2
demo9.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
するだけです(つまり、クエリを実行するために、、、、から最もコストの低い実行方法を選択します) 。ここで使用すると仮定すると、テーブル内のデータが小さすぎるため、テーブル内で満足のいくレコードを見つける必要があり、テーブルにセカンダリ インデックスを確立していないため、ここでのクエリ テーブルのアクセス方法は次のようになります。に設定すると、そのように実行されます。接続クエリのパフォーマンスを向上させる方法については後ほど説明しますが、最初に基本的な概念を明確にしましょう。したがって、クエリ プロセスは次の図に示すとおりです。代价最小
访问方法
单表查询语句
const
ref
ref_or_null
range
index
all
demo9
驱动表
demo9
demo9.m1>1
demo9
all
全表扫描
单表查询
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
テーブルは havedemo10.m2 = 2
、demo10.n2 < 'd'
this两个过滤条件
と同等になり、demo10
テーブル内で実行されます。单表查询
-
その際に
demo9.m1 = 3
、demo9.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
この条件を削除すると、demo9
table からレコードが 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
。つまり、句と句内连接
は同等です。where
ON
通常の状況では、関与する のみを指し单表的过滤条件放到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提供了好多不同的语法
ましょう。demo9
demo10
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 join
とright join
簡単に区別できるため、内部リンクを記述することをお勧めします。ここで、文節と文節は内连接
途中で等価であるため、内部接続では必要ないことに注意してください。on
where
强制写明on子句
先ほども述べたように、连接的本质就是把各个连接表中的记录都取出来依次匹配的组合加入结果集并返回给用户
。どのテーブルが駆動テーブルとして使用されるかに関係なく、2 つのテーブル間の接続は笛卡尔积
同じである必要があります。については内连接来说,由于凡是不符合on子句或where子句中的条件的记录都会被过滤掉
、実際には、2 つのテーブル間の接続のデカルト積からフィルター条件を満たさないレコードを追い出すことと同じなので、 になります对于内连接来说,驱动表和被驱动表是可以互换的,并不会影响最后的查询结果
。しかし外连接
、由于驱动表中的记录即使在被驱动表中找不到符合ON子句条件的记录时也要将其加入到结果集
の場合、現時点では駆動テーブルと被駆動テーブルの関係、つまり が非常に重要です左外连接和右外连接的驱动表和被驱动表不能轻易互换
。
まとめ
上記では多くのことを述べてきましたが、誰もが簡単に理解できるように、テーブルdemo9
とdemo10
合計の 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
单表扫描
索引来加快查询速度
demo9
demo10
mysql> select * from demo9, demo10 where demo9.m1 > 1 and demo9.m1 = demo10.m2 and demo10.n2 < 'd';
実際には、アルゴリズムによって実行される接続クエリを使用しており嵌套循坏连接
、上記のクエリ実行プロセス テーブルをプルダウンすると、次のことがわかります。
駆動テーブルをクエリしたdemo9
後の結果セットには 2 つのレコードがあり、嵌套循坏连接
アルゴリズムには次のものが必要です对被驱动表查询两次
。
初め:
このときdemo9.m1 = 2
、demo10
テーブルを再度クエリするには、demo10
クエリ ステートメントは次と同等です。
select * from demo10 where demo10.m2 = 2 and demo10.m2 < 'd';
2回目:
このときdemo9.m1 =3
、demo10
テーブルを再度クエリするには、demo10
クエリ ステートメントは次と同等です。
select * from demo10 where demo10.m2 = 3 and demo10.m2 < 'd';
demo9.m1 = demo10.m2
2 つのテーブルに関係する元のフィルター条件は、demo10
table をクエリするときにdemo9
テーブルに関する条件をすでに決定していることがわかります。そのためdemo10
、テーブルに対するクエリを最適化するだけで済みます。demo10
テーブルに対する上記の 2 つのクエリ ステートメントでは、使用される列は次のとおりです。と列をm2
使用するとn2
、次のことができます。
-
在m2列上建立索引
、などm2列的条件是等值查找
、はいdemo10.m2 = 2
、demo10.m2 = 3
つまり、使用される可能性のあるアクセスメソッドであるため、アクセスメソッドがテーブルでクエリを実行するために使用されるref
と仮定すると、この条件が真であるかどうかを判断する前にテーブルに戻る必要があります。ref
demo10
demo10.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 つしかないテーブルとは異なり、数万のレコードがあることはまれで、数百万、数千万、さらには数億のレコードを持つテーブルがどこにでも存在しますdemo9
。demo10
以下のテーブルのすべてのレコードをメモリに完全に保存できない可能性があるため、テーブルの前のレコードをスキャンするときに後者のレコードがまだディスク上にある可能性があり、後者のレコードをスキャンするとメモリが不足する可能性があります。 , そのため、以前のレコードをメモリ内で解放された状態から移動する必要があります。前に述べたように、ネストされたループ結合アルゴリズムを使用して 2 つのテーブルを結合するプロセスでは、駆動テーブルに何度もアクセスする必要があります。駆動テーブル内のデータが大きすぎてインデックスを使用してアクセスできない場合、それは同等です。このテーブルをディスクから何度も読み取るコストはI/O
非常に高いため、駆動テーブルへのアクセス数を最小限に抑える方法を見つける必要があります。
被驱动表
大量のデータが含まれている場合被驱动表
レコード被驱动表
がメモリにロードされます内存中的每一条记录只会和驱动表结果集的一条记录做匹配,之后就会被从内存中清除掉
。次に、驱动表
結果セットから別のレコードを取得し、再度被驱动表
記録し加载到内存中
、驱动表
結果セットにあるレコードの数だけ被驱动表
ディスクからメモリにロードすることを繰り返します。被驱动表
したがって、メモリにレコードをロードするときにレコード多条驱动表
内のレコードを一度に照合できるので、ディスクから駆動テーブルを繰り返しロードするコストを大幅に削減できます。したがって、接続クエリを実行する前に固定サイズのメモリを適用し、最初に駆動テーブルの結果セットにいくつかのレコードをインストールし、次に駆動テーブルと駆動テーブルの各レコードのスキャンを開始するという概念が提案されていますMySQL
。table複数のドライバー テーブル レコードを 1 回の合計で照合します。照合プロセスはすべてメモリ内で行われるため、これが当てはまります。使用されるプロセスを以下の図に示します。join buffer
join buffer
join buffer
join 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 Buffer
join_buffer_size
-
ON子句和where子句是等价的
したがって、内部接続では ON 句を必須にする必要はありません。 -
内部結合の場合、
不符合on子句或where子句中的条件的记录都会被过滤掉
実際にはすべてが 2 つのテーブル間の結合のデカルト積からフィルター条件を満たさないレコードを追い出すことと同じであるため、内部結合の場合、驱动表和被驱动表是可以互换,并不会影响最后的查询结果
.
今日の勉強はこれで終わりです、あなたが壊れない自分になれることを願っています
~~~
先を見据えて点と点を結ぶことはできません。過去を振り返って接続することしかできません。したがって、点と点が何らかの形であなたの将来につながると信じなければなりません。あなたは何かを信頼しなければなりません - 自分の直感、運命、人生、カルマ、何でも。このアプローチは私を決して失望させず、私の人生に大きな変化をもたらしました
私のコンテンツがお役に立てましたら、どうぞ点赞
、、、、創作は簡単ではありません。皆さんのサポートが私が頑張れる原動力です评论
!收藏
この記事は以下を参照しています: 子供たち「MySQL はどのように機能するか」