1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SQLパズル p178 42 魚のサンプリング調査の 解法3の学習メモ

Posted at

概要

SQLパズル p178 42 魚のサンプリング調査の 解法3の学習メモです。
COALESCE式の挙動を理解するのに手間取りました。
大枠はよくあるグループ化と集約関数系の問題です。

要件

group_id、fish_name毎の平均取得数【匹/回】を取得したい。
今回の場合だと
group_id=1 fish_name='pike'の平均取得数を取得したい。

image.png

SQL

DDL

DROP TABLE IF EXISTS Samples;
CREATE TABLE Samples
(sample_id INTEGER NOT NULL,
 fish_name CHAR(20) NOT NULL,
 found_tally INTEGER NOT NULL,
    PRIMARY KEY (sample_id, fish_name));


DROP TABLE IF EXISTS SampleGroups;
CREATE TABLE SampleGroups
(group_id INTEGER NOT NULL,
 group_descr CHAR(20) NOT NULL,
 sample_id INTEGER NOT NULL,
    PRIMARY KEY (group_id, sample_id));

INSERT INTO Samples VALUES (1, 'minnow', 18);
INSERT INTO Samples VALUES (1, 'pike', 7);
INSERT INTO Samples VALUES (2, 'pike', 4);
INSERT INTO Samples VALUES (2, 'carp', 3);
INSERT INTO Samples VALUES (3, 'carp', 9);

INSERT INTO SampleGroups VALUES (1, 'muddy water', 1);
INSERT INTO SampleGroups VALUES (1, 'muddy water', 2);
INSERT INTO SampleGroups VALUES (1, 'muddy water', 3);
INSERT INTO SampleGroups VALUES (2, 'fresh water', 1);
INSERT INTO SampleGroups VALUES (2, 'fresh water', 3);
INSERT INTO SampleGroups VALUES (2, 'fresh water', 2);


DROP TABLE IF EXISTS Samples_clone;
CREATE TABLE Samples_clone
(sample_id INTEGER NOT NULL,
 fish_name CHAR(20),
 found_tally INTEGER
);
INSERT INTO Samples_clone VALUES (1, 'minnow', 18);
INSERT INTO Samples_clone VALUES (1, 'pike', 7);
INSERT INTO Samples_clone VALUES (2, 'pike', 4);
INSERT INTO Samples_clone VALUES (2, 'carp', 3);
INSERT INTO Samples_clone VALUES (4, null, null);
INSERT INTO Samples_clone VALUES (4, null, null);
INSERT INTO Samples_clone VALUES (4, 'pike', 9);
INSERT INTO Samples_clone VALUES (4, null, 3);

答えのSQL

-- 42 魚のサンプリング調査 その3
-- COALESCEを使った解法
-- COALESCEは一匹も取得できなかった場合に対処するために使われる
SELECT COALESCE(fish_name, 'pike'),
        AVG(COALESCE(found_tally,0))
        FROM Samples AS SA
	        RIGHT OUTER JOIN
			SampleGroups AS SG
			ON SA.sample_id = SG.sample_id
		AND SA.fish_name = 'pike'
		WHERE SG.group_id = 1
		GROUP BY COALESCE(fish_name,'pike')
;

-- 外部結合
-- 3回目は1匹も取得されていないのでSamplesのレコードがnullになる
SELECT *
FROM Samples AS SA
	RIGHT OUTER JOIN
	SampleGroups AS SG
	ON SA.sample_id = SG.sample_id
		AND SA.fish_name = 'pike'
	WHERE
	SG.group_id = 1
	;

--  fish_nameで部分集合を作り平均値を求める
-- ※fish_name=null のときは 'pike'の部分集合に含める。
-- 回答のSQLの処理を詳しく把握するためにSELECT句に追加する
SELECT COALESCE(fish_name,'pike'),count(*)
	,AVG(found_tally) AS NG_avg_no_COALESCE
	,AVG(COALESCE(found_tally,0)) AS OK_avg_no_COALESCE
FROM Samples AS SA
	RIGHT OUTER JOIN
	SampleGroups AS SG
	ON SA.sample_id = SG.sample_id
		AND SA.fish_name = 'pike'
	WHERE
	SG.group_id = 1
	GROUP BY COALESCE(fish_name,'pike')
;



--GROUP BY fish_name でグループ化した場合、部分集合がnullとpikeの2つができてしまい
-- 正しい平均取得数を取得できない
SELECT fish_name,count(*)
	,AVG(found_tally) AS NG_avg_no_COALESCE
	,AVG(COALESCE(found_tally,0)) AS OK_avg_no_COALESCE
FROM Samples AS SA
	RIGHT OUTER JOIN
	SampleGroups AS SG
	ON SA.sample_id = SG.sample_id
		AND SA.fish_name = 'pike'
	WHERE
	SG.group_id = 1
	GROUP BY fish_name
;




-- COALESCEの動きを確認する用
-- 今回初めて見るものなので簡単に確認
SELECT COALESCE(fish_name,'nulldesu'),count(*)
FROM Samples_clone
GROUP BY COALESCE(fish_name,'nulldesu')
	;
	

image.png

番外編:COALESCEの動きを確認する用

image.png

参考

SQLパズル p178

SQLパズル サポートページ
https://mickindex.sakura.ne.jp/database/db_support_sqlpuzzle.html#LocalLink-p42

達人に学ぶSQL徹底指南書第2版 該当する問題なし

github差分
https://github.com/RYA234/SQL_Puzzle_Learning/commit/7063438271a2c938e8c41a6abbb2078a3c57e461

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?