728x90

-jsp:include 를 이용하는 경우

jsp:include에 대한 설명 https://sesok808.tistory.com/331

jsp:include action 방식 참고 사이트 : https://all-record.tistory.com/106 

 

사용법 ex)

<jsp:include page="common/nav.jsp" flush="false"/> 

<!-- page: 이동할 jsp 파일을 의미 ,  flush: jsp:include태그를 읽는 시점에 , 현재까지 저장된 출력버퍼를 비울지 여부-->

<!-- 보통은 false 시켜 놓는다 -->

 

-Jquery 를 이용하는 경우 , Javascript의 Ajax를 이용하는 경우

https://kyung-a.tistory.com/18

728x90

-MVC 프레임워크-

 

*프론트 컨트롤러 패턴

-기존의 컨트롤러들의 앞에 공통컨트롤러(서블릿)을 놓는것

-공통처리기능

-프론트 컨트롤러를 제외한 나머지 컨트롤러는 서블릿을 사용하지 않아도 됨

-스프링 웹 MVC의 DispatcherServlet이 FrontController 패턴으로 구현되어 있음

 

request.getRequestURI(); // http://localhost:8080 이후 부분만 호출 된다 ex) /front-controller/v1/hello

"/front-controller/v1/*" : /front-controller/v1 를 포함한 하위 모든 요청은 이 서블릿에서 받아들인다

실제 개발의 대부분의 경우에 절대경로를 사용하는 것이 좋다

 

728x90

스프링자체가 싱글톤을 보장해줘서 , 스프링내에선 싱글톤 안써도 됨

 

-Junit5

@AfterEach,@BeforeEach :  테스트가 시작되기 후, 전 마다 수행되는 어노테이션

@AfterEach// 메소드에 붙음 , 다음 테스트에 영향을 주지 않기 위해 종료되어야할 리소스를 처리하는 부분

@BeforeEach// 메소드에 붙음 , 테스트 할 때의 초기 환경을 setup

@Test//Junit이 수행할 코드 ,메소드에 붙음

given (ㅇㅇ 주어졌을때) -> when (ㅇㅇ 실행했을때 ) -> then (결과가 ㅇㅇ 이어야되) 로직으로 수행됨

 

then부분 ex)

Assertions.assertThat(출력값).isEqualTo(기댓값);

Assertions.assertThat(컬렉션객체).contains(member1,member2);

 

( Junit 5 기준 명령어 참고 )

 

템플릿 엔진(뷰 템블릿) : html 에다가 자바코드를 중간중간 삽입하는 것 , jsp(legacy) 타임리프(new)

 

정적 HTML 문서 : 항상고정된 HTML

동적 HTML 문서 : 시시각각 변화되는 회원목록같은 HTML

동적 HTML case : 서블릿+자바내부 html (very old) ,  템플릿 엔진단독 이용 , 서블릿+템플릿엔진(new)

 

-JSP

JSP도 서블릿으로 변환됨, 따라서  request, response를 사용가능함

JSP는 자바코드를 그대로 다 사용할 수 있다.

JSP는 <% ~ %> 에 자바코드를 입력할 수 있다

<%= ~ %> 에서는 자바코드를 출력한다

 

-서블릿과 JSP의 한계

서블릿만으로 뷰화면을 만들려면 ,  자바코드 내부에 html 을 써줘야한다는 불편함이 존재

서블릿과 JSP로 뷰화면을 만든다면 , 뷰파일에 자바영역과 , HTML 영역이 둘다 존재 ( 커밋시 문제 , 역할이 너무많음 , 유지보수의 지옥 ) 

=> MVC 패턴의 등장 (보여주는것과 비즈니스로직과의 분리) ( JSP는 뷰를 그리는 것에만 집중하게함 )

 

 

변경의 라이프사이클이 다른것들(비즈니스로직,UI)을 분리해주는것이 유지보수에 좋다

 

-MVC 패턴1(legacy)
컨트롤러(Servlet): 비즈니스 로직을 다 수행 , Model에 데이터를 담음 , 뷰로 제어권을 넘김

모델(Model)객체 : 데이터담기는 곳
뷰(ex JSP): Model에 있는 데이터를 참고해서 뷰를 완성하고 , 클라이언트에게 보냄

 

-MVC 패턴2(new)  ( CS 에서 배웠던 부분과 약간 다르지만 , 뭐 비슷하다 )

컨트롤러 : HTTP요청이 옳바른지 스펙을 확인 , 서비스 호출 , 종합적인 조종역할

서비스,리포지토리 : 비즈니스 로직, 데이터 접근 수행

모델(Model)객체 : 데이터 담기는 곳

뷰(ex JSP): Model에 있는 데이터를 참고해서 뷰를 완성하고 , 클라이언트에게 보냄

 

Servlet을 컨트롤러로 , JSP 를 뷰로처리

request.setAttribute 는 임시저장소 지만 , Model에 데이터가 들어감

컨트롤러에서는 HttpServletRequest객체.setAttribute("이름","값") 으로 Model에 데이터를 넣을수 있고,

뷰에서는 request.getAttribute("이름")로 Model에서 데이터를 꺼낼 수 있다

 

RequestDispatcher dispatcher =request.getRequestDispatcher(jsp파일경로문자열);//Controller->view 로 이동할때 사용
dispatcher.forward(request,response); //  다른 서블릿이나 JSP로 이동할 수 있는 기능 , 서버내부에서 다시 호출이 발생                                                       ,리다이렉트(웹브라우저에 갔다가 서버로 재요청)와 다르게 서버내부이동임,

                                                      Client->Server->Servlet 의 dispatcher->jsp->client

 

경로 ex)  현재 페이지 : http://localhost:8080/servlet-mvc/members/new-form

상대경로 : 클라이언트에서 save 호출시 -> http://localhost:8080/servlet-mvc/members/save

절대경로(추천) : 클라이언트에서 /save 호출시 -> http://localhost:8080/save

 

WEB-INF 폴더 : WEB-INF 내부에 있는 파일들은 컨트롤러를 거쳐야지 불려 질수 있음 ( 외부에서 호출 불가능 )

 

-redirect vs forward

redirect : client에 응답이 나가고 , HTTP헤더의 로케이션값으로 다시 서버에 요청하면서 로케이션값이 

            웹 브라우저의 url로 변경이됨 , 즉 server->client 가 두번 발생

forward : 서버 내부에서 일어나는 호출이기때문에 클라이언트가 전혀 인지하지 못함

 

jsp에서 request.getAttribute("이름") 으로 모델객체내용을 받을 수 있지만 너무 김 , 대신에..

${이름.필드변수} 로 바로 호출 가능  (프로퍼티 접근법, 필드변수만 적으면 상황에 맞게 자동으로 get or set이 호출)

 

jsp 선언부에 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 를 호출 하여 jstl 이용가능

 

MVC 패턴에서 , client에서 오는 요청은 무조건 먼저 Controller를 거친다 ( 규칙 ) 

 

