スキンケアデータベースで練習問題(SQLのJOIN・GROUP BY・WITH句など)

IT

こんばんは、Yuinaです🗺️

今日は、データベースの練習問題を作ってみました。

今回のブログでは、スキンケア大好きギャルたちの「レビュー」や「購入履歴」から、人気のアイテムや肌タイプ別の傾向をデータで見ていきます💄💕「綺麗な肌を目指す方たちのスキンケア記録」がデータになってたら、めっちゃ楽しそうじゃないですか〜?

加えて、自分の肌タイプに合う商品がデータでわかったら、最強にうれしくないですか?!

それでは、うるツヤ肌とSQLスキル、ダブルで手に入れちゃいましょうっ👑✨

環境:MySQL / SQLite / PostgreSQL(構文を少し調整すればOK)
テーマ:👇✨

このプロジェクトは、スキンケアに関するユーザーの行動・商品レビュー・購入傾向などを分析できるように設計された練習用のデータベースです。
データ分析やSQLの練習にピッタリなように、「肌タイプ」「商品カテゴリ」「レビュー」「継続購入者」などを用意しました。

テーブル名内容
usersスキンケアを使っているユーザーのプロフィール情報(年齢、肌タイプなど)
products商品の情報(カテゴリや価格など)
purchasesユーザーがいつどの商品を買ったかの履歴
reviews商品の評価(5段階)やコメントなどのレビュー

users:

user_id,name,age,skin_type
1,ゆいぴ,33,sensitive
2,まいち,31,combination
3,さきぽよ,28,oily
4,なつたん,33,combination
5,えりちょ,18,sensitive
6,りんご姫,25,dry
7,あやまる,30,sensitive
8,ちなてぃ,20,sensitive
9,ももにゃん,27,oily
10,かれんぴ,30,sensitive

product:

product_id,name,category,price
1,うるぷる化粧水,toner,3223
2,てかりブロック乳液,moisturizer,2316
3,ガチ守る日焼け止め,sunscreen,1396
4,ふわふわ洗顔フォーム,cleanser,2947
5,モチモチ美容液,serum,3008
6,毛穴バイバイパック,mask,1632
7,透き通る化粧水,toner,1336
8,水光肌ジェル,moisturizer,1567
9,バリアサポートUV,sunscreen,3047
10,ぷるんリップ美容液,serum,1752

purchase:

purchase_id,user_id,product_id,purchase_date
1,1,5,2025-02-02
2,1,10,2025-02-28
3,1,9,2025-03-24
4,2,8,2025-04-20
5,2,4,2025-01-05
6,2,2,2025-03-28
7,2,7,2025-03-24
8,3,9,2025-03-27
9,3,3,2025-03-01
10,3,3,2025-03-07

reviews:

review_id,user_id,product_id,rating,comment
1,10,8,2,乾燥しにくくなった!
2,4,9,5,ヒリヒリしないの最高🥹
3,3,4,2,ヒリヒリしないの最高🥹
4,6,5,1,マジ神!肌変わった🥺
5,5,1,1,ヒリヒリしないの最高🥹
6,10,4,3,ヒリヒリしないの最高🥹
7,5,6,4,ヒリヒリしないの最高🥹
8,2,6,4,香りがエモい!
9,3,4,4,うーん、ちょっとベタつくかも
10,10,7,5,友達にもおすすめしたい💕

全テーブルとカラム:

mysql> show tables;
+---------------------+
| Tables_in_skin_care |
+---------------------+
| product             |
| purchase            |
| reviews             |
| users               |
+---------------------+
4 rows in set (0.02 sec)

mysql> desc product;
+------------+------+------+-----+---------+-------+
| Field      | Type | Null | Key | Default | Extra |
+------------+------+------+-----+---------+-------+
| product_id | int  | YES  |     | NULL    |       |
| name       | text | YES  |     | NULL    |       |
| category   | text | YES  |     | NULL    |       |
| pri      | int  | YES  |     | NULL    |       |
+------------+------+------+-----+---------+-------+
4 rows in set (0.01 sec)

mysql> desc purchase;
+---------------+------+------+-----+---------+-------+
| Field         | Type | Null | Key | Default | Extra |
+---------------+------+------+-----+---------+-------+
| purchase_id   | int  | YES  |     | NULL    |       |
| user_id       | int  | YES  |     | NULL    |       |
| product_id    | int  | YES  |     | NULL    |       |
| purchase_date | date | YES  |     | NULL    |       |
+---------------+------+------+-----+---------+-------+
4 rows in set (0.01 sec)

mysql> desc reviews;
+------------+------+------+-----+---------+-------+
| Field      | Type | Null | Key | Default | Extra |
+------------+------+------+-----+---------+-------+
| review_id  | int  | YES  |     | NULL    |       |
| user_id    | int  | YES  |     | NULL    |       |
| product_id | int  | YES  |     | NULL    |       |
| rating     | int  | YES  |     | NULL    |       |
| comment    | text | YES  |     | NULL    |       |
+------------+------+------+-----+---------+-------+
5 rows in set (0.01 sec)

