라이징 프로그래머2
의 3주차 과제가 시작되었다. 이번 주차에는 데이터베이스를 다루며, 그중 일부분인 ERD 설계를 해보려고 한다. 본인은 AQueryTool
을 이용해서 진행했고 데이터베이스는 MySQL
을 사용했다.
일명 개체-관계 다이어그램
이라 불리는 ERD
는 다음과 같다.
ER(엔티티 관계) 다이어그램은 사람, 개체 또는 개념과 같은 "엔티티"가 시스템 내에서 서로 어떻게 관련되는지를 보여주는 순서도 유형이다. ERD 또는 ER 모델이라고도 하며 직사각형, 다이아몬드, 타원 및 연결 선과 같은 정의 된 기호세트를 사용하여 엔티티, 관계 및 속성의 상호 연결성을 묘사한다. 엔티티는 명사로, 관계는 동사로 사용하여 문법 구조를 반영한다.
개체-관계 모델
데이터 모델링 분야에서 개체-관계 모델이란 구조화된 데이터에 대한 일련의 표현이다. 구조화된 데이터를 저장하기 위해 데이터베이스를 사용한다. 이때, 이 데이터의 "구조" 및 그에 수반한 제약 조건들은 다양한 기법에 의해 설계될 수 있다. 그 기법 중 하나가 개체-관계 모델링(Entity-Relationship Diagram) 이라 하며 줄여서 ERM이라고 한다. 그리고 ERM 프로세스의 산출물을 가리켜 개체-관계 다이어그램(Entity-Relationship Diagram) 이라 한다. 데이터 모델링 과정은 데이터 모델을 그림으로 표현하기 위해 표시법을 필요로 한다. ERD는 개념적 데이터 모델 혹은 시멘틱 데이터 모델의 한 타입이다.
쉽게 말하자면 데이터베이스의 여러 테이블들을 개체라고 봤을 때, 각각의 개체들의 관계를 정의한 관계도라고 보면 된다. 저~~엉말 쉽게 생각하면 벤 다이어그램이 포함 관계를 나타내는 것처럼, 개체 간의 어떠한 관계를 나타낸다고 보면 된다.
무겁고 복잡, 불편한 기존 ERD 프로그램을 가볍고 깔끔하게, 사용하기 좋게 만든 ERD 프로그램이다.
AQueryTool
을 이용해서 음악 어플리케이션인 Melon
의 모바일 화면을 토대로 ERD를 설계해보았다. 전부 다 완성된 것은 아니며, 일부 화면들의 조합으로 이루어졌기 때문에 아직 부족한 부분들이 많다.
여기서 간과했던 것이, 어플리케이션의 주요 기능에 초점을 맞춰서 설계를 해야한다는 것이다. 내 경우에는 곡 재생목록, 좋아요 목록, 플레이리스트, 곡 정보, 앨범 정보 등을 토대로 설계를 하였다. 물론 이들도 중요하지만 Melon
과 같은 스트리밍 서비스의 경우, 어떤 유저가 어떤 음악을 언제, 얼마나 들었는지와 같은 것들이 꽤나 중요할 것인데 이 부분을 빼먹었다. 그리고 그를 기반으로 음원 차트를 상정하고 추천 서비스도 제공할 수 있어야 한다. 따라서 이를 HISTORY
와 같은 테이블을 하나 생성하던지 하는 등의 방안을 마련해 보완해줄 예정이다.
# 1. 최신곡
SELECT MUSIC_ID AS "음악 번호",
MUSIC_TITLE AS "곡 제목",
SINGER_NAME AS "가수 이름",
ALBUM_IMAGE AS "앨범 이미지 URL",
COUNTRY AS "국가",
DATE_FORMAT(RELEASE_DATE, '%Y.%m.%d')
FROM MUSIC m
INNER JOIN (SELECT ALBUM_ID, ALBUM_IMAGE, SINGER_NAME, COUNTRY, RELEASE_DATE
FROM SINGER s
INNER JOIN ALBUM a ON a.SINGER_ID = s.SINGER_ID) SINGER_AND_ALBUM
ON m.ALBUM_ID = SINGER_AND_ALBUM.ALBUM_ID;
# WHERE COUNTRY = 'KOR'
# ORDER BY RELEASE_DATE DESC;
# 2. 최신앨범
SELECT ALBUM_ID AS "앨범 번호",
ALBUM_TITLE AS "앨범 제목",
SINGER_NAME AS "가수 이름",
ALBUM_IMAGE AS "앨범 이미지 URL",
DATE_FORMAT(RELEASE_DATE, '%Y.%m.%d') AS "발매일",
COUNTRY AS "국가",
CASE
WHEN SCORED_USER_COUNT = 0 THEN 0
ELSE ROUND(TOTAL_SCORE / SCORED_USER_COUNT, 1)
END AS "별점",
SCORED_USER_COUNT AS "별점 매긴 유저수"
FROM ALBUM
INNER JOIN SINGER ON ALBUM.SINGER_ID = SINGER.SINGER_ID
# WHERE COUNTRY = 'KOR'
ORDER BY ALBUM.RELEASE_DATE DESC;
# 3. 음악 번호에 해당하는 뮤직비디오
SELECT MUSIC_VIDEO_ID AS "뮤비 번호",
MUSIC_VIDEO_NAME AS "뮤비 제목",
SINGER_NAME AS "가수 이름",
MUSIC_VIDEO_IMAGE AS "뮤비 썸네일 이미지 URL",
CASE
WHEN
CAST(SUBSTRING(TIME_FORMAT(PLAYTIME, '%i:%s'), 1, 1) AS UNSIGNED) < 1
THEN SUBSTRING(TIME_FORMAT(PLAYTIME, '%i:%s'), 2)
ELSE TIME_FORMAT(PLAYTIME, '%i:%s')
END AS "재생시간",
VIEW_COUNT AS "조회수",
DATE_FORMAT(RELEASE_DATE, '%Y.%m.%d') AS "뮤비 공개일"
FROM MUSIC_VIDEO
INNER JOIN (SELECT MUSIC.MUSIC_ID, SINGER_AND_ALBUM.SINGER_NAME
FROM MUSIC
INNER JOIN (SELECT ALBUM_ID, SINGER_NAME
FROM SINGER
INNER JOIN ALBUM ON SINGER.SINGER_ID = ALBUM.SINGER_ID) SINGER_AND_ALBUM
ON MUSIC.ALBUM_ID = SINGER_AND_ALBUM.ALBUM_ID) MUSIC_AND_SINGER_AND_ALBUM
ON MUSIC_VIDEO.MUSIC_ID = MUSIC_AND_SINGER_AND_ALBUM.MUSIC_ID
ORDER BY RELEASE_DATE;
# 4. 전체 유저가 좋아요 한 음악 (서브 쿼리 + 조인 버전)
SELECT NICKNAME AS "닉네임",
MUSIC_TITLE AS "곡 제목",
SINGER_NAME AS "가수 이름",
ALBUM_IMAGE AS "앨범 이미지 URL",
DATE_FORMAT(USER_LIKE_MUSICS.REGISTED_DATE, '%Y.%m.%d') AS "좋아요 한 날짜"
FROM USER
INNER JOIN (SELECT USER_ID,
MUSIC_LIKED_USER.MUSIC_ID,
MUSIC_TITLE,
SINGER_NAME,
ALBUM_IMAGE,
MUSIC_LIKED_USER.REGISTED_DATE
FROM MUSIC_LIKED_USER
INNER JOIN
(SELECT MUSIC.MUSIC_ID, MUSIC.MUSIC_TITLE, SINGER_NAME, ALBUM_IMAGE
FROM MUSIC
INNER JOIN (SELECT ALBUM_ID, SINGER_NAME, ALBUM_IMAGE
FROM SINGER
INNER JOIN ALBUM ON SINGER.SINGER_ID = ALBUM.SINGER_ID) SINGER_AND_ALBUM
ON MUSIC.ALBUM_ID = SINGER_AND_ALBUM.ALBUM_ID) TEMP
ON MUSIC_LIKED_USER.MUSIC_ID = TEMP.MUSIC_ID) USER_LIKE_MUSICS
ON USER.USER_ID = USER_LIKE_MUSICS.USER_ID
ORDER BY USER_LIKE_MUSICS.REGISTED_DATE DESC;
# 4. 전체 유저가 좋아요 한 음악 (조인 버전)
SELECT NICKNAME AS "닉네임",
MUSIC_TITLE AS "곡 제목",
SINGER_NAME AS "가수 이름",
ALBUM_IMAGE AS "앨범 이미지 URL",
DATE_FORMAT(MUSIC_LIKED_USER.REGISTED_DATE, '%Y.%m.%d') AS "좋아요 한 날짜"
FROM (((MUSIC_LIKED_USER INNER JOIN MUSIC M ON MUSIC_LIKED_USER.MUSIC_ID = M.MUSIC_ID)
INNER JOIN ALBUM ON M.ALBUM_ID = ALBUM.ALBUM_ID)
INNER JOIN SINGER ON ALBUM.SINGER_ID = SINGER.SINGER_ID)
INNER JOIN USER ON USER.USER_ID = MUSIC_LIKED_USER.USER_ID
ORDER BY MUSIC_LIKED_USER.REGISTED_DATE DESC;
# 5. 전체 유저가 좋아요 한 앨범
SELECT NICKNAME AS "닉네임",
ALBUM_TITLE AS "앨범 제목",
SINGER_NAME AS "가수 이름",
ALBUM_IMAGE AS "앨범 이미지 URL",
CASE
WHEN SCORED_USER_COUNT = 0 THEN 0
ELSE TOTAL_SCORE / SCORED_USER_COUNT
END AS "별점",
SCORED_USER_COUNT AS "별점 매긴 유저수",
DATE_FORMAT(ALBUM_LIKED_USER.REGISTED_DATE, '%Y.%m.%d') AS "좋아요 한 날짜"
FROM ((ALBUM_LIKED_USER
INNER JOIN ALBUM A on ALBUM_LIKED_USER.ALBUM_ID = A.ALBUM_ID)
INNER JOIN SINGER ON A.SINGER_ID = SINGER.SINGER_ID)
INNER JOIN USER ON USER.USER_ID = ALBUM_LIKED_USER.USER_ID
ORDER BY ALBUM_LIKED_USER.REGISTED_DATE DESC;
# 6. 재생목록 (1번 재생목록, 1번 유저라 가정)
SELECT LISTED_MUSIC_IN_PLAYLIST.PLAYLIST_ID AS "플레이리스트 번호",
PLAYLIST_NAME AS "플레이리스트 이름",
LISTED_MUSIC_IN_PLAYLIST.MUSIC_ID AS "재생목록 중 음악 번호",
MUSIC_TITLE AS "곡 제목",
SINGER_NAME AS "가수 이름",
ALBUM_IMAGE AS "앨범 이미지 URL",
DATE_FORMAT(LISTED_MUSIC_IN_PLAYLIST.REGISTED_DATE, '%Y.%m.%d') AS "곡 추가일",
(SELECT COUNT(*)
FROM LISTED_MUSIC_IN_PLAYLIST
WHERE LISTED_MUSIC_IN_PLAYLIST.PLAYLIST_ID = PLAYLIST.PLAYLIST_ID) AS "재생목록 곡 수"
FROM (LISTED_MUSIC_IN_PLAYLIST
INNER JOIN (SELECT MUSIC_ID, MUSIC_TITLE, SINGER_NAME, ALBUM_IMAGE
FROM (SELECT MUSIC_ID, MUSIC_TITLE, SINGER_ID, ALBUM_IMAGE
FROM MUSIC
INNER JOIN ALBUM A on MUSIC.ALBUM_ID = A.ALBUM_ID) AM
INNER JOIN SINGER ON AM.SINGER_ID = SINGER.SINGER_ID) M
ON LISTED_MUSIC_IN_PLAYLIST.MUSIC_ID = M.MUSIC_ID)
INNER JOIN PLAYLIST ON PLAYLIST.PLAYLIST_ID = LISTED_MUSIC_IN_PLAYLIST.PLAYLIST_ID
WHERE PLAYLIST.PLAYLIST_ID = 1
AND USER_ID = 1
ORDER BY REGISTED_DATE DESC;
# 7. 음악 재생 화면 (내가 2번 유저이고 1번 음악이라 가정)
SELECT MUSIC_ID AS "음악 번호",
MUSIC_TITLE AS "곡 제목",
SINGER_NAME AS "가수 이름",
ALBUM_IMAGE AS "앨범 이미지 URL",
LYRICS AS "가사",
CASE
WHEN (EXISTS(SELECT *
FROM MUSIC_LIKED_USER
WHERE MUSIC_LIKED_USER.MUSIC_ID = MUSIC.MUSIC_ID
AND MUSIC_LIKED_USER.USER_ID = 2))
THEN '좋아요 함'
ELSE '좋아요 하지 않음'
END AS "좋아요 여부",
(SELECT COUNT(*) FROM MUSIC_LIKED_USER WHERE MUSIC_LIKED_USER.MUSIC_ID = MUSIC.MUSIC_ID) AS "좋아요 수",
CASE
WHEN CAST(SUBSTRING(TIME_FORMAT(PLAYTIME, '%i:%s'), 1, 1) AS UNSIGNED) = 0
THEN SUBSTRING(TIME_FORMAT(PLAYTIME, '%i:%s'), 2)
ELSE TIME_FORMAT(PLAYTIME, '%i:%s')
END AS "총 재생시간"
FROM (MUSIC INNER JOIN ALBUM A on MUSIC.ALBUM_ID = A.ALBUM_ID)
INNER JOIN (SELECT SINGER_ID, SINGER_NAME FROM SINGER) S
ON A.SINGER_ID = S.SINGER_ID
WHERE MUSIC_ID = 1;
# 8. 음악 정보 - 가사 (1번 유저라고 가정)
SELECT MUSIC_ID AS "음악 번호",
MUSIC_TITLE AS "곡 제목",
SINGER_NAME AS "가수 이름",
SINGER_IMAGE AS "가수 이미지 URL",
GENRE AS "장르",
SOUND_QUALITY AS "음질",
ALBUM_TITLE AS "앨범 제목",
ALBUM_IMAGE AS "앨범 이미지 URL",
LYRICS AS "가사",
(SELECT COUNT(*)
FROM MUSIC_LIKED_USER
WHERE MUSIC_LIKED_USER.MUSIC_ID = MUSIC.MUSIC_ID) AS "좋아요 수",
CASE
WHEN EXISTS(SELECT *
FROM MUSIC_LIKED_USER
WHERE MUSIC_LIKED_USER.MUSIC_ID = MUSIC.MUSIC_ID
AND MUSIC_LIKED_USER.USER_ID = 2)
THEN '좋아요 함'
ELSE '좋아요 하지 않음'
END AS "좋아요 여부",
(SELECT COUNT(*) FROM MUSIC_REPLY WHERE MUSIC_REPLY.MUSIC_ID = MUSIC.MUSIC_ID) AS "댓글 수"
FROM (MUSIC
INNER JOIN ALBUM A on MUSIC.ALBUM_ID = A.ALBUM_ID)
INNER JOIN SINGER ON A.SINGER_ID = SINGER.SINGER_ID;
# 9. 음악 정보 - 상세정보 (1번 유저라 가정)
# 10. 음악 정보 - 댓글 (1번 유저라 가정)
SELECT MUSIC.MUSIC_ID AS "음악 번호",
MUSIC_TITLE AS "곡 제목",
SINGER_NAME AS "가수 이름",
SINGER_IMAGE AS "가수 이미지 URL",
GENRE AS "장르",
SOUND_QUALITY AS "음질",
ALBUM_TITLE AS "앨범 제목",
ALBUM_IMAGE AS "앨범 이미지 URL",
(SELECT COUNT(*)
FROM MUSIC_LIKED_USER
WHERE MUSIC_LIKED_USER.MUSIC_ID = MUSIC.MUSIC_ID) AS "좋아요 수",
CASE
WHEN EXISTS(SELECT *
FROM MUSIC_LIKED_USER
WHERE MUSIC_LIKED_USER.MUSIC_ID = MUSIC.MUSIC_ID
AND MUSIC_LIKED_USER.USER_ID = 1)
THEN '좋아요 함'
ELSE '좋아요 하지 않음'
END AS "좋아요 여부",
(SELECT COUNT(*) FROM MUSIC_REPLY WHERE MUSIC_REPLY.MUSIC_ID = MUSIC.MUSIC_ID) AS "댓글 수",
NICKNAME AS "닉네임",
REPLY_COMMENT AS "댓글 내용",
DATE_FORMAT(MUSIC_REPLY.REGISTED_DATE, '%Y.%m.%d') AS "댓글 작성일시" # 시간 차이 반영해서 수정해야함
# 댓글 좋아요 수 컬럼
# 댓글 싫어요 수 컬럼
# 댓글에 대한 답글 수 컬럼
FROM (((MUSIC
INNER JOIN ALBUM A on MUSIC.ALBUM_ID = A.ALBUM_ID)
INNER JOIN SINGER ON A.SINGER_ID = SINGER.SINGER_ID)
INNER JOIN MUSIC_REPLY ON MUSIC.MUSIC_ID = MUSIC_REPLY.MUSIC_ID)
INNER JOIN USER ON MUSIC_REPLY.USER_ID = USER.USER_ID
WHERE MUSIC_REPLY.MUSIC_ID = 1
ORDER BY MUSIC_REPLY.REGISTED_DATE DESC;