-MVC 패턴의 한계 ( jsp 와 servlet 만으로 구현한 mvc 패턴 )  

컨트롤러내의 dispatcher 중복

viewPath="/WEB-INF/views/members.jsp" 의 내용중복

HttpServletRequest , HttpServletResponse 객체들이 꼭 사용되는 것은 아님 , service메소드 인자로 항상 쓰인다는 문제

공통처리가 어렵다 ( 로그처리 , 각종 중복코드 , 중복호출문제 )

=>

프론트 컨트롤러 패턴 : client 로부터 요청이 올때 다양한 공통처리를 미리 설계 해주는  객체(수문장역할)를 경유하여 서블릿으로 통하게 하는 방식 ( SPRING MVC 의 핵심 ) ,

대표컨트롤러를 두어 이후 나오는 컨트롤러들이 호출되게함

 

728x90

Junit 테스트 진행중 아래와 같은 에러가 떴다.

 

 org.junit.platform.launcher.core.EngineDiscoveryOrchestrator lambda$logTestDescriptorExclusionReasons$7

INFO: 0 containers and 2 tests were Method or class mismatch


Settings -> Build, Execution, Deployment -> Build Tools -> Gradle 에서

Build and run using Run tests using의 속성을 Intellij IDEA로 설정하면 해결된다.

728x90

스프링부트는 WAS를 내장하고 있음

스프링은 WAS를 설치해줘야함

 

-서블릿-

introduction

 

@ServletComponentScan // 현재 패키지포함한 하위패키지 까지 서블릿을 찾아서 자동으로 등록후 실행하게 해줌

 

서블릿클래스를 작성하려면 , HttpServlet 를 상속받아야 함

 

@WebServlet(name , urlPatterns )  // url이 호출되면 ,  해당 어노테이션이 붙은 클래스내부의 service 메소드가 호출됨 

 

HttpServletRequest 객체 : WAS가 client->server로 오는 http 내용을 파싱해서 가공한 것

HttpServletResponse 객체 : WAS가 server->client로 보내는 http 내용을 가공하는데 이용함

 

쿼리 파라미터 : URL의 ? 뒤에 붙은 데이터들

request.getParameter() : 쿼리 파라미터 조회

response.setContentType() : 전송 데이터 타입 결정(http 헤더 정보)

response.setCharacterEncoding() : 문자 인코딩 방식 결정(http 헤더 정보)

요즘은 문자인코딩 방식은 utf-8로 통일되어 있음

response.getWriter().write() : http 바디에 데이터 삽입

 

*로그확인

application.properties -> logging.level.org.apache.coyote.http11=debug    ->http 헤더 정보를 보여줌 

-개발에서만 적용해보고 , 운영서버에서는 삭제해줘야함

 

main/webapp/index.html 은 웰컴페이지로 초기화면임

 

HttpServletRequest

 

     

HttpServletRequest : 서블릿이 개발자대신에 Http 메시지를 파싱해서 HttpServletRequest 객체에 담아줌

request.setAttribute(name, value)  : 임시 저장소 기능

request.getAttribute(name) : 임시 저장소 값 조회

request.getSession : 세션 정보를 받아옴 , HttpSession 객체로도 동일한 기능 수행

HTTP 스펙을 꼭 아는게 좋음

 

@WebServlet 이 붙은 클래스 내부의 메소드인 service는 접근제어자가 protected 이어야 한다.

 

HTTP 구성도 :  START LINE , 헤더 , 바디

 

*HTTP 해더조회

request.getHeaderNames(); //HTTP에 있는 모든 헤더정보를 열거형으로 반환  (old)

request.getHeaderNames().asIterator()
                .forEachRemaining(headerName -> System.out.println(headerName + ": " + request.getHeader(headerName)));   (new)

request.getHeader("원하는 헤더종류") // 헤더종류에 대한 값 반환

 

HTTP헤더 -> Accept-Language :  가장우선하는 언어 순으로 데이터가 들어감

 

request.getServerName() = localhost
request.getServerPort() = 8080
request.getLocale() = ko_KR //가장 우선순위 높은 브라우저 언어를 꺼내줌

request.getCookies() //쿠키들을 가져와서 반환해줌 , 쿠키도 HTTP 헤더에 담긴다
request.getContentType() = null // GET 방식은 null이다
request.getContentLength() = -1
request.getCharacterEncoding() = UTF-8

 

*네트워크 정보 조회

[Remote 정보] // 상대(클라이언트)의 정보
request.getRemoteHost() = 0:0:0:0:0:0:0:1
request.getRemoteAddr() = 0:0:0:0:0:0:0:1
request.getRemotePort() = 62036

[Local 정보] // 내 (서버) 정보
request.getLocalName() = 0:0:0:0:0:0:0:1
request.getLocalAddr() = 0:0:0:0:0:0:0:1
request.getLocalPort() = 8080

 

*HTTP 요청 데이터

-GET(url+쿼리파라미터)

ex ) http://url/?username=hello&age=20

body 에 데이터를 보내지 않음

url의 쿼리 파라미터에 데이터를 포함해서 전송할뿐

검색,필터,페이징등

 

-POST(HTML form)

 

Content-Type : body에 대한 타입

 

메세지 바디에 쿼리 파라미터 형식으로 전달 , username=hello&age=20

회원가입,상품주문

 

-HTTP message body

HTTP message body 에 데이터를 직접 담아서 요청

 REST API=HTTP API 에서 주로 사용

JSON(주로쓰임),XML,TEXT 형식으로 담아서 보냄

POST,PUT,PATCH 사용가능

 

*GET  쿼리파라미터

request.getParameterNames().asIterator()
         .forEachRemaining(paramName ->System.out.println(paramName+"="+request.getParameter(paramName)));  //모든요청파라미터 전부꺼냄

request.getParameter(name) // 단일 파라미터 조회   , 이름중복않됬을 경우에 사용하는게 원칙 

request.getParameterValues(name); // 이름 동일한 복수파라미터들 반환

HTTP 바디가 비어있으므로, content-type 이 null이다

 

*POST HTML Form

HTTP 메시지바디에 데이터가 다들어감

 

클라이언트에서 html 페이지는 서블릿 설정안해도 , url만 잘 작성하면 들어갈수 있다(정적 html)

request.getParameter(name) 으로 get방식과 동일하게 post방식에서도 데이터를 꺼내온다

content-type 이 필수로 적혀 있음

 

*HTTP message body

메시지바디에 내가원하는 데이터를 직접실어서 서버에 전송하는 것

HTTP API=REST API  방식

전송 데이터중 JSON,XML(legacy),TEXT 가 있지만 대부분 JSON 사용

서버 <-> 서버 통신  ,  앱 <->서버  , js기반클라이언트 <->서버

request.getInputStream();//얻어온 HTTP 바디의 내용을 바이트코드로 반환

StreamUtils.copyToString(인풋스트림, StandardCharsets.UTF_8);

