LoginSignup
27
31

More than 3 years have passed since last update.

【新人教育 資料】第6章 SQLへの道 〜ソート編〜

Last updated at Posted at 2016-02-17

【新人教育 資料】第6章 SQLへの道 〜ソート編〜

あらすじ

新人がいっぱい入ってくる。新人のレベルもバラバラ。教育資料も古くなっているので、更新しましょう。
どうせなら、公開しちゃえばいいじゃん。という流れになり、新人教育用の資料を順次更新していくことにしました。

※後々、リクエストに応じて更新することが多いのでストックしておくことをおすすめします。

自分はTEMONA株式会社でCTOをしていますが、頭でっかちに理論ばっかり学習するよりは、イメージがなんとなく掴めるように学習し、実践の中で知識を深めていく方が効率的に学習出来ると考えています。

※他の登壇やインタビュー記事はWantedlyから見てください。

教育スタイルとしては正しい事をきっちりかっちり教えるのではなく、未経験レベルの人がなんとなく掴めるように、資料を構成していきます。

以下のようなシリーズネタで進めます。

No. 記事
1 【新人教育 資料】第1章 SQLへの道 〜DB編〜
2 【新人教育 資料】第2章 SQLへの道 〜3値論理編〜
3 【新人教育 資料】第3章 SQLへの道 〜基本数学編〜
4 【新人教育 資料】第4章 SQLへの道 〜SQL基本操作編〜
5 【新人教育 資料】第5章 SQLへの道 〜絞込編〜
6 【新人教育 資料】第6章 SQLへの道 〜ソート編〜
7 【新人教育 資料】第7章 SQLへの道 〜集合関数(SUM、MAX、MIN、AVG、COUNT)編〜
8 【新人教育 資料】第8章 SQLへの道 〜グループ編〜
9 【新人教育 資料】第9章 SQLへの道 〜結合編〜

では、今回もはじめていきましょう!

SQL ソート ORDER BY

ソートは、データの並べ替えのことです。昇順、降順のことですね。
めちゃくちゃ大事な事が標準SQL規格99に書いてあります。

mariadbのドキュメントを参考に見てみると
https://mariadb.com/kb/en/sql-99/order-by-clause/

An ORDER BY clause may optionally appear after a <query expression>: it specifies the order rows should have when returned from that query (if you omit the clause, your DBMS will return the rows in some random order). The query in question may, of course, be a VALUES statement, a TABLE statement or (most commonly) a SELECT statement. (For now, we are ignoring the possibility that the SQL statement is DECLARE CURSOR, since that is strictly a matter for "SQL in host programs", a later chapter.)

要約するとORDER BY で並び替えを明示しない限り、RMDBSの実装に委ねることになるということです。
特にPostgreSQLは追記型DBのアーキテクチャを採用しているので、ORDER BY でソートを明示しないと
びっくりするぐらい順序が変わってしまいます。

データベースの管理として追記型DBでは、レコードに更新があった場合に、元々あったレコードを利用するのではなく、前のあったレコードとは別に新しいレコードを追加させて、前のレコードのポインタ(位置情報だけを)を消すようなアーキテクチャです。

今回は紹介しませんが
PostgreSQLでは、あるテーブルをORDER BY 句でソートキーを明示せずにSELECTし、結果を取得した後、特定のレコードをUPDATE文で更新をした後、再度SELECTすると結果の順序が変わるのが確認出来ると思います。

では実際にSQLとしてどのようにソートを表現するのか見ていきましょう。

事前準備(終わっていない人は実行)

前回同様に、勉強用のレポジトリを用意しているので
下記のレポジトリをForkして、Readmeを参考に環境構築をしてください。
https://github.com/TEMONA/mysql_study

※全てにおいて自己責任でお願いします。

上記の事前準備が終わっていることとして、下記の説明を進めていきます。

$ mysql -u root
mysql> use mysql_study;

ORDER BY

SELECT * FROM テーブル名 ORDER BY ソートしたいカラム[,ソートしたいカラム] 並び順の指定
指定 説明
ASC 昇順
DESC 降順

並び順の指定を省略した場合はASCがデフォルトです。
複数のソートキーも対応しています。はじめに書くほど優先でカラムが実行されます。

弊社が用意したレポジトリのテストデータを利用した場合には
ユーザテーブルに1,000件データが入っているので、例にとりみていきましょう。

今までに教えていないところも触れながらいきます。

SELECT COUNT データの件数を数える

データの件数を数えます。

select count(*) from テーブル名;

実際に実行してみましょう

select count(*) from users;

image

LIMIT 取得する件数を表示する

select * from テーブル名 LIMIT 取得件数;

実際にやってみましょう。

select * from users LIMIT 10;

画像を見て頂くとわかりますが、ageの列をみると、並びはバラバラですね。
image

ORDER BY 並び順を省略してみる

select * from users ORDER BY age LIMIT 10;

image

ORDER BY ASC

select * from users ORDER BY age LIMIT 10;

image

DESCRIBE

テーブルの定義情報を確認することが出来るのがDESCRIBE文です。

DESCRIBE テーブル名;

image

