코드스테이츠 백엔드 부트캠프 33일차 - 관계형 데이터베이스(SQL, Schema)

wish17·2023년 1월 31일
0
post-thumbnail

Daily Coding 13번

문자열을 배열로 입력받아 세로로 읽은 결과를 리턴하라.

  • 비어있는 행렬은 무시

public class ReadVertically {
    public String readVertically(String[] arr) {
        int maxLength=0;
        String result = "";

        for(String o : arr){
            maxLength = Math.max(maxLength,o.length());
        }
        System.out.println(maxLength);

        for(int i=0; i<maxLength; i++){
            for(String o : arr){
                if(o.length()>i){
                    result += o.charAt(i);
                }
            }
        }
        return result;
    }
}


//입력
readVertically.readVertically(new String[]{
        "나듕달",
        "랏귁아",
        "말에",
        "싸",
        "미"
        });
        
//출력
나랏말싸미듕귁에달아

Section2 - 관계형 데이터베이스(RDBMS)

과제 - Learn Schema / SQL

데이터베이스 연결과 스키마

과제 진행 중 알게 된(알아야 할) 개념들

  • gitignore

    • 특정 파일이나 디렉토리를 git 버전 관리에서 의도적으로 추적하지 않도록 설정하는 파일
  • build.gradle

    • 그루비(Grrovy)를 기반으로 한 빌드 자동화, 개발 지원에 중점을 둔 빌드 도구
  • SQL에서 사용하는 단위

기본키(primary key)

외래키(foreign key)

Q: 왜 Properties.java 파일은 .gitignore에 존재할까요?
(Properties class = MYSQL root 비밀번호가 포함 된 class)
A: 비밀번호 노출을 막기 위해

SQL 실제 실행 순서

FROM  > ON > JOIN > WHERE > GROUP BY > HAVING >SELECT > DISTINCT >ORDER BY

MySQL - 테이블 만들기

content 테이블 구조

  • user 테이블
FieldTypeNullKeyDefaultExtra
idintNoPKnullauto_increment
namevarchar(255)Nonull
emailvarchar(255)Nonull
CREATE TABLE `user` (
  `id` int PRIMARY KEY AUTO_INCREMENT,
  `name` varchar(255) not NULL,
  `email` varchar(255) not NULL
);
  • content 테이블
FieldTypeNullKeyDefaultExtra
idintNoPKnullauto_increment
titlevarchar(255)Nonull
bodyvarchar(255)Nonull
created_attimestampNoCURRENT_TIMESTAMPDEFAULT_GENERATED
userIdintYesFKnull
CREATE TABLE `content` (
  `id` int PRIMARY KEY AUTO_INCREMENT,
  `title` varchar(255) not NULL,
  `body` varchar(255) not NULL,
  `created_at` timestamp not NULL DEFAULT CURRENT_TIMESTAMP,
  `userId` int,
  FOREIGN KEY (`userId`) REFERENCES `user` (`id`)
);

MySQL - 테이블 조회

  • 모든 테이블 조회 = "SHOW TABLES;"

  • USER 테이블 조회 = "DESCRIBE USER;"

  • CONTENT 테이블 조회 = "DESCRIBE CONTENT;"

처음에 SELECT * FROM USER; 이렇게 하면 되는 줄 알고 헛짓 했다.
SELECT * FROM USER;는 테이블의 모든 열을 조회하는 것이지 테이블을 조회하는게 아니다.

문제풀이 과정

2-5 Q: user 테이블에서 특정 조건을 가진 데이터를 찾아라.

//1번 방법
SELECT name FROM user WHERE not name='luckykim'

//2번 방법
SELECT name FROM user WHERE name <> 'luckykim'

2-10 Q: luckykim이 작성한 컨텐츠에 자유롭게 title과 body를 추가하시오.

insert into content (
title, 
body, 
userid) values(
'홍길동', 
'잘생겼다', 
(SELECT id From user where name='luckykim'))