//바이트를 문자열로 변환할 때는 어떤 인코딩 인지 알려줘야함 , 역의 경우도 마찬가지

 

-JSON 형식으로 데이터 주고받기

content-type: application/json 으로 해줘야함

서버에서 json데이터를 객체로 파싱해서 사용함

json의 key값에 매칭되는 클래스를 만들어주고 , getter &setter 을 생성해줘야함 -> 

jackson이 setter에 매칭되게 객체로 파싱해줌

getter , setter를 삽입 대신에  , @Getter @Setter 를 클래스위에 넣어주면 동일한 기능을 함(롬복기능)

( 자바 빈 개념 참고 )

 

* Lombok은 여러가지 어노테이션을 제공, 컴파일과정에서 코드를 생성해 주는 방식으로 동작하는 라이브러리

 

 

private ObjectMapper objectMapper = new ObjectMapper(); // instance value
HelloData helloData=objectMapper.readValue(제이슨문자열, HelloData.class); //json형태 문자열-> 객체로 파싱 (old) 

나중에 Spring mvc가 제공하는 기능을 이용하면 , 더 간략화 된다(new)

 

JSON 변환 라이브러리 : Jackson(default),Gson

 

HttpServletResponse

HTTP 응답 메세지 생성

200같은 HTTP 응답코드 , 헤더 , 바디를 일부자동생성 or 완전 자동생성 해줌

사용자가 Content-Type,쿠키,Redirect  셋팅을 편리하게 할 수 있게 해줌

response.setStatus(응답코드);//HTTP 응답코드를 넣읗 수 있음

 

HTTP스펙에서 응답의 첫번째는 'status-line' (응답코드) 이라고함

response.setHeader(name,value) // 헤더정보를 삽입

 

-Header 편의 메서드

response.setContentType("text/plain");   // 콘텐트 편의메소드
response.setCharacterEncoding("utf-8"); // 콘텐트 편의메소드

response.addCookie(cookie); // 쿠키 편의 메소드

response.setStatus(HttpServletResponse.SC_FOUND); //redirect 편의 메소드 , HTTP 응답코드 수정
response.setHeader("Location", "/basic/hello-form.html"); //redirect 편의 메소드 ,  리다이렉트할 위치지정

response.sendRedirect("/basic/hello-form.html"); //redirect 편의 메소드 , 윗 두줄과 동일한 기능을 수행(302상태코드)

( 301 , 302 리다이렉션의 의미 )

 

PrintWriter writer = response.getWriter(); // 메세지 바디에 데이터 삽입
writer.println("ok");

 

-HTTP 응답스펙

START LINE , 헤더 , 바 디

 

*HTTP응답데이터 - 단순 텍스트 , HTML , JSON 응답

-단순텍스트

PrintWriter writer = response.getWriter(); // 메세지 바디에 데이터 삽입
writer.println("ok");

 

-HTML 응답(HTTP 바디에 html내용 삽입하기)

HTTP 응답으로 HTML을 반환할 때는 content-type: text/html 로 지정해줘야함

헤더편의 메소드를 통해 Encoding방식을 잡아줘야함

 

-JSON 보내기

HTTP 메세지 바디에 JSON 넣어보내기

객체 -> JSON 으로 파싱해야함

private ObjectMapper objectMapper = new ObjectMapper(); //인스턴스 value
objectMapper.writeValueAsString(객체);  // json형 문자열반환  (old)

 

application/json은 스펙상 utf-8형식이 디폴트로 알아서 잡혀진다

 

Form data를 body에 담아 전송할 때는 POST방식만 허용한다

728x90

공통키=대칭키=암호화와 복호와에 동일한 키를 사용한다고하여 공통키라고도 부름 
공개키 기법 = 비대칭키 기법 
(개인키 = 비밀키 = 비공개키)

 

대칭키와 공개키 와 SSL암호화방식-아주상세함
https://preamtree.tistory.com/38

개인키로 복호화하는 경우(일반)와
개인키로 암호화하는 경우
https://brunch.co.kr/@artiveloper/24#comment


'21년이전 > IT용어' 카테고리의 다른 글

MIME타입  (0) 2021.06.14
오버헤드  (0) 2021.06.14
AOP ( Aspect Oriented Programming )  (0) 2021.04.28
Maven  (0) 2021.04.23
sp (stored procedure)  (0) 2021.04.21
728x90

본인은 JDK 11 을 이용중.

 

에러발생

Execution failed for task ':compileJava'.
> invalid source release: 11 

 

해결과정

  1. JDK 환경변수 등록
  2. Intellij의 JDK경로설정
  3. (프로젝트의 .idea 삭제)
  4. intellij 재부팅

 

1.  JDK 경로등록 , intellij 에서 시스템변수를 경유하여 JDK를 등록해준다.

2-1. File->Project Structure->SDKs ,  원하는 JDK ver을 등록 후  'JDK home path' 경로 확인. 

      'Name: 11' 이 의미하는 바는  지정된 JDK를 Intellij에서 다룰때의 명칭이 11인 것 

2-2.  Project에 지정할 JDK 설정 ,  Edit버튼 옆에 있는 '11' 표시는 2-1에서 설정해준 Name명칭

 

2-3. FILE->settings , JAVA Compiler  설정 

2-4. intellij 재부팅

 

3. 윗 과정후에도  invalid source release: 11 오류가 발생한다면 .idea(intellij 설정정보파일) 을 삭제시켜본다

4. intellij을 재부팅시킨다 , 재부팅후 JDK설정정보를 요구한다면 다시 설정해주면 되고, 요구 안한다면 .idea가 자동으로 다시 생성될것

 

 

 

 

한글이 깨지는 내부문제 발생

https://goddaehee.tistory.com/248 에서 1,2 부분을 따라하면 된다.

 

해결

728x90

-----------------------------------백준------------------------------

(1)별 찍기 - 1

문제

첫째 줄에는 별 1개, 둘째 줄에는 별 2개, N번째 줄에는 별 N개를 찍는 문제

입력

첫째 줄에 N(1 ≤ N ≤ 100)이 주어진다.

출력

첫째 줄부터 N번째 줄까지 차례대로 별을 출력한다.

 

 

내답 ) 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package stringTest;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class 별찍기1 {
    public static void main(String[] args) throws IOException{
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n=Integer.valueOf(reader.readLine());
        StringBuilder sb = new StringBuilder(n*(n+1)/2+n);
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=i;j++
                sb.append('*');
            sb.append('\n');
        }
        System.out.println(sb);
        //test
        System.out.println("sb.capacity() : "+sb.capacity());
        System.out.println("sb.length() : "+sb.length());
    }
}
 
cs

문자열 끝부분 추가이벤트가 빈번하여 StringBuilder를 이용해주었고 ,

StringBuilder의 새로운배열 생성을 막기위해서 기본크기를 n*(n+1)/2 + n  으로 지정해주었다

 

 

(2)더하기 사이클

문제