上記の図で説明すると
左のカラムから「Field(カラム名)」、「Type(データ型)」、「Null(null制約)」、「Default(初期値)」、「Extra(その他)」となります。

RMDBSの種類によってDESCRIBE文の構文は違うので注意してください。

EXPLAIN

ここからは少しコストの話しも含めて理解するために「EXPLAIN」というSQLをご紹介します。
RMDBSではオプティマイザという機能があり、データの分布情報を示す、統計情報に合わせてRMDBSの内部で処理の最適化を行ってくれます。EXPLAINを使う事で内部の実行コストがある程度分かります。

EXPLAIN 出力フォーマット

カラム 意味
id SELECT 識別子。
select_type SELECT 型
table 出力行のテーブル
partitions 一致するパーティション
type 結合型
possible_keys 選択可能なインデックス
key 実際に選択されたインデックス
key_len 選択されたキーの長さ
ref インデックスと比較されるカラム
rows 調査される行の見積もり
filtered テーブル条件によってフィルタ処理される行の割合
Extra 追加情報

詳細のところはまたどこかで説明するとして今回紹介しておきたいのは

type」です。

種類 説明
const PRIMARY KEYまたはUNIQUEインデックスのルックアップによるアクセス。最速。
eq_ref JOINにおいてPRIARY KEYまたはUNIQUE KEYが利用される時のアクセスタイプ。constと似ているがJOINで用いられるところが違う。
ref ユニーク(PRIMARY or UNIQUE)でないインデックスを使って等価検索(WHERE key = value)を行った時に使われるアクセスタイプ。
range インデックスを用いた範囲検索。
index フルインデックススキャン。インデックス全体をスキャンする必要があるのでとても遅い。
ALL フルテーブルスキャン。インデックスがまったく利用されていないことを示す。OLTP系の処理では改善必須。

詳細は以下のページを御覧ください。
MySQL 5.6 リファレンスマニュアル / ... / EXPLAIN 出力フォーマット

実際にEXPLAIN文を追加してSQLクエリを実行していきましょう。

主キーをソートキーに指定してみる

EXPLAIN SELECT * FROM users ORDER BY id DESC  LIMIT 10 ;

「type」が「index」になっていますね。
「rows」が「10」になっていますね。

これは索引となるINDEXだけをみたので、検索時に触った件数が「10」件だった事が読み取れます。

image

主キー以外をソートキーに指定してみる

では主キー以外をソートキーに指定してみるとどうでしょうか?

EXPLAIN SELECT * FROM users ORDER BY name DESC  LIMIT 10 ;

「type」が「all」になっていますね。
「rows」が「100」になっていますね。
「Extra」が「Using filesort」になっていますね。

これは索引となるINDEXだけが存在しなかったため、テーブルの情報を全件検索した後に、ソート(並び替え)を行ってから、結果を表示していることが読み取れます。名前のカラムに対して降順で10件取得したいだけなのに、わざわざテーブルの全件を検索してから、並び替えた後に検索結果を10件と絞り込んでいます。

このテーブルの全件を検索してからという箇所が大変やっかいな話しなのです。

【新人教育 資料】ハードウェア編で紹介した、まな板や冷蔵庫の話で説明します。

RMDBSはパフォーマンスを最大化するためにハードウェアのリソース(CPU,メモリ,HDD)を頑張って利用する設計に大抵はなっていますが、このテーブル上のデータを律儀に全件を検索してからというのは、モノを取り出すのに遅い、冷蔵庫から毎回データを取り出していることになります。

しかも10件だけ表示したいのに、テーブルの全件を検索するなんて、とてもナンセンスですよね。
100件だけだったら、何も差を感じないと思いますが、これがテーブルに保持されているレコード件数が1000万件だったらどうでしょうか?想像してみてください。

image

最近のRMDBSのオプティマイザもだいぶ頭が良くなっていますが、解決するには色々チューニングも含めて対応していく必要があります。

※少しむずかしい話もしってみたいという人は以下のページをよく読んでみてください。
8.2.1.15 ORDER BY の最適化

照合順序というお話

あとで書きます。

演習

参考文献

mysql5.6 リファレンスマニュアル: https://dev.mysql.com/doc/refman/5.6/ja/

あとがき

この章で紹介しましたが、ORDER BY句がない場合は取得順序に保証がありません。特にPostgreSQLのような追記型だと如実に困るのでORDER BY句は必ず入れるようにしましょう。例えば、何かのデータの表示をID降順で表示させたい場合には、UPDATE文が実行されしまうと、表示順序が変わってしまいます。ORDER BYを明示で入れることが必要です。

実際にはDBをデータ移行する自体が発生しましたとか、マスタ用のデータで表示順序を変えたいみたいな要望が出たりするので
データ保持用のカラム以外に、表示順序番号みたいなカラムを別途用意し、意図的に表示順序変えられるようにするなどの考慮が必要になってきます。

次回は「【新人教育 資料】第7章 SQLへの道 〜集合関数(SUM、MAX、MIN、AVG、COUNT)編〜」をお送りする予定です。

明日はヒカラボに登壇する予定です。もしかしてこの記事を見てくれている人もお会いするかもしれないですね。もし見たって人がいれば声をかけてください。

27
31
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
31