VoiceSementle / init.sql
SJLee-0525
[CHORE] test9
9f30ef0
raw
history blame
8.47 kB
-- ============================================
-- ์Œ์„ฑ ๊ผฌ๋งจํ‹€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ
-- ============================================
-- ============================================
-- 1. ํผ์ฆ ์ •๋ณด ํ…Œ์ด๋ธ” (๋งˆ์Šคํ„ฐ ๋ฐ์ดํ„ฐ)
-- ============================================
CREATE TABLE puzzles (
puzzle_number INT PRIMARY KEY,
-- ์ •๋‹ต ์ •๋ณด
answer_word VARCHAR(100) NOT NULL,
-- ํผ์ฆ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ
difficulty VARCHAR(20), -- 'easy', 'medium', 'hard'
category VARCHAR(50), -- ์นดํ…Œ๊ณ ๋ฆฌ (์˜ˆ: 'meme', 'movie', 'song')
-- ๋‚ ์งœ ์ •๋ณด
puzzle_date DATE NOT NULL UNIQUE, -- ํผ์ฆ ๋‚ ์งœ (YYYY-MM-DD)
-- ํƒ€์ž„์Šคํƒฌํ”„
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- ์ œ์•ฝ ์กฐ๊ฑด
CONSTRAINT positive_puzzle_number CHECK (puzzle_number > 0)
);
CREATE INDEX idx_puzzle_date ON puzzles(puzzle_date);
COMMENT ON TABLE puzzles IS '์ผ์ผ ํผ์ฆ ์ •๋ณด - ์ •๋‹ต ๋‹จ์–ด';
-- ============================================
-- 2. ์ถ”์ธก ๊ธฐ๋ก ํ…Œ์ด๋ธ” (์Œ์„ฑ ํŒŒ์ผ ์—†์ด ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋งŒ)
-- ============================================
CREATE TABLE guess_records (
-- ๊ธฐ๋ณธ ํ‚ค
id BIGSERIAL PRIMARY KEY,
-- ๊ฒŒ์ž„ ์ปจํ…์ŠคํŠธ
session_id VARCHAR(100) NOT NULL,
puzzle_number INT NOT NULL,
-- AI ๋ถ„์„ ๊ฒฐ๊ณผ
pitch DECIMAL(5, 2), -- AI ์Œ๋†’์ด ๋ถ„์„ (0-100)
rhythm DECIMAL(5, 2), -- AI ๋ฆฌ๋“ฌ๊ฐ ๋ถ„์„ (0-100)
energy DECIMAL(5, 2), -- AI ์—๋„ˆ์ง€ ๋ถ„์„ (0-100)
pronunciation DECIMAL(5, 2), -- AI ๋ฐœ์Œ ๋ถ„์„ (0-100)
transcript TEXT, -- AI ๋ถ„์„ ๋Œ€์‚ฌ (score, kept for compatibility)
user_text TEXT, -- STT transcription from MCP
overall DECIMAL(5, 2), -- AI ์ด์  (0-100)
advice TEXT, -- AI ์กฐ์–ธ
-- ์ •๋‹ต ์—ฌ๋ถ€
is_correct BOOLEAN DEFAULT FALSE,
-- ํƒ€์ž„์Šคํƒฌํ”„
guess_timestamp BIGINT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- ์ œ์•ฝ ์กฐ๊ฑด
CONSTRAINT pitch_range CHECK (pitch IS NULL OR (pitch >= 0 AND pitch <= 100)),
CONSTRAINT rhythm_range CHECK (rhythm IS NULL OR (rhythm >= 0 AND rhythm <= 100)),
CONSTRAINT energy_range CHECK (energy IS NULL OR (energy >= 0 AND energy <= 100)),
CONSTRAINT pronunciation_range CHECK (pronunciation IS NULL OR (pronunciation >= 0 AND pronunciation <= 100)),
CONSTRAINT overall_range CHECK (overall IS NULL OR (overall >= 0 AND overall <= 100))
);
-- ============================================
-- ์ธ๋ฑ์Šค
-- ============================================
CREATE INDEX idx_guess_session ON guess_records(session_id);
CREATE INDEX idx_guess_puzzle ON guess_records(puzzle_number);
CREATE INDEX idx_guess_created ON guess_records(created_at);
CREATE INDEX idx_guess_correct ON guess_records(is_correct);
CREATE INDEX idx_guess_overall ON guess_records(overall);
COMMENT ON TABLE guess_records IS '์‚ฌ์šฉ์ž ์ถ”์ธก ๊ธฐ๋ก - AI ๋ถ„์„ ๊ฒฐ๊ณผ๋งŒ ์ €์žฅ (์Œ์„ฑ ํŒŒ์ผ ์—†์Œ)';
-- ============================================
-- 3. ๋‚ ์งœ๋ณ„ ์ „์ฒด ํ†ต๊ณ„ VIEW (๋Œ€์‹œ๋ณด๋“œ์šฉ)
-- ============================================
CREATE OR REPLACE VIEW daily_statistics AS
SELECT
p.puzzle_date,
-- ์ฐธ์—ฌ์ž ์ˆ˜ (ํ•ด๋‹น ๋‚ ์งœ์— ์‹œ๋„ํ•œ ๊ณ ์œ  ์„ธ์…˜ ์ˆ˜)
COUNT(DISTINCT gr.session_id) as participants,
-- ์ •๋‹ต์ž ์ˆ˜ (ํ•ด๋‹น ๋‚ ์งœ์— ์ •๋‹ต ๋งž์ถ˜ ๊ณ ์œ  ์„ธ์…˜ ์ˆ˜)
COUNT(DISTINCT CASE WHEN gr.is_correct = true THEN gr.session_id END) as winners,
-- ์ •๋‹ต๋ฅ 
ROUND(
CASE
WHEN COUNT(DISTINCT gr.session_id) > 0
THEN (COUNT(DISTINCT CASE WHEN gr.is_correct = true THEN gr.session_id END)::NUMERIC
/ COUNT(DISTINCT gr.session_id) * 100)
ELSE 0
END,
1
) as success_rate,
-- ์‹œ๋„ ํšŸ์ˆ˜ (ํ•ด๋‹น ๋‚ ์งœ ์ด ์Œ์„ฑ ์‹œ๋„ ์ˆ˜)
COUNT(*) as total_attempts,
-- ํ‰๊ท  ์‹œ๋„ ํšŸ์ˆ˜ (์ฐธ์—ฌ์ž๋‹น)
ROUND(
CASE
WHEN COUNT(DISTINCT gr.session_id) > 0
THEN COUNT(*)::NUMERIC / COUNT(DISTINCT gr.session_id)
ELSE 0
END,
1
) as avg_attempts_per_user
FROM puzzles p
LEFT JOIN guess_records gr ON p.puzzle_number = gr.puzzle_number
GROUP BY p.puzzle_date
ORDER BY p.puzzle_date DESC;
COMMENT ON VIEW daily_statistics IS '๋‚ ์งœ๋ณ„ ์ „์ฒด ๊ฒŒ์ž„ ํ†ต๊ณ„ - ๋Œ€์‹œ๋ณด๋“œ์šฉ';
-- ============================================
-- 4. ์ „์ฒด ๋ˆ„์  ํ†ต๊ณ„ VIEW (๋Œ€์‹œ๋ณด๋“œ์šฉ)
-- ============================================
CREATE OR REPLACE VIEW overall_statistics AS
SELECT
-- ์ „์ฒด ์ฐธ์—ฌ์ž ์ˆ˜ (๋ชจ๋“  ์‹œ๊ฐ„ ๋™์•ˆ ์ฐธ์—ฌํ•œ ๊ณ ์œ  ์„ธ์…˜ ์ˆ˜)
COUNT(DISTINCT session_id) as total_participants,
-- ์ „์ฒด ์ •๋‹ต์ž ์ˆ˜ (ํ•œ ๋ฒˆ์ด๋ผ๋„ ์ •๋‹ต ๋งž์ถ˜ ๊ณ ์œ  ์„ธ์…˜ ์ˆ˜)
COUNT(DISTINCT CASE WHEN is_correct = true THEN session_id END) as total_winners,
-- ์ „์ฒด ์ •๋‹ต๋ฅ  (์ •๋‹ต์ž ์ˆ˜ / ์ฐธ์—ฌ์ž ์ˆ˜)
ROUND(
CASE
WHEN COUNT(DISTINCT session_id) > 0
THEN (COUNT(DISTINCT CASE WHEN is_correct = true THEN session_id END)::NUMERIC
/ COUNT(DISTINCT session_id) * 100)
ELSE 0
END,
1
) as overall_success_rate,
-- ์ „์ฒด ์‹œ๋„ ํšŸ์ˆ˜
COUNT(*) as total_attempts,
-- ์ „์ฒด ํผ์ฆ ์ˆ˜
(SELECT COUNT(*) FROM puzzles) as total_puzzles,
-- ์ „์ฒด ํ‰๊ท  ์‹œ๋„ ํšŸ์ˆ˜
ROUND(
CASE
WHEN COUNT(DISTINCT session_id) > 0
THEN COUNT(*)::NUMERIC / COUNT(DISTINCT session_id)
ELSE 0
END,
1
) as avg_attempts_per_user
FROM guess_records;
COMMENT ON VIEW overall_statistics IS '์ „์ฒด ๋ˆ„์  ๊ฒŒ์ž„ ํ†ต๊ณ„ - ๋Œ€์‹œ๋ณด๋“œ์šฉ';
-- ============================================
-- 5. ๊ฐœ๋ณ„ ์‚ฌ์šฉ์ž ํ†ต๊ณ„ VIEW
-- ============================================
CREATE OR REPLACE VIEW user_statistics AS
WITH
daily_activity AS (
SELECT
gr.session_id,
p.puzzle_date,
COUNT(*) as attempts,
MAX(CASE WHEN gr.is_correct = true THEN 1 ELSE 0 END) as won
FROM guess_records gr
JOIN puzzles p ON gr.puzzle_number = p.puzzle_number
GROUP BY gr.session_id, p.puzzle_date
),
streaks AS (
SELECT
session_id,
puzzle_date,
won,
puzzle_date - (ROW_NUMBER() OVER (PARTITION BY session_id ORDER BY puzzle_date))::int AS play_streak_group
FROM daily_activity
),
current_play_streak AS (
SELECT
session_id,
COUNT(*) as play_streak
FROM streaks
WHERE play_streak_group = (
SELECT play_streak_group
FROM streaks s2
WHERE s2.session_id = streaks.session_id
ORDER BY puzzle_date DESC
LIMIT 1
)
GROUP BY session_id
),
win_streaks AS (
SELECT
session_id,
COUNT(*) as win_streak
FROM (
SELECT
session_id,
puzzle_date,
won,
SUM(CASE WHEN won = 0 THEN 1 ELSE 0 END)
OVER (PARTITION BY session_id ORDER BY puzzle_date DESC) as loss_count
FROM daily_activity
) ws
WHERE loss_count = 0 AND won = 1
GROUP BY session_id
)
SELECT
da.session_id,
COUNT(DISTINCT da.puzzle_date) as total_plays,
SUM(da.attempts) as total_attempts,
SUM(da.won) as wins,
ROUND(
CASE
WHEN COUNT(DISTINCT da.puzzle_date) > 0
THEN (SUM(da.won)::NUMERIC / COUNT(DISTINCT da.puzzle_date) * 100)
ELSE 0
END,
1
) as win_rate,
COALESCE(cps.play_streak, 0) as play_streak,
COALESCE(ws.win_streak, 0) as win_streak
FROM daily_activity da
LEFT JOIN current_play_streak cps ON da.session_id = cps.session_id
LEFT JOIN win_streaks ws ON da.session_id = ws.session_id
GROUP BY da.session_id, cps.play_streak, ws.win_streak;
COMMENT ON VIEW user_statistics IS '๊ฐœ๋ณ„ ์‚ฌ์šฉ์ž ๊ฒŒ์ž„ ํ†ต๊ณ„';
-- ============================================
-- ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ (ํ…Œ์ŠคํŠธ์šฉ)
-- ============================================
INSERT INTO puzzles (puzzle_number, answer_word, puzzle_date, difficulty, category)
VALUES
(1, 'Tralalero Tralala', '2024-11-24', 'easy', 'meme'),
(2, 'Wingardium Leviosa', '2024-11-25', 'medium', 'movie'),
(3, 'Baby shark, doo doo doo doo doo doo', '2024-11-26', 'hard', 'song');