0보다 크거나 같고, 99보다 작거나 같은 정수가 주어질 때 다음과 같은 연산을 할 수 있다. 먼저 주어진 수가 10보다 작다면 앞에 0을 붙여 두 자리 수로 만들고, 각 자리의 숫자를 더한다. 그 다음, 주어진 수의 가장 오른쪽 자리 수와 앞에서 구한 합의 가장 오른쪽 자리 수를 이어 붙이면 새로운 수를 만들 수 있다. 다음 예를 보자.

26부터 시작한다. 2+6 = 8이다. 새로운 수는 68이다. 6+8 = 14이다. 새로운 수는 84이다. 8+4 = 12이다. 새로운 수는 42이다. 4+2 = 6이다. 새로운 수는 26이다.

위의 예는 4번만에 원래 수로 돌아올 수 있다. 따라서 26의 사이클의 길이는 4이다.

N이 주어졌을 때, N의 사이클의 길이를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N이 주어진다. N은 0보다 크거나 같고, 99보다 작거나 같은 정수이다.

출력

첫째 줄에 N의 사이클 길이를 출력한다.

 

내 1차답 ) - 문자열 , char , 숫자 혼용하여 풀었다.. 어우 복잡해.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class 더하기사이클 {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String nString=reader.readLine();
        String baseString=new String(nString);
        
        for(int i=1;;i++) {
            char first= (baseString.length()>1) ? baseString.charAt(0) : '0'
            char second=(baseString.length()>1) ? baseString.charAt(1) : baseString.charAt(0);
            char[] cs = {second, (char)((first+second-'0'-'0')%10 +'0') };
            baseString=(second!='0') ? new String( cs ) : new String( cs[1]+"") ; 
            if(nString.equals(baseString)) {
                System.out.println(i);
                break;
            }
        }
    }
}
 
cs

 

내 2차답 ) -  숫자로 변환하여 처리  ( 효율적 ) 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class 더하기사이클 {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int nInt=Integer.valueOf(reader.readLine());
        int baseInt = nInt;
        for(int i=1;;i++) {
            int firstInt = (baseInt>=10)?baseInt/10:0;
            int secondInt = baseInt%10;     
            int sumInt = firstInt + secondInt;        
            baseInt=secondInt*10+sumInt%10;
            if(baseInt==nInt) {
                System.out.println(i);
                break;
            }
        }
    }
}
 
cs

 

bea1799 님의 답안 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.io.*;
import java.util.*;
 
import java.io.*;
import java.util.StringTokenizer;
 
public class Main {
 
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
 
        int N = Integer.parseInt(br.readLine());
        int X = N;
        int cycle = 0;
        do{
            cycle++;
            if(N < 10){
                N = (N * 10+ N;
                //System.out.println(N);
            } else{
                N = (N % 10* 10 + (((N / 10+ (N % 10)) % 10);
                //System.out.println(N);
            }
 
        }while(X != N);
        System.out.println(cycle);
    }
}
cs

내 답안 2와 형식이 비슷하다.

 

 

 

(3)숫자의 개수 (下)

문제

세 개의 자연수 A, B, C가 주어질 때 A × B × C를 계산한 결과에 0부터 9까지 각각의 숫자가 몇 번씩 쓰였는지를 구하는 프로그램을 작성하시오.

예를 들어 A = 150, B = 266, C = 427 이라면 A × B × C = 150 × 266 × 427 = 17037300 이 되고, 계산한 결과 17037300 에는 0이 3번, 1이 1번, 3이 2번, 7이 2번 쓰였다.

입력

첫째 줄에 A, 둘째 줄에 B, 셋째 줄에 C가 주어진다. A, B, C는 모두 100보다 크거나 같고, 1,000보다 작은 자연수이다.

출력

첫째 줄에는 A × B × C의 결과에 0 이 몇 번 쓰였는지 출력한다. 마찬가지로 둘째 줄부터 열 번째 줄까지 A × B × C의 결과에 1부터 9까지의 숫자가 각각 몇 번 쓰였는지 차례로 한 줄에 하나씩 출력한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class 숫자의개수 {
    public static void main(String[] args) throws IOException {
        BufferedReader reader= new BufferedReader(new InputStreamReader(System.in));
        int[] ansAry=new int[10]; 
        int total=1;
        for(int i=1;i<=3;i++)
            total*=Integer.valueOf(reader.readLine());
        String totalStr=String.valueOf(total);
        for(int i=0;i<totalStr.length();i++) {
            int idx=totalStr.charAt(i)-'0';
            ansAry[idx]+=1;
        }
        for(int i : ansAry)
            System.out.println(i);
    }
}
 
cs

별거없다 , 문자열 기초 문제였다.

 

(4) 숫자의합 (下)

문제

N개의 숫자가 공백 없이 쓰여있다. 이 숫자를 모두 합해서 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 숫자의 개수 N (1 ≤ N ≤ 100)이 주어진다. 둘째 줄에 숫자 N개가 공백없이 주어진다.

출력

입력으로 주어진 숫자 N개의 합을 출력한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class 숫자의합 {
    public static void main(String[] args) throws IOException{
        BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
        int nLen=Integer.valueOf(reader.readLine());
        String nStr=reader.readLine();
        int answer=0;
        for(int i=0;i<nLen;i++) {
            answer+=nStr.charAt(i)-'0';
        }
        System.out.print(answer);
    }
}
cs

문자열통으로 읽어와서 하나하나더해준 것이 끝,  기초 문자열 문제

 

ksj3452 님의 답 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.io.IOException;
import java.io.InputStream;
 
public class Main {
 
    public static void main(String[] args) throws IOException {
        InputStream is = System.in;
        while(is.read() != '\n');
        int sum = 0;
        int cur;
        while ((cur = is.read()) != '\n') {
            sum += cur - 48;
        }
        System.out.println(sum);
    }
 
}
cs

이 분은 Stream으로 입력을 받아오게 코드 하셨다.

InputStream은 JDK 1.0  1996년에 출현하셨다.

is.read() 는 1바이트를 읽어와서 아스키값(int)를 반환한다.

8 줄 같이 작성하신건 , 첫입력값인 N이 필요없어서 날리는 작업을 진행하신것이다.

 

InputStream에 대한 참고용도 사이트

 

(5) OX퀴즈 (中下)

문제

"OOXXOXXOOO"와 같은 OX퀴즈의 결과가 있다. O는 문제를 맞은 것이고, X는 문제를 틀린 것이다. 문제를 맞은 경우 그 문제의 점수는 그 문제까지 연속된 O의 개수가 된다. 예를 들어, 10번 문제의 점수는 3이 된다.

"OOXXOXXOOO"의 점수는 1+2+0+0+1+0+0+1+2+3 = 10점이다.

OX퀴즈의 결과가 주어졌을 때, 점수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수가 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있고, 길이가 0보다 크고 80보다 작은 문자열이 주어진다. 문자열은 O와 X만으로 이루어져 있다.

출력

각 테스트 케이스마다 점수를 출력한다.

 

내답 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
 
public class OX퀴즈 {
    public static void main(String[] args) throws IOException {
        BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
        int caseNum=Integer.valueOf(reader.readLine());
        for(int i=1;i<=caseNum;i++) {
            StringTokenizer tokenizer=new StringTokenizer(reader.readLine(), "X");//X가 1개든 , X가 2개이상이든 내가원하는 대로 구분해줌
            int total=0;
            while(tokenizer.hasMoreTokens()) {
                int seqLen=tokenizer.nextToken().length();
                total+= seqLen*(seqLen+1)/2;
            }
            System.out.println(total);
        }
    }
}
cs

StringTokenizer를 이용하여 연속된 'O'의 갯수를 구분해주었다.

15 번째 줄의 n(n+1)/2 공식을 이용하여 연속된 O일 경우의 성적을 도출 하였다.

출력시간 136 ms.

ps1)  BuildWriter를 이용해보았으나 , 더 많은 시간이 들었다. 아마 BuildWriter를 이용하기 위해 생성하는 객체의 코스트가 기존출력비용보다 더 높은 것 같다.

ps2) StringBuilder 를 이용하여 total 와 '\n' 을 메서드체이닝하여 append 해줘봤으나  기존코드와 큰 차이가 없었다.

 

