Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# ========================================
# プロジェクト用 .gitignore
# ========================================

# バイトコード / キャッシュ
__pycache__/
*.py[cod]
*$py.class
*.pyc
*.pyo
*.pyd

# 仮想環境
.env
.venv
.venv3.10.13
env/
venv/
ENV/
.Python

# パッケージ管理
*.egg
*.egg-info/
dist/
build/
.eggs/
wheels/
pip-wheel-metadata/
*.manifest
*.spec

# ログ / 一時ファイル
*.log
*.pot
*.tmp
*.swp
*.swo

# テスト関連
.coverage
.tox/
.nox/
.pytest_cache/
htmlcov/
coverage.xml
*.cover
*.py,cover

# Jupyter Notebook
.ipynb_checkpoints/
*/.ipynb_checkpoints/*
*.nbconvert.ipynb

# IDE / エディタ関連
.vscode/
.idea/
*.sublime-project
*.sublime-workspace

# OS依存ファイル
.DS_Store
Thumbs.db

# MyPy / Pyre / Type checker
.mypy_cache/
.dmypy.json
dmypy.json
.pyre/
.pytype/

# その他キャッシュ
.cache/
.pybuilder/

# データファイル(大容量のもの)
data/raw/
*.csv
*.xlsx
*.parquet
*.h5
*.hdf5

# 機械学習モデル
models/
*.pkl
*.joblib
*.pb

# 画像・動画ファイル
*.png
*.jpg
*.jpeg
*.gif
*.mp4
*.avi

# 環境ファイル
config.ini
credentials.json

# Node.js関連
node_modules/
package-lock.json
bun.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "bc30cee6",
"metadata": {},
"source": [
"# MySQL 8.0.40\n",
"\n",
"## 0) 前提\n",
"\n",
"* エンジン: **MySQL 8**\n",
"* 並び順: 任意(`ORDER BY` を付けない)\n",
"* `NOT IN` は NULL 罠のため回避\n",
"* 判定は **ID 基準**(ここでは `class`)、表示は仕様どおりの列名と順序\n",
"\n",
"## 1) 問題\n",
"\n",
"* `Courses` から **受講生が5人以上いる class を求める**\n",
"* 入力テーブル例: `Courses(student, class)`(主キー: `(student, class)`)\n",
"* 出力仕様: 列は `class` のみ/順序任意/重複なし\n",
"\n",
"## 2) 最適解(単一クエリ)\n",
"\n",
"> ウィンドウ関数で各クラスの人数を数え、閾値で抽出して最終投影。\n",
"\n",
"```sql\n",
"WITH win AS (\n",
" SELECT\n",
" class,\n",
" COUNT(*) OVER (PARTITION BY class) AS cnt\n",
" FROM Courses\n",
")\n",
"SELECT DISTINCT\n",
" class\n",
"FROM win\n",
"WHERE cnt >= 5;\n",
"\n",
"Runtime 311 ms\n",
"Beats 58.52%\n",
"\n",
"```\n",
"\n",
"* `(student, class)` が PK のため同一学生の同一クラス重複は存在せず、`COUNT(*)` で十分\n",
"* 結果順は任意なので `ORDER BY` なし\n",
"\n",
"## 3) 代替解\n",
"\n",
"> 単純集約で十分なサイズ・要件なら `GROUP BY ... HAVING` が最軽量。\n",
"\n",
"```sql\n",
"SELECT\n",
" class\n",
"FROM Courses\n",
"GROUP BY class\n",
"HAVING COUNT(*) >= 5;\n",
"\n",
"Runtime 308 ms\n",
"Beats 62.45%\n",
"\n",
"```\n",
"\n",
"## 4) 要点解説\n",
"\n",
"* **方針**: クラス単位で人数を数え、しきい値(5)以上のみ返す\n",
"* **NULL / 重複**: 主キー制約により `(student, class)` の重複はなし。`class` 自体が NULL の行がある想定なら、条件側で `class IS NOT NULL` を併記(今回は問題仕様上不要)\n",
"* **安定性**: 並び順指定なしで I/O を節約\n",
"\n",
"## 5) 計算量(概算)\n",
"\n",
"* ウィンドウ(最適解): パーティション内で **O(N)**〜**O(N log N)**(実装依存)\n",
"* 集約(代替解): ハッシュ集約で **O(N)** 近似\n",
"\n",
"## 6) 図解(Mermaid 超保守版)\n",
"\n",
"```mermaid\n",
"flowchart TD\n",
" A[入力 テーブル Courses] --> B[クラス単位で人数カウント]\n",
" B --> C[人数が5以上を抽出]\n",
" C --> D[出力 class 列のみ]\n",
"```\n",
"結論から言うと、この問題では **`GROUP BY ... HAVING` が最もシンプルで、実務でもまずこれを使います。**\n",
"ただし速度面をもう一段伸ばす余地はあります。\n",
"\n",
"## 速くするための実務的ポイント\n",
"\n",
"### 1) 二次インデックスを追加(最有効)\n",
"\n",
"`GROUP BY class` の集約を軽くするには **`class` にインデックス**を張るのが一番効きます。\n",
"\n",
"```sql\n",
"-- 目的: クラス単位の集約をインデックス範囲走査で処理\n",
"CREATE INDEX idx_courses_class ON Courses(class);\n",
"-- 余力があれば順序付与用に\n",
"-- CREATE INDEX idx_courses_class_student ON Courses(class, student);\n",
"```\n",
"\n",
"* PK が `(student, class)` なので、そのままだと `class` での集約に不利。\n",
"* `idx_courses_class` があると MySQL は **インデックス順に走査しながらグループ化**でき、\n",
" 場合によっては **テンポラリやファイルソートを回避**します(`EXPLAIN` で `Using index for group-by` を目指す)。\n",
"\n",
"### 2) クエリは `GROUP BY ... HAVING` を採用\n",
"\n",
"ウィンドウ関数版は **同じクラスの行を全て数えてから DISTINCT** するので一手間多く、通常やや不利です。\n",
"以下で十分最適です。\n",
"\n",
"```sql\n",
"SELECT\n",
" class\n",
"FROM Courses\n",
"GROUP BY class\n",
"HAVING COUNT(*) >= 5;\n",
"```\n",
"\n",
"### 3) 「存在判定」最適化(インデックスがある前提の代替案)\n",
"\n",
"**「5件目が存在するか」だけ**を確かめる相関サブクエリは、クラスごとに **最大5行だけ**見れば良いので、\n",
"**`(class)` か `(class, student)` インデックス**がある環境では速くなることがあります。\n",
"\n",
"```sql\n",
"-- (class, student) インデックスがあると更に安定\n",
"SELECT DISTINCT c.class\n",
"FROM Courses c\n",
"WHERE EXISTS (\n",
" SELECT 1\n",
" FROM Courses i\n",
" WHERE i.class = c.class\n",
" ORDER BY i.student\n",
" LIMIT 4, 1 -- 5件目が取れれば「5人以上」と判定\n",
");\n",
"```\n",
"\n",
"> 注意: これは **インデックスの効き**に強く依存します。`EXPLAIN` で内側が `range` / `ref` になっているか確認を。\n",
"\n",
"### 4) 実行計画チェック\n",
"\n",
"`EXPLAIN` で見るポイント\n",
"\n",
"* `type`: `range` / `ref`(全表 `ALL` は避けたい)\n",
"* `key`: 上記の新インデックスが選ばれているか\n",
"* `Extra`: `Using index for group-by` が出るとご機嫌\n",
"\n",
"## まとめ(提案の優先度)\n",
"\n",
"1. ✅ **`CREATE INDEX idx_courses_class (class)` を追加**\n",
"2. ✅ 本番クエリは **`GROUP BY ... HAVING COUNT(*) >= 5`**\n",
"3. ⭕ 負荷やデータ分布次第で、**`EXISTS + LIMIT 4,1`** 案を A/B して速い方を採用\n",
"\n",
"この3点で、提示の ~310ms からの短縮が十分見込めます。インデックス追加が難しい(LeetCode 等)なら、現状の **`GROUP BY ... HAVING` が最適解**です。\n"
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Loading