MySQL

重複行のまとめ方はGROUP BY?DISTINCT?

More than 1 year has passed since last update.


使うべきはどちらなのか?

重複レコードをまとめる時に、ふとGROUP BY と DISTINCT のどちらを使えば効率が良いのか迷うということがおきた。

結論から言ってしまうとどちらの関数も重複行をまとめるという目的で使われるため、どちらが良いと言うことはないらしい。が、気になったので調べてみる。

DISTINCTは実行した結果のテーブルから、重複している行を削除した結果を出す。

GROUP BY は実行した結果をグループ化して更に集計する際に用いる。

つまり


  • 単に重複を除いた結果をそのまま出すだけの場合はDISTINCT句

  • まとめた結果に対して何らかの処理を加える必要がある場合はGROUP BY句ということになる。

なので以下のようなクエリは

SELECT COUNT(column_xxx) FROM table_xxx GROUP BY column_xxx;

↓↓↓

SELECT COUNT(DISTINCT column_xxx) FROM table_xxx;

と記載すべきなんだろうけど、実際に測ってみるとGROUP BYの方が実行速度が早かったり。この辺りは内部でどういう処理をしているのか気になるところ。

だってGROUP BYで重複レコードがまとまるのはグループ化したから結果的にまとまったという、ある意味副作用なわけですし…DISTINCTの立場は一体…。

何万行もあるレコードをまとめる場合はEXISTS句を使う方が効率がいいです。先ほどの2つは内部で暗黙のソートがなされますが、EXISTS句はそうではないのでこちらのほうが高速といえますね。



追記 (2018/04/17)

結局どっちを使えばいいの?とフレンドからツッコミ今更になって少し更新してみた。

試しにこんなテーブルを作ってみる。

CREATE TABLE `company_users` (

`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`sex` varchar(255) DEFAULT NULL,
`position` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `company_users` (`id`, `name`, `age`, `sex`, `position`)

VALUES
(1, '鈴木一郎', 20, '男', '一般社員'),
(2, '田中二郎', 33, '男', '係長'),
(3, '高橋三葉', 27, '女', '一般社員'),
(4, '伊東四郎', 40, '男', '部長'),
(5, '佐藤五郎', 58, '男', '会長'),
(6, '渡辺六実', 44, '女', '社長'),
(7, '山本七海', 22, '女', '一般社員'),
(8, '山田八一', 36, '男', '係長'),
(9, '中村九', 52, '男', '一般社員'),
(10, '小林十成', 20, '男', '一般社員');

どういうタイミングでDISTINCTが使えるかと言うと特定のカラムのユニークの値だけ使う時です。

上記例のテーブルで会社にどんな役職があるのかをユニークで抜き出したい場合は以下のように記述します。

SELECT

DISTINCT `position` AS '役職'
FROM
`company_users`
;

役職

一般社員

係長

部長

会長

社長

ただし、同様の取得内容はGROUP BYでも記述できます。

SELECT

`position` AS '役職'
FROM
`company_users`
GROUP BY
`position`
;

役職

一般社員

係長

部長

会長

社長

別の例として、男女のそれぞれの人数を調べたいという場合のクエリをGROUP BYで書くと以下の様に書くことが出来ます。

SELECT

`sex` AS '性別',
COUNT(`sex`) AS '人数'
FROM
`company_users`
GROUP BY
`sex`
;

性別
人数


3


7

同様の内容をDISTINCTを用いて書くことは出来ません。

DISTINCTは 重複した内容を除外する という挙動となるため、こういった場合に利用することが出来ないです。

また、例外はありますがDISTINCTは2つ以上記載することが出来ません。

-- この書き方はエラーになる。

SELECT
DISTINCT `sex`,
DISTINCT `position`
FROM
`company_users`
;

-- 関数内であれば一応記述可能。あまり意味は無いが…。

SELECT
COUNT(DISTINCT `sex`),
COUNT(DISTINCT `position`)
FROM
`company_users`
;


まとめ

実際の所、DISTINCTの活躍の場は多くないと思うのですが、用途が限定的ではっきりしている。

DISTINCTで書ける場所はGROUP BYで書いた場合と実行計画や実速度と比較してどっちを使うか決めればいいんじゃないかな…?