결론 .  알고리즘 구조가 랭커들 코드보다 아주 약간 성능이 떨어지는것 같다. 

 

 

tkddnjs8521 님의 답 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        int test_case = Integer.parseInt(br.readLine());
        for(int i = 0; i < test_case; i++) {
            int cnt = 0;
            int sum = 0;
            for(byte value : br.readLine().getBytes()) {
                if(value == 'O') {
                    cnt++;
                    sum += cnt;
                }
                else {
                    cnt = 0;
                }
            }
            sb.append(sum).append("\n");
        }
        System.out.println(sb);
    }
}    
cs

13 번재 줄의 br.readLine() 의 결과값은 String ,    getBytes() 는 String 을 byte[] 로 반환해준다.

13 번재 줄의 반복문을 보면 , OX 문장한줄의 원소하나씩 접근해서 처리해주었다.

22 번재 줄에서는 메소드 체이닝으로 sum과 '개행문자'를 넣어주었다.

 

(6) 단어의 개수(中)

문제

영어 대소문자와 띄어쓰기만으로 이루어진 문자열이 주어진다. 이 문자열에는 몇 개의 단어가 있을까? 이를 구하는 프로그램을 작성하시오. 단, 한 단어가 여러 번 등장하면 등장한 횟수만큼 모두 세어야 한다.

입력

첫 줄에 영어 대소문자와 띄어쓰기로 이루어진 문자열이 주어진다. 이 문자열의 길이는 1,000,000을 넘지 않는다. 단어는 띄어쓰기 한 개로 구분되며, 공백이 연속해서 나오는 경우는 없다. 또한 문자열의 앞과 뒤에는 공백이 있을 수도 있다.

출력

첫째 줄에 단어의 개수를 출력한다.

 

내답1 ) 

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
 
public class 단어의개수{
    public static void main(String[] args) throws IOException{
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String string=reader.readLine().trim();
        StringTokenizer tokenizer = new StringTokenizer(string, " ");
        System.out.println(tokenizer.countTokens());
    }
}
cs

문자열을 한줄 받고 -> 공백으로 토큰을 구분해주고 -> tokenizer.countTokens() 를 이용하여 토근들의 갯수를 반환

메모리 19392KB  , 248ms 소요

 

하지만 탑랭커에 비해서 보다많은 메모리와 ,시간이 소요 되었다 ,  

trim내부 substring의 코스트와 ,  StringTokenizer 자체의 토큰들 생성에 코스트가 있는것 같다. 

 

내답 2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package stringTest;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class 단어의개수{
    public static void main(String[] args) throws IOException{
        BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
        int curInt;
        int beforeInt;
        int cnt=0;
        beforeInt=reader.read();
        cnt+=(beforeInt!=' ')?1:0//첫시작이 문자일경우 바로 1추가
        while( (curInt=reader.read())!='\n') {
            if(beforeInt==' ' && curInt!=' ')
                cnt++;
            beforeInt=curInt;
        }
        System.out.println(cnt);
    }
}
 
cs

원소 하나하나씩 접근으로 풀어보았다 

분기점은 이전문자값(beforeInt)==공백이면서 현재문자값(curInt)==문자 일경우 , cnt++ 해주었음

14 번재줄을 보면 , 문자열의 첫 시작이 문자인 경우는 예외 케이스로 정하였기 때문에 따로 처리 해줌

메모리 16932KB  , 212ms 소요로 개선하였음

 

otis 님의 답 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.io.IOException;
public class Main {
    public static void main(String[] args) throws IOException
    {
        int previous_ch = 32;//공백 ASCII
        int count =0;
        while(true)
        {
            int ch = System.in.read();
            if(ch==32)//공백 ASCII
            {
                if(previous_ch!=32)
                    count++;
            }
            if(ch==10//LF = 개행 
            {
                if(previous_ch!=32)
                    count++;
                break;
            }
            previous_ch = ch;  
        }
        System.out.println(count);
    }
}
cs

이분은 입력값을 하나하나 읽는 방식으로 해결하셨다

System.in.read() 는 문자를 하나씩읽어서 아스키(int형) 으로 반환 해준다 ( 9번재 줄) 

 

현재 입력값 ch 와 이전입력값 previous_ch를 두어서 count되는 시점을 조정하셨다

count++ 되는 시점은 아래 두가지 경우와 같다

  previous_ch 의 초깃값을 32으로 줌으로써 , 입력값의 시작이 공백인 경우를 제외 시켜줄수 있게 코드를 짜셨다는 것도

눈여겨볼 수 있다

 

( ASCII표System.in.read() 에 대한 설명참고 )

( System.in.read() 는 '개행'을 기준으로 구분되며 , 사용자가 엔터를 쳤을시 엔터도 버퍼에 들어간다 -_-

재밌는건 OS 에 따라 엔터가 CR+LF 일수도 있고 LF 만일수도 있는데 ㅋㅋ

내가 알기로 window의 엔터 = CR+LF ,  Linux계열의 엔터는 = LF )

 

(7)알파벳 찾기(中下) 

문제

알파벳 소문자로만 이루어진 단어 S가 주어진다. 각각의 알파벳에 대해서, 단어에 포함되어 있는 경우에는 처음 등장하는 위치를, 포함되어 있지 않은 경우에는 -1을 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 단어 S가 주어진다. 단어의 길이는 100을 넘지 않으며, 알파벳 소문자로만 이루어져 있다.

출력

각각의 알파벳에 대해서, a가 처음 등장하는 위치, b가 처음 등장하는 위치, ... z가 처음 등장하는 위치를 공백으로 구분해서 출력한다.

만약, 어떤 알파벳이 단어에 포함되어 있지 않다면 -1을 출력한다. 단어의 첫 번째 글자는 0번째 위치이고, 두 번째 글자는 1번째 위치이다.

 

내 답) 두가지 경우로 풀어 보았다