mysql> desc users;
+-----------+------+------+-----+---------+-------+
| Field     | Type | Null | Key | Default | Extra |
+-----------+------+------+-----+---------+-------+
| user_id   | int  | NO   |     | NULL    |       |
| name      | text | NO   |     | NULL    |       |
| age       | int  | NO   |     | NULL    |       |
| skin_type | text | NO   |     | NULL    |       |
+-----------+------+------+-----+---------+-------+
4 rows in set (0.00 sec)

練習問題


問題1:カテゴリごとの商品の平均価格を求めよ(GROUP BY)

問題2:肌質ごとのユーザー数(WITH句 + GROUP BY)

問題3:一人あたりの平均購入金額を算出せよ(JOIN + GROUP BY)

問題4:課金率を求めよ(ゲームでいう課金率)

 定義:課金ユーザー数 ÷ 全ユーザー数(1回でも購入した人を課金ユーザーとみなす)

問題5:最もレビュー数が多い商品は?(JOIN + 集計)

問題6:30代で、最も使われているカテゴリ(JOIN + WHERE + GROUP)

答え

問題1:

SELECT 
	category, 
	AVG(price) 
FROM 
	users t1 
INNER JOIN purchase t2 
ON 
	t1.user_id = t2.user_id 
LEFT JOIN product t3
ON 
	t2.product_id = t3.product_id 
GROUP BY 
	category;

※ROUND(AVG(price),2)で小数点以下を四捨五入するとより良し!

問題2:

SELECT
  t1.name,
  AVG(t3.price) AS avg_price
FROM 
  users t1
INNER JOIN purchase t2
  ON t1.user_id = t2.user_id
INNER JOIN product t3
  ON t2.product_id = t3.product_id
GROUP BY
  t1.user_id, t1.name;

問題3:

SELECT
	AVG(price) 
FROM 
	users t1 
INNER JOIN 
	purchase t2 
ON 
	t1.user_id = t2.user_id 
LEFT JOIN 
	product t3 
ON 
	t2.product_id = t3.product_id;

userspurchaseINNER JOIN:「購入履歴があるユーザー」のみ対象。

purchaseproductLEFT JOIN:「購入履歴はあるけど、productに紐づいていない」ケースも含む。

※ROUND(AVG(price),2)で小数点以下を四捨五入するとより良し!

問題4:

WITH  
  paying_user AS (
    SELECT COUNT(DISTINCT user_id) AS paying 
    FROM purchase
  ),
  total_user AS (
    SELECT COUNT(*) AS total 
    FROM users
  )
SELECT   
  paying_user.paying * 1.0 / total_user.total AS paying_ratio 
FROM   
  paying_user, 
  total_user;

問題5:

SELECT 
  t1.name, 
  COUNT(t2.review_id) AS review
FROM 
  product t1 
LEFT OUTER JOIN reviews t2 
  ON t1.product_id = t2.product_id 
GROUP BY 
  t1.name 
ORDER BY 
  review DESC
LIMIT 1;

LEFT OUTER JOIN :「レビューが1件もない商品」も集計対象に含みたい。

問題6:

SELECT
	t3.category,
	COUNT(*) AS category_count 
FROM   
	users t1
INNER JOIN 
	purchase t2 
ON 
	t1.user_id = t2.user_id
INNER JOIN 
	product t3 
ON 
	t2.product_id = t3.product_id 
WHERE   
	t1.age BETWEEN 30 AND 39 
GROUP BY   
	t3.category 
ORDER BY   
	category_count DESC
LIMIT 2;

userspurchaseINNER JOIN:ユーザーと紐づく「購入履歴」がある人だけを対象にしたい。

購入していないユーザー(=データがない)は分析に不要。

purchaseproductINNER JOIN:対応する商品(=購入された商品)が存在するものだけを対象にして、その商品の「カテゴリ」が知りたい。

購入していないユーザー(=データがない)は分析に不要。

まとめ

今回は、SQLのJOINを使って「レビュー数」や「カテゴリ別集計」など、よくある分析をやってみました✨

実際に手を動かしてみると、INNER JOINとLEFT JOINの違いや、GROUP BYの扱いに悩むことも多くて…まだまだ初心者です💦

次回は、「課金率」や「リピート率」など、ユーザー行動にフォーカスした分析をやってみる予定です!

また、分析の設計やクエリの書き方について、「こうした方がいいよ」みたいなアドバイスがあれば、

ガンガンコメントで教えてください🙏一緒にSQLを上達していけたら嬉しいです!

ありがとうございました!

コメント

タイトルとURLをコピーしました