veza/veza-backend-api/migrations/051_stats_views.sql
senke db7ad3dd7a [FIX] Fix migration errors for missing tables
- Add table existence checks before adding constraints/triggers
- Fix playback_analytics references (table doesn't exist)
- Fix playlist_versions references (table doesn't exist)
- Fix follows.deleted_at reference (column doesn't exist)
- Fix marketplace_products/orders triggers (tables don't exist)
- All migrations now pass successfully
2026-01-04 01:44:13 +01:00

137 lines
5.2 KiB
SQL

-- 051_stats_views.sql
-- Database Views for Common Queries (BE-DB-013: Add database views for common queries)
-- Create views for user_stats, track_stats, playlist_stats
-- === USER_STATS VIEW ===
-- View for user statistics aggregating data from multiple tables
CREATE OR REPLACE VIEW public.user_stats AS
SELECT
u.id AS user_id,
u.username,
u.email,
u.created_at AS user_created_at,
-- Track statistics
COUNT(DISTINCT t.id) AS tracks_count,
COALESCE(SUM(t.play_count), 0) AS total_track_plays,
COALESCE(SUM(t.like_count), 0) AS total_track_likes,
COALESCE(SUM(t.download_count), 0) AS total_track_downloads,
-- Playlist statistics
COUNT(DISTINCT p.id) AS playlists_count,
COALESCE(SUM(p.track_count), 0) AS total_playlist_tracks,
COALESCE(SUM(p.follower_count), 0) AS total_playlist_followers,
-- Social statistics
COUNT(DISTINCT f1.id) AS followers_count,
COUNT(DISTINCT f2.id) AS following_count,
-- Playback statistics
COUNT(DISTINCT tp.track_id) AS unique_tracks_played,
COUNT(tp.id) AS total_plays,
COALESCE(SUM(tp.duration), 0) AS total_play_duration,
CASE
WHEN COUNT(tp.id) > 0 THEN COALESCE(SUM(tp.duration), 0)::FLOAT / COUNT(tp.id)
ELSE 0
END AS average_play_duration,
-- Like statistics
COUNT(DISTINCT tl.track_id) AS liked_tracks_count,
-- Comment statistics
COUNT(DISTINCT tc.id) AS comments_count
FROM public.users u
LEFT JOIN public.tracks t ON t.creator_id = u.id AND t.deleted_at IS NULL
LEFT JOIN public.playlists p ON p.user_id = u.id AND p.deleted_at IS NULL
LEFT JOIN public.follows f1 ON f1.followed_id = u.id
LEFT JOIN public.follows f2 ON f2.follower_id = u.id
LEFT JOIN public.track_plays tp ON tp.user_id = u.id
LEFT JOIN public.track_likes tl ON tl.user_id = u.id
LEFT JOIN public.track_comments tc ON tc.user_id = u.id AND tc.deleted_at IS NULL
WHERE u.deleted_at IS NULL
GROUP BY u.id, u.username, u.email, u.created_at;
COMMENT ON VIEW public.user_stats IS 'Aggregated user statistics view including tracks, playlists, social, and playback metrics';
-- === TRACK_STATS VIEW ===
-- View for track statistics aggregating plays, likes, comments, and downloads
CREATE OR REPLACE VIEW public.track_stats AS
SELECT
t.id AS track_id,
t.title,
t.artist,
t.creator_id,
t.created_at AS track_created_at,
t.published_at,
-- Denormalized counts from tracks table
t.play_count AS views,
t.like_count AS likes,
t.comment_count AS comments,
t.download_count AS downloads,
-- Playback statistics
COUNT(DISTINCT tp.user_id) AS unique_listeners,
COUNT(tp.id) AS total_plays,
COALESCE(SUM(tp.duration), 0) AS total_play_time,
CASE
WHEN COUNT(tp.id) > 0 THEN COALESCE(SUM(tp.duration), 0)::FLOAT / COUNT(tp.id)
ELSE 0
END AS average_duration,
-- Share statistics
COUNT(DISTINCT ts.id) AS share_links_count,
COALESCE(SUM(ts.access_count), 0) AS total_share_accesses
FROM public.tracks t
LEFT JOIN public.track_plays tp ON tp.track_id = t.id
LEFT JOIN public.track_shares ts ON ts.track_id = t.id AND ts.deleted_at IS NULL
WHERE t.deleted_at IS NULL
GROUP BY t.id, t.title, t.artist, t.creator_id, t.created_at, t.published_at,
t.play_count, t.like_count, t.comment_count, t.download_count;
COMMENT ON VIEW public.track_stats IS 'Aggregated track statistics view including plays, likes, comments, and downloads';
-- === PLAYLIST_STATS VIEW ===
-- View for playlist statistics aggregating tracks, followers, and plays
CREATE OR REPLACE VIEW public.playlist_stats AS
SELECT
p.id AS playlist_id,
p.name AS playlist_name,
p.user_id AS creator_id,
p.created_at AS playlist_created_at,
p.updated_at AS playlist_updated_at,
-- Denormalized counts from playlists table
p.track_count AS tracks_count,
p.follower_count AS followers_count,
-- Track statistics (aggregated from playlist_tracks)
COUNT(DISTINCT pt.track_id) AS actual_track_count,
COALESCE(SUM(t.play_count), 0) AS total_track_plays,
COALESCE(SUM(t.like_count), 0) AS total_track_likes,
COALESCE(AVG(t.duration), 0) AS average_track_duration,
COALESCE(SUM(t.duration), 0) AS total_playlist_duration,
-- Collaboration statistics
COUNT(DISTINCT pc.user_id) AS collaborators_count,
-- Share statistics
COUNT(DISTINCT psl.id) AS share_links_count,
COALESCE(SUM(psl.access_count), 0) AS total_share_accesses
FROM public.playlists p
LEFT JOIN public.playlist_tracks pt ON pt.playlist_id = p.id
LEFT JOIN public.tracks t ON t.id = pt.track_id AND t.deleted_at IS NULL
LEFT JOIN public.playlist_collaborators pc ON pc.playlist_id = p.id AND pc.deleted_at IS NULL
LEFT JOIN public.playlist_share_links psl ON psl.playlist_id = p.id AND psl.deleted_at IS NULL
WHERE p.deleted_at IS NULL
GROUP BY p.id, p.name, p.user_id, p.created_at, p.updated_at,
p.track_count, p.follower_count;
COMMENT ON VIEW public.playlist_stats IS 'Aggregated playlist statistics view including tracks, followers, plays, and collaboration metrics';
-- Indexes for better query performance (if needed)
-- Note: Views don't support indexes directly, but indexes on underlying tables will help