a~z 에 매칭되는 인덱스사이즈 26의 배열생성후 -1로 초기화 , 배열의 값은 최초문자 위치 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
 
public class 알파벳찾기 {
    public static void main(String[] args) throws IOException{
        BufferedReader reader= new BufferedReader(new InputStreamReader(System.in));
        int aToz[]=new int[26];
        Arrays.fill(aToz, -1);
        char[] cs=reader.readLine().toCharArray();
        for(int i=0;i<cs.length;i++) {
            int spellNum=cs[i]-'a';
            if(aToz[spellNum]==-1) aToz[spellNum]=i;
        }
        for(int ans : aToz) {
            System.out.print(ans);
            System.out.print(' ');
        }
    }
}
 
cs

 

답 2) reader.read()를 이용하여 한문자씩 처리해보았다 , 속도는 답1과 비슷비슷.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
 
public class 알파벳찾기 {
    public static void main(String[] args) throws IOException{
        BufferedReader reader= new BufferedReader(new InputStreamReader(System.in));
        int aToz[]=new int[26];
        Arrays.fill(aToz, -1);
        int asc,count=0;
        while( (asc=(char)reader.read())!=13){ //13==CF  윈도우는 '\n'==CR+LF 이다 , 리눅스는 LF
            int spellNum=asc-'a';
            if(aToz[spellNum]==-1) aToz[spellNum]=count;
            count++;
        }
        for(int ans : aToz) {
            System.out.print(ans);
            System.out.print(' ');
        }
    }
}
cs

특이사항으로는 , 백준은 개행을 LF ,  윈도우환경에서는 CR+LF로 다르다는 점

 

alswhdgus9 님의 답 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
 
public class 알파벳찾기 {
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
 
        String S = br.readLine();
        char[] chArr = S.toCharArray();//입력 문자열 
 
        for (char ch = 'a'; ch <= 'z'; ch++) {
            int i;
            if(ch != 'a') bw.write(' '); //문자 'a'가 아니면 공백출력예약
            for(i =0;i<chArr.length;i++) {
                if(chArr[i] == ch) { // chArr에서 일치하는 최초문자 발견하자마자 출력예약후 break
                    bw.write(String.valueOf(i)); 
                    break;
                }
            }
            if(i == chArr.length) bw.write("-1"); //i 가 chArr의 끝길이 까지 도달했따면 , -1 을 출력
        }
 
        bw.flush();
        bw.close();
        br.close();
    }
}
cs

BufferedWriter 를 이용하여 출력하셨다 , 

17 번째 줄을 이용하여 첫시작은 공백이외의 값이 나오도록 하셨고,

15 번재줄에서 'a' ~ 'z' 순서로 1중 반복문 , 18번재 줄에서 chArr원소에 일일히 접근하는 2중 반복문을 두셧다.

개인적인 생각으로 , 2중 반복문을 통해서 charArr의 첫원소부터 검사하는 것을 매 반복마다 수행할텐데 

조금 비효율적으로 느껴진다

 

(8)문자열 반복(下)

문제

문자열 S를 입력받은 후에, 각 문자를 R번 반복해 새 문자열 P를 만든 후 출력하는 프로그램을 작성하시오. 즉, 첫 번째 문자를 R번 반복하고, 두 번째 문자를 R번 반복하는 식으로 P를 만들면 된다. S에는 QR Code "alphanumeric" 문자만 들어있다.

QR Code "alphanumeric" 문자는 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\$%*+-./: 이다.

입력

첫째 줄에 테스트 케이스의 개수 T(1 ≤ T ≤ 1,000)가 주어진다. 각 테스트 케이스는 반복 횟수 R(1 ≤ R ≤ 8), 문자열 S가 공백으로 구분되어 주어진다. S의 길이는 적어도 1이며, 20글자를 넘지 않는다. 

출력

각 테스트 케이스에 대해 P를 출력한다.

 

 

내답 ) 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class 문자열반복 {
 
    public static void main(String[] args) throws IOException{
        BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
        int t=Integer.valueOf(reader.readLine());
        StringBuilder builder=new StringBuilder(t*161);//161 = R의 맥시멈 * s의최대길이 + 개행문자
        for(int i=1;i<=t;i++) {
            int r=reader.read()-'0';
            reader.read();
            char cs[]=reader.readLine().toCharArray();
            for(char c : cs) 
                for(int k=0;k<r;k++)
                    builder.append(c);
            builder.append('\n');
        }
        System.out.print(builder);
    }
}
cs

9 번재 줄에서 t 값을 읽어올때 , reader.read() 으로 했다가 자꾸 오류내는 실수함

1<=t<1000 범위이기에 reader.readLine 으로 문자열로 읽어와야 했었음

그 외엔 , 출력이 빈번해서 StringBuilder.append 를 자주 이용해줌

 

(9)그대로출력하기(中)

문제

입력 받은 대로 출력하는 프로그램을 작성하시오.

입력

입력이 주어진다. 입력은 최대 100줄로 이루어져 있고, 알파벳 소문자, 대문자, 공백, 숫자로만 이루어져 있다. 각 줄은 100글자를 넘지 않으며, 빈 줄은 주어지지 않는다. 또, 각 줄은 공백으로 시작하지 않고, 공백으로 끝나지 않는다.

출력

입력받은 그대로 출력한다.

 

내답1 ) 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Scanner;
 
public class 그대로출력하기 {
    public static void main(String[] args) throws IOException{
        Scanner scanner= new Scanner(System.in);
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
        while(scanner.hasNext()) {
            writer.write(scanner.nextLine());
            writer.write('\n');
        }
        writer.flush();
        writer.close();
        scanner.close();
    }
}
cs

문자열의 줄 수를 몰라도 출력할 수 있어야한다

scanner.hasNext() 를 이용하여 다음 문자열이 있다면 true , 없으면 false 를 반환 하도록 함 ( 백준기준 )

다만 , IDE 에서는 scanner.hasNext 에 의해서 프로그램이 끝나지 않음

 

내답2 ) 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
 
public class 그대로출력하기 {
    public static void main(String[] args) throws IOException{
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
        while(true) {
            String s= reader.readLine();
            if(s==nullbreak
            writer.write(s);
            writer.write('\n');
        }
        writer.flush();
        writer.close();
    }
}
cs

reader.readLine() 에 더 이상 읽어올 값이 없다면 , null을 반환 (백준기준)

다만 , IDE 에서는 reader.readLine() 에 의해서 프로그램이 끝나지 않음

 

( scanner.hasNext , reader.readLine 참고 )

 

wngksdma123 님의 답 ) 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.io.BufferedReader;
import java.io.IOException;
 
 
public class Main {
    public static void main(String[] args) throws IOException {
        StringBuilder sb = new StringBuilder();
        BufferedReader br = new BufferedReader(new java.io.InputStreamReader(System.in));
        for (int i = 0; i < 100; i++) {
            String str;
            if( (str = br.readLine()) == null)
                break;
            sb.append(str).append("\n");
        }
        System.out.println(sb);
    }
}
cs