cf. 쿼리문 안에 쿼리문 쓰는건 피하는게 좋다. (성능적인 이슈 때문에)
문제에서는 'luckykim'의 id를 알려줘서 사실 이중 쿼리문 사용하지 않아도 됐다.

3-0-0 스키마

3-2-2 Q: user의 name과 email 그리고 그 user가 속한 role name(컬럼명: roleName)을 찾아라.

SELECT user.name AS userName, email, role.name AS roleName
FROM user 
LEFT JOIN role 
ON user.roleId = role.id;
SELECT user.name, email, role.name
FROM user 
LEFT JOIN role 
ON user.roleId = role.id;

위 두가지 방법 다 됨.
(TMI. AS를 쓰도록 유도한 문제이지만 오류 case 설정을 하다 보니 문제의 의도가 제대로 구현되지 않아 AS를 안써도 통과는 된다)

Q 3-2-6. minsanggu이 작성한 content의 category name을 찾으시오.

첫 시도 (오류)

SELECT category.name FROM content 
JOIN user ON userId = user.id WHERE user.name = 'wish'
join content_category on contentId = content.id 
join category on categoryId=category.Id

조인이 잘되고 있는지 한단계씩 확인하기 위한 테스트를 해봤다.

//입력
SELECT * FROM user JOIN content ON userId = user.id WHERE user.name = 'wish'

//출력
[{roleId=1, name=wish, roleName=1, created_at=2023-02-01 16:51:03.0, id=1, title=developer proverb, body=Even if the server goes down, there is a backup, userId=1, email=wjwee9@codestates.com}]

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

//입력2
SELECT * FROM user JOIN content ON userId = user.id JOIN content_category ON categoryId = content.id WHERE user.name = 'wish'

//출력2
[{roleId=1, name=wish, roleName=1, contentId=1, created_at=2023-02-01 16:55:32.0, id=1, title=developer proverb, body=Even if the server goes down, there is a backup, userId=1, email=wjwee9@codestates.com, categoryId=1}, 
{roleId=1, name=wish, roleName=4, contentId=2, created_at=2023-02-01 16:55:32.0, id=1, title=developer proverb, body=Even if the server goes down, there is a backup, userId=1, email=wjwee9@codestates.com, categoryId=1}]

위와 같이 join없이 바로 확인해 봐도 'minsanggu' 의 결과가 행1개만 나와서 혼란에 빠졌었다. ( inner join은 교집합 이기 때문에 join을 하면 할수록 더 적어져야 한다고 생각했기 때문 )

하지만 1:many 관계에서 join으로 인해 오히려 행의 갯수가 늘어날 수 있다. (입력2의 경우 같이 user, 같은 content를 갖지만 category만 다른 행이 존재)

첫 시도의 경우 SQL의 실행 순서를 고려하지 않아서 오류가 난 것이었다.

wherejoin colum_name on ~~ 이후에 사용 가능하다.

아래와 같이 where 순서 바꾸니까 됐다.

SELECT category.name FROM content 
JOIN user ON userId = user.id 
join content_category on contentId = content.id 
join category on categoryId=category.Id WHERE user.name = 'Wish'";

category 기준으로 생각하니 더 간편함.
(맨 끝에서부터 순서대로 JOIN 하자)

SELECT category.name FROM category 
join content_category on categoryId = category.id
join content on contentId=content.Id
JOIN user ON userId = user.id
WHERE user.name = 'wish'";

3-2-10. 각 user(컬럼명을 name으로 바꿔라)가 작성한 글의 개수 (컬럼명을 ContentCount로 바꿔라)를 출력하라.

SELECT user.name, count(content.id) AS 'ContentCount' 
FROM user 
LEFT JOIN content 
ON userId = user.id 
GROUP BY user.name

처음에 inner join으로 했다가 오류가 나왔다.
content를 하나도 갖고 있지 않은 user가 있기 때문에 오류가 난 것.


Q1 : 프라이머리 키들도 다 낫널 붙여주는게 좋지 않나?

A1 : 어차피 id가 없는 행은 존재할 수 없다. 즉, 낫널은 의미가 없다.

0개의 댓글