序文
みなさん、こんにちは。カタツムリを拾う男の子です。
日常の開発では、groupbyをよく使用します。親愛なる友人、group byがどのように機能するか知っていますか?group byとhavingの違いは何ですか?group byの最適化のアイデアは何ですか?group byを使用するときに注意する必要がある問題は何ですか?この記事はあなたと一緒に学び、グループを征服します〜
- groupbyを使用した簡単な例
- グループ化の仕組み
- group by+whereとgroupby+の違い
- 最適化のアイデアによるグループ化
- groupbyの使用に関する注意
- 本番の遅いSQLを最適化する方法
1.groupbyを使用した簡単な例
Group byは通常、統計をグループ化するために使用され、それが表すロジックは、特定のルールに従ってグループ化することです。簡単な例から始めて、一緒に確認してみましょう。
従業員テーブルが使用され、テーブル構造が次のようになっているとします。
CREATE TABLE `staff` (
`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`id_card` varchar(20) NOT NULL COMMENT '身份证号码',
`name` varchar(64) NOT NULL COMMENT '姓名',
`age` int(4) NOT NULL COMMENT '年龄',
`city` varchar(64) NOT NULL COMMENT '城市',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COMMENT='员工表';
复制代码
テーブルインベントリデータは次のとおりです。
現在、このような要件があります。各都市の従業員数を数えます。対応するSQLステートメントは次のように記述できます。
select city ,count(*) as num from staff group by city;
复制代码
実行結果は以下のとおりです。
このSQLステートメントのロジックは非常に明確ですが、その基礎となる実行フローは何ですか?
2.主成分分析によるグループ化
2.1分析の説明
最初にexplainを使用して実行プランを表示しましょう
explain select city ,count(*) as num from staff group by city;
复制代码
- ExtraフィールドのUsingtemporaryは、グループ化が実行されるときに一時テーブルが使用されることを示します。
- ExtraフィールドのUsingfilesortは、ソートが使用されていることを示します
一時テーブルと並べ替えを使用してグループ化するにはどうすればよいですか?このSQLの実行フローを見てみましょう
2.2groupbyの簡単な実行プロセス
explain select city ,count(*) as num from staff group by city;
复制代码
このSQLの実行プロセスを見てみましょう。
- cityとnumの2つのフィールドを持つメモリ一時テーブルを作成します。
- 全表はスタッフのレコードをスキャンし、city='X'のレコードを順次取得します。
- 一時テーブルにcity='X'の行があるかどうかを判別し、ない場合は、レコード(X、1)を挿入します。
- 一時テーブルにcity='X'の行がある場合は、xの行のnum値に1を追加します。
- トラバーサルが完了したら、フィールドの都市に従って並べ替えて結果セットを取得し、クライアントに返します。
このプロセスの実行図は次のとおりです。
一時テーブルの順序は何ですか?
並べ替えが必要なフィールドを並べ替えバッファに入れ、並べ替え後に戻ることです。ここで注意してください、ソートはフルフィールドソートとROWIDソートに分けられます
フルフィールドソートの場合、クエリして返す必要のあるフィールドはソートバッファに入れられ、ソートフィールドに従ってソートされ、直接返されます。フルフィールドソートとROWIDソートのどちらを使用するかを決定するにはどうすればよいですか?データベースパラメータmax_length_for_sort_dataによって制御されます
並べ替えについて詳しく知りたい方は、私の記事を読んでください。
- 一度読んで理解してください:詳細な説明による注文
3.場所と持っていることの違い
- +whereによるgroupbyの実行プロセス
- +haveによるgroupbyの実行プロセス
- 同時に、where、group by、およびhaingの実行順序があります。
3.1groupby+の実行プロセスここで
前のセクションのSQLが単純すぎると感じる友人もいます。where条件が追加され、where条件列にインデックスが付けられている場合、実行プロセスはどのようになりますか?
では、次のように条件を追加し、idx_ageのインデックスを追加しましょう。
select city ,count(*) as num from staff where age> 30 group by city;
//加索引
alter table staff add index idx_age (age);
复制代码
もう一度分析してみましょう:
explain select city ,count(*) as num from staff where age> 30 group by city;
复制代码
Explain実行プランの結果から、クエリ条件がidx_ageのインデックスにヒットし、一時テーブルと並べ替えを使用していることがわかります。
インデックス条件の使用:インデックスがプッシュダウンされて最適化され、インデックスに従って可能な限りデータがフィルタリングされ、サーバーレイヤーに戻されて他の条件に従ってフィルタリングされることを示します。ここに単一のインデックスのインデックスプッシュダウンがあるのはなぜですか?Explainの出現は、必ずしもインデックスプッシュダウンが使用されることを意味するのではなく、単に使用できることを意味しますが、必ずしも使用されるわけではありません。アイデアや質問がある場合は、WeChatに追加して話し合うことができます。
実行フローは次のとおりです。
- cityとnumの2つのフィールドを持つメモリ一時テーブルを作成します。
- インデックスツリーidx_ageをスキャンして、年齢が30を超える主キーIDを見つけます。
- 主キーIDを使用して、テーブルに戻り、city='X'を見つけます。
- 一時テーブルにcity='X'の行があるかどうかを判別し、ない場合は、レコード(X、1)を挿入します。
- 一時テーブルにcity='X'の行がある場合は、xの行のnum値に1を追加します。
- 手順2と3を繰り返して、条件を満たすすべてのデータを見つけます。
- 最後に、フィールドの都市に従って並べ替え、結果セットを取得してクライアントに返します。
3.2+持っていることによるグループの実行
各都市の従業員数を照会し、従業員数が3以上の都市を取得する場合は、問題を非常にうまく解決できます。SQLJiangziは次のように書いています。
select city ,count(*) as num from staff group by city having num >= 3;
复制代码
クエリ結果は次のとおりです。
持つことはグループ化フィルター条件と呼ばれ、返された結果セットに作用します。
3.3 where、group by、およびhingの実行順序
SQLにwhere、group by、およびhas句が同時に含まれている場合、実行順序はどのようになりますか?
たとえば、このSQL:
select city ,count(*) as num from staff where age> 19 group by city having num >= 3;
复制代码
- where句を実行して、19歳を超える従業員データを検索します
- 都市ごとにグループ化された、従業員データのgroupby句。
- group by句によって形成された都市グループの場合、集計関数を実行して、各グループの従業員数を計算します。
- 最後に、have句を使用して、従業員数が3以上の都市グループを選択します。
3.4ここで+違いの要約
- のグループ化後のフィルタリングにはhave句が使用され、行条件のフィルタリングにはwhere句が使用されます。
- 一般に、group byおよび(count()、sum()、avg()、max()、min())などの集計関数で表示されます。
- 集計関数はwhere条件句では使用できませんが、having句は使用できます。
- 持つことはgroupbyの後でのみ使用でき、groupbyの前に実行されます
4.groupbyの問題
groupbyを使用する際の主な注意点は次のとおりです。
- group byは集計関数で使用する必要がありますか?
- groupbyのフィールドはselectに表示される必要があります
- groupbyによって引き起こされる遅いSQLの問題
4.1 group byは集計関数で使用する必要がありますか?
group byは、統計をグループ化することを意味します。通常、(count()、sum()、avg()、max()、min())などの集計関数で使用されます。
- count()番号
- sum()合計
- avg()平均
- max()最大値
- min()最小値
集計関数なしで使用できますか?
私はMysql5.7を使用していますが、問題ありません。エラーは報告されず、返されるのはグループのデータの最初の行です。
たとえば、このSQL:
select city,id_card,age from staff group by city;
复制代码
クエリ結果は
比較してみましょう。返されるのは各グループの最初のデータです
もちろん、通常使用する場合は、重複を削除するなどの特別なシナリオがない限り、group byを集計関数と組み合わせて使用します。もちろん、個別に再利用することもできます。
4.2 group byが後に続くフィールドは、selectに表示される必要があります。
必ずしも、次のSQLのように:
select max(age) from staff group by city;
复制代码
実行結果は以下のとおりです。
グループ化フィールドの都市はselectの背後になく、エラーを報告しません。もちろん、これはさまざまなデータベースやさまざまなバージョンに関連している可能性があります。使用する場合は、最初に確認できます。紙に書くものは浅く、どうしたらいいかわからないということわざがあります。
4.3groupbyによって引き起こされる遅いSQLの問題
最も重要な注意点として、group byを不適切に使用すると、SQLの速度が低下する問題が発生しやすくなります。デフォルトでは一時テーブルと並べ替えの両方を使用するためです。ディスク一時テーブルも使用される場合があります。
実行プロセス中にメモリ一時テーブルのサイズが上限に達すると(この上限を制御するパラメータはtmp_table_sizeです)、メモリ一時テーブルはディスク一時テーブルに変換されます。データ量が多い場合、このクエリに必要なディスク一時テーブルが大量のディスク領域を占有する可能性があります。
これらはすべて、SQLの速度低下につながるx要因です。最適化ソリューションについて一緒に説明しましょう。
5.groupbyのいくつかの最適化スキーム
最適化する方向はどれですか?
- 方向1:デフォルトでソートされるので、ランク付けしないでください。
- 方向2:一時テーブルはgroup byのパフォーマンスに影響を与えるXファクターであるため、一時テーブルを使用できませんか?
一緒に考えてみましょう。groupbyステートメントを実行するために一時テーブルが必要なのはなぜですか。group byのセマンティックロジックは、さまざまな値の出現回数をカウントすることです。これらの値が最初から順番に並んでいる場合、一時的なテーブルを使用して結果を記録およびカウントする代わりに、統計を直接スキャンできますか?
- groupbyの後のフィールドにインデックスが付けられます
- ソートせずにnullで並べ替える
- メモリ内の一時テーブルのみを使用するようにしてください
- 使用SQL_BIG_RESULT
5.1groupbyの後にフィールドにインデックスを追加する
group byの後のフィールド値が最初から正しいことを確認するにはどうすればよいですか?もちろん、それは索引付けです。
このSQLに戻りましょう
select city ,count(*) as num from staff where age= 19 group by city;
复制代码
その実行計画
それにジョイントインデックスを追加するとidx_age_city(age、city)
alter table staff add index idx_age_city(age,city);
复制代码
実行プランをもう一度見てみると、並べ替えも一時テーブルも必要ないことがわかりました。
適切なインデックスを追加することは、グループ化を最適化するための最も簡単で効果的な方法です。
5.2ソートせずにnullで並べ替え
すべてのシナリオがインデックス作成に適しているわけではありません。インデックスの作成に適していないシナリオに遭遇した場合、どうすればそれを最適化できますか?
結果セットを並べ替える必要がない場合は、nullによる順序を使用できます。
select city ,count(*) as num from staff group by city order by null
复制代码
実行計画は次のとおりです。ファイルソートはもうありません。
5.3メモリ内の一時テーブルのみを使用するようにしてください
group byでカウントするデータが少ない場合は、可能な限りメモリ一時テーブルのみを使用するようにできます。groupbyのプロセスでデータを適合できない場合、ディスク一時テーブルの使用に時間がかかるためです。したがって、ディスク一時テーブルの使用を回避するために、tmp_table_sizeパラメーターを適切に増やすことができます。
5.4SQL_BIG_RESULTによる最適化
データ量が多すぎる場合はどうなりますか?tmp_table_sizeを無限に増やすことはできませんか?しかし、最初にメモリ一時テーブルに入れられたデータを監視し、データ挿入によって上限に達したことが検出されたときに、それをディスク一時テーブルに変換することはできませんか?それは少し賢くないです。
したがって、推定データ量が比較的大きい場合は、SQL_BIG_RESULTヒントを使用して、ディスク一時テーブルを直接使用します。MySQlオプティマイザは、ディスク一時テーブルがB +ツリーに格納されており、ストレージ効率がアレイほど高くないことを検出しました。したがって、配列に直接格納されます
SQ1の例は次のとおりです。
select SQL_BIG_RESULT city ,count(*) as num from staff group by city;
复制代码
実行プランのExtraフィールドからわかるように、実行では一時テーブルは使用されず、並べ替えのみが行われます。
実行フローは次のとおりです。
- sort_bufferを初期化し、cityフィールドに配置します。
- テーブルスタッフをスキャンし、都市の値を順番に取り出して、sort_bufferに保存します。
- スキャンが完了したら、sort_bufferのcityフィールドを並べ替えます
- ソートが完了すると、順序付けられた配列が取得されます。
- 順序付けられた配列に従って、各値の出現回数をカウントします。
6.本番の遅いSQLを最適化する方法
最近、group byに関連するSQLの生成が遅いことに遭遇しました。それを最適化する方法を、紹介します。
テーブルの構造は次のとおりです。
CREATE TABLE `staff` (
`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`id_card` varchar(20) NOT NULL COMMENT '身份证号码',
`name` varchar(64) NOT NULL COMMENT '姓名',
`status` varchar(64) NOT NULL COMMENT 'Y-已激活 I-初始化 D-已删除 R-审核中',
`age` int(4) NOT NULL COMMENT '年龄',
`city` varchar(64) NOT NULL COMMENT '城市',
`enterprise_no` varchar(64) NOT NULL COMMENT '企业号',
`legal_cert_no` varchar(64) NOT NULL COMMENT '法人号码',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COMMENT='员工表';
复制代码
クエリのSQLは次のとおりです。
select * from t1 where status = #{status} group by #{legal_cert_no}
复制代码
このSQLの=が妥当かどうかについては説明しません。そのようなSQLの場合、どのように最適化しますか?アイデアを持っている友達は、メッセージを残して話し合うことができます。または、WeChatやグループディスカッションに私を追加することもできます。記事が間違っていると思われる場合は、それを取り上げることもできます。一緒に進歩しましょう。