13 번째 줄의 메소드 체이닝 효과외엔 비슷비슷하다

 

 

----------------------------------카카오-----------------------------

ㅇㄹ

728x90

-1.없어진 기록 찾기 문제

 

  • 문제 설명

 

ANIMAL_INS 테이블은 동물 보호소에 들어온 동물의 정보를 담은 테이블입니다. ANIMAL_INS 테이블 구조는 다음과 같으며, ANIMAL_ID, ANIMAL_TYPE, DATETIME,INTAKE_CONDITION, NAME, SEX_UPON_INTAKE는 각각 동물의 아이디, 생물 종, 보호 시작일, 보호 시작 시 상태, 이름, 성별 및 중성화 여부를 나타냅니다.

 

 

 

 

 

 

 

ANIMAL_OUTS 테이블은 동물 보호소에서 입양 보낸 동물의 정보를 담은 테이블입니다. ANIMAL_OUTS 테이블 구조는 다음과 같으며, ANIMAL_ID, ANIMAL_TYPE, DATETIME, NAME, SEX_UPON_OUTCOME는 각각 동물의 아이디, 생물 종, 입양일, 이름, 성별 및 중성화 여부를 나타냅니다. ANIMAL_OUTS 테이블의 ANIMAL_ID는 ANIMAL_INS의 ANIMAL_ID의 외래 키입니다.

 

 

 

천재지변으로 인해 일부 데이터가 유실되었습니다. 입양을 간 기록은 있는데, 보호소에 들어온 기록이 없는 동물의 ID와 이름을 ID 순으로 조회하는 SQL문을 작성해주세요.

 

내 답 ) 

1
2
3
4
#OUTS 테이블에만 존재하는 것을 뽑아 내면 된다.
SELECT outs.ANIMAL_ID,outs.NAME from ANIMAL_OUTS outs left join
ANIMAL_INS ins on outs.ANIMAL_ID=ins.ANIMAL_ID 
where ins.ANIMAL_ID is null order by outs.ANIMAL_ID;
cs

 

jeong-in 님의 답 ) 

1
select animal_id, name from animal_outs where animal_id not in (select animal_id from animal_ins);
cs

 

bigMango 님의 답 )

1
2
3
4
5
select o.animal_id, o.name
from animal_outs o
    left join animal_ins i using(animal_id)
where i.animal_id is null
order by o.animal_id;
cs

mysql에서 join을 사용할때  using 이나 on이나 결과는 동일하다 .

다만 ,  using 은 두 테이블간 필드이름이 같은 경우에만 사용하고  ,

on 은 두 테이블간 필드이름이 같던 다르던 사용가능하다.

상황에 따라서 using을 사용하면 조금이나마 코드가 짧아지겠다..

 

 

 

-2. 있었는데요 없었습니다

  • 문제설명

기본 문제형식은 1번과 동일합니다.

관리자의 실수로 일부 동물의 입양일이 잘못 입력되었습니다.

 보호 시작일보다 입양일이 더 빠른 동물의 아이디와 이름을 조회하는 SQL문을 작성해주세요.

 이때 결과는 보호 시작일이 빠른 순으로 조회해야합니다.

 

내답 ) 

1
2
3
4
#INNER JOIN 이용후 조건 만족하는 데이터 찾아서 출력
SELECT INS.ANIMAL_ID,OUTS.NAME FROM ANIMAL_INS INS INNER JOIN
ANIMAL_OUTS OUTS USING(ANIMAL_ID) WHERE INS.DATETIME>OUTS.DATETIME
ORDER BY INS.DATETIME ASC;
cs

 

herjh0405 님의 답 ) 

1
2
3
4
5
6
SELECT AI.ANIMAL_ID, AI.NAME FROM ANIMAL_INS AS AI
JOIN 
ANIMAL_OUTS AS AO
ON AI.ANIMAL_ID = AO.ANIMAL_ID
WHERE AI.DATETIME > AO.DATETIME
ORDER BY AI.DATETIME
cs

2: INNER JOIN 에서 INNER는 생략이 가능하다.

 

박현수 님의 답 )

1
2
3
SELECT a.animal_id, a.name from animal_ins a, animal_outs b
where a.animal_id = b.animal_id and a.datetime > b.datetime
order by a.datetime
cs

 

 

-3. 오랜 기간 보호한 동물(1)

  • 문제설명

기본 문제형식은 1번과 동일합니다.

아직 입양을 못 간 동물 중, 가장 오래 보호소에 있었던 동물 3마리의 이름과 보호 시작일을 조회하는 SQL문을 작성해주세요. 이때 결과는 보호 시작일 순으로 조회해야 합니다.

내답 )

1
2
3
4
5
6
SELECT INS.NAME,INS.DATETIME FROM ANIMAL_INS INS LEFT JOIN
ANIMAL_OUTS OUTS USING(ANIMAL_ID) WHERE OUTS.ANIMAL_ID IS NULL
ORDER BY INS.DATETIME LIMIT 0,3;
#INS 에만 있는 기록 중 , 가장 오래 보호소에 있었던 동물 3마리를
#이름,보호시작일 조회 (ORDER BY ANIMAL_INS.DATETIME ASC)
=> INS만의 차집합을 뽑아내면 해결.
cs

 

이필환님의 답)

1
2
3
4
5
6
SELECT NAME, DATETIME
from ANIMAL_INS
where ANIMAL_ID not in (select ANIMAL_ID from ANIMAL_OUTS)
order by DATETIME 
limit 3
;
cs

join 없이 서브쿼리로 해결하셨다.

 

-4. 보호소에서 중성화한 동물

  • 문제설명

기본 문제형식은 1번과 동일합니다.

보호소에서 중성화 수술을 거친 동물 정보를 알아보려 합니다. 

보호소에 들어올 당시에는 중성화되지 않았지만, 보호소를 나갈 당시에는 중성화된 동물의 아이디와 생물 종, 이름을 조회하는 아이디 순으로 조회하는 SQL 문을 작성해주세요.

중성화를 거치지 않은 동물은 성별 및 중성화 여부에 Intact, 중성화를 거친 동물은 Spayed 또는 Neutered라고 표시되어있습니다. 

내답 )

1
2
3
4
5
6
7
8
9
10
SELECT INS.ANIMAL_ID,INS.ANIMAL_TYPE,INS.NAME FROM ANIMAL_INS INS
INNER JOIN ANIMAL_OUTS OUTS USING(ANIMAL_ID) 
WHERE INS.SEX_UPON_INTAKE LIKE 'Intact%' and
OUTS.SEX_UPON_OUTCOME NOT LIKE 'Intact%' ORDER BY INS.ANIMAL_ID;
#중성화를 거치지 않은 동물은  Intact 문자열이 들어가있음.
#먼저 INS와 OUTS에 동일한 동물이 존재해야하므로 ,
#INS와 OUTS가 ANIMAL_ID로 inner join 후 
#INS.SEX_UPON_INTAKE 가 Intact 를 포함하며 , 
#OUTS.SEX_UPON_OUTCOME 가 Intact를 포함하지 않는 경우의
# 아이디와 생물 종, 이름을 조회 (ORDER BY INS.ANIMAL_ID ASC)
cs

가뜩이나 인덱스가 적용안된 SEX_UPON_INTAKE(SEX_UPON_OUTCOME) 에  

WHERE 조건으로 Spayed , Neutered 를  두가지 모두 사용하는 것은 부담스러워서 Intact만을 사용하였다.

 

lazy-sky 님의 답 )

1
2
3
4
5
6
SELECT O.ANIMAL_ID, O.ANIMAL_TYPE, O.NAME
FROM ANIMAL_OUTS AS O
LEFT OUTER JOIN ANIMAL_INS AS I
ON O.ANIMAL_ID = I.ANIMAL_ID
WHERE O.SEX_UPON_OUTCOME != I.SEX_UPON_INTAKE
ORDER BY ANIMAL_ID;
cs

실로 놀랍다.

먼저 , 

1. 범위성 측면에서 ANIMAL_OUTS 테이블은 ANIMAL_INS 테이블보다 작고,

   ANIMAL_OUTS 테이블에 있는 동물은 ANIMAL_INS 테이블에 기본적으로 모두 포함되어있다.

=> 'A' LEFT OUTER JOIN 'B' 의  'A'부분에 ANIMAL_OUTS를 놓음 으로써 INNER JOIN 과 동일한 결과가 나온다.

2. INS테이블의 성별과 OUTS테이블이 성별이 다르다는 것은 , 의미하는 바가 한가지이다.

(중성 -> 성별을갖는것 은 될수가 없다.)성별을갖는것 ->중성O 이 만족한다, 즉 중성화가 된다..

=> WHERE  O.SEX_UPON_OUTCOME != I.SEX_UPON_INTAKE 으로 표현이 된다.

 

'21년이전 > 국비-sql' 카테고리의 다른 글

SQL - JOIN  (0) 2021.06.22
SQL - DDL  (0) 2021.05.12
DB - 개념모델링  (0) 2021.05.10
국비 SQL - 명령어 모음  (0) 2021.02.22
728x90

-JOIN  

*LEFT JOIN

*INNER JOIN

*RIGHT JOIN

*FULL OUTER JOIN

 

*INNER JOIN    
-양쪽다 존재하는 것만 출력한다 .  
-NULL 행이 존재하지 않게 된다.
-JOIN 성능중 가장 좋다

*LEFT JOIN
-가장 일반적으로 사용된다
-기준테이블에만 존재하는 것과 INNER JOIN 내용이 함께 출력된다. 
-실제론 LEFT JOIN , INNER JOIN만 이용된다. 

*RIGHT JOIN
-LEFT JOIN과 똑같은 역할을 한다, 테이블 기준만 다르다.

*FULL OUTER JOIN
-mysql은 지원하지 않고 , union으로 만들수 있다

 

하단은 JOIN을 설명하기 위한 샘플예제이다.  왼쪽) 학생테이블 , 오른쪽) 상담사테이블 

학생 테이블의 consultor_id는 상담사 테이블의 consultor_id(PK)를 참조한 외래키 이다.

학생 테이블의 student_id=10,11 인 값은 해당 테이블내에만 존재하고

상담사 테이블의 consultor_id=5 인값은  해당 테이블내에만 존재한다.

 

 

공통질문 

student_id 오름차순 으로 표시하라.

 

Q1) 상담사 정보가 학생테이블에 표시되도록 하며, 모든 학생 테이블을 보여라. ( 상담사 정보가 없다면 NULL로 표시 )

 

Result) 표준형태의 LEFT JOIN 을 수행. 

1
SELECT * FROM student st LEFT JOIN consultor co ON st.consultor_id=co.consultor_id ORDER BY st.student_id ASC;
cs

 

비교용도 테이블)

왼쪽) 학생테이블 , 오른쪽) 상담사테이블

 

 

 

 

Q2) 학생 테이블에 상담사 정보가 표시되도록 하며 , 상담사가 학생에게 붙은 경우만의 테이블을 보여라.

Result1)  LEFT JOIN에 조건 절을 추가하여 INNER JOIN 효과를 냈음.

1
SELECT * FROM student st LEFT JOIN consultor co ON st.consultor_id=co.consultor_id WHERE st.consultor_id IS NOT NULL;
cs

비교용도 테이블)

 

Result2) 표준 형태의 INNER JOIN 수행.

1
SELECT * FROM student st INNER JOIN consultor co ON st.consultor_id=co.consultor_id ORDER BY st.student_id ASC;
cs

비교용도 테이블)

 

RESULT 1 와 RESULT 2 는 동일하다.

 

 

 

 

Q3) 학생 테이블에 상담사 정보가 표시되도록 하며 , 상담사가 정해지지 않은 경우만의 테이블을 보여라.

 

RESULT) LEFT JOIN 에 조건절을 추가하여 해결.

1
SELECT * FROM student st LEFT JOIN consultor co ON st.consultor_id=co.consultor_id WHERE co.consultor_id IS NULL;
cs

 

비교용도 테이블)

 

 

 

 

Q4) 학생,상담사 테이블의 합집합을 표시하라 . 

 

Result) mysql 에서 FULL OUTER JOIN 효과를 내기 위해 union을 사용한다.

1
2
(SELECT * FROM student st LEFT JOIN consultor co ON st.consultor_id=co.consultor_id) UNION
(SELECT * FROM student st RIGHT JOIN consultor co ON st.consultor_id=co.consultor_id );
cs

비교용도 테이블)

 

 

 

 

Q5) 상담사가 안붙은 학생정보와 학생이 안붙은 상담사 정보를 가져오세요

 

Result) LEFT JOIN , RIGHT JOIN 에 각각 조건절을 달아주고  UNION 시킴

1
2
(SELECT * FROM student st LEFT JOIN consultor co ON st.consultor_id=co.consultor_id WHERE co.consultor_id IS NULL) UNION
(SELECT * FROM student st RIGHT JOIN consultor co ON st.consultor_id=co.consultor_id WHERE st.consultor_id IS NULL);
cs

 

 

 

 

 

 

참고로 시각적으로 JOIN 효과 보여주는 사이트  https://sql-joins.leopard.in.ua/  

'21년이전 > 국비-sql' 카테고리의 다른 글

프로그래머스 - JOIN 문제모음  (0) 2021.06.22
SQL - DDL  (0) 2021.05.12
DB - 개념모델링  (0) 2021.05.10
국비 SQL - 명령어 모음  (0) 2021.02.22

+ Recent posts