<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>justkeepgoing</title>
        <link>https://velog.io/</link>
        <description>차선이 모여 최선이 된다.</description>
        <lastBuildDate>Sat, 28 Jan 2023 14:03:23 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. justkeepgoing. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dev-yongjun" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Spring] 스프링 컨테이너와 스프링 빈, 의존성 주입]]></title>
            <link>https://velog.io/@dev-yongjun/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%99%80-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85</link>
            <guid>https://velog.io/@dev-yongjun/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%99%80-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85</guid>
            <pubDate>Sat, 28 Jan 2023 14:03:23 GMT</pubDate>
            <description><![CDATA[<h1 id="스프링-컨테이너">스프링 컨테이너</h1>
<p>의존성 주입이란 애플리케이션 작동에 필요한 객체를 제 3자로부터 제공받는 것이다. 그리고 그 제 3자가 스프링 컨테이너이다. <strong>그래서 스프링 컨테이너는 애플리케이션에서 사용되는 객체(스프링 빈)의 라이프사이클(객체 생성, 의존성 주입, 소멸)을 관리한다.</strong> 스프링 컨테이너의 종류는 다음과 같다.</p>
<ul>
<li><p>BeanFactory</p>
<ul>
<li>spring-bean.jar에 포함되어 있는 스프링 컨테이너 인터페이스이다. 객체 생성, 의존성 주입과 같은 간단한 기능만 제공한다.</li>
<li>구현 클래스 : XmlBeanFactory</li>
</ul>
</li>
<li><p>ApplicationContext</p>
<ul>
<li>spring-context.jar에 포함되어 있는 스프링 컨테이너 인터페이스이다. 객체 생성, 의존성 주입, 관점지향 프로그래밍, 국제화, EJB 연동 등 다양한 기능을 제공한다.</li>
<li>구현 클래스 : ClasspathXmlApplicationContext, FilesystemXmlApplicationContext, GenericXmlApplicationContext</li>
</ul>
</li>
<li><p>WebApplicationContext</p>
<ul>
<li>spring-web.jar에 포함되어 있는 스프링 컨테이너 인터페이스이다. ApplicationContext 인터페이스와 기능은 유사하고, 웹 환경에 최적화되어 있다.<ul>
<li>구현 클래스 : XmlWebApplicationContext</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1 id="스프링-빈">스프링 빈</h1>
<p><strong>위에서 언급했듯이 스프링 빈이란 스프링 컨테이너가 관리하는 Java 객체를 의미한다.</strong> 스프링 컨테이너가 스프링 빈을 관리하도록 하기 위해서는 스프링 빈 설정파일에 객체들의 정보를 정의해서 스프링 빈으로 등록해야 한다.</p>
<h1 id="의존성-주입">의존성 주입</h1>
<h2 id="객체간의-의존관계">객체간의 의존관계</h2>
<pre><code class="language-java">package com.sample.controller;

import com.sample.dao.UserOracleDao;

public class UserController {

    private UserOracleDao userOracleDao;

    public void setUserOracleDao(UserOracleDao userOracleDao) {
        this.userOracleDao = userOracleDao;
    }

    // 의존성 주입이 되었는지 확인하는 메서드.
    public void test() {
        System.out.println(userOracleDao);
    }
}</code></pre>
<p><code>UserController</code>는 <code>UserOracleDao</code>를 멤버변수로 갖고 있고, <code>UserOracleDao</code>를 매개변수로 전달받아 자기 자신의 멤버변수에 대입하는 메서드를 갖고 있다. 그리고 대입된 멤버변수인<code>UserOracleDao</code>를 출력하는 메서드를 갖고 있다. 즉, <code>UserController</code>는 <code>UserOracleDao</code>를 의존한다.</p>
<h2 id="스프링-빈-등록">스프링 빈 등록</h2>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] Spring이란?]]></title>
            <link>https://velog.io/@dev-yongjun/Spring-Spring%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@dev-yongjun/Spring-Spring%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Wed, 04 Jan 2023 15:24:27 GMT</pubDate>
            <description><![CDATA[<h1 id="spring이란">Spring이란?</h1>
<blockquote>
<p><strong>Spring</strong>이란 Java 기반의 엔터프라이즈 애플리케이션 개발을 위한 오픈소스 경량급 애플리케이션 프레임워크다.</p>
</blockquote>
<ul>
<li>엔터프라이즈 애플리케이션<ul>
<li>엔터프라이즈 애플리케이션은 기업 또는 정부와 같은 환경에서 작동하도록 설계된 대규모 소프트웨어 시스템이다.
은행 거래 애플리케이션이나 정부24같은 인터넷 행정 처리 웹사이트 등 전산 시스템이라 불리는 것들이 그 예시이다.</li>
</ul>
</li>
</ul>
<ul>
<li>오픈소스<ul>
<li>소스코드가 공개되어 있고 특별한 비용지불없이 자유롭게 이용가능하다는 뜻이다.</li>
</ul>
</li>
</ul>
<ul>
<li>경량급<ul>
<li>스프링이 불필요하게 반복되는 코드없이 작성되었다는 의미이다. 스프링 등장 이전의 엔터프라이즈급  프레임워크는 다양한 경우를 처리하기 위해 다양한 기능을 갖도록 만들다 보니 하나의 기능을 위해 너무 많은 구조가 필요하게 되었다. 그러나 스프링은 이러한 복잡성을 해결했다.</li>
</ul>
</li>
</ul>
<ul>
<li>애플리케이션 프레임워크<ul>
<li>특정 계층이나 기술에 국한되지 않고 애플리케이션 전 영역을 지원하는 프레임워크를 말한다. 일반적으로 다른 프레임워크들은 웹 영역이나 데이터베이스 영역과 같이 특정한 분야에 대해서만 지원했다. 반면 스프링은 프로젝트 전체를 설계하는 용도로 사용할 수 있다.</li>
</ul>
</li>
</ul>
<h1 id="spring의-주요-특징">Spring의 주요 특징</h1>
<ul>
<li><p>의존성 주입 (Dependency Injection)</p>
</li>
<li><p>제어의 역전 (Inversion Of Control)</p>
</li>
<li><p>관점지향 프로그래밍 지원 (Aspect Oriented Programming)</p>
</li>
<li><p>MVC 패턴</p>
</li>
<li><p>POJO 기반의 구성 (Plain Old Java Object)</p>
</li>
<li><p>트랜잭션 지원</p>
</li>
</ul>
<h2 id="의존성-주입">의존성 주입</h2>
<p>&#39;의존성&#39;을 가진다는 것은  A 객체가 동작하기 위해 B 객체를 필요로 한다는 의미이다. 즉, 두 객체가 서로 결합되어 있는 형태이다. 그리고 &#39;의존성 주입&#39;은 필요한 객체를 직접 생성하는 것이 아니라 외부로 부터 제공받는 방식을 말한다.</p>
<p>그렇다면 어떻게 외부로 부터 필요한 객체를 &#39;주입&#39;받는가? 스프링 프레임워크가 이러한 의존성 주입을 지원한다. <strong>스프링은 객체들 간의 의존관계를 파악해서,  스프링이 직접 객체를 생성하고 제공한다.</strong> 스프링의 가장 핵심적인 특징이 의존성 주입이다.</p>
<p>필요한 객체를 직접 생성하는 것이 아닌, 외부로부터 객체를 제공받으면 클래스간의 결합도를 낮출 수 있다. 의존 관계에 있는 두 클래스 중 한 클래스가 수정되어도 다른 클래스는 수정할 필요가 없다는 의미다. 즉, 확장성이 높은 유연한 프로그램을 만들 수 있다. 이것이 의존성 주입을 사용하는 이유이다. </p>
<h2 id="제어의-역전">제어의 역전</h2>
<p>제어의 역전이란 객체의 생명주기를 개발자가 아닌 제 3자가 관리하도록 하는 디자인 패턴이다. 스프링 프레임워크에서는 컨테이너가 그 역할을 수행한다. </p>
<p>위에서 언급했듯이 의존성 주입은 필요한 객체를 스프링 프레임워크로부터 제공받는 것이다. 그래서 의존성 주입은 제어의 역전을 달성하기 위한 방법 중 하나이다.</p>
<h2 id="관점지향-프로그래밍-지원">관점지향 프로그래밍 지원</h2>
<p>관점지향 프로그래밍이란 프로그램의 공통기능과 핵심기능을 분리시키는 것이다. 로그출력 같은 것이 공통기능의 예시이다. 이를 통해 코드 중복을 피하고 유지보수를 용이하게 할 수 있다. </p>
<p>물론 공통기능을 수행하는 클래스를 따로 만들어 공통기능에 대한 메소드를 필요할 때 마다 호출할 수 있지만, 이것이 관점지향 프로그래밍은 아니다. 관점지향 프로그래밍은 핵심 기능을 수행하는 클래스 내에 공통기능과 관련된 코드가 존재하지 않도록 하는 것이 핵심이다.</p>
<h2 id="mvc-패턴">MVC 패턴</h2>
<h2 id="pojo-기반의-구성">POJO 기반의 구성</h2>
<p>Plain Old Java Object로 직역하면 순수한 자바 객체이다. POJO 기반의 구성이라는 것은 스프링 프레임워크를 이용할 때, 일반적인 Java 언어로 코드를 작성할 수 있다는 의미이다. </p>
<p>이 점이 중요한 이유는 코드가 특정 기술에 종속되면 객체지향의 장점을 잃어버리기 때문이다. 예를 들어 어떤 프레임워크를 사용하기 위해 그 프레임워크가 제공하는 API를 상속, 구현해해야 한다면 코드의 확장성이 떨어진다. </p>
<p>스프링 이전의 프레임워크들은 이러한 문제점을 갖고 있어 개발자가 해당 프레임워크에 종속되었다. 그러나 스프링 프레임워크는 스프링 기술과 관련된 코드가 애플리케이션 코드에 등장하지 않도록 했다.</p>
<h2 id="선언적-트랜잭션-지원">선언적 트랜잭션 지원</h2>
<p>트랜잭션이란 데이터베이스의 상태를 변화시키는 하나의 논리적인 단위이다. 예를 들어 그룹웨어에서 쪽지 기능이 처리된다고 하자. 주고 받은 쪽지는 발신자 측의 보낸 쪽지함에, 수신자 측의 받은 편지함에 동시에 저장되어야 한다. 이 중 어느 한 과정이라도 실패한다면 쪽지 기능이 성공적으로 수행됬다고 할 수 없다. 이처럼 트랜잭션은 여러 번의 데이터베이스 액세스 작업이 묶여있는 논리적인 단위이다.</p>
<p>서비스 클래스의 메서드에는 여러 번의 데이터 베이스 액세스 작업이 포함되어 있는데, 이 중 하나의 작업이라도 실패한다면 나머지 작업도 취소되어야 한다. 그런데 스프링 프레임워크를 사용하면 트랜잭션 처리를 어노테이션이나 xml파일의 설정을 통해 할 수 있다. 개발자가 트랜잭션을 위한 코드를 작성할 필요가 없다는 말이다. 그리고 이러한 방법을 &#39;선언적 트랜잭션&#39;이라고 한다.</p>
<h1 id="라이브러리-vs-프레임워크">라이브러리 vs 프레임워크</h1>
<p><strong>라이브러리는 개발에 필요한(자주 사용되는) 기능의 구현체다.</strong> 개발자는 필요한 기능이 구현되어 있는 라이브러리를 다운받아서 그 라이브러리의 기능을 사용한다.</p>
<p>반면, <strong>프레임워크는 프로그램의 뼈대나 근간을 이루는 코드들의 묶음이다. 달리 말해, 개발 방식 혹은 개발 패턴의 구현체다.</strong> 개발자는 해당 프레임워크가 제시하는 방식에 맞게 클래스나 인터페이스, 환경설정 정보를 구성하면 프레임워크가 지원하는 기능을 제공받을 수 있다.</p>
<p>즉, <strong>라이브러리와 프레임워크의 주요한 차이점은 &#39;누가 제어를 하느냐&#39;이다.</strong> 개발자는 라이브러리를 &#39;직접&#39; 호출해서 사용하고, 개발자가 작성한 애플리케이션 코드는 프레임워크에 의해 사용된다. 프레임워크의 경우 처럼 프로그램의 흐름이 개발자가 아닌 제 3자에게 넘어간 것을 &#39;제어의 반전&#39;이라고 하는데, 이 역시 Spring에 반영되어 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JSP] 웹 애플리케이션과 Servlet, 그리고 JSP]]></title>
            <link>https://velog.io/@dev-yongjun/JSP-Servlet%EA%B3%BC-JSP</link>
            <guid>https://velog.io/@dev-yongjun/JSP-Servlet%EA%B3%BC-JSP</guid>
            <pubDate>Thu, 17 Nov 2022 13:34:47 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dev-yongjun/post/a05b7c5b-ba95-4613-9655-be582d80bbad/image.png" alt=""></p>
<h1 id="웹-애플리케이션">웹 애플리케이션</h1>
<blockquote>
<p>웹 애플리케이션(web application) 또는 웹 앱은 소프트웨어 공학적 관점에서 인터넷이나 인트라넷을 통해 웹 브라우저에서 이용할 수 있는 응용 소프트웨어를 말한다.</p>
</blockquote>
<p>소프트웨어를 브라우저에서 이용할 수 있다는 것은 기존의 응용 소프트웨어 혹은 프로그램이라고 불리는 것들을 생각해보면 이해하기 쉽다. 이러한 것들은 하드 디스크에 설치해서 실행했다. 즉, 운영체제 위에서 작동했다. <strong>그러나 웹 애플리케이션은 이보다 한 층 더 올라가 브라우저 위에서 이용할 수 있는 소프트웨어이다.</strong> 그리고 프로그램은 사용자의 입력을 받아 설계된 명령어에 따른 결과를 반환한다. </p>
<p><strong>그래서 웹 애플리케이션은 사용자와 &#39;상호작용&#39;하며 브라우저에서 이용가능한 프로그램이다.</strong></p>
<p><img src="https://velog.velcdn.com/images/dev-yongjun/post/7cc2b62d-1a29-4e6e-b05d-fb645c6b4ca4/image.png" alt=""></p>
<p>브라우저에 접속해서 웹 사이트에 들어가는게 원래 그런거 아닌가? 라고 생각할 수 있지만 그렇지 않다. 과거의 웹 사이트는 브라우저의 요청에 따라 그저 서버에 저장된 문서를 사용자에게 보여줄 뿐이었다. 이러한 웹 사이트를 <strong>정적 웹 페이지</strong>라 부른다. </p>
<p>그러나 웹 애플리케이션이라고 부르는 것들은 앞서 언급했다시피 사용자와 상호작용하는 기능을 수행한다. 즉, 사용자의 입력에 따라 특정한 연산을 수행하고 그에 따른 결과를 반환한다. 이러한 이유로 웹 애플리케이션을 <strong>동적 웹 페이지</strong>라고 부른다. 오늘날 대부분의 웹 사이트는 동적 웹 페이지로 구성되어 있다. 대표적인 예가 로그인이다. 로그인은 특정 사용자임을 인증하고 해당 사용자에 따라 다른 화면을 보여준다. 로그인 뿐만 아니라 결제, 댓글, 좋아요와 같은 기능들이 모두 동적 웹 페이지의 예이다.</p>
<h2 id="웹-서버와-웹-애플리케이션-서버">웹 서버와 웹 애플리케이션 서버</h2>
<p>앞서 설명했듯이 웹 브라우저는 &#39;요청&#39;하고 서버는 컨텐츠를 &#39;응답&#39;한다. 브라우저의 주소창에 <a href="https://naver.com">https://naver.com</a> 를 입력하는 것 그 자체가 NAVER 웹 페이지에 접근하겠다는 요청이다.</p>
<p><strong>웹 서버</strong>는 브라우저의 요청에 대해 정적 컨텐츠를 응답한다. 단지 서버에 저장된 컨텐츠를 어떠한 가공없이 응답으로 보낸다. 그러나 <strong>웹 애플리케이션 서버(WAS : Web Application Server)</strong>는 브라우저의 요청에 따라 특정한 연산을 수행한 결과 즉, 동적 컨텐츠를 응답한다. 그리고 이 WAS에 웹 애플리케이션이 배포되어 있다. 웹 애플리케이션의 로직은 WAS에서 실행되고 사용자는 브라우저에서 웹 애플리케이션에 요청을 보낸다.</p>
<p>WAS는 동적 컨텐츠 뿐만 아니라 정적 컨텐츠도 응답할 수 있기 때문에 WAS만으로도 웹 시스템을 구성할 수 있다. 그러나 WAS의 부담을 줄이기 위해 웹 서버는 정적 컨텐츠만 응답하도록 하고, WAS는 웹 애플리케이션의 로직을 수행하도록 하여 동적 컨텐츠를 응답하도록 구성한다. </p>
<p><img src="https://velog.velcdn.com/images/dev-yongjun/post/866616ad-6f25-4baa-a948-083974562734/image.png" alt=""></p>
<h2 id="http">HTTP</h2>
<p>HTTP(HyperText Transfer Protocol)는 컴퓨터간의 데이터 교환방식을 정의한 약속이다. 웹 브라우저와 웹 서버는 서로 요청과 응답을 주고 받으며 &#39;통신&#39;한다. 여기서 요청하는 측을 클라이언트, 요청에 대해 응답을 제공하는 측을 서버라고 한다. <strong>Http는 이처럼 클라이언트와 서버가 주고받는 메세지의 양식을 규정한 통신규약이다. 그리고 웹 브라우저와 웹 서버는 이 Http를 기반으로 작동한다.</strong></p>
<h1 id="servlet이란">Servlet이란?</h1>
<p><strong>클라이언트 요청을 처리하고 그 결과를 다시 클라이언트에게 전송하는 Servlet 클래스의 구현 규칙을 지킨 동적 컨텐츠를 생성하기 위한 Java 프로그램이다.</strong> 클라이언트의 요청에 대해 동적 컨텐츠를 응답하기 때문에 당연히 웹 애플리케이션 &#39;서버&#39;에서 작동하는 프로그램이다.</p>
<p>이 말이 어떤 의미인지 이해하기 위해 Servlet이 없을 때, 웹 애플리케이션이 브라우저의 요청을 수행하도록 개발자가 어떻게 해야하는지 생각해보자. 아래 사진은 HTTP의 클라이언트의 요청메세지와 서버의 응답메세지이다. 만약 Servlet을 사용하지 않는다면 개발자는 이 메세지를 컴퓨터가 해석할 수 있도록 &#39;파싱&#39;해야한다. HTTP메세지는 그저 문자열이기 때문이다. 요청 메세지를 분석해서 웹 애플리케이션 로직을 수행하는 데 필요한 데이터를 읽어오고, 로직을 수행한 뒤 그 결과를 반환하기 위해 응답 메세지를 구성해야 한다. <strong>Servlet이 있다면 이러한 웹 애플리케이션 로직 전, 후의 작업을 대신 수행하여 개발자는 로직에 보다 집중할 수 있다.</strong></p>
<p><img src="https://velog.velcdn.com/images/dev-yongjun/post/2a91dc30-642e-47c0-bbf3-8dcb5d87a2e1/image.png" alt=""></p>
<p>즉, 개발자는 클라이언트의 각 요청에 필요한 Servlet 클래스들을 구현해두고, 요청이 올 때마다 그에 대한 처리를 Servlet 객체가 대신하도록 한다. Servlet 객체는 생성, 로직 실행, 소멸을 반복하며 클라이언트의 요청을 처리한다. <strong>그런데 이러한 Servlet 객체(프로그램)의 동작을 개발자가 직접 제어하지 않는다. &#39;서블릿 컨테이너&#39;라고 하는 것이 Servlet 객체의 동작을 관리한다.</strong> 다음은 WAS와 서블릿 컨테이너의 구조이다.</p>
<p><img src="https://velog.velcdn.com/images/dev-yongjun/post/011b9232-15da-4583-b6a4-bb0b6fddb5ba/image.png" alt=""></p>
<h2 id="httpservlet-클래스의-구조">HttpServlet 클래스의 구조</h2>
<p>HTTP를 기반으로 클라이언트와 서버가 통신할 수 있도록 Servlet을 구현하려면 HttpServlet 클래스를 상속받은 클래스를 구현해야 한다. *<em>다시 말해, 웹 브라우저의 요청을 처리하기 위한 Servlet 클래스를 정의하려면 반드시 <code>HttpServlet</code>를 상속받아야 한다. *</em> HttpServelt 클래스 또한 상위 추상 클래스(GenericServlet)와 인터페이스(Servlet)를 구현한 클래스이다. 이제 그 상속 구조를 알아보자.</p>
<p><img src="https://velog.velcdn.com/images/dev-yongjun/post/dfa76ea8-ea9d-4081-9f74-a5e144d5fe05/image.png" alt=""></p>
<ul>
<li><p>** Servlet &lt; Interface &gt;**</p>
<ul>
<li>모든 서블릿 클래스가 반드시 구현해야하는 인터페이스다.</li>
<li>서블릿의 생명 주기와 관련된 추상 메소드가 정의되어 있다.</li>
<li>주요 메소드<ul>
<li><code>void init()</code><ul>
<li>서블릿 객체가 초기화될 때 실행되는 메소드다.</li>
</ul>
</li>
<li><code>void service(ServletRequest request, ServletResponse response)</code><ul>
<li>서블릿 객체가 클라이언트의 요청을 처리할 때 실행되는 메소드다.</li>
</ul>
</li>
<li><code>void destroy()</code><ul>
<li>서블릿 객체가 소멸될 때 실행되는 메소드다.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>** GenericServlet &lt; Abstract class &gt;**</p>
<ul>
<li>Servlet 인터페이스를 구현하는 추상 클래스다.</li>
<li>Service()를 제외한 Servlet 인터페이스의 메소드를 모두 구현해 두었다.</li>
<li>Servlet 인터페이스에 정의되어 있지 않은 추가 메소드를 제공한다.</li>
<li>주요 메소드<ul>
<li><code>String getInitParameter(String name) {...}</code><ul>
<li>서블릿 초기화 파라미터값을 반환한다.</li>
</ul>
</li>
<li><code>ServletConfig getServletConfig() {...}</code><ul>
<li>ServletConfig 객체를 반환한다.</li>
</ul>
</li>
<li><code>ServletContext getServletContext() {...}</code><ul>
<li>ServletContext 객체를 반환한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>** HttpServlet &lt; Abstract class &gt;**</p>
<ul>
<li>GenericServlet 클래스를 상속받아 HTTP 프로토콜에 알맞은 동작을 수행하도록 service() 메소드를 재정의한 클래스다.</li>
<li>HttpServletRequest는 HTTP요청에 있는 정보를 서블릿에게 전달하기 위해 사용되는 객체다.</li>
<li>HttpServletResponse는 클라이언트에게 보낼 HTTP응답을 구성하기 위해 사용되는 객체다.</li>
<li>주요 메소드<ul>
<li><code>void doGet(HttpServletRequest request, HttpServletResponse response) {...}</code><ul>
<li>GET방식의 요청을 처리하는 메소드다.</li>
</ul>
</li>
<li><code>void doPut(HttpServletRequest request, HttpServletResponse response) {...}</code><ul>
<li>PUT방식의 요청을 처리하는 메소드다.</li>
</ul>
</li>
<li><code>void doPost(HttpServletRequest request, HttpServletResponse response) {...}</code><ul>
<li>POST방식의 요청을 처리하는 메소드다.</li>
</ul>
</li>
<li><code>void doDelete(HttpServletRequest request, HttpServletResponse response) {...}</code><ul>
<li>DELETE방식의 요청을 처리하는 메소드다.</li>
</ul>
</li>
<li><code>void service(HttpServletRequest request, HttpServletResponse response) {...}</code><ul>
<li>클라이언트의 요청 방식에 알맞은 메소드를 실행하도록 정의되어 있다.</li>
<li>요청방식에 상관없이 항상 실행되는 메소드다.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>** 사용자 정의 Servlet 클래스** 
<code>public class HelloServlet extends HttpServlet {...}</code></p>
<ul>
<li>HttpServlet 클래스를 상속받아 정의하는 클래스다.</li>
<li>** 어떠한 요청방식을 처리할 것인지 개발자의 의도대로 service()메소드를 재정의한다.**</li>
</ul>
</li>
</ul>
<h2 id="servlet의-동작-과정">Servlet의 동작 과정</h2>
<ol>
<li><p>웹 브라우저(클라이언트)가 URL요청을 보내면 웹 서버는 HTTP Request 메세지를 해석한 뒤, 서블릿에 대한 요청이면 메세지를 서블릿 컨테이너로 보낸다.</p>
</li>
<li><p><strong>서블릿 컨테이너가 HttpServletRequest, HttpServletResponse 객체를 생성한다.</strong> (서블릿이 직접 생성하지 않는다.)</p>
</li>
<li><p>서블릿 컨테이너가 web.xml을 분석해서 어느 서블릿에 대한 요청인지 찾는다.</p>
</li>
<li><p>해당 서블릿이 최초 요청이라면, 서블릿 클래스를 메모리에 로드하고 객체를 생성한다. *<em>이후의 요청은 이 때 생성한 객체를 사용하여 요청을 처리한다. 이처럼 객체를 한번만 생성하여 사용하는 것을 &#39;싱글톤 패턴&#39;이라고 한다.  *</em></p>
</li>
<li><p><code>init()</code> 메서드를 실행해서 서블릿 객체의 초기화작업을 수행한다. 이 메서드는 서블릿 객체가 생성된 다음에 호출되는 메서드다. 그래서 최초 요청이 아니라면 <code>init()</code>는 실행되지 않는다.</p>
</li>
<li><p><code>service()</code> 메서드를 실행해서 클라이언트의 요청을 처리한다. 이 때 서블릿 컨테이너가 생성한 HttpServletRequest, HttpServletResponse 객체를 인자로 전달받는다.</p>
</li>
<li><p><code>service()</code> 메서드가 종료되면 서블릿 프로그램이 종료된다. 그리고 서블릿 컨테이너는 실행결과를 웹 서버에 전달하고, HttpServletRequest, HttpServletResponse 객체를 소멸시킨다. 웹 서버는 클라이언트의 요청에 대해 응답을 보낸다.</p>
</li>
<li><p><code>destroy()</code> 메서드는 서블릿이 수정되었을 때 실행된다. 이미 메모리에 로드되어 있는 서블릿을 소멸시켜야 수정된 서블릿을 로드할 수 있기 때문이다.</p>
</li>
</ol>
<p>코드와 콘솔창을 통해 보다 자세히 알아보자.</p>
<pre><code class="language-java">import java.io.IOException;
import java.io.PrintWriter;
import java.rmi.ServerException;

import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class HelloServlet extends HttpServlet {

    // 서블릿 최초 요청 시 init()가 실행된다.
    @Override
    public void init() throws ServletException {
        System.out.println(&quot;HelloServlet의 init() 메서드 실행 ...&quot;);
    }

    // 소스코드가 변경되면 destroy()가 실행되어 객체가 소멸된다.
    @Override
    public void destroy() {
        System.out.println(&quot;HelloServlet의 destroy() 메서드 실행 ...&quot;);
    }

    // HTML 컨텐츠를 응답으로 보낸다.
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServerException, IOException {
        System.out.println(&quot;HelloServlet의 service() 메서드 실행 ...&quot;);

        // HttpServletRequest 객체를 이용해 응답메세지의 타입을 html로 지정한다.
        response.setContentType(&quot;text/html; charset=utf-8&quot;);

        // 웹 브라우저에 출력하기 위한 객체를 HttpServletRequest객체를 이용해 생성한다.
        PrintWriter out = response.getWriter();

        // 위에서 생성한 PrintWriter 객체를 이용해 웹 브라우저에 출력할 컨텐츠를 작성한다.
        out.print(&quot;&lt;!DOCTYPE html&gt;&quot;);
        out.print(&quot;&lt;html lang=&#39;ko&#39;&gt;&quot;);
        out.print(&quot;&lt;head&gt;&quot;);
        out.print(&quot;&lt;meta charst=&#39;utf-8&#39;&gt;&quot;);
        out.print(&quot;&lt;title&gt;헬로 서블릿&lt;/title&gt;&quot;);
        out.print(&quot;&lt;/head&gt;&quot;);
        out.print(&quot;&lt;h1&gt;헬로 서블릿&lt;/h1&gt;&quot;);
        out.print(&quot;&lt;p&gt;나는 서블릿입니다.&lt;/p&gt;&quot;);
        out.print(&quot;&lt;p&gt;서블릿의 라이프 사이클 메서드를 확인하세요.&lt;/p&gt;&quot;);
        out.print(&quot;&lt;p&gt;&lt;/p&gt;&quot;);
        out.print(&quot;&lt;/body&gt;&quot;);
        out.print(&quot;&lt;/html&gt;&quot;);
    }
}</code></pre>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;web-app xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns=&quot;https://jakarta.ee/xml/ns/jakartaee&quot; xmlns:web=&quot;http://xmlns.jcp.org/xml/ns/javaee&quot; xsi:schemaLocation=&quot;https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd http://xmlns.jcp.org/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd&quot; id=&quot;WebApp_ID&quot; version=&quot;5.0&quot;&gt;

      &lt;!--
    서블릿의 경로와 별칭을 지정한다.
    --&gt;
    &lt;servlet&gt;
        &lt;servlet-name&gt;hello-servlet&lt;/servlet-name&gt;
        &lt;servlet-class&gt;com.sample.servlet.HelloServlet&lt;/servlet-class&gt;
    &lt;/servlet&gt;
      &lt;!--
    위에서 지정한 서블릿의 별칭과 URL주소를 매핑한다.
    --&gt;
    &lt;servlet-mapping&gt;
        &lt;servlet-name&gt;hello-servlet&lt;/servlet-name&gt;
        &lt;url-pattern&gt;/hello&lt;/url-pattern&gt;
    &lt;/servlet-mapping&gt;    

&lt;/web-app&gt;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/5c889d68-0e68-4a56-b039-32b4aadf5295/image.png" align="left">

<p><img src="https://velog.velcdn.com/images/dev-yongjun/post/9df04706-18f6-4e94-bafd-d87dd7c38953/image.png" alt=""></p>
<p>만약 여기서 소스코드가 수정되면 자동으로 <code>destroy()</code> 메서드가 실행되어 HelloServlet 객체가 소멸된다.</p>
<p><img src="https://velog.velcdn.com/images/dev-yongjun/post/b1df71ad-fe19-4c3a-b0ca-f62f846aab55/image.png" alt=""></p>
<h2 id="단점">단점</h2>
<p><strong>코드를 보면 알 수 있듯 Java 소스코드 내에 HTML 태그가 삽입되어 있다.</strong> 위 예시는 간단한 HTML 컨텐츠를 응답으로 보내는 형태이지만, 동적 컨텐츠 응답이라는 서블릿의 목적에 맞게 코드를 작성하면 훨씬 더 복잡해진다. 또한 서블릿 클래스를 작성한 후, URL주소와 해당 서블릿을 web.xml을 통해 매핑해야 한다. 이러한 단점을 극복하기 위해 JSP가 등장했다.</p>
<h1 id="jspjava-server-page">JSP(Java Server Page)</h1>
<p><strong>JSP는 HTML내에 Java코드를 삽입하여 &#39;웹 서버&#39;에서 동적 웹 페이지를 생성하여 웹 브라우저에 제공하는 서버 사이드 스크립트 언어다.</strong> 위에서 언급한 Servlet의 단점을 극복하기 위해 등장했다. 웹 브라우저로부터 요청이 들어오면 WAS는 JSP파일을 Java코드로 이루어진 Servlet으 변환한다. 그리고 이후의 과정은 Servlet이 실행되는 과정과 똑같다.</p>
<p>아래는 웹 브라우저로부터 요청이 들어온 후, JSP가 실행되는 과정이다.</p>
<p><img src="https://velog.velcdn.com/images/dev-yongjun/post/e6c116ab-8bb9-4a10-8b9b-3856bdb415d4/image.png" alt=""></p>
<h1 id="reference">Reference</h1>
<p><a href="https://ko.wikipedia.org/wiki/%EC%9B%B9_%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98">https://ko.wikipedia.org/wiki/%EC%9B%B9_%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98</a>
<a href="https://medium.com/@chrisjune_13837/web-%EC%9B%B9%EC%84%9C%EB%B2%84-%EC%95%B1%EC%84%9C%EB%B2%84-was-app%EC%9D%B4%EB%9E%80-692909a0d363">https://medium.com/@chrisjune_13837/web-%EC%9B%B9%EC%84%9C%EB%B2%84-%EC%95%B1%EC%84%9C%EB%B2%84-was-app%EC%9D%B4%EB%9E%80-692909a0d363</a>
<a href="https://blog.naver.com/allstar927/90161809512">https://blog.naver.com/allstar927/90161809512</a>
<a href="https://ssup2.github.io/theory_analysis/Servlet_Servlet_Container/">https://ssup2.github.io/theory_analysis/Servlet_Servlet_Container/</a>
<a href="https://woojong92.tistory.com/entry/Servlet%EA%B5%AC%EC%A1%B0%EC%99%80-HttpServlet-%ED%81%B4%EB%9E%98%EC%8A%A4">https://woojong92.tistory.com/entry/Servlet%EA%B5%AC%EC%A1%B0%EC%99%80-HttpServlet-%ED%81%B4%EB%9E%98%EC%8A%A4</a>
<a href="https://dinfree.com/lecture/backend/javaweb_2.2.html">https://dinfree.com/lecture/backend/javaweb_2.2.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 계층형 쿼리]]></title>
            <link>https://velog.io/@dev-yongjun/HierarchicalQuery</link>
            <guid>https://velog.io/@dev-yongjun/HierarchicalQuery</guid>
            <pubDate>Sun, 06 Nov 2022 12:31:09 GMT</pubDate>
            <description><![CDATA[<h1 id="계층형-쿼리란">계층형 쿼리란?</h1>
<ul>
<li><p>한 테이블에 저장되어 있는 데이터들이 상하(부모, 자식) 관계를 가질 때, 이 구조를 이용해서 테이블을 조회하는 SQL이다.</p>
</li>
<li><p>계층형 쿼리를 이용해서 트리 구조의 형태로 결과를 조회할 수 있다.</p>
</li>
<li><p>일반적으로 조직도나 카테고리, 메뉴 등의 정보가 트리구조를 가지고 있다.</p>
</li>
</ul>
<h2 id="형식">형식</h2>
<pre><code class="language-sql">SELECT [LEVEL,] 컬럼1, 컬럼2
FROM 테이블명
[WHERE 조건식]
START WITH 조건식
CONNECT BY PRIOR 조건식
-- []안의 SQL은 생략 가능</code></pre>
<ul>
<li><p><code>LEVEL</code>은 조회되는 각 행에 대한 레벨을 출력하는 의사컬럼이다. 예를 들어, 최상위행은1, 그 하위행은 2, 그 하위의 하위행은 3을 출력한다. </p>
</li>
<li><p><code>START WITH</code>절은 계층 검색의 시작점을 지정한다.</p>
</li>
<li><p><code>CONNECT BY</code>절은 계층 검색의 방향을 지정한다.</p>
<ul>
<li>상위에서 하위로 검색 : <code>CONNECT BY PRIOR 부모컬럼 = 자식컬럼</code></li>
<li>하위에서 상위로 검색 : <code>CONNECT BY PRIOR 자식컬럼 = 부모컬럼</code></li>
</ul>
</li>
</ul>
<h2 id="상위행에서-하위행으로-조회">상위행에서 하위행으로 조회</h2>
<pre><code class="language-sql">-- 100번 직원 및 부하직원을 조회하기
-- MANAGER_ID가 EMPLOYEE_ID를 참조한다.
SELECT LEVEL , EMPLOYEE_ID, FIRST_NAME, MANAGER_ID
FROM EMPLOYEES
START WITH EMPLOYEE_ID = 100
CONNECT BY PRIOR EMPLOYEE_ID = MANAGER_ID;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/638e7627-cb09-44bb-a717-6f28dba2a58d/image.png" align="left">

<p><img src="https://velog.velcdn.com/images/dev-yongjun/post/ae1eb0fd-67be-4d56-b6e4-e99d0f808a57/image.png" alt=""></p>
<p>100번 직원부터 검색을 시작하고 <code>LEVEL</code>컬럼의 숫자가 해당 레코드가 어느 계층에 있는지 나타낸다. MANAGER_ID는 EMPLOYEE_ID를 참조하므로 EMPLOYEE_ID가 부모컬럼, MANAGER_ID가 자식컬럼이다. 상위에서 하위로 검색한다.</p>
<h2 id="하위행에서-상위행으로-조회">하위행에서 상위행으로 조회</h2>
<pre><code class="language-sql">-- 206번직원의 상사을 조회하기
SELECT LEVEL, EMPLOYEE_ID, FIRST_NAME, MANAGER_ID
FROM EMPLOYEES
START WITH EMPLOYEE_ID = 206
CONNECT BY PRIOR MANAGER_ID = EMPLOYEE_ID
ORDER BY LEVEL DESC;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/6e62486c-5efa-4759-9909-1ffea6935396/image.png" align="left">

]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 분석함수]]></title>
            <link>https://velog.io/@dev-yongjun/AnalyticFunction</link>
            <guid>https://velog.io/@dev-yongjun/AnalyticFunction</guid>
            <pubDate>Sun, 06 Nov 2022 04:54:12 GMT</pubDate>
            <description><![CDATA[<h1 id="분석함수란">분석함수란?</h1>
<p>테이블의 데이터를 특정 컬럼을 기준으로 행들을 그룹화하여 결과를 조회하는 함수이다. 분석함수에는 순위함수<code>(RANK, DENSE_RANK, ROW_NUMBER)</code>와 집계함수<code>(SUM, MIN, MAX, AVG, COUNT)</code>가 있다.</p>
<h2 id="순위함수">순위함수</h2>
<pre><code class="language-sql">SELECT 순위함수() OVER (PARTITION BY 컬럼명 ORDER BY 컬럼명 정렬옵션) AS 컬럼1,
       컬럼2,
       컬럼3,
       . . .
FROM 테이블명
-- PARTITION BY절은 생략 가능하다.</code></pre>
<p>ORDER BY절에 기준이되는 컬럼과 정렬옵션을 지정하고 PARTITION BY절에 그룹화할 컬럼을 지정한다. PARTITION BY절은 생략이 가능한데, 이러한 경우 테이블 전체를 대상으로 순위를 조회한다.</p>
<hr>
<pre><code class="language-sql">-- 급여를 기준으로 직원들을 정렬했을 때 급여 순위가 1~3위에 해당하는 직원 3명 조회
SELECT RANK, FIRST_NAME, SALARY
FROM (SELECT RANK() OVER (ORDER BY SALARY DESC) AS RANK, FIRST_NAME, SALARY
      FROM EMPLOYEES)
WHERE RANK &gt;=1 AND RANK &lt;=3;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/87bec81a-7e5e-4b94-bd01-ef96a80cc182/image.png" align="left">

<p><code>RANK()</code>함수는 동점자를 같은 순위로 간주한다. 그래서 SALARY가 17000인 직원 두명을 2위로 출력했다.</p>
<hr>
<pre><code class="language-sql">-- 직원들의 부서별 급여 순위 조회
SELECT DENSE_RANK() OVER (PARTITION BY E.DEPARTMENT_ID ORDER BY E.SALARY DESC) AS DENSE_RANK,
       E.FIRST_NAME,
       D.DEPARTMENT_NAME,
       E.SALARY
FROM EMPLOYEES E, DEPARTMENTS D
WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/9b990fa3-a5de-450b-a1fd-b2c9048047e8/image.png" align="left">

<p>먼저 DEPARTMENT_ID별로 행을 그룹화한 후, SALARY에 따라 내림차순으로 순위를 부여했다. <code>RANK()</code>와 마찬가지로 동점자는 같은 순위로 간주한다.</p>
<hr>
<pre><code class="language-sql">-- 직원들의 직군별 급여 순위 조회
SELECT ROW_NUMBER() OVER (PARTITION BY E.JOB_ID ORDER BY E.SALARY DESC) AS ROWNUMBER,
       E.FIRST_NAME, 
       J.JOB_TITLE, 
       E.SALARY
FROM EMPLOYEES E, JOBS J
WHERE E.JOB_ID = J.JOB_ID;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/706803ff-842b-45d4-919e-d6c9d3aabbc8/image.png" align="left">

<p>JOB_ID별로 행을 그룹화한 후, SALARY에 따라 내림차순으로 순위를 부여했다. 앞의 <code>RANK(), DENSE_RANK()</code>와는 다르게 <code>ROW_NUMBER()</code>는 같은 값이어도 다른 순위를 부여한다.</p>
<h3 id="rank-dense_rank-row_number의-차이">RANK, DENSE_RANK, ROW_NUMBER의 차이</h3>
<pre><code class="language-sql">-- 직원들을 급여가 높은 순으로 순위를 부여해서 조회
-- RANK(), DENSE_RANK(), ROW_NUMBER의 차이
SELECT RANK() OVER (ORDER BY SALARY DESC)            AS SALARY_RAKING, 
       DENSE_RANK() OVER (ORDER BY SALARY DESC)     AS SALARY_DENSE_RANKING,
       ROW_NUMBER() OVER (ORDER BY SALARY DESC)     AS SALARY_ROW_NUMBER,
       FIRST_NAME, 
       SALARY
FROM EMPLOYEES;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/fed8082e-46f5-40b1-844f-30de0238f47e/image.png" align="left">

<p>그림에서 볼 수 있듯이 <code>ROW_NUMBER()</code>는 데이터가 같은 값이어도 다른 순위를 매긴다. 그러나 <code>RANK()</code>와 <code>DENSE_RANK()</code>는 같은 값이면 같은 순위를 매긴다. 그리고 <code>RANK()</code>는 차순위를 동순위의 개수만큼 더한 값으로 매기지만, <code>DENSE_RANK()</code>는 동순위 다음 바로 다음 값을 차순위로 매긴다.</p>
<h2 id="집계함수">집계함수</h2>
<pre><code class="language-sql">SELECT 집계함수(컬럼명) OVER (PARTITION BY 컬럼명) AS 컬럼1,
       컬럼2,
       컬럼3,
       . . .
FROM 테이블명
-- PARTITION BY절은 생략 가능하다.
-- PARTITION BY절이 다중행 함수의 GROUP BY절의 역할을 대신한다. </code></pre>
<p>PARTITION BY절에 그룹화할 컬럼을 지정하고 집계함수의 괄호 안에 분석하고자 하는 컬럼을 지정한다. 다중행 함수에서 GROUP BY절로 그룹화했다면 
분석함수에서는 PARTITION BY절이 그 역할을 대신한다.</p>
<hr>
<pre><code class="language-sql">-- 직원 이름, 급여, 부서 평균급여, 직원급여와 부서평균급여 차이 조회
-- 분석함수 활용
SELECT  FIRST_NAME, 
        SALARY, 
        TRUNC(AVG(SALARY) OVER (PARTITION BY DEPARTMENT_ID)) AS AVG_SALARY, 
        SALARY - TRUNC(AVG(SALARY) OVER (PARTITION BY DEPARTMENT_ID)) AS SALARY_GAP
FROM EMPLOYEES
ORDER BY EMPLOYEE_ID;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/e313448a-2049-4bda-b338-3a59a102aa57/image.png" align="left">

<p>DEPARTMENT_ID로 행을 그룹화한 후, SALARY의 평균을 계산했다. 이 결과를 이용해 부서 평균급여, 직원급여와 부서 평균급여의 차이를 출력한다.</p>
<pre><code class="language-sql">-- 다중행함수(그룹함수) 활용
SELECT B.FIRST_NAME, 
       B.SALARY, 
       A.AVG_SALARY, 
       B.SALARY - A.AVG_SALARY AS SALARY_GAP
FROM (SELECT DEPARTMENT_ID, TRUNC(AVG(SALARY)) AS AVG_SALARY
      FROM EMPLOYEES
      GROUP BY DEPARTMENT_ID) A, 
      EMPLOYEES B
WHERE A.DEPARTMENT_ID = B.DEPARTMENT_ID
ORDER BY B.EMPLOYEE_ID;</code></pre>
<p>위 집계함수의 예제를 다중행함수를 이용해서 조회하는 쿼리다. 다중행 함수를 이용하면 GROUP BY절에 지정된 컬럼만 SELECT절에서 조회할 수 있으므로 DEPARTMENT_ID를 제외한 다른 컬럼을 조회하려면 서브쿼리로 작성해야 한다. 집계함수는 GROUP BY절을 사용하지 않기 때문에 쿼리를 보다 간결하게 작성할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] TOP-N 분석]]></title>
            <link>https://velog.io/@dev-yongjun/TOP-N</link>
            <guid>https://velog.io/@dev-yongjun/TOP-N</guid>
            <pubDate>Sat, 05 Nov 2022 06:51:11 GMT</pubDate>
            <description><![CDATA[<h1 id="top-n-분석이란">TOP-N 분석이란?</h1>
<p>조건에 맞는 최상위 레코드 n개 혹은 최하위 레코드 n개를 조회하는 쿼리다. <code>ROWNUM</code>이라는 컬럼을 사용한다. <code>ROWNUM</code>은 조회된 행에 행번호를 1번부터 순서대로 부여하는 &#39;의사컬럼&#39;이다.</p>
<pre><code class="language-sql">SELECT ROWNUM, COLUMN1, COLUMN2, COLUMN3 . . .
FROM (SELECT COLUMN1, COLUMN2, COLUMN3 . . .
      FROM 테이블
      ORDER BY 분석대상컬럼명 정렬방향)
WHERE ROWNUM &lt;= N;</code></pre>
<pre><code class="language-sql">-- 직원들을 급여순으로 순위를 매겼을 때 1, 2, 3위 직원의 이름과 급여 조회
SELECT ROWNUM, FIRST_NAME, SALARY
FROM (SELECT FIRST_NAME, SALARY
      FROM EMPLOYEES
      ORDER BY SALARY DESC)
WHERE ROWNUM &lt;= 3;</code></pre>
<p>FROM절의 서브쿼리에서 직원이름, 급여를 조회하고 급여의 내림차순으로 정렬한다. 그 다음 메인쿼리에서 서브쿼리에서 조회된 테이블에 <code>ROWNUM</code>컬럼을 통해 순서대로 행번호를 부여한다. <code>ROWNUM &lt;= 3</code>이라는 조건을 붙이면 상위 세 명의 이름과 급여를 조회할 수 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 제약조건]]></title>
            <link>https://velog.io/@dev-yongjun/Constraint</link>
            <guid>https://velog.io/@dev-yongjun/Constraint</guid>
            <pubDate>Wed, 02 Nov 2022 13:07:32 GMT</pubDate>
            <description><![CDATA[<h1 id="제약조건이란">제약조건이란?</h1>
<p>테이블의 특정 컬럼에 저장될 수 있는 데이터의 조건이다. 제약조건을 지정하면 조건에 부합하지 않는 데이터는 저장할 수 없다. 오라클에서 사용하는 제약조건은 다음과 같다.</p>
<ul>
<li><p>NOT NUUL</p>
<ul>
<li>지정한 컬럼에 NULL을 허용하지 않는다.</li>
</ul>
</li>
</ul>
<pre><code class="language-sql">CREATE TABLE 테이블명 (
    컬럼명 자료형 NOT NULL);</code></pre>
<ul>
<li><p>UNIQUE</p>
<ul>
<li>지정한 컬럼의 값은 유일한 값이어야 한다. 즉, 중복을 허용하지 않는다.</li>
<li>그러나 NULL은 중복이 가능하다.<pre><code class="language-sql">CREATE TABLE 테이블명 (
컬럼명 자료형 UNIQUE);</code></pre>
</li>
</ul>
</li>
<li><p>PRIMARY KEY</p>
<ul>
<li>테이블의 각 행을 대표하는 컬럼에 정의되는 제약조건이다.</li>
<li>지정한 컬럼의 값이 유일한 값이면서 NULL을 허용하지 않는다.</li>
<li>테이블에 한 컬럼만 지정 가능하다.</li>
<li>하나 이상의 컬럼을 사용해서 정의할 수 있다. (복합키)<pre><code class="language-sql">CREATE TABLE 테이블명 (
컬럼명 자료형 PRIMARY KEY);</code></pre>
</li>
</ul>
</li>
<li><p>FOREIGN KEY</p>
<ul>
<li>해당 컬럼의 값이 다른 테이블(혹은 같은 테이블)의 특정 컬럼이 가지고 있는 값과 관련있는 값만 가져야 한다.</li>
<li>다른 컬럼이 참조하는 컬럼은 반드시 기본키 제약조건이 정의된 컬럼이어야 한다.</li>
<li>예를 들어, 주문 테이블의 주문자 아이디는 회원 테이블의 회원 아이디 컬럼의 값과 일치하는 값만 허용된다.
```sql</li>
</ul>
</li>
<li><ul>
<li>컬럼레벨 제약조건 정의, []는 생략 가능
CREATE TABLE 테이블명 (
 컬럼명 자료형 [CONSTRAINT 제약조건] REFERENCES 참조테이블 (참조컬럼));</li>
</ul>
</li>
</ul>
<p>-- 테이블레벨 제약조건 정의, []는 생략 가능
CREATE TABLE 테이블명 (
    [CONSTRAINT 제약조건] FOREIGN KEY (컬럼명) REFERENCES 참조테이블 (참조컬럼));</p>
<pre><code>
* CHECK

  * 제시된 조건을 만족하는 값만 가질 수 있게 한다.
  * 예를 들어, 성별 컬럼은 &#39;남&#39;, &#39;여&#39; 둘 중 한 값만 가질 수 있다.
```sql  
CREATE TABLE 테이블명 (
    컬럼명 자료형 CHECK (조건식));</code></pre><h2 id="제약조건-정의-방법">제약조건 정의 방법</h2>
<pre><code class="language-sql">-- 컬럼레벨 제약조건 정의, []는 생략 가능
CREATE TABLE 테이블명 (
    컬럼명 자료형 [CONSTRAINT 제약조건별칭] 제약조건,
    컬럼명 자료형 [CONSTRAINT 제약조건별칭] 제약조건,
                                             . . .);

-- 테이블레벨 제약조건 정의, []는 생략 가능
CREATE TABLE 테이블명 (
    컬럼명 자료형,
    컬럼명 자료형,
    . . . 
    [CONSTRAINT 제약조건별칭] 제약조건 (컬럼명1),
    [CONSTRAINT 제약조건별칭] 제약조건 (컬럼명2, 컬럼명3),
                                              . . .);</code></pre>
<ul>
<li><p>하나의 컬럼에 여러 제약조건을 지정할 경우 첫 번째 제약조건은 컬럼레벨에서 정의하고 나머지는 테이블레벨에서 정의한다.</p>
</li>
<li><p>두 개 이상의 컬럼에 같은 제약조건을 정의해야할 경우 테이블레벨로 정의한다.</p>
</li>
<li><p>일반적으로 하나의 컬럼에 NOT NULL 제약조건과 다른 제약조건을 같이 정의할 때, NOT NULL 제약조건은 컬럼레벨로 정의하고 나머지 컬럼의 제약조건은 테이블레벨로 정의한다.</p>
</li>
</ul>
<h2 id="주의사항">주의사항</h2>
<p>제약조건은 테이블에 데이터를 저장할 때 뿐만 아니라 변경, 삭제할 때에도 제약조건을 검사한다.</p>
<p>예를 들어, <strong><code>SAMPLE_CART_ITEMS</code> 테이블의 <code>PRODUCT_NO</code>는 <code>SAMPLE_PRODCUTS</code>의 <code>PRODUCT_NO</code> 컬럼의 값을 참조한다.</strong> 그러면 SAMPLE_CART_ITEMS FOREIGN KEY는 PRODUCT_NO이고, SAMPLE_PRODCUTS의 PRIMARY KEY는 PRDUCT_NO이다. </p>
<p>이때, <strong><code>SAMPLE_PRODUCTS</code> 테이블의 <code>PRODUCT_NO</code>는 함부로 UPDATE나 DELETE작업을 수행할 수 없다.</strong> SAPLE_CART_ITEMS 테이블에서 이미 PRODUCT_NO를 참조하고 있기 때문이다. 만약 SAPLE_CART_ITEMS 테이블에서 참조하고 있는 PRODUCT_NO를 변경, 삭제하려고 한다면 다음과 같은 오류가 발생할 것이다.</p>
<p><img src="https://velog.velcdn.com/images/dev-yongjun/post/11a2b2f6-a03e-46fb-b07f-bd1731aec5f2/image.png" alt="">
<img src="https://velog.velcdn.com/images/dev-yongjun/post/d2178386-5e95-4c83-bf86-c7ebe0fae892/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 시퀀스]]></title>
            <link>https://velog.io/@dev-yongjun/Sequnce</link>
            <guid>https://velog.io/@dev-yongjun/Sequnce</guid>
            <pubDate>Wed, 02 Nov 2022 13:06:46 GMT</pubDate>
            <description><![CDATA[<h1 id="시퀀스란">시퀀스란?</h1>
<p>일련번호를 자동으로 생성하는 데이터베이스 객체다. 중복을 허용하지 않는, 행을 구분하기 위한 기본키 값을 생성하는 데 주로 사용된다.</p>
<h2 id="시퀀스-생성과-생성-옵션">시퀀스 생성과 생성 옵션</h2>
<pre><code class="language-sql">-- 생성옵션은 생략 가능하다.
CREATE SEQUNCE 시퀀스명
INCREMENT BY value
START WITH value
MAXVALUE value
MINVALUE value
CACHE value</code></pre>
<ul>
<li><p>INCREMENT BY value : 한번에 value값 만큼 증가시킨다. 기본값은 1이다.</p>
</li>
<li><p>START WITH value : 시작값을 value로 지정한다. 기본값은 1이다. START WITH값은 시퀀스 생성 이후 절대로 수정할 수 없다. 만약 수정될 경우 일련번호가 중복될 수도 있기 때문이다.</p>
</li>
<li><p>MAXVALUE value : 최대값을 value로 지정한다. 기본값은 NOMAXVALUE다.</p>
</li>
<li><p>MINVALUE value : 최대값을 value로 지정한다. 기본값은 NOMINVALUE다.</p>
</li>
<li><p>CACHE value : 지정된 value개 만큼 일련번호를 미리 생성해서 메모리에 캐시한다.기본값은 CACHE 20이다. NOCACHE로 설정하면 일련번호를 미리 생성해두지 않는다.</p>
</li>
</ul>
<h2 id="시퀀스-사용">시퀀스 사용</h2>
<pre><code class="language-sql">-- 새 일련번호 발행
시퀀스명.NEXTVAL;

-- 현재 일련번호 조회
시퀀스명.CURRVAL;</code></pre>
<p>CURRVAL는 시퀀스에서 마지막으로 생성한 번호를 반환한다. 그래서 CURRVAL를 시퀀스 생성 직후 사용하면 번호가 발행된 적이 없으므로 오류가 발생한다.</p>
<h2 id="시퀀수-수정-및-삭제">시퀀수 수정 및 삭제</h2>
<pre><code class="language-sql">-- 시퀀스 수정
ALTER SEQUENCE 시퀀스명;
INCREMENT BY value
START WITH value
MAXVALUE value
MINVALUE value
CACHE value

-- 시퀀스 삭제
DROP SEQUENCE 시퀀스명;</code></pre>
<p>앞서 언급했다시피 START WITH 값은 수정할 수 없다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 뷰]]></title>
            <link>https://velog.io/@dev-yongjun/Veiw</link>
            <guid>https://velog.io/@dev-yongjun/Veiw</guid>
            <pubDate>Wed, 02 Nov 2022 13:05:51 GMT</pubDate>
            <description><![CDATA[<h1 id="뷰veiw란">뷰(Veiw)란?</h1>
<p><strong>하나 이상의 테이블을 조회하는 SELECT문을 저장한 객체다.</strong> SELECT문을 저장한 것이므로 가상의 테이블이라 볼 수 있고, 물리적인 데이터가 저장된 것은 아니다. 그래서 INSERT, UPDATE, DELETE 작업의 수행이 사실상 불가능하다.</p>
<p>FROM절에서 사용하는 서브쿼리인 &#39;인라인 뷰&#39;와 비슷하지만 인라인 뷰는 SQL문 실행 이후 사라지지만, 뷰 객체는 영구적으로 존재한다.</p>
<h2 id="뷰의-사용목적">뷰의 사용목적</h2>
<p>앞서 공부했듯이 하나의 SQL문 내에 여러 개의 SELECT문이 존재할 수 있다. 그러한 <strong>SELECT문을 뷰로 저장해두면 전체 SQL문의 복잡도를 줄일 수 있다.</strong></p>
<p>하나의 데이터베이스에는 여러 사용자가 접근할 수 있다. <strong>접근하는 사용자에 따라 특정 데이터를 숨기기 위해 사용한다.</strong> 예를 들어, 어떤 작업을 수행하기 위해 직원테이블을 다루어야 하는데 직원의 주민등록번호나 연봉은 민감한 정보다. 이때 작업자가 이러한 민감한 정보를 조회하는 것을 막기 위해 작업에 필요한 컬럼만 SELECT한 뷰를 제공한다.</p>
<h2 id="뷰의-생성-및-삭제">뷰의 생성 및 삭제</h2>
<pre><code class="language-sql">-- 뷰 생성
CREATE OR REPLACE VIEW 뷰이름
AS SELECT문;

-- 뷰 삭제
DROP VIEW 뷰이름;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 인덱스]]></title>
            <link>https://velog.io/@dev-yongjun/Index</link>
            <guid>https://velog.io/@dev-yongjun/Index</guid>
            <pubDate>Tue, 01 Nov 2022 14:50:30 GMT</pubDate>
            <description><![CDATA[<h1 id="인덱스란">인덱스란?</h1>
<p>데이터 검색 성능의 향상을 위해 테이블의 컬럼에 사용하는 데이터베이스 객체이다. 테이블에 저장된 특정 행의 주소를 책의 목록처럼 만든 것이다. 인덱스를 사용해서 원하는 데이터를 검색하는 방법이 <code>Index Scan</code>이고, 인덱스를 사용하지 않고 데이터의 처음부터 끝까지 검색하는 방법이 <code>Table Full Scan</code>이다.</p>
<h2 id="b-tree">B-tree</h2>
<p>B-tree는 아래 그림의 첫 번째와 같이 데이터가 트리형태로 저장되어 있다. 숫자가 있는 원을 &#39;노드&#39;라고 하고, 최상단에 있는 노드를 루트 노드(Root Node)라고 한다. 루트 노드를 기준으로 좌우가 균형을 이루고 있기 때문에 Balanced Tree를 줄여서 B-tree라고 한다. (사람들이 그렇게 추측한다.)</p>
<p>검색하고자 하는 값이 현재 검색한 값보다 작으면 왼쪽으로, 크면 오른쪽으로 검색한다. 이러한 B-트리가 RDBMS에서 주로 사용하는 인덱스의 구조다. 
그림에서 볼 수 있듯이 19를 검색하려고 할 때, 일반적인 오름차순 배열은 4번을 검색해야하지만, B-tree구조라면 2번만에 검색할 수 있다.
<img src="https://velog.velcdn.com/images/dev-yongjun/post/9193c57f-f1e5-43c3-909c-053b31e8e54a/image.png" alt=""></p>
<h2 id="인덱스-생성-및-삭제">인덱스 생성 및 삭제</h2>
<p>기본키 혹은 고유키로 지정된 컬럼은 자동으로 인덱스가 생성된다. 그 외의 컬럼에 인덱스를 생성하기 위해서는 CREATE문을 사용해야 한다. 사용방법은 다음과 같다.</p>
<pre><code class="language-sql">-- 인덱스 생성
CREATE INDEX 인덱스명
ON 테이블명 (컬럼명1 정렬옵션
            컬럼명2 정렬옵션
             ...              );

-- 인덱스 삭제
DROP INDEX 인덱스명;</code></pre>
<h2 id="인덱스-사용이-무조건-좋을까">인덱스 사용이 무조건 좋을까?</h2>
<p>그렇지는 않다. B-tree로 구성된 인덱스를 &#39;탐색&#39;하려면 우선 B-tree구조로 &#39;정렬&#39;이 되어야 한다. 그래서 인덱스를 사용하면 데이터 조회는 보다 빠르게 완료할 수 있지만, 데이터 생성 및 수정은 데이터를 정렬해서 저장해야 하기 때문에 오히려 느려질 수 있다. 즉, 인덱스 사용은 생성/수정 작업의 성능을 희생하고 조회작업의 성능을 개선하는 것이다.</p>
<p>또한, 인덱스를 통해 데이터를 탐색할 때 &#39;key&#39;라는 값을 통해 원하는 값을 찾아나간다. 이 값을 저장하기 위한 공간도 따로 할당해야 하기 때문에 무분별한 인덱스 사용은 전체 쿼리 성능을 비효율적으로 만들 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] DDL(CREATE, ALTER, RENAME, TRUNCATE, DROP)]]></title>
            <link>https://velog.io/@dev-yongjun/DDL</link>
            <guid>https://velog.io/@dev-yongjun/DDL</guid>
            <pubDate>Mon, 31 Oct 2022 15:19:43 GMT</pubDate>
            <description><![CDATA[<h1 id="데이터-정의어ddl">데이터 정의어(DDL)</h1>
<p>데이터 정의어라 부르는 DDL(Data Definition Language)은 데이터베이스 내의 데이터를 저장하고 관리하기 위해 필요한 여러 &#39;객체&#39;들을 생성, 수정, 삭제하기 위해 사용하는 명령어다. 그래서 DDL에는 CREATE문, ALTER문, RENAME문, TRUNCATE문, DROP문이 있다.</p>
<p>앞서 다뤘던 DML은 하나의 트랜잭션으로 묶고 COMMIT과 ROLLBACK을 통해 해당 SQL문의 작업내용을 제어했다. 즉, SQL문이 실행되고 트랜잭션 명령어를 또 실행해야 DB에 작업내용이 반영된다.</p>
<p>그러나 DDL은 그렇지 않다. DDL은 명령어를 실행하는 즉시 작업내용이 DB에 반영된다. 따라서 자동으로 COMMIT되고 ROLLBACK은 불가능하기 때문에 DDL을 사용할 때는 주의가 필요하다.</p>
<p>DDL은 테이블뿐만 아니라 다른 객체(시퀀스, 뷰 등)에서도 사용 가능하지만 지금은 테이블을 중점적으로 다루며 각 명령어들을 다루도록 하겠다.</p>
<h2 id="careate문">CAREATE문</h2>
<pre><code class="language-sql">-- 새 테이블 생성
CREATE TABLE 테이블명 (
    컬럼1 자료형 제약조건
    컬럼2 자료형 제약조건
    컬럼3 자료형 제약조건
            .
            .
            .
    );</code></pre>
<p><strong>자료형</strong>은 해당 컬럼에 저장될 데이터의 타입을 정의한다. 자주 쓰이는 데이터타입은 다음과 같다. 고정길이는 할당된 데이터공간이 10byte이고 저장한 데이터가 3byte라면 나머지 7byte는 공백으로 채워지는 형식이다. 가변길이는 데이터공간이 저장되는 데이터만큼 할당되는 형식이다.</p>
<table>
<thead>
<tr>
<th align="left">데이터타입</th>
<th>설명</th>
<th align="center">기본값</th>
</tr>
</thead>
<tbody><tr>
<td align="left">CHAR(n)</td>
<td>n byte문자열, 최대 2000byte, 고정길이 문자</td>
<td align="center">1byte</td>
</tr>
<tr>
<td align="left">VARCHAR2(n)</td>
<td>n byte문자열, 최대 4000byte, 가변길이 문자</td>
<td align="center">1byte</td>
</tr>
<tr>
<td align="left">NUMBER(P,S)</td>
<td>P : 소수점을 포함한 전체 자리수<br>S : 소수점 자리수<br>가변길이 숫자</td>
<td align="center">0</td>
</tr>
<tr>
<td align="left">DATE</td>
<td>날짜</td>
<td align="center">-</td>
</tr>
</tbody></table>
<p><strong>제약조건</strong>은 각 컬럼에 특정 데이터만 저장될 수 있도록 또는 데이터의 중복을 방지하도록 조건을 지정하는 것이다. 이와 관련한 여러 제약조건이 있는데 추후에 다루도록 한다.</p>
<h2 id="alter문">ALTER문</h2>
<pre><code class="language-sql">-- ADD절로 컬럼 추가
ALTER TABLE 테이블명
ADD 컬럼 자료형;

-- RENAME절로 컬럼이름 변경
ALTER TABLE 테이블명
RENAME 기존 컬럼명 TO 새 컬럼명;

-- MODIFY절로 열 길이(데이터 저장공간) 변경
ALTER TABLE 테이블명
MODIFY 컬럼 자료형;

-- DROP절로 특정 열 삭제
ALTER TABLE 테이블명
DROP COLUMN 컬럼;</code></pre>
<h2 id="rename문">RENAME문</h2>
<pre><code class="language-sql">-- 테이블 이름 변경
RENAME 기존 테이블명 TO 새 테이블명;</code></pre>
<h2 id="truncate문">TRUNCATE문</h2>
<pre><code class="language-sql">-- 테이블 내의 모든 데이터를 삭제한다.
TRUNCATE TABLE 테이블명;</code></pre>
<h2 id="drop문">DROP문</h2>
<pre><code class="language-sql">-- 테이블을 삭제한다.
DROP TABLE 테이블명;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 트랜잭션]]></title>
            <link>https://velog.io/@dev-yongjun/transaction</link>
            <guid>https://velog.io/@dev-yongjun/transaction</guid>
            <pubDate>Mon, 31 Oct 2022 15:19:11 GMT</pubDate>
            <description><![CDATA[<h1 id="트랜잭션이란">트랜잭션이란?</h1>
<p>논리적인 작업 단위를 구성하는 데이터 조작어(DML)의 모음이다. 즉, 어떤 기능을 수행하기 위한 최소 작업 단위이다.</p>
<h2 id="트랜잭션의-사용-목적">트랜잭션의 사용 목적</h2>
<p>예를 들어 계좌이체 기능을 생각해보자. A계좌의 10만원을 B계좌로 송금하려고 한다. 그렇다면 A계좌와 B계좌의 잔액을 변경하기 위한 UPDATE문이 각각 한번씩 실행되어야 한다. 그런데 데이터 전송 중 오류가 발생하여 첫 번째 A계좌에 대한 UPDATE문이 실행되고 작업이 종료되었다.</p>
<p>결과적으로 A계좌에서는 돈이 빠져나갔지만 B계좌에는 송금이 이루어지지 않은 상태이다. 따라서 이러한 사고를 막기 위해서는 A, B 계좌의 UPDATE문이 모두 수행되던가 혹은 모두 수행되지 않던가 해야 한다.</p>
<p>이를 위해 &#39;트랜잭션&#39;을 사용한다. 특정 작업을 위한 SQL문을 한 세트로 묶어 그것들이 모두 수행되거나 모두 수행되지 않도록 하는 것이다.</p>
<h2 id="트랜잭션-처리-명령어">트랜잭션 처리 명령어</h2>
<ul>
<li><p>COMMIT : 트랜잭션 내의 모든 DML 실행 결과를 데이터베이스에 영구적으로 반영시킨다.</p>
</li>
<li><p>ROLLBACK : 트랜잭션 내의 모든 DML 실행 결과를 데이터베이스 반영을 전부 취소한다.</p>
</li>
</ul>
<h2 id="트랜잭션의-시작과-종료">트랜잭션의 시작과 종료</h2>
<ul>
<li><p>트랜잭션의 시작</p>
<ul>
<li>첫번째 DML 명령이 실행될때 새 트랜잭션 자동으로 시작된다.</li>
</ul>
</li>
<li><p>트랜잭션의 종료</p>
<ul>
<li>COMMIT 또는 ROLLBACK 명령이 실행될 때 기존 트랜잭션이 종료된다.</li>
<li>트랜잭션이 종료되면 새로운 트랜잭션이 자동으로 시작된다.</li>
<li>SQL 명령어 편집툴을 종료할때 기존 트랜잭션이 종료된다.</li>
<li>시스템에 장애가 발생할 때 기존 트랜잭션이 종료된다.
```sql</li>
</ul>
</li>
<li><ul>
<li>트랜잭션 자동으로 시작, hong사용자가 22번, 23번 상품을 장바구니에 저장함
INSERT INTO SAMPLE_CART_ITEMS (USER_ID, PRODUCT_NO, ITEM_AMOUNT) VALUES (&#39;hong&#39;, 22, 3);
INSERT INTO SAMPLE_CART_ITEMS (USER_ID, PRODUCT_NO, ITEM_AMOUNT) VALUES (&#39;hong&#39;, 23, 1);</li>
</ul>
</li>
<li><ul>
<li>22번, 23번 상품의 재고수량을 변경
UPDATE SAMPLE_PRODUCTS
SET PRODUCT_STOCK = PRODUCT_STOCK - 3
WHERE PRODUCT_NO = 22;
UPDATE SAMPLE_PRODUCTS
SET PRODUCT_STOCK = PRODUCT_STOCK - 1
WHERE PRODUCT_NO = 23;</li>
</ul>
</li>
<li><ul>
<li>트랜잭션 내 모든 DML 작업의 실행결과를 영구적으로 DB에 반영, </li>
</ul>
</li>
<li><ul>
<li>트랜잭션 종료와 동시에 트랜잭션 시작
COMMIT;</li>
</ul>
</li>
</ul>
<p>-- 트랜잭션 내 모든 DML 작업의 실행결과의 DB반영 취소
-- 위에서 실행된 DML작업의 반영은 취소할 수 없다. 다른 트랜잭션의 작업이기 때문이다.
-- 현재 트랜잭션 내에서는 DML작업을 전혀 수행하지 않았기 때문에 취소할 작업이 없다.
-- 트랜잭션 종료와 동시에 트랜잭션 시작
ROLLBACK;</p>
<p>-- 사용자 등록
INSERT INTO SAMPLE_USERS (USER_ID, USER_PASSWORD, USER_NAME, USER_EMAIL)
VALUES (&#39;ahn&#39;, &#39;zxcv2134&#39;, &#39;안중근&#39;, &#39;ahn@gmailcom&#39;);
-- ahn사용자가 21번 상품을 장바구니에 저장
INSERT INTO SAMPLE_CART_ITEMS (USER_ID, PRODUCT_NO, ITEM_AMOUNT)
VALUES (&#39;ahn&#39;, 21, 4);
-- 21번 상품의 재고수량 변경
UPDATE SAMPLE_PRODUCTS
SET PRODUCT_STOCK = PRODUCT_STOCK - 4
WHERE PRODUCT_NO = 21;
-- 이 트랜잭션 내에서 실행된 DML작업의 실행결과의 DB반영을 취소
ROLLBACK;</p>
<p>```</p>
<h2 id="세션과-읽기-일관성">세션과 읽기 일관성</h2>
<p><strong>데이터베이스에서 세션이란 데이터베이스에 접속하여 작업을 수행한 후, 종료하기까지의 기간이다.</strong> 그리고 세션이 여러 개라는 말은 특정 데이터베이스에 접속해서 작업하고있는 연결에 여러 개라는 의미이다. 예를 들어, 특정 계정으로 명령 프롬프트창과 sql developer에 각각 접속해 작업한다면 세션은 두 개가 된다.</p>
<p>앞서 다룬 트랜잭션은 특정 작업을 위한 SQL문의 집합이라고 했다. 그리고 COMMIT과 ROLLBACK을 통해 트랜잭션의 시작과 종료를 제어한다. <strong>그렇다면 세션은 데이터베이스의 접속시점부터 종료까지의 기간이므로 하나의 세션은 여러 트랜잭션을 포함한다. 다시 말해 세션이 트랜잭션보다 큰 범위의 개념이다.</strong></p>
<p>그러면 특정 세션에서 데이터를 변경중 일 때 다른 세션에서는 해당 데이터가 어떻게 보일까? <strong>데이터를 다루고 있는 세션에서 COMMIT 혹은 ROLLBACK이 되기 전까지는 본래의 데이터가 출력된다. 이것이 읽기 일관성이다.</strong> 예를 들어, 명령 프롬프트에서 특정 테이블의 행을 삭제했다면 이 작업이 COMMIT되기 전까지는 sql developer에서는 삭제되기 전의 데이터가 출력된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 서브쿼리]]></title>
            <link>https://velog.io/@dev-yongjun/subquery</link>
            <guid>https://velog.io/@dev-yongjun/subquery</guid>
            <pubDate>Tue, 25 Oct 2022 15:41:51 GMT</pubDate>
            <description><![CDATA[<h1 id="서브쿼리">서브쿼리</h1>
<ul>
<li><p>SELECT문의 내부에 정의된 또 다른 SELECT문을 서브쿼리라고 한다.</p>
</li>
<li><p>서브쿼리의 결과를 활용해서 기능을 수행하는, 즉, 서브쿼리를 포함하는 SELECT문을 메인쿼리라고 한다.</p>
</li>
<li><p>서브쿼리를 포함시킬 수 있는 절은 다음과 같다.</p>
<ul>
<li>WHERE절</li>
<li>HAVING절</li>
<li>FROM절</li>
</ul>
</li>
</ul>
<h2 id="서브쿼리의-특징-및-주의점">서브쿼리의 특징 및 주의점</h2>
<ul>
<li><p>서브쿼리는 메인쿼리보다 먼저 실행된다.</p>
</li>
<li><p>서브쿼리는 반드시 괄호로 묶고 조건식의 오른쪽에 위치시킨다.</p>
</li>
<li><p>서브쿼리의 실행결과가 단일행인지, 다중행인지에 따라 적절한 연산자를 사용해야 한다.</p>
</li>
</ul>
<h2 id="단일행-서브쿼리">단일행 서브쿼리</h2>
<ul>
<li><p>SELECT문의 실행 결과로 단 하나의 행을 출력하는 서브쿼리를 의미한다.</p>
</li>
<li><p>단일행 연산자</p>
<ul>
<li>&#39;같지 않음&#39;을 제외한 나머지 연산자는 일반적인 비교연산자와 동일하다.</li>
<li>단일행 서브쿼리의 &#39;같지 않음&#39;을 의미하는 연산자는 <code>&lt;&gt;, ^=, !=</code>이다.</li>
</ul>
</li>
</ul>
<pre><code class="language-sql">-- 부서아이디가 60인 직원 중에서 전체직원의 평균급여보다 급여를 많이받는 직원의 아이디, 이름, 급여, 부서이름을 조회
SELECT E.FIRST_NAME, E.SALARY, D.DEPARTMENT_NAME
FROM EMPLOYEES E, DEPARTMENTS D
WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID
AND E.DEPARTMENT_ID = 60
AND E.SALARY &lt; (SELECT AVG(SALARY)
                FROM EMPLOYEES);</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/6da579b3-69ad-448b-b5b3-6e2b1a6bb3c6/image.png" align="left">

<h2 id="다중행-서브쿼리">다중행 서브쿼리</h2>
<ul>
<li><p>SELECT문의 실행 결과로 여러 개의 행을 출력하는 서브쿼리를 의미한다.</p>
</li>
<li><p>서브쿼리의 결과가 여러 행이므로 앞서 다룬 단일행 연산자와는 다른 연산자를 사용해야 한다.</p>
</li>
<li><p>다중행 연산자는 다음과 같다.</p>
<ul>
<li><code>IN</code></li>
<li><code>&gt;ANY, &lt;ANY</code></li>
<li><code>&gt;ALL, &lt;ALL</code></li>
</ul>
</li>
</ul>
<h3 id="in-연산자"><code>IN</code> 연산자</h3>
<p>다중행 연산자 중 <code>IN</code> 연산자는 앞서 WHERE절에서 다룬 <code>IN</code> 연산자와 기능이 동일하다.</p>
<pre><code class="language-sql">-- &#39;Seattle&#39;에 소재지를 두고 있는 부서에 속한 직원의 이름과 소속 부서 조회
SELECT D.DEPARTMENT_NAME, E.FIRST_NAME
FROM EMPLOYEES E, DEPARTMENTS D
WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID 
AND E.DEPARTMENT_ID IN (SELECT DEPARTMENT_ID
                        FROM DEPARTMENTS
                        WHERE LOCATION_ID = (SELECT LOCATION_ID
                                             FROM LOCATIONS
                                             WHERE CITY = &#39;Seattle&#39;));</code></pre>
<p>우선 Seattle의 소재지 아이디를 찾고, 해당 소재지에 위치한 부서의 부서 아이디를 찾아야 한다. 그래서 서브쿼리 2개가 중첩되었다. 각각의 서브쿼리를 실행해보며 전체 SQL문을 이해해본다.</p>
<pre><code class="language-sql">-- Seattle의 소재지 아이디 조회
SELECT LOCATION_ID
FROM LOCATIONS
WHERE CITY = &#39;Seattle&#39;;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/ace4c888-e9dd-45f7-8183-85495ea881d4/image.png" align="left">
Seattle의 LOCATION_ID가 1700이고 DEPARTMENTS테이블은 LOCATION_ID컬럼을 갖고 있다. 이를 통해 Seattle에 위치한 부서를 조회할 수 있다.

<p>두 번째 서브쿼리를 다르게 작성하면 아래와 같다.</p>
<pre><code class="language-sql">-- 소재지가 Seattle(소재지아이디가 1700)인 부서의 부서아이디 조회
SELECT DEPARTMENT_ID
FROM DEPARTMENTS
WHERE LOCATION_ID = 1700;  </code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/2cfe2957-38ac-429d-9b5b-146193e81f71/image.png" align="left">

<p>위에서 획득한 Seattle의 LOCATION_ID를 통해 Seattle에 위치한 부서의 DEPARTMENT_ID를 획득했다. 총 21개의 DEPARTMENT_ID를 획득했고, 마지막으로 이 부서에 속한 직원들의 이름과 소속 부서 이름을 조회한다. 최종 결과는 다음과 같다.
<img src="https://velog.velcdn.com/images/dev-yongjun/post/4e7f4aeb-5a3b-4733-a620-600de16729ed/image.png" align="left"></p>
<h3 id="any-연산자"><code>ANY</code> 연산자</h3>
<p>기본적으로 <code>ANY</code>연산자는 출력된 서브쿼리의 여러 행중 적어도 하나가 조건식을 만족하면 메인쿼리의 조건식을 <code>true</code>로 판정한다. 그래서 등가 비교 연산자와 함께 <code>=ANY</code>로 사용하면 <code>IN</code> 연산자와 같은 역할을 한다.</p>
<p>그러나 대소 비교 연사자와 함께 <code>&lt;ANY, &gt;ANY</code> 처럼 사용하는 경우는 조금 다르다. 결론부터 말하자면 <code>A &gt;ANY B</code>는 A가 서브쿼리B 결과의 여러 행중에서 적어도 한 행보다 크면 해당 조건식을 <code>true</code>로 판정한다. 예제를 통해 알아보자.</p>
<pre><code class="language-sql">-- 80번 부서에 소속된 직원의 급여보다 급여를 많이 받는 직원의 아이디, 이름, 급여 조회
-- &gt;ANY는 80번 부서의 어느 급여보다도 크면 true. 즉, 80번 부서의 최저 급여보다 커야 함.
SELECT EMPLOYEE_ID, FIRST_NAME, SALARY
FROM EMPLOYEES
WHERE DEPARTMENT_ID != 80
AND SALARY &gt;ANY (SELECT SALARY
            FROM EMPLOYEES
            WHERE DEPARTMENT_ID = 80);</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/e9a8f058-9b11-45e8-920c-6b3b86c59277/image.png" align="left">
80번 부서의 직원보다 급여를 많이 받는 직원을 조회하므로 메인쿼리의 WHERE절에서 80번 부서에 소속되지 않은 직원들만 조회하는 조건을 지정했다. 서브쿼리의 결과로 80번 부서 직원들의 급여가 출력되고 이 중 가장 적은 급여보다 많은 급여를 받는 직원을 조회한다. 

<p>즉, 이 예제에서 <code>WHERE SALARY &gt;ANY (SELECT ...)</code>는 <code>WHERE SALARY &gt; (SELECT MIN(SALARY) ...)</code> 와 같다.</p>
<h3 id="all-연산자"><code>ALL</code> 연산자</h3>
<p><code>A &gt;ALL B</code>는 A가 서브쿼리B 결과의 모든 행보다 크면 해당 조건식을 <code>true</code>로 판정한다.</p>
<pre><code class="language-sql">-- 80번 부서에 소속된 직원의 급여보다 급여를 많이 받는 직원의 아이디, 이름, 급여 조회
-- &gt;ALL은 80번 부서의 모든 급여보다 커야 함. 즉, 80번 부서의 최대 급여보다 커야 함.
SELECT EMPLOYEE_ID, FIRST_NAME, SALARY
FROM EMPLOYEES
WHERE DEPARTMENT_ID != 80
AND SALARY &gt;ALL (SELECT SALARY
                FROM EMPLOYEES
                WHERE DEPARTMENT_ID = 80);</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/c48e7f21-a8c4-46ad-9b17-b70a5befa27f/image.png" align="left">

<p><code>ANY</code>의 예제와 비슷한 예제이다. 서브쿼리의 모든 행보다 큰 값만 <code>ture</code>판정한다는 점이 다르다.</p>
<p>즉, 이 예제에서 <code>WHERE SALARY &gt;ALL (SELECT ...)</code>는 <code>WHERE SALARY &gt; (SELECT MAX(SALARY) ...)</code> 와 같다.</p>
<h2 id="having절의-서브쿼리">HAVING절의 서브쿼리</h2>
<pre><code class="language-sql">-- 직원수가 가장 많은 부서의 아이디와 사원수 조회
SELECT DEPARTMENT_ID, COUNT(*)
FROM EMPLOYEES
WHERE DEPARTMENT_ID IS NOT NULL
GROUP BY DEPARTMENT_ID
HAVING COUNT(*) = (SELECT MAX(COUNT(*))
                   FROM EMPLOYEES
                   WHERE DEPARTMENT_ID IS NOT NULL
                   GROUP BY DEPARTMENT_ID);</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/f9cf157d-2606-4c98-a64f-d297949d820b/image.png" align="left">
먼저 메인쿼리에서 DEPARTMENT_ID로 그룹화하고 COUNT(*)함수를 실행하면 부서 아이디 별 직원수를 조회할 수 있다. 그 후, HAVING절에서 직원수가 가장 많은 부서의 직원수를 조회하고 그 직원수와 일치하는 행을 필터링한다.

<h2 id="from절의-서브쿼리">FROM절의 서브쿼리</h2>
<p>SELECT문을 통해 테이블의 특정 데이터를 추출한 후 FROM절에서 서브쿼리로 사용하는 방법이다. 즉, 서브쿼리로 조회한 테이블을 기존 테이블과 조인하는 것이다. &#39;인라인 뷰&#39;라고도 한다.</p>
<pre><code class="language-sql">SELECT E.FIRST_NAME, D.DEPARTMENT_ID, D.DEPARTMENT_NAME, J.JOB_TITLE
FROM (SELECT FIRST_NAME, DEPARTMENT_ID, JOB_ID
      FROM EMPLOYEES
      WHERE DEPARTMENT_ID = 60) E,
                    DEPARTMENTS D,
                           JOBS J
WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID
AND E.JOB_ID = J.JOB_ID;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/cd4c77f7-e423-45f1-80b1-0e54ff7bebc1/image.png" align="left">
부서번호가 60인 직원들의 이름, 부서번호, 직종번호를 조회한 테이블을 서브쿼리로 작성하여 FROM절에서 다른 테이블과 조인했다.

<p>FROM절에 너무 많은 서브쿼리를 작성하면 가독성이나 성능이 저하될 수 있기 때문에 아래와 같이 WITH절을 사용하기도 한다.</p>
<h2 id="다중열-서브쿼리">다중열 서브쿼리</h2>
<p>서브쿼리의 SELECT절에 비교할 컬럼을 여러 개 지정하는 방법이다. 메인쿼리에서 비교할 컬럼을 괄호로 묶고, 서브쿼리의 SELECT절에서는 괄호로 묶은 컬럼과 같은 자료형을 갖는 컬럼을 지정한다.</p>
<pre><code class="language-sql">-- 부서별 최저급여 조회했을 때, 각 부서에서 최저급여를 받는 직원의 이름, 급여, 부서아이디 조회
SELECT DEPARTMENT_ID, FIRST_NAME, SALARY
FROM EMPLOYEES
WHERE (DEPARTMENT_ID, SALARY) IN (SELECT DEPARTMENT_ID, MIN(SALARY)
                                     FROM EMPLOYEES
                                  WHERE DEPARTMENT_ID IS NOT NULL 
                                  GROUP BY DEPARTMENT_ID);</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/32a9bf8f-74f4-4ac3-ae99-39c03241a436/image.png" align="left">
서브쿼리에서 각 부서 아이디와 그 부서의 최저급여를 조회했다. 메인쿼리의 WHERE절에서는 서브쿼리의 각 컬럼의 데이터와 일치하는 행을 필터링한다. 즉, 직원의 급여와 서브쿼리에서 조회된 급여가 일치하고 부서 아이디가 일치하는 행을 출력한다.

<h2 id="상호연관-서브쿼리">상호연관 서브쿼리</h2>
<ul>
<li><p>서브쿼리가 메인쿼리에서 사용되는 컬럼을 참조하는 방법이다.</p>
</li>
<li><p>서브쿼리에서 메인쿼리의 컬럼/표현식을 사용한다.</p>
</li>
<li><p>일반적인 서브쿼리는 메인쿼리보다 먼저 실행되고, 단 한번만 실행되지만 상호연관 서브쿼리는 그렇지 않다.</p>
</li>
<li><p>상호연관 서브쿼리는 메인쿼리의 모든 행에 대해서 매번 실행된다. 따라서 쿼리의 성능이 떨어지는 원인이 될 수 있다.</p>
</li>
</ul>
<pre><code class="language-sql">SELECT A.DEPARTMENT_ID, A.EMPLOYEE_ID, A.FIRST_NAME, A.SALARY
FROM EMPLOYEES A
WHERE A.SALARY = (SELECT MIN(B.SALARY)
                  FROM EMPLOYEES B
                  WHERE A.DEPARTMENT_ID = B.DEPARTMENT_ID
                  GROUP BY B.DEPARTMENT_ID)
ORDER BY A.DEPARTMENT_ID ASC;</code></pre>
<p>메인쿼리에서 EMPLOYEES테이블이 사용되고 서브쿼리에서도 EMPLOYEES테이블의 컬럼이 사용된다. 서브쿼리에서 EMPLOYEES테이블을 DEPARTMENT_ID로 그룹화하고 각 부서의 최저급여를 찾는다. 그리고 메인쿼리에서 그 최저급여와 같은 값을 갖고있는 행을 찾아 출력한다. 이 과정이 모든 행에대해 매번 실행된다. 이러한 이유로 쿼리 성능을 저하시키는 원인이 될 수있다.</p>
<h2 id="스칼라-서브쿼리">스칼라 서브쿼리</h2>
<ul>
<li><p>SELECT절에 적는 서브쿼리이다.</p>
</li>
<li><p>스칼라 서브쿼리의 결과는 반드시 1행 1열이어야 한다.</p>
</li>
<li><p>메인쿼리의 결과만큼 반복수행된다.</p>
</li>
</ul>
<pre><code class="language-sql">SELECT A.DEPARTMENT_ID, A.DEPARTMENT_NAME, (SELECT COUNT(*)
                                            FROM EMPLOYEES B
                                            WHERE B.DEPARTMENT_ID = A.DEPARTMENT_ID) AS CNT
FROM DEPARTMENTS A;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/367e4cf1-e326-43eb-979f-8f5acd2c8174/image.png" align="left">
먼저 메인쿼리가 실행되어 DEPARTMENTS테이블의 DEPARTMENT_ID와 DEPARTMENT_NAEM을 출력한다. 그 후 서브쿼리에서 메인쿼리의 DEPARTMENT_ID와 일치하는 부서의 직원수 총합을 출력한다. 이 과정이 메인쿼리가 종료될 때까지 반복수행한다.]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 조인]]></title>
            <link>https://velog.io/@dev-yongjun/join</link>
            <guid>https://velog.io/@dev-yongjun/join</guid>
            <pubDate>Tue, 25 Oct 2022 00:30:31 GMT</pubDate>
            <description><![CDATA[<h1 id="조인">조인</h1>
<ul>
<li><p>두 개 이상의 테이블을 가로로 연결해서 데이터를 조회하는 것.</p>
</li>
<li><p>여러 테이블에 흩어져 있는 정보중에서 사용자가 원하는 정보만 가져와서 가상의 테이블처럼 활용한다.</p>
</li>
</ul>
<p>예를 들어, EMP테이블에 있는 사원들이 속한 부서이름을 알고 싶다. 그러나 EMP테이블에는 사원들이 속한 부서의 부서번호(DEPTNO)만 있을 뿐, 부서이름은 존재하지 않는다. 부서이름은 DEPT테이블에 존재한다. 그래서 EMP테이블과 DEPT테이블을 조인하여 원하는 데이터를 조회한다.</p>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/bdda1b04-b500-41fb-acec-6760b61fc2ed/image.png" align="left">

<h2 id="조인의-방법">조인의 방법</h2>
<ul>
<li><p>오라클 조인</p>
<pre><code class="language-sql">SELECT A.COLUMN1, B.COLUMN2
FROM TABLE1 A ,TABLE2 B
WHERE A.COLUMN1 = B.COLUMN2;</code></pre>
<p>A와 B는 각각 TABLE1과 TABLE2의 별칭이다. 조인 조건과 조회할 데이터에 사용되는 컬럼을 별칭과 함께 지정한다. TABLE1의 COLUMN1과 TABLE2의 COLUMN2가 일치하는 행을 출력한다.</p>
</li>
<li><p>ANSI 조인</p>
<pre><code class="language-sql">SELECT A.COLUMN1, B.COLUMN2
FROM TABLE1 A JOIN TABLE2 B
ON A.COLUMN1 = B.COLUMN2;</code></pre>
<p>ANSI 조인은 모든 RDBMS에서 사용하는 표준 조인 방법이다. 오라클 조인과 다른 점은 FROM절의 두 테이블을 콤마(<code>,</code>) 대신 <code>JOIN</code>으로 연결하고, 조인 조건을 <code>WHERE절</code> 대신 <code>ON</code>으로 표현한다.</p>
</li>
</ul>
<p>조인이 수행될 때는 두 개 이상의 테이블이 사용된다. 둘 중 먼저 읽는 테이블을 선행 테이블, 뒤에 오는 테이블을 후행 테이블이라고 한다. 선행 테이블은 조회할 데이터가 적은 테이블로 선택하는 것이 효율적이다.</p>
<h2 id="크로스-조인-cartesian-product">크로스 조인 (Cartesian product)</h2>
<ul>
<li><p>조인 조건을 지정하지 않고, 두 개 이상의 테이블을 조인하는 것.</p>
</li>
<li><p>조인 조건을 지정하지 않으므로 조인된 테이블의 모든 데이터를 전부 가져온다.</p>
<ul>
<li>예를 들어, A테이블과 B테이블의 행의 개수가 각각 10개, 4개라면 A테이블의 한 행당 B테이블의 모든행이 연결되어 총 40개의 행이 조회된다. 따라서 조인 작업에 참조되는 테이블의 행의 개수를 모두 곱한 값이 조회결과가 된다.</li>
</ul>
</li>
</ul>
<pre><code class="language-sql">SELECT *
FROM DEPARTMENTS, LOCATIONS;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/0ab89eac-f642-44ab-9c4f-9a98b611acac/image.png" align="left">
<img src="https://velog.velcdn.com/images/dev-yongjun/post/8ae0bf64-0db0-4e4c-8ccc-04b9e397cabc/image.png" align="left">
DEPARTMENTS테이블과 LOCATIONS테이블을 크로스 조인했다. DEPARTMENTS테이블의 한 행당, LOCATIONS테이블의 모든행이 연결된다. 그래서 27*23으로 621개의 행이 출력되었다.

<h2 id="등가-조인-equi-join">등가 조인 (Equi Join)</h2>
<ul>
<li><p>테이블을 연결한 후, 출력할 행을 각 테이블의 특정 열과 일치하는 데이터를 기준으로 선정한다.</p>
</li>
<li><p>일반적으로 많이 사용되는 조인 방식이다.</p>
</li>
<li><p>내부 조인 혹은 단순 조인이라고도 부른다.</p>
</li>
</ul>
<pre><code class="language-sql">SELECT E.FIRST_NAME, E.SALARY, E.DEPARTMENT_ID, D.DEPARTMENT_NAME
FROM EMPLOYEES E, DEPARTMENTS D
WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID
AND E.SALARY &gt;= 10000;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/d11f39a4-5fab-4bf7-b4cc-336d2235a682/image.png" align="left">
EMPLOYEES 테이블과 DEPARTMENTS 테이블을 조인했다. 그리고 각 테이블의 DEPARTMENT_ID가 같고 EMPLOYEES 테이블의 SALARY가 10000이상인 행을 출력했다. 

<p>DEPARTEMENTS 테이블의 DEPARTMENT_NAME은 EMPLOYEES 테이블에 존재하지 않지만 조회가 가능한 이유는 DEPARTMENTS 테이블을 조인했기 때문이다.</p>
<hr>
<pre><code class="language-sql">SELECT E.FIRST_NAME, E.SALARY, E.DEPARTMENT_ID, D.DEPARTMENT_NAME, L.CITY
FROM EMPLOYEES E, DEPARTMENTS D, LOCATIONS L
WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID
AND D.LOCATION_ID = L.LOCATION_ID
AND E.SALARY &gt;= 10000;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/ea7072fe-053d-4546-bc37-0502dff11ab6/image.png" align="left">

<p>위 예제에 LOCATIONS 테이블을 추가로 조인했다. DEPARTMENTS, LOCATIONS 테이블 둘 다 LOCATION_ID 컬럼이 존재한다. 이 컬럼의 데이터가 같은 행을 출력했다. <code>D.LOCATION_ID = L.LOCATION_ID</code> 조건이 존재하지 않는다면 EMPLOYEES, DEPARTMENTS 테이블과 LOCATIONS 테이블이 크로스 조인된다.</p>
<h2 id="비등가-조인-non-equi-join">비등가 조인 (Non-Equi Join)</h2>
<ul>
<li>조인 조건을 지정할 때, 조인 대상 테이블의 특정 컬럼이 크거나 작은 경우의 조건으로 데이터를 조회한다.</li>
</ul>
<pre><code class="language-sql">SELECT E.FIRST_NAME, E.SALARY, G.SALARY_GRADE
FROM EMPLOYEES E, SALARY_GRADES G
WHERE G.MIN_SALARY &lt;=  E.SALARY AND E.SALARY &lt;= G.MAX_SALARY;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/d4cd2888-e7d4-40f2-8e11-1207870eb2ac/image.png" align="left">

<img src="https://velog.velcdn.com/images/dev-yongjun/post/f5dcf8c4-a365-42bc-8f16-5276f120930d/image.png" align="left">

<p>SALARY_GRADES 테이블은 MIN_SALARY와 MAX_SALARY에 따라 SALARY_GRADE가 나뉘어 진다. SALARY_GRADES 테이블과 EMPLOYEES 테이블을 조인한 후, EMPLOYEES 테이블의 SALARY가 SALARY_GRADE의 어느 등급에 속하는지 출력한다.</p>
<h2 id="외부-조인-outer-join">외부 조인 (Outer join)</h2>
<ul>
<li>외부조인은 한쪽 테이블에는 데이터가 있고 다른쪽 테이블에는 데이터가 존재하지 않을 때, 데이터가 있는 쪽 테이블의 내용을 누락없이 출력하는 조인방법이다.</li>
</ul>
<pre><code class="language-sql">SELECT E.EMPLOYEE_ID, E.DEPARTMENT_ID
FROM EMPLOYEES E, DEPARTMENTS D
WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/2ebcd801-38ee-4877-934b-27d67aa649da/image.png" align="left">

<img src="https://velog.velcdn.com/images/dev-yongjun/post/bb3d9257-19d6-4ecb-82bb-3df7a93adcca/image.png" align="left">
우선 EMPLOYEES과 DEPARTMENTS 테이블을 등가 조인한 결과를 살펴보자. 

<p><strong>EMPLOYEE_ID가 178인 행은 조인 결과에 포함되지 않았는데, 이는 EMPLOYEES테이블에서 EMPLOYEE_ID가 178인 행의 DEPARTMENT_ID가 NULL이기 때문이다.</strong></p>
<p>이처럼 조인 기준 컬럼의 데이터가 없는 행도 강제로 출력하기 위해 외부 조인을 사용한다.</p>
<hr>
<pre><code class="language-sql">SELECT E.EMPLOYEE_ID, E.DEPARTMENT_ID
FROM EMPLOYEES E, DEPARTMENTS D
WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID (+);</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/af114b48-3159-49ab-ac3d-f4706bb4d8a7/image.png" align="left">

<p>NULL이 존재하지 않는 쪽(D.DEPARTMENT_ID)에 <code>(+)</code>를 붙여 외부 조인을 사용했다. EMPLOYEES 테이블에서 DEPARTMENT_ID가 NULL인행도 출력이된다.</p>
<h2 id="자체-조인-self-join">자체 조인 (Self Join)</h2>
<ul>
<li><p>하나의 테이블을 이용해서 조인을 구성한다.</p>
</li>
<li><p>하나의 테이블에 안에 상위데이터, 하위데이터가 있는 경우 상위데이터와 하위데이터를 서로 연관지어서 조회할 때 자체 조인이 필요하다.</p>
</li>
<li><p>하나의 테이블을 역할을 각각 나누어서 조인해야 한다.</p>
</li>
</ul>
<pre><code class="language-sql">-- 모든 직원아이디, 직원이름과 해당 직원의 관리자아이디, 관리자이름 조회한다.
SELECT EMP.EMPLOYEE_ID AS EMP_ID, EMP.FIRST_NAME AS EMP_NAME, 
       MGR.EMPLOYEE_ID AS MGR_ID, MGR.FIRST_NAME AS MGR_NAME
FROM EMPLOYEES EMP, EMPLOYEES MGR
WHERE EMP.MANAGER_ID = MGR.EMPLOYEE_ID (+)
ORDER BY EMP.EMPLOYEE_ID ASC;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/61170e08-28bc-496d-8633-0d0b9ed4bc7f/image.png" align="left">

<p>EMPLOYEES 테이블을 직원정보를 담는 테이블과 매니저정보를 담는 테이블로 구분해서 자체 조인했다. <strong><code>EMP</code>의 MANAGER_ID와 <code>MGR</code>의 EMPLOYEE_ID가 일치하는 조건으로 조인하면 각 직원의 매니저 아이디와 그 매니저의 직원 아이디가 연결된다.</strong> </p>
<p>그런데 EMPLOYEE_ID가 100인 직원은 매니저가 존재하지 않으므로(MANAGER_ID의 값이 NULL) 조회 결과에 포함되지 않을 것이다. 이를 해결하기 위해 <code>MGR.EMPLOYEE_ID</code>에 <code>(+)</code>를 추가하여 외부 조인했다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 다중행 함수]]></title>
            <link>https://velog.io/@dev-yongjun/multipleRowFunction</link>
            <guid>https://velog.io/@dev-yongjun/multipleRowFunction</guid>
            <pubDate>Mon, 24 Oct 2022 13:30:01 GMT</pubDate>
            <description><![CDATA[<h1 id="다중행-함수그룹-함수">다중행 함수(그룹 함수)</h1>
<ul>
<li><p>여러 행이 입력되고, 한 행씩 반환한다.</p>
</li>
<li><p>중첩은 한 번만 허용된다.</p>
</li>
<li><p>WHERE절에서 사용할 수 없다.</p>
</li>
<li><p>그룹 함수가 아니거나 그룹 함수가 포함되어 있지 않은 표현식은 SELECT절에 사용할 수 없다.</p>
<ul>
<li>그룹 함수의 결과는 1행 혹은 그룹당 1행인데, 그룹 함수와 관련이 없는 컬럼은 그룹 함수의 결과와 행 개수가 일치하지 않기 때문이다.</li>
</ul>
</li>
</ul>
<h2 id="count-함수">COUNT 함수</h2>
<ul>
<li>COUNT (*) : 조회된 행의 개수를 반환<pre><code class="language-sql">SELECT COUNT (*)
FROM EMPLOYEES
WHERE DEPARTMENT_ID =80;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/ae303328-fadb-48f2-a1f6-d721f573d735/image.png" align="left">
DEPARTMENT_ID가 80인 직원들의 행의 개수를 반환한다. 즉, 부서아이디가 80인 직원들의 수를 조회한다.

</li>
</ul>
<hr>
<ul>
<li>COUNT (컬럼 혹은 표현식) : 컬럼 혹은 표현식이 NULL이 아닌 행의 개수만 반환<pre><code class="language-sql">SELECT COUNT (COMMISSION_PCT)
FROM EMPLOYEES;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/2b25ede4-d8f8-4e88-a9f2-ee0f4849e0eb/image.png" align="left">
COMMISSION_PCT가 NULL이 아닌 행의 개수를 반환한다. 즉, 커미션을 받는 직원 수를 조회한다

</li>
</ul>
<hr>
<h2 id="sum-avg-min-max-함수">SUM, AVG, MIN, MAX 함수</h2>
<ul>
<li>MIN (컬럼 혹은 표현식) : 컬럼 혹은 표현식의 최소값를 반환한다.</li>
<li>MAX (컬럼 혹은 표현식) : 컬럼 혹은 표현식의 최대값를 반환한다.</li>
<li>SUM (컬럼 혹은 표현식) : 컬럼 혹은 표현식의 합계를 반환한다.</li>
<li>AVG (컬럼 혹은 표현식) : 컬럼 혹은 표현식의 평균를 반환한다.</li>
</ul>
<p>위 함수 모두 NULL값은 제외된다.</p>
<pre><code class="language-sql">SELECT MIN(SALARY) AS SALARY_MIN,
       MAX(SALARY) AS SALARY_MAX, 
       SUM(SALARY) AS SALARY_SUM, 
       TRUNC(AVG(SALARY)) AS SALARY_AVG
FROM EMPLOYEES;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/55cb7ad0-56a4-48e4-8495-40905ec738c8/image.png" align="left">
직원 전체의 최소,최대,평균급여와 급여합계를 조회한다.

<h2 id="group-by절">GROUP BY절</h2>
<p>그 동안 다룬 그룹 함수는 지정된 테이블의 여러 행을 입력받아 하나의 결과만 반환했다. <strong>GROUP BY절을 사용하면 특정 컬럼을 기준으로 그룹화하여 행을 입력받고 그룹당 한 행씩 결과를 반환받을 수 있다.</strong> 그리고 GROUP BY절에 컬럼을 여러 개 입력하면 지정된 순서대로 그룹화 한다.</p>
<p>위에서 언급했다시피 그룹 함수가 아니거나 그룹 함수가 포함되어 있지 않은 표현식은 SELECT절에 사용할 수 없다. <strong>그러나 GROUP BY절에 등장한 컬럼 혹은 표현식은 SELECT절에 그룹 함수와 함께 적을 수 있다. 그룹 함수 결과의 개수가 그룹화한 행의 개수와 일치하기 때문이다.</strong></p>
<p>또한, ORDER BY절에서는 컬럼의 별칭을 사용할 수 없다. ORDER BY절이 별칭이 지정되는 SELECT절보다 나중에 실행되기 때문이다.</p>
<pre><code class="language-sql">SELECT DEPARTMENT_ID,
           TO_CHAR(HIRE_DATE, &#39;YYYY&#39;) AS HIRE_YEAR,
           COUNT(*) AS CNT
FROM EMPLOYEES
WHERE DEPARTMENT_ID IS NOT NULL
GROUP BY DEPARTMENT_ID, TO_CHAR(HIRE_DATE, &#39;YYYY&#39;);</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/63f81425-9826-4cd0-b262-a7c3cbc423b5/image.png" align="left">
DEPARTMENT_ID로 그룹화한 후, 입사년도별로 직원수를 조회한다.

<hr>
<pre><code class="language-sql">SELECT DEPARTMENT_ID,
       MIN(SALARY) AS SALARY_MIN,
       MAX(SALARY) AS SALARY_MAX, 
       SUM(SALARY) AS SALARY_SUM, 
       TRUNC(AVG(SALARY)) AS SALARY_AVG
FROM EMPLOYEES
WHERE DEPARTMENT_ID IS NOT NULL
GROUP BY DEPARTMENT_ID;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/7344969b-9dbc-496f-a70e-73fced539891/image.png" align="left">
DEPARTMENT_ID별로 그룹화한 후, 그룹별 최소,최대,평균급여와 급여합계를 조회한다.

<h2 id="having절">HAVING절</h2>
<p>HAVING절은 GROUP BY절을 통해 그룹화한 결과값의 범위를 필터링하는데 사용한다. 그래서 HAVING절은 GROUP BY절이 존재할 때만 사용할 수 있다.
HAVING절과 WHERE절은 조회할 값을 특정 기준으로 필터링한다는 점에서 유사하지만 분명히 차이가 있다. 보다 정확히 이해하기 위해 각 절의 실행순서를 살펴본다.</p>
<ol>
<li>FROM : 조회할 테이블 확인</li>
<li>ON : 조인 조건 확인</li>
<li>JOIN : 테이블 조인 (병합)</li>
</ol>
<p><strong>4. WHERE : 행 추출 조건 확인</strong>
<strong>5. GROUP BY : 특정 컬럼을 기준으로 행을 그룹화</strong>
<strong>6. HAVING : 그룹화 이후 행 추출 조건 확인</strong></p>
<ol start="7">
<li>SELECT : 조회할 컬럼 지정</li>
<li>DISTINCT : 중복 제거</li>
<li>ORDER BY : 행 정렬**</li>
</ol>
<p>중요한 부분은 4,5,6이다. WHERE절에서 먼저 실행되고, 그 후에 GROUP BY절과 HAVING절이 실행된다. 즉<strong>, WHERE절에서 지정된 조건으로 먼저 데이터를 필터링한 후, 해당 데이터를 갖고 GROUP BY절에서 지정된 컬럼을 기준으로 그룹화한다. 마지막으로 그룹화된 데이터를 HAVING절에서 지정된 조건으로 필터링한다.</strong></p>
<p>다시 말해, WHERE절은 그룹화할 대상을 필터링한다. 따라서 그룹화할 대상이 감소되고 쿼리 성능이 향상된다.</p>
<pre><code class="language-sql">SELECT TO_CHAR(HIRE_DATE, &#39;YYYY&#39;) AS 입사년도,
       COUNT(*) AS 사원수
FROM EMPLOYEES
WHERE DEPARTMENT_ID IN (30, 50, 80)
GROUP BY TO_CHAR(HIRE_DATE, &#39;YYYY&#39;)
HAVING COUNT(*) &gt;= 10
ORDER BY 입사년도;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/44e2bebe-c3eb-485f-8364-3a3323f632e4/image.png" align="left">
DEPARTMENT_ID가 30, 50, 80인 사원들을 먼저 조회한 후, 그 사원들을 대상으로 입사년도별로 그룹화한다. 그 중에서 입사한 사원수가 10명이상인 입사년도만 출력했다.

<h2 id="rollup">ROLLUP</h2>
<p>GROUP BY절에 2개 이상의 컬럼 혹은 표현식이 있으면 첫 번째 식을 기준으로 그룹화를 하고, 해당 그룹안에서 두 번째 식을 기준으로 다시 그룹화한다. 이런 경우 ROLLUP은 첫 번째 그룹에 대한 부분합을 계산한다.</p>
<pre><code class="language-sql">SELECT DEPARTMENT_ID, 
           JOB_ID, 
           COUNT(*) AS CNT,
           SUM(SALARY)
FROM EMPLOYEES
GROUP BY ROLLUP (DEPARTMENT_ID, JOB_ID)
ORDER BY DEPARTMENT_ID ASC, JOB_ID ASC;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/49afe176-ea93-4158-9757-f1be63b58143/image.png" align="left">
DEPARTMENT_ID를 기준으로 그룹화하고, 그 다음 JOB_ID를 기준으로 그룹화했다. NULL이 그룹화된 행의 부분합이다. 마지막 행의 NULL은 전체 데이터에 대한 합계다. 즉, 21번 행의 NULL은 DEPARTMENT_ID가 80인 그룹의 부분합을 나타내고 마지막 33번째 행의 NULL은 직원전체의 부분합을 나타낸다.]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 단일행 함수]]></title>
            <link>https://velog.io/@dev-yongjun/singleRowFunction</link>
            <guid>https://velog.io/@dev-yongjun/singleRowFunction</guid>
            <pubDate>Sat, 22 Oct 2022 14:58:05 GMT</pubDate>
            <description><![CDATA[<p>단일행 함수를 다루기 전에 먼저 그 상위 개념인 내장 함수부터 알아보고 가자. 내장 함수의 특징은 다음과 같다.</p>
<ul>
<li>SQL을 작성할 때 유용한 기능이 제공되는 함수다.</li>
<li>DBMS 제품마다 차이가 있다.</li>
<li>&#39;함수&#39;라는 의미 그대로 특정 값을 입력하면 함수의 연산 과정에 따라 다른 값이 반환된다.</li>
<li>단일행 함수와 다중행 함수(그룹 함수)로 나뉜다</li>
</ul>
<h1 id="단일행-함수">단일행 함수</h1>
<ul>
<li>한 행씩 입력되고, 한 행씩 반환한다.</li>
<li>중첩해서 사용이 가능하다.</li>
<li>문자함수, 숫자함수, 날짜함수, 변환함수, 기타함수(nvl, case, decode)가 있다.</li>
</ul>
<h2 id="문자함수">문자함수</h2>
<ul>
<li>LENGTH (컬럼명 혹은 표현식) : 문자열의 길이를 반환한다.<pre><code class="language-sql">SELECT FIRST_NAME, LENGTH (FIRST_NAME)
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 60;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/a4d1acd1-e8d3-48ec-b324-dca9a2b8ec2e/image.png" align="left">
DEPARTMENT_ID가 60인 직원들의 이름의 글자 수를 출력했다.

</li>
</ul>
<hr>
<ul>
<li>LOWER (컬럼명 혹은 표현식) : 문자를 소문자로 변환한다.</li>
<li>UPPER (컬럼명 혹은 표현식) : 문자를 대문자로 변환한다.<pre><code class="language-sql">SELECT FIRST_NAME, LOWER (FIRST_NAME), UPPER (FIRST_NAME)
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 60;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/8c9a8ab5-9b4e-48fb-98e7-ba02e20178c4/image.png" align="left">
DEPARTMENT_ID가 60인 직원들의 이름을 각각 대문자 소문자로 바꾸어 출력한다.

</li>
</ul>
<hr>
<ul>
<li>SUBSTR (컬럼명 혹은 표현식, 시작위치) : 문자열에서 지정된 시작위치부터 끝까지 잘라서 반환한다.</li>
<li>SUBSTR (컬럼명 혹은 표현식, 시작위치, 길이) : 문자열에서 지정된 시작위치부터 길이만큼 잘라서 반환한다.<pre><code class="language-sql">SELECT JOB_ID, SUBSTR (JOB_ID, 4), SUBSTR (JOB_ID, 4, 2)
FROM EMPLOYEES
WHERE SALARY &gt;= 10000;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/ad34a0b1-93e5-469a-95ac-7087f03748ba/image.png" align="left">
SALARY가 10000 이상인 직원들의 JOB_ID와 JOB_ID의 네 번째 글자부터 끝까지, 네 번째 글자부터 길이2만큼 잘라서 출력한다.

</li>
</ul>
<hr>
<ul>
<li>INSTR (컬럼명 혹은 표현식, &#39;텍스트&#39;) : 지정된 텍스트가 등장하는 위치를 반환한다.</li>
<li>INSTR (컬럼명 혹은 표현식, &#39;텍스트&#39;, 검색 시작 위치) : 지정된 텍스트가 등장하는 위치를 검색 시작 위치부터 찾아서 반환한다.<pre><code class="language-sql">SELECT FIRST_NAME, 
     INSTR (FIRST_NAME, &#39;A&#39;), 
     INSTR (FIRST_NAME, &#39;e&#39;, 2)
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 60;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/f35bc3c1-f9a9-408e-a28f-fa39e15f9b11/image.png" align="left">
DEPARTMENT_ID가 60인 직원들의 이름에서 'A'가 등장하는 위치와, 두 번째 위치에서 'e'가 등장하는 위치를 출력한다.

</li>
</ul>
<hr>
<ul>
<li>LPAD (컬럼명 혹은 표현식, 길이, &#39;텍스트&#39;) : 지정된 길이보다 짧으면 왼쪽에 텍스트를 채워서 지정된 길이로 맞춘다.</li>
<li>RPAD (컬럼명 혹은 표현식, 길이, &#39;텍스트&#39;) : 지정된 길이보다 짧으면 오른쪽에 텍스트를 채워서 지정된 길이로 맞춘다.<pre><code class="language-sql">SELECT FIRST_NAME, LPAD (FIRST_NAME, 15, &#39;#&#39;), 
                 RPAD (FIRST_NAME, 15, &#39;#&#39;)
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 60;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/8fb3dfa9-ac90-450a-8f64-b9f754891f34/image.png" align="left">

</li>
</ul>
<hr>
<ul>
<li>CONCAT (컬럼명 혹은 표현식, 컬럼명 혹은 표현식) : 두 값을 합쳐서 새로운 텍스트를 반환한다.<pre><code class="language-sql">SELECT CONCAT (FIRST_NAME, LAST_NAME)
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 60;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/5f39f130-0c08-4aa4-b77f-85282e2642c5/image.png" align="left">
DEPARTMENT_ID가 60인 직원들의 FIRST_NAME과 LAST_NAME을 결합해서 출력한다.

</li>
</ul>
<hr>
<ul>
<li>컬럼명 혹은 텍스트 || 컬럼명 혹은 텍스트 : 텍스트를 연결한다.
(JAVA에서 문자열을 잇는 +와 비슷하다.)<pre><code class="language-sql">SELECT FIRST_NAME || &#39; &#39; || LAST_NAME
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 60;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/339e2fb6-77ae-4356-8f87-319e6f9267fb/image.png" align="left">
위의 CONCAT과 비슷하지만 FRIST_NAME과 LAST_NAME 뿐만 아니라'||'를 통해 공백문자도 결합해서 출력했다.

</li>
</ul>
<hr>
<ul>
<li>REPLACE (컬럼명 혹은 문자열, &#39;텍스트&#39;, &#39;대체할 텍스트&#39;) : 텍스트에 해당하는 문자를 지정된 텍스트로 대체한다.<pre><code class="language-sql">SELECT FIRST_NAME, REPLACE (FIRST_NAME, &#39;a&#39;, &#39;*&#39;)
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 60;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/108b0d41-83f7-48a7-9ee3-668c8b68f36c/image.png" align="left">

</li>
</ul>
<h2 id="숫자-함수">숫자 함수</h2>
<p>숫자 함수를 설명하기 위해 DUAL테이블을 활용한다. DUAL테이블은 1행1열 테이블로 함수나 산술계산 결과를 단순히 확인하기 위해 사용한다.</p>
<ul>
<li>ROUND (컬럼 혹은 표현식) : 소수점 첫 번째 자리에서 반올림한다.</li>
<li>ROUND (컬럼 혹은 표현식, 자리수) : 지정된 자리수까지 반올림한다. (음수도 가능)<pre><code class="language-sql">SELECT ROUND (123.45), -- 소수점 첫 번째 자리에서 반올림한다. 
         ROUND (123.4567, 3), -- 소수점 세 번째자리까지 반올림한다.
         ROUND (123.4567, 0), -- 1의 자리까지 반올림한다. (소수점 첫 번째 자리에서 반올림 한다.)
         ROUND (123.4567, -1), -- 10의 자리까지 반올림한다.
         ROUND (123.4567, -2) -- 100의 자리까지 반올림한다.
FROM DUAL;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/d3137530-01ce-4501-bcd4-a0464e593968/image.png" align="left">

</li>
</ul>
<hr>
<ul>
<li>TRUNC (컬럼 혹은 표현식) : 소수점부분을 전부 버린다.</li>
<li>TRUNC (컬럼 혹은 표현식, 자리수) : 지정된 자리수만 남기고 버린다.<pre><code class="language-sql">SELECT TRUNC (1234.567), -- 소수점 이하를 전부 버린다.
         TRUNC (1234.56, 1), -- 소수점 첫 번째자리만 남기고 버린다.
         TRUNC (1234.1, 0), -- 1의 자리만 남기고 버린다.
         TRUNC (1234.1, -2), -- 100의 자리만 남기고 버린다.
         TRUNC (1234.1, -3) -- 1000의 자리만 남기고 버린다.
FROM DUAL;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/a5cbfe38-f8ec-479a-902b-7814aaf6495d/image.png" align="left">

</li>
</ul>
<hr>
<ul>
<li>MOD (컬럼 혹은 표현식, 숫자) : 숫자만큼 나눈 나머지 값을 반환한다.<pre><code class="language-sql">SELECT 5/2, MOD (5, 2)
FROM DUAL;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/3bf45c61-2151-4204-9190-ca09ab3af92d/image.png" align="left">

</li>
</ul>
<h2 id="날짜-함수">날짜 함수</h2>
<ul>
<li>SYSDATE : 시스템의 현재 날짜와 시간정보가 포함된 날짜정보를 반환한다. 입력값이 없는 함수는 ()를 생략한다.<pre><code class="language-sql">SELECT SYSDATE
FROM DUAL;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/ca96c8b0-51ae-4a85-9aa2-12ef73395fe2/image.png" align="left">

</li>
</ul>
<hr>
<ul>
<li>MONTHS_BETWEEN (날짜, 날짜) : 두 날짜 사이의 개월 수를 반환한다.<pre><code class="language-sql">SELECT FIRST_NAME, 
     HIRE_DATE, 
     MONTHS_BETWEEN (SYSDATE, HIRE_DATE),
     TRUNC (MONTHS_BETWEEN (SYSDATE, HIRE_DATE))
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 60;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/e2b5b4c2-40f2-4564-b0d5-b70d43d5a87b/image.png" align="left">

</li>
</ul>
<p>DEPARTMENT_ID가 60인 직원의 이름과 고용일, 고용일과 현재 날짜 사이의 개월 수 차이를 출력했다. <code>MONTHS_BETWEEN</code>함수는 소수점 까지 표현하므로 <code>TRUNC</code>함수를 중첩해서 소수점을 버린 컬럼도 출력했다.</p>
<hr>
<ul>
<li>ADD_MONTHS (날짜, 개월 수) : 지정된 날짜에서 개월 수 만큼 경과한 날짜를 반환한다.<pre><code class="language-sql">SELECT SYSDATE,
     ADD_MONTHS (SYSDATE, 1), 
     ADD_MONTHS (SYSDATE, 2),  
     ADD_MONTHS (SYSDATE, -3)
FROM DUAL;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/f866949c-fb43-4909-8356-8505d2564072/image.png
" align="left"></li>
</ul>
<hr>
<ul>
<li>ROUND (날짜) : 정오를 기준으로 날짜를 반올림한다. 즉, 정오가 넘어가면 날짜가 하루 증가된 값을 반환한다. (시, 분, 초 값은 전부 0이다.)</li>
<li>TRUNC (날짜) : 날짜에서 시분초값을 전부 버린다.<pre><code class="language-sql">SELECT SYSDATE,
     ROUND (SYSDATE),
     TRUNC (SYSDATE)
FROM DUAL;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/590edf1b-40dd-4f09-83ff-9aa1cf1f4303/image.png
" align="left"></li>
</ul>
<hr>
<ul>
<li>날짜 + 숫자 : 지정된 날짜에서 숫자만큼 경과된 날짜를 반환한다. </li>
<li>날짜 - 숫자 : 지정된 날짜에서 숫자만큼 이전의 날짜를 반환한다.</li>
</ul>
<p>위 함수에서 정수 1을 하루로 간주한다. 예를 들어, <code>SYSDATE - 1</code>이면 현재 시스템 날짜에서 하루 전의 날짜, 시간을 반환한다. 또는 <code>SYSDATE + 6/24</code>이면 현재 시스템 날짜에서 6시간이 지난 날짜, 시간을 반환한다.</p>
<pre><code class="language-sql">SELECT SYSDATE + 3,        
            SYSDATE - 3,    
            SYSDATE + 3/24, 
            SYSDATE - 3/24
FROM DUAL;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/5c5f9672-460f-4499-aa8b-12eca8ee8feb/image.png" align="left">

<hr>
<h2 id="형변환-함수">형변환 함수</h2>
<ul>
<li>TO_CHAR (날짜, &#39;포맷문자&#39;) : 날짜를 지정된 포맷의 문자열로 변환한다.</li>
</ul>
<p>날짜의 포맷문자는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>형식</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>CC</td>
<td>세기</td>
</tr>
<tr>
<td>YYYY,RRRR</td>
<td>4자리 연도</td>
</tr>
<tr>
<td>YY,RR</td>
<td>2자리 연도</td>
</tr>
<tr>
<td>MM</td>
<td>2자리 월</td>
</tr>
<tr>
<td>MON</td>
<td>언어별 월 이름 약자(August -&gt; Aug)</td>
</tr>
<tr>
<td>MONHT</td>
<td>언어별 월 이름 전체</td>
</tr>
<tr>
<td>DD</td>
<td>2자리 일</td>
</tr>
<tr>
<td>DDD</td>
<td>1년 중 며칠 (1~366)</td>
</tr>
<tr>
<td>DY</td>
<td>언어별 요일 이름 약자(수요일 -&gt; 수)</td>
</tr>
<tr>
<td>DAY</td>
<td>언어별 요일 이름 전체</td>
</tr>
<tr>
<td>W</td>
<td>1년 중 몇 번째 주(1~53)</td>
</tr>
</tbody></table>
<pre><code class="language-sql">SELECT EMPLOYEE_ID, 
       FIRST_NAME, 
       TO_CHAR(SYSDATE, &#39;MM&#39;)
       HIRE_DATE
FROM EMPLOYEES
WHERE TO_CHAR (HIRE_DATE, &#39;MM&#39;) = TO_CHAR (SYSDATE, &#39;MM&#39;);</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/d9cbe003-114c-4cec-99d5-87e700798d38/image.png" align="left">
연도와 상관없이 이번 달 입사자를 출력한다.

<p>시간의 포맷문자는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>형식</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>HH24</td>
<td>24시간으로 표현한 시간</td>
</tr>
<tr>
<td>HH, HH12</td>
<td>12시간으로 표현한 시간</td>
</tr>
<tr>
<td>MI</td>
<td>분</td>
</tr>
<tr>
<td>SS</td>
<td>초</td>
</tr>
<tr>
<td>AM, PM, A.M, P.M</td>
<td>오전, 오후</td>
</tr>
</tbody></table>
<pre><code class="language-sql">SELECT TO_CHAR (SYSDATE, &#39;HH:MI:SS AM&#39;),
       TO_CHAR (SYSDATE, &#39;HH24:MI:SS A.M.&#39;)
FROM DUAL;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/6f5088ab-b597-4f1a-a118-15fdfac0113b/image.png" align="left">

<hr>
<ul>
<li>TO_CHAR(숫자, &#39;포맷문자&#39;) : 숫자를 지정된 포맷의 텍스트로 변환한다.</li>
</ul>
<p>숫자의 포맷문자는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>형식</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>9</td>
<td>숫자의 한 자리를 의미(빈 자리 채우지 않음)</td>
</tr>
<tr>
<td>0</td>
<td>숫자의 한 자리를 의미(빈 자리를 0으로 채움)</td>
</tr>
<tr>
<td>$</td>
<td>달러 표시를 붙여서 출력</td>
</tr>
<tr>
<td>L</td>
<td>(Local) 지역 화폐 단위 기호를 붙여서 출력</td>
</tr>
<tr>
<td>.</td>
<td>소수점 표시</td>
</tr>
<tr>
<td>,</td>
<td>천 단위 구분기호를 표시</td>
</tr>
</tbody></table>
<pre><code class="language-sql">SELECT FIRST_NAME, TO_CHAR(SALARY, &#39;$99,999.99&#39;)
FROM EMPLOYEES
WHERE SALARY &gt;= 15000;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/7b70b9e2-2a88-4bce-a5dc-2a96589de91c/image.png" align="left">
SALARY가 15000이상인 직원들의 이름과 SALARY를 달러 기호를 붙이고 천 단위 구분기호를 붙여 소수점 두 번째 자리까지 출력했다.

<hr>
<ul>
<li>TO_NUMBER (&#39;텍스트&#39;, &#39;포맷문자&#39;) : 지정된 포맷문자와 일치하는 텍스트를 숫자로 변환한다<pre><code class="language-sql">SELECT TO_NUMBER(&#39;1,234&#39;, &#39;9,999&#39;) + TO_NUMBER(&#39;1,234&#39;, &#39;9,999&#39;)
FROM DUAL;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/f7f118f1-10bd-4d6b-a38d-e620345b141a/image.png" align="left">

</li>
</ul>
<hr>
<ul>
<li>TO_DATE(&#39;텍스트&#39;, &#39;포맷문자&#39;) : 지정된 포맷문자와 일치하는 텍스트를 날짜로 변환<pre><code class="language-sql">SELECT TO_DATE (&#39;2001/01/01&#39;, &#39;YYYY/MM/DD&#39;)
FROM DUAL;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/b00978b5-c0df-4142-8802-ef8b3db8b0cb/image.png" align="left">

</li>
</ul>
<h2 id="기타함수nvl-decode">기타함수(NVL, DECODE)</h2>
<ul>
<li>NVL (컬럼 혹은 표현식, 대체할 값) : 지정된 컬럼 혹은 표현식의 값이 NULL이면 대체할 값을 반환한다. 원래 값과 대체할 값의 타입이 같은 타입이어야 한다.<pre><code class="language-sql">SELECT EMPLOYEE_ID, FIRST_NAME, SALARY, COMMISSION_PCT,
          NVL(COMMISSION_PCT, 0),
          SALARY + SALARY*NVL(COMMISSION_PCT, 0) REAL_SALARY
FROM EMPLOYEES
WHERE SALARY &gt;= 10000;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/7d55e9fd-f062-4ece-ada2-2cf885fc6680/image.png" align="left">
SALARY가 10000이상인 직원들의 EMPLOYEE_ID, FIRST_NAME, COMMISSION_PCT 그리고 SALARY에 COMMISSION_PCT를 반영한 컬럼을 출력했다. `NVL`함수를 사용한 컬럼의 COMMISSION_PCT는 값이 NULL이면 0으로 표현된다.

</li>
</ul>
<hr>
<ul>
<li>NVL2 (컬럼 혹은 표현식, NULL이 아닐 때 대체할 값, NULL일 때 대체할 값) : 지정된 컬럼이 NULL이거나 NULL이 아닐 때 대체할 값을 각각 반환한다. 대체할 값은 데이터타입이 동일한 타입이어야 한다.<pre><code class="language-sql">SELECT EMPLOYEE_ID, FIRST_NAME, NVL2 (COMMISSION_PCT, &#39;커미션 받음&#39;, &#39;커미션 받지 않음&#39;)
FROM EMPLOYEES
WHERE SALARY &gt;= 10000;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/0f55c439-d24b-4d89-a298-97676456725d/image.png" align="left">
SALARY가 10000이상인 직원들의 EMPLOYEE_ID, FIRST_NAME, COMMISSION_PCT의 여부를 출력했다.

</li>
</ul>
<hr>
<ul>
<li>DECODE 함수 : 특정 컬럼이나 표현식의 값에 따라 다른 값을 반환한다.<pre><code class="language-sql">SELECT 컬럼1, 컬럼2
DECODE (컬럼 혹은 표현식, 값1, 표현식1,     -- 컬럼 혹은 표현식의 값이 값1과 일치하면 표현식1이 최종 결과가 된다.
                      값2, 표현식2,     -- 컬럼 혹은 표현식의 값이 값1과 일치하면 표현식1이 최종 결과가 된다.
                      값3, 표현식3,     -- 컬럼 혹은 표현식의 값이 값1과 일치하면 표현식1이 최종 결과가 된다.
                      표현식4)          -- 컬럼 혹은 표현식의 값이 값1,2,3, 모두와 일치하지 않으면 표현식4가 최종결과가 된다.
     컬럼3 ...
FROM 테이블명;</code></pre>
</li>
</ul>
<pre><code class="language-sql">SELECT FIRST_NAME, DEPARTMENT_ID,
            DECODE (DEPARTMENT_ID, 10, &#39;A팀&#39;, 
                                   20, &#39;A팀&#39;, 
                                   30, &#39;A팀&#39;, 
                                   40, &#39;A팀&#39;, 
                                   50, &#39;B팀&#39;, 
                                   60, &#39;B팀&#39;, 
                                   70, &#39;B팀&#39;, 
                                   80, &#39;C팀&#39;, 
                                   &#39;D팀&#39;) AS TEAM
FROM EMPLOYEES;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/bc515220-b2b0-4f93-97a4-270dcd4bcac0/image.png" align="left">
DEPARTMENT_ID를 A(10, 20, 30), B(40, 50, 60, 70),C(80)로 묶고 나머지는 D팀으로 지정하여 TEAM이라는 컬럼으로 출력했다.

<h3 id="case문">CASE문</h3>
<p>CASE문은 DECODE함수와 마찬가지로 특정 조건에 따라 반환할 데이터를 지정할 때 사용한다. 하지만 CASE문은 일반적인 프로그래밍 언어의 if-else if문 처럼 DECODE보다 유연하게 사용할 수 있다.</p>
<pre><code class="language-sql">SELECT 컬럼1, 컬럼2 ...
CASE
          WHEN 조건식 THEN 표현식
          WHEN 조건식 THEN 표현식
          WHEN 조건식 THEN 표현식
          ELSE 표현식
 END
 FROM 테이블명</code></pre>
<pre><code class="language-sql">SELECT FIRST_NAME, SALARY,
            CASE
            WHEN SALARY &gt;= 10000 THEN SALARY*0.1
            WHEN SALARY &gt;= 5000 THEN SALARY*0.2
            ELSE SALARY*0.3
            END AS BONUS
FROM EMPLOYEES;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/225885f3-4a4a-4315-b9b9-99ad95da7efb/image.png" align="left">
직원들의 SALARY를 10000이상, 5000이상, 그 외로 나누고 각 범위에 따라 일정 비율을 곱한 값을 반환하고 BONUS라는 컬럼으로 표현했다.]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] DML(SELECT, INSERT, UPDATE, DELETE)]]></title>
            <link>https://velog.io/@dev-yongjun/DML</link>
            <guid>https://velog.io/@dev-yongjun/DML</guid>
            <pubDate>Wed, 19 Oct 2022 15:24:38 GMT</pubDate>
            <description><![CDATA[<h1 id="데이터-조작어dml">데이터 조작어(DML)</h1>
<p>데이터 조작어라 부르는 DML(Data Manipulation Language)은 데이터베이스 내에 저장된 데이터를 조회, 추가, 수정, 삭제하기 위해 사용하는 명령어다. 그래서 DML에는 SELECT문, INSERT문, UPDATE문, DELETE문이 있다.</p>
<h2 id="select문과-from절">SELECT문과 FROM절</h2>
<p>SELECT문은 데이터베이스에 저장되어 있는 데이터를 조회하는 데 사용한다. SELECT문의 FROM절은 조회할 테이블을 지정하고, SELECT절은 조회할 테이블의 열을 지정한다.</p>
<pre><code class="language-sql">SELECT *
FROM 테이블명;</code></pre>
<p><code>*</code>는 해당 테이블의 모든 열을 조회한다는 의미이다. 위와 같은 경우 FROM절에서 지정한 테이블의 모든 열을 조회한다.</p>
<h3 id="특정-컬럼을-지정하여-조회">특정 컬럼을 지정하여 조회</h3>
<pre><code class="language-sql">SELECT 컬럼1, 컬럼2, 컬럼3 ...
FROM 테이블명;</code></pre>
<p>위와 같이 작성하여 원하는 컬럼만 지정해 조회할 수 있다.</p>
<p>다음은 EMPLOYEES테이블에서 직원아이디, 이름, 연봉 컬럼을 조회한 예시다.</p>
<pre><code class="language-sql">SELECT EMPLOYEE_ID, FIRST_NAME, SALARY
FROM EMPLOYEES;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/ad72b1ca-c2a5-4bae-96a2-8d94bd19bf61/image.png" align="left">

<h3 id="연산자를-활용한-컬럼">연산자를 활용한 컬럼</h3>
<p>연산자를 활용해서 컬럼을 조회할 수 있다. 컬럼 한 개에 대한 연산식 뿐만 아니라 두 컬럼을 연산자를 통해 결합하여 하나의 컬럼처럼 조회할 수 있다.</p>
<p>다음은 EMPLOYEES테이블의 SALARY에 12를 곱한 값을 나타내는 컬럼을 추가로 출력한 예시다.</p>
<pre><code class="language-sql">SELECT FIRST_NAME, SALARY, SALARY*12
FROM EMPLOYEES;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/e21d6af9-77d0-4399-bca2-6d6741f4a1b1/image.png" align="left">

<p>다음은 JOBS테이블의 MAX_SALARY와 MIN_SALARY의 차를 나타내는 컬럼을 추가로 출력한 예시다. 각각의 MAX_SALARY와 MIN_SALARY컬럼을 연산자로 조합하여 하나의 컬럼처럼 출력했다.</p>
<pre><code class="language-sql">SELECT JOB_ID, MAX_SALARY-MIN_SALARY
FROM JOBS;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/882faff8-8ac4-45f9-ac45-04442854cd82/image.png" aglign="left">

<h3 id="컬럼의-별칭-지정">컬럼의 별칭 지정</h3>
<pre><code class="language-sql">SELECT 컬럼 별칭
FROM 테이블명;

SELECT 컬럼 &quot;별칭&quot;
FROM 테이블명;

SELECT 컬럼 AS 별칭
FROM 테이블명;

SELECT 컬럼 AS &quot;별칭&quot;
FROM 테이블명;</code></pre>
<p>위 네 가지 방법으로 컬럼의 별칭을 지정할 수 있다. 별칭을 지정하면 조회 결과에 컬럼이름 대신 별칭이 나온다. 가독성을 위해 첫 번째 방법은 지양하는 것이 좋다고 생각한다.</p>
<p>다음은 조회할 컬럼을 지정한 후 별칭을 적용한 예시다.</p>
<pre><code class="language-sql">SELECT FIRST_NAME AS 이름, SALARY AS 월급, SALARY*12 AS 연봉
FROM EMPLOYEES;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/bbda3275-a406-41d9-a7f6-3b2b3da32f17/image.png" align="left">


<h3 id="distinct">DISTINCT</h3>
<pre><code class="language-sql">SELECT DISTINCT 컬럼
FROM 테이블명;</code></pre>
<p>DISTINCT는 중복되는 데이터가 있는 행을 제외하고 출력하는 SQL의 예약어이다. 위와 같이 SELECT절의 컬럼 앞에 DISTINCT를 붙여 해당 컬럼의 값이 중복되는 행을 제외하고 출력할 수 있다.</p>
<pre><code class="language-sql">SELECT DISTINCT MANAGER_ID
FROM EMPLOYEES;

SELECT DISTINCT MANAGER_ID, DEPARTMENT_ID
FROM EMPLOYEES;</code></pre>
<p>첫 번째 방법은 MANAGER_ID값이 같은 행을 제외한 후 출력하고, 두 번째 방법은 MANAGER_ID, DEPARTMENT_ID 둘 다 같아야 중복되는 행이라 간주한다.</p>
<h2 id="order-by절">ORDER BY절</h2>
<p>ORDER BY절은 조회한 데이터를 특정 컬럼을 기준으로 정렬하는 데 사용한다. SELECT문을 작성할 때, 여러 절 중 가장 마지막 부분에 작성한다.</p>
<pre><code class="language-sql">SELECT 컬럼1, 컬럼2, 컬럼3 ...
FROM 테이블명
ORDER BY 정렬기준 정렬옵션</code></pre>
<p>정렬옵션으로는 <code>ASC</code>와 <code>DESC</code>가 있다. <code>ASC</code>는 Ascending의 약자로 오름차순을 뜻하며, <code>DESC</code>는 Descending의 약자로 내림차순을 뜻한다. 정렬옵션을 생략하면 기본값은 <code>ASC</code>가 적용된다.</p>
<h3 id="오름차순">오름차순</h3>
<pre><code class="language-sql">SELECT FIRST_NAME AS 이름,
       HIRE_DATE AS 입사일, 
       SALARY AS 월급
FROM EMPLOYEES
ORDER BY 입사일 ASC;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/38c59def-6bb1-421d-aeaa-930147c500c7/image.png" align="left">

<p>입사일을 기준으로 오름차순 정렬했다. ORDER BY에 컬럼명이 아니라 별칭을 적용해도 에러가 발생하지 않는 이유는 ORDER BY절이 가장 마지막에 실행되기 때문이다. SELECT절에서 먼저 컬럼의 별칭이 지정된 후 ORDER BY절이 실행된다.</p>
<h3 id="내림차순">내림차순</h3>
<pre><code class="language-sql">SELECT FIRST_NAME AS 이름,
           HIRE_DATE AS 입사일, 
           SALARY AS 월급
FROM EMPLOYEES
ORDER BY 월급 DESC;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/86ba1198-12e2-4277-bbb9-e5e8645ff440/image.png" align="left">

<p>월급을 기준으로 내림차순 정렬했다.</p>
<h3 id="여러-개의-정렬-기준-사용">여러 개의 정렬 기준 사용</h3>
<pre><code class="language-sql">SELECT FIRST_NAME AS 이름,
           JOB_ID AS 직종, 
           SALARY AS 월급
FROM EMPLOYEES
ORDER BY 직종 ASC, 월급 DESC;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/d9cf708b-e40b-4f4c-a306-29824e6b3b47/image.png" align="left">

<p>첫 번째 정렬 기준으로 JOB_ID를 지정했고, JOB_ID가 같을 경우 SALARY를 내림차순하여 정렬했다.</p>
<h3 id="주의-사항">주의 사항</h3>
<p><strong>ORDER BY절을 통해 조회된 데이터를 정렬하는 것은 많은 자원을 소모한다.</strong> 그래서 출력할 데이터를 선정하는 시간보다 정렬하는 데 시간이 더 걸려 서비스 응답 속도가 느려질 수 있다. <strong>따라서 정렬이 꼭 필요한 경우가 아니라면 ORDER BY절은 넣지 않는 것이 좋다.</strong></p>
<h2 id="where절">WHERE절</h2>
<pre><code class="language-sql">SELECT 컬럼1, 컬럼2 ...
FROM 테이블명
WHERE 조건식</code></pre>
<p>WHERE절은 SELECT문으로 데이터를 조회할 때 특정 조건을 기준으로 원하는 행을 출력하는 데 사용한다. WHERE절 또한 연산자를 사용하여 더욱 구체적으로 데이터를 조회할 수 있다.</p>
<p>WHERE절의 조건식에 사용되는 컬럼명은 별칭을 사용할 수 없다. SQL의 실행순서 상 WHERE절이 SELECT절보다 먼저 실행되기 때문이다. 즉, WHERE절로 먼저 필터링을 한 후 SELECT절을 통해 컬럼에 별칭이 지정된다.</p>
<p>WHERE절에서 사용하는 연산자나 기호에 대해 주의해야 할 점은 다음과 같다.</p>
<ul>
<li><p>SQL에서 값이 같은지 비교하는 연산자는 <code>=</code>이다.
(일반적인 프로그래밍 언어처럼 <code>==</code>이 아니다.)</p>
</li>
<li><p>SQL에서 문자열을 나타내는 기호는 작은 따옴표(<code>&#39;&#39;</code>)다.
일반적인 프로그래밍 언어처럼 큰 따옴표(<code>&quot;&quot;</code>)가 아니다.</p>
</li>
<li><p>값이 NULL인 경우 비교 연산자를 통해 조회할 수 없다.</p>
</li>
<li><p>예약어(SELECT, FROM 등)는 대소문자를 구분하지 않지만, 데이터의 &#39;값&#39;은 대소문자를 구분한다. 
예를 들어, SQL을 작성할 때 JOB_ID의 IT_PROG과 IT_Prog는 다르게 간주한다.</p>
</li>
</ul>
<h3 id="비교-연산자">비교 연산자</h3>
<pre><code class="language-sql">SELECT FIRST_NAME AS 이름,
           SALARY AS 월급
FROM EMPLOYEES
WHERE SALARY &gt;= 10000;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/6d93fc2b-2396-4da8-9a3d-e06d242c4497/image.png" align="left">

<p>SALARY를 기준으로 SALARY가 10000이상인 행만 출력했다. 위에서 언급했던 것 처럼 WHERE절에는 별칭을 사용할 수 없다.</p>
<pre><code class="language-sql">SELECT FIRST_NAME AS 이름,
           JOB_ID AS 직종,
           SALARY AS 월급
FROM EMPLOYEES
WHERE JOB_ID = &#39;IT_PROG&#39;;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/05de8e7d-4f39-49c4-b44b-b96a783c45f3/image.png" align="left">

<p>JOB_ID가 IT_PROG인 행만 출력했다.</p>
<h3 id="null">NULL</h3>
<p><strong>데이터베이스에서 null은 아직 값이 결정되지 않았은 것을 의미한다. 그리고 null이 포함된 산술 연산의 결과는 항상 null이다.</strong> Java에서 null의 의미와 다르다.</p>
<p><strong>위에서 언급했다시피 값이 null인 경우, 비교 연산자를 통해 조회할 수 없다. 대신에 IS NULL 혹은 IS NOT NULL이라는 예약어를 사용한다.</strong></p>
<pre><code class="language-sql">SELECT FIRST_NAME AS 이름,
           JOB_ID AS 직종,
           COMMISSION_PCT
FROM EMPLOYEES
WHERE COMMISSION_PCT IS NULL;
</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/10033a3e-87da-4a79-82ca-47a7b4103c86/image.png" align="left">

<p>COMMISSION_PCT가 없는 직원의 이름, 직종, COMMISSION_PCT를 출력한 예시다. COMMISSION_PCT가 없는 행만 출력했으므로 결과도 당연히 COMMISSION_PCT열의 모든 값은 null이다.</p>
<h3 id="논리-연산자">논리 연산자</h3>
<p>논리 연산자를 통해 여러 조건을 조합하여 WHERE절을 작성할 수 있다.</p>
<pre><code class="language-sql">SELECT FIRST_NAME AS 이름,
           JOB_ID AS 직종,
           SALARY AS 급여,
           COMMISSION_PCT
FROM EMPLOYEES
WHERE (COMMISSION_PCT IS NOT NULL) AND SALARY &gt;= 10000;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/c0d76c03-43f5-4bac-9c1a-8f30ff26017f/image.png" align="left">

<p>COMMISSION_PCT가 있는 직원들 중, 급여가 10000이상인 직원들의 이름, 직종, 급여를 출력했다.</p>
<pre><code class="language-sql">SELECT FIRST_NAME AS 이름,
           JOB_ID AS 직종,
           SALARY AS 급여,
           DEPARTMENT_ID AS 부서아이디
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 40 OR DEPARTMENT_ID = 70 OR DEPARTMENT_ID = 110;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/1da62ca0-3304-4fa4-b1cf-20e94ba77332/image.png" align="left">

<p>DEPARTMENT_ID가 40,70,110 중 하나인 직원들의 이름, 직종, 급여, 부서아이디를 출력했다.</p>
<h3 id="between">BETWEEN</h3>
<pre><code class="language-sql">SELECT 컬럼1, 컬럼2 ...
FROM 테이블명
WHERE 컬럼 BETWEEN 하한값 AND 상한값;</code></pre>
<p>BETWEEN은 지정한 컬럼의 값의 범위에 속하는 행을 출력한다. 위에서 다룬 논리 연산자로 표현하면 <code>WHERE 하한값 &lt;= 컬럼 AND 컬럼 &lt;= 상한값</code>과 같다.</p>
<pre><code class="language-sql">SELECT FIRST_NAME AS 이름,
           SALARY AS 급여
FROM EMPLOYEES
WHERE SALARY BETWEEN 5000 AND 10000;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/e740d2a2-1e13-49a5-9f5a-839d27e790f2/image.png" align="left">

<p>급여가 5000이상, 10000이하인 직원들의 이름과 급여를 출력했다.</p>
<h3 id="in">IN</h3>
<pre><code class="language-sql">SELECT 컬럼1, 컬럼2 ...
FROM 테이블명
WHERE 컬럼 IN (값1, 값2 ...);</code></pre>
<p>IN은 지정한 컬럼에서 괄호 안의 값들 중 하나와 일치하는 행을 출력한다. 위에서 다룬 논리 연산자로 표현하면 <code>WHERE 컬럼 = 값1 OR 컬럼 = 값2</code>와 같다.</p>
<pre><code class="language-sql">SELECT FIRST_NAME AS 이름,
           DEPARTMENT_ID AS 부서아이디
FROM EMPLOYEES
WHERE DEPARTMENT_ID IN (40, 70, 110);</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/b5a98f37-aa92-427b-92be-fac3e8254c0f/image.png" align="left">

<p>DEPARTMENT_ID가 40, 70, 110 중 하나인 직원들의 이름과 부서아이디를 출력했다.</p>
<h3 id="like-패턴">LIKE &#39;패턴&#39;</h3>
<pre><code class="language-sql">SELECT 컬럼1, 컬럼2 ...
FROM 테이블명
WHERE 컬럼 LIKE &#39;문자_&#39;

SELECT 컬럼1, 컬럼2 ...
FROM 테이블명
WHERE 컬럼 LIKE &#39;_문자&#39;

SELECT 컬럼1, 컬럼2 ...
FROM 테이블명
WHERE 컬럼 LIKE &#39;문자%&#39;

SELECT 컬럼1, 컬럼2 ...
FROM 테이블명
WHERE 컬럼 LIKE &#39;%문자%&#39;</code></pre>
<p>LIKE 뒤의 <code>_, %</code>는 SQL의 패턴문자다. 패턴문자의 의미는 다음과 같다.</p>
<ul>
<li><p><code>_</code>는 임의의 문자 하나를 의미한다.</p>
<ul>
<li>예를 들어, <code>NAME LIKE &#39;박_&#39;</code>는 NAME 컬럼에서 성이 &#39;박&#39;이고 이름이 한 글자인 사람을 조회한다.</li>
</ul>
</li>
<li><p><code>%</code>는 0개 이상의 임의의 문자열을 의미한다</p>
<ul>
<li><p>예를 들어, <code>NAME LIKE &#39;박%&#39;</code>은 NAME 컬럼에서 성이 &#39;박&#39;인 사람을 조회한다. 문자 <code>박</code> 뒤로 어느 문자열이 와도 상관이 없다.</p>
</li>
<li><p><code>TITLE LIKE &#39;%자바%&#39;</code>는 TITLE 컬럼에서 <code>자바</code>가 포함되어 있는 책을 조회한다.</p>
</li>
</ul>
</li>
</ul>
<pre><code class="language-sql">SELECT EMPLOYEE_ID AS 직원아이디
       FIRST_NAME AS 이름
FROM EMPLOYEES
WHERE FIRST_NAME LIKE &#39;A%&#39;;</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/d4f87cbb-520b-4f6e-bc7b-c0d71545b029/image.png" align="left">

<p>FIRST_NAME이 &#39;A&#39;로 시작하는 직원들의 직원아이디와 이름을 출력했다.</p>
<h2 id="insert문">INSERT문</h2>
<ul>
<li>테이블에 새로운 행을 추가한다.<pre><code class="language-sql">INSERT INTO 테이블명 (컬럼1, 컬럼2, 컬럼3)
VALUES (값1, 값2, 값3);</code></pre>
</li>
</ul>
<pre><code class="language-sql">-- SAMPLE_USERS 테이블에 새로운 행을 추가한다.
INSERT INTO SAMPLE_USERS (USER_ID, USER_PASSWORD, USER_EMAIL, USER_NAME)
VALUES (&#39;hong&#39;, &#39;zxcv1234&#39;, &#39;hong@gmail.com&#39;, &#39;홍길동&#39;);</code></pre>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/86f3f4b1-d0ad-428d-928a-239417756f62/image.png" align="left">
SMAPLE_USERS에 정의된 컬럼에 데이터를 입력하지 않으면 자동으로 NULL이 입력된다. 그러나 NULL을 허용하지 않는 컬럼이라면 오류가 발생한다.

<h2 id="update문">UPDATE문</h2>
<ul>
<li>테이블에 존재하는 행의 데이터를 변경한다.</li>
<li>WHERE절에 조건식이 없으면 모든 행에서 해당 컬럼의 값을 변경한다.</li>
<li>WHERE 조건식이 있으면 조건식을 만족하는 행에서만 해당 컬럼의 값을 변경한다.<pre><code class="language-sql">UPDATE 테이블
SET
  컬럼1 = 값1,
  컬럼2 = 값2,
  컬럼3 = 값3
WHERE 조건식</code></pre>
```sql</li>
<li><ul>
<li>SAMPLE_USERS 테이블에서 USER_TEL이 존재하는 행의 USER_POINT를 10000으로 변경한다.
UPDATE SAMPLE_USERS
SET
 USER_POINT = 10000
WHERE
 USER_TEL IS NOT NULL;<pre><code></code></pre></li>
</ul>
</li>
</ul>
<img src="https://velog.velcdn.com/images/dev-yongjun/post/c3e8340d-9e51-46a6-8682-9e6f72df3525/image.png" align="left">
<img src="https://velog.velcdn.com/images/dev-yongjun/post/528a1290-c8ea-43c5-83b1-669132826396/image.png
" align="left">

<h2 id="delete문">DELETE문</h2>
<ul>
<li>테이블에 저장된 행을 삭제한다.</li>
<li>WHERE절에 조건식이 없으면 테이블의 모든 행을 삭제한다.</li>
<li>WHERE 조건식이 있으면 조건식을 만족하는 행만 삭제한다.<pre><code class="language-sql">DELETE FROM 테이블명
WHERE 조건식</code></pre>
```sql</li>
<li><ul>
<li>SAMPLE_PRODUCTS 테이블에서 PRODUCT_MAKER가 &#39;삼성&#39;인 행을 삭제한다.
DELETE SAMPLE_PRODUCTS
WHERE PRODUCT_MAKER = &#39;삼성&#39;;<pre><code>&lt;img src=&quot;https://velog.velcdn.com/images/dev-yongjun/post/1a077ce3-d822-4c36-b97d-742b7c72bfb7/image.png&quot; align=&quot;left&quot;&gt;
&lt;img src=&quot;https://velog.velcdn.com/images/dev-yongjun/post/34c220af-7924-4b1a-b898-4bdfbbcadea6/image.png&quot; align=&quot;left&quot;&gt;
</code></pre></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 테이블 간의 관계]]></title>
            <link>https://velog.io/@dev-yongjun/Table</link>
            <guid>https://velog.io/@dev-yongjun/Table</guid>
            <pubDate>Wed, 19 Oct 2022 15:12:50 GMT</pubDate>
            <description><![CDATA[<h1 id="테이블-간의-관계">테이블 간의 관계</h1>
<p>다음과 같은 그림을 &#39;ER(Entity-Relational) 다이어그램&#39; 이라고 한다. 앞으로 SQL을 배우며 사용할 테이블 간의 관계를 나타낸 그림이다.
가장 위의 텍스트가 테이블 이름이고 그 아래에 있는 것들이 컬럼 이름이다. 예를 들어, REGIONS 테이블은 region_id, region_name 컬럼을 갖고 있다.
<img src="https://velog.velcdn.com/images/dev-yongjun/post/c4366447-c195-4dd8-8926-30ef1786d257/image.jpg" alt=""></p>
<ul>
<li><p>EMPLOYEES</p>
<ul>
<li><p>employee_id는 사원의 고유한 번호다.</p>
</li>
<li><p>job_id는 JOBS테이블의 job_id를 참조한다.</p>
</li>
<li><p>manager_id는 EMPLOYEES 테이블의 employee_id를 참조한다.</p>
</li>
<li><p>department_id는 DEPARTMENTS 테이블의 department_id를 참조한다.</p>
</li>
</ul>
</li>
<li><p>DEPARTMENTS</p>
<ul>
<li><p>department_id는 DEPARTMENTS 테이블의 고유한 번호다.</p>
</li>
<li><p>manager_id는 EMPLOYEES 테이블의 employee_id를 참조한다. 
절대 EMPLOYEES 테이블의 manager_id를 참조하는 것이 아니다.</p>
</li>
<li><p>location_id는 LOCATIONS 테이블의 location_id를 참조한다.</p>
</li>
</ul>
</li>
<li><p>LOCATIONS</p>
<ul>
<li><p>location_id는 소재지의 고유한 번호다.</p>
</li>
<li><p>country_id는 COUNTRIES 테이블의 country_id를 참조한다.</p>
</li>
</ul>
</li>
<li><p>COUNTRIES</p>
<ul>
<li><p>country_id는 국가의 고유한 아이디다.</p>
</li>
<li><p>region_id는 REGIONS 테이블의 region_id를 참조한다.</p>
</li>
</ul>
</li>
<li><p>REGIONS</p>
<ul>
<li>region_id는 지역의 고유한 번호다.</li>
</ul>
</li>
<li><p>JOBS</p>
<ul>
<li>job_id는 직종의 고유한 아이디다.</li>
</ul>
</li>
<li><p>JOB_HISTORY</p>
<ul>
<li><p>employee_id와 start_date가 고유한 값이다. 각각이 고유한 값이 아니라 이 둘을 결합한 것을 고유한 값으로 본다.</p>
</li>
<li><p>employee_id는 EMPLOYEES 테이블의 employee_id를 참조한다.</p>
</li>
<li><p>job_id는 JOBS테이블의 job_id를 참조한다.</p>
</li>
<li><p>department_id는 DEPARTMENTS 테이블의 department_id를 참조한다.</p>
</li>
</ul>
</li>
</ul>
<h2 id="기본키primary-key와-외래키foreign-key">기본키(Primary Key)와 외래키(Foreign Key)</h2>
<p>위에서 언급한 &#39;고유한 값&#39;을 기본키(Primary Key)라고 한다. 즉, 행을 구분하는 유일한 값이다. 절대 중복될 수 없다.
그리고 한 테이블에 포함되어 있으면서 다른 테이블의 기본키로 지정된 키를 외래키(Foreign Key)라고 한다. 특정 테이블에서 다른 테이블을 참조할 때 외래키를 활용한다.</p>
<p>데이터베이스에서 참조한다는 것은 예를 들어, EMPLOYEES 테이블에서 각 직원의 부서명을 알고 싶을 때 EMPLOYEES 테이블 내에 있는 department_id를 활용해서 DEPARTMENTS 테이블의 department_name을 찾는다는 의미이다. EMPLOYEES 테이블내에는 부서명(department_name)이 없기 때문이다. </p>
<p>이때 department_id는 EMPLOYEES 테이블에서는 외래키이고, DEPARTMENTS 테이블에서는 기본키가 된다.</p>
<h2 id="복합키composite-key">복합키(Composite Key)</h2>
<p>복합키(Composite Key)는 여러 열을 조합하여 기본키처럼 사용하는 키를 뜻한다. 상황에 따라 하나의 기본키만으로는 행을 식별할 수 없는 경우가 있기 때문이다.</p>
<p>예를 들어, JOB_HISTORY 테이블이 그렇다. 이 테이블은 직원들의 직종 변경 이력을 담고 있다. 그래서 특정 직원의 직종 변경 이력이 여러 번이라면 테이블 내에 employee_id가 중복될 수 있고, 직종 변경 날짜가 같은 직원이 있다면 start_date가 중복될 수 있다.</p>
<p>따라서 employee_id와 start_date를 조합한 형태를 기본키로 사용한다. 이를 복합키라고 한다.</p>
<h2 id="1n-관계">1:N 관계</h2>
<p>그림에서 빨간색 동그라미 부분이 1:N 관계 즉, 일대다 관계이다. 일대다 관계의 의미는 한 테이블의 레코드가 다른 테이블의 레코드를 여러 개 가질 수 있다는 것이다.</p>
<p>예를 들어, COUNTRIES 테이블의 region_id는 REGIONS 테이블의 region_id를 참조한다. 그리고 REGIONS 테이블의 region_id는 COUNTRIES 테이블에서 여러 개의 region_id를 가질 수 있다.
<img src="https://velog.velcdn.com/images/dev-yongjun/post/afd15540-c014-4900-9dad-6ad833336814/image.png" alt=""></p>
<p>그림에서 볼 수 있듯이, REGIONS 테이블에서 region_id는 중복되지 않고 반드시 한 개씩 존재하지만, COUNTRIES 테이블에서 region_id는 여러 개 존재한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] SQL이란?]]></title>
            <link>https://velog.io/@dev-yongjun/SQL</link>
            <guid>https://velog.io/@dev-yongjun/SQL</guid>
            <pubDate>Thu, 13 Oct 2022 15:52:53 GMT</pubDate>
            <description><![CDATA[<h1 id="sqlstructured-query-language">SQL(Structured Query Language)</h1>
<ul>
<li><p>관계형 데이터베이스에 접근할 때 사용하는 프로그래밍 언어다.</p>
</li>
<li><p>직역하면 구조화된 &#39;질의&#39; 언어다. 즉, 사용자가 원하는 동작을 DBMS에게 요청할 때 사용한다.</p>
</li>
</ul>
<h2 id="sql의-종류">SQL의 종류</h2>
<ul>
<li><p>DDL(Data Definition Language)</p>
<ul>
<li>데이터 정의 언어</li>
<li>데이터베이스의 구조를 정의하는 명령어다.</li>
<li>데이터베이스의 주요 객체를 생성,삭제,변경하는 작업 수행한다.</li>
</ul>
</li>
</ul>
<p>주요 명령어는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>CREATE</td>
<td>새로운 데이터베이스 객체 생성</td>
</tr>
<tr>
<td>DROP</td>
<td>기존 데이터베이스 객체 삭제</td>
</tr>
<tr>
<td>ALTER</td>
<td>기존 데이터베이스 객체 변경</td>
</tr>
<tr>
<td>TRUNCATE</td>
<td>데이터를 되돌릴 수 없도록 제거</td>
</tr>
</tbody></table>
<ul>
<li><p>DML(Data Manipulation Language)</p>
<ul>
<li>데이터 조작 언어</li>
<li>데이터베이스에서 데이터를 추가/조회/변경/삭제하는 작업을 수행한다.</li>
</ul>
</li>
</ul>
<p>주요 명령어는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>INSERT</td>
<td>테이블에 새로운 데이터 행을 추가한다.</td>
</tr>
<tr>
<td>SELECT</td>
<td>테이블에서 데이터를 조회한다</td>
</tr>
<tr>
<td>UPDATE</td>
<td>테이블의 데이터를 변경한다</td>
</tr>
<tr>
<td>DELETE</td>
<td>테이블에 저장된 특정 데이터 행을 삭제한다</td>
</tr>
</tbody></table>
<ul>
<li><p>DCL(Data Control Language)</p>
<ul>
<li>데이터 제어 언어</li>
<li>권한제어, 트랜잭션 제어 작업을 수행한다.</li>
</ul>
</li>
</ul>
<p>주요 명령어는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>GRANT</td>
<td>특정 사용자에게 특정 작업을 수행을 권한을 부여</td>
</tr>
<tr>
<td>REVOKE</td>
<td>사용자에게 부여된 권한을 박탈</td>
</tr>
<tr>
<td>COMMIT</td>
<td>트랜잭션을 실행</td>
</tr>
<tr>
<td>ROLLBACK</td>
<td>트랜잭션을 취소</td>
</tr>
<tr>
<td>SAVEPOINT</td>
<td>롤백지점을 설정한다</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 데이터베이스란?]]></title>
            <link>https://velog.io/@dev-yongjun/DataBase</link>
            <guid>https://velog.io/@dev-yongjun/DataBase</guid>
            <pubDate>Thu, 13 Oct 2022 15:18:02 GMT</pubDate>
            <description><![CDATA[<p>SQL은 데이터베이스를 다루기 위해 사용하는 언어다. 그래서 SQL을 공부하기전에 데이터베이스가 무엇인지 아주 간략하게 짚고 넘어간다.</p>
<h1 id="데이터베이스란">데이터베이스란?</h1>
<ul>
<li><p>데이터를 저장해 놓은 곳</p>
</li>
<li><p>여러 사람들이 공유하고 사용할 목적으로 통합관리되는 정보의 집합</p>
</li>
<li><p>관련성 있는 데이터의 모음</p>
</li>
</ul>
<h2 id="관계형-데이터베이스">관계형 데이터베이스</h2>
<ul>
<li><p>관계형 모델에서는 데이터베이스를 관계(Relation)의 집합으로, 관계를 행(Tuple)의 집합으로, 행을 속성(Attribute)의 집합으로 보는 것.</p>
</li>
<li><p>관계형 데이터베이스에서는 관계가 테이블, 행이 레코드, 속성이 컬럼으로 대치된다.</p>
</li>
<li><p>관계라는 이름으로 불리는 이유는 레코드와 컬럼이 어떤 관계에 의해 모여진 집합으로 보기 떄문이다</p>
</li>
<li><p>관계형 데이터 모델에서 속성은 해당 속성이 가질 수 있는 모든 값에 대한 도메인을 가지며, 원자적이어야한다.</p>
</li>
</ul>
<p>관계형 데이터베이스의 특성을 표로 이해해보자</p>
<p><img src="https://velog.velcdn.com/images/dev-yongjun/post/cfa89e20-ade6-44dd-aca2-dd1d00f97f14/image.png" alt=""></p>
<p>여기서 &#39;속성이 원자적이어야 한다.&#39; 라는 말의 의미는 해당 속성이 더 이상 하위 개념으로 분류될 수 없어야 한다는 의미다. 예를 들어 직급이라는 속성은 사원, 대리, 팀장 등으로 나눌 수 있다 그러나 위의 그림처럼 과장이라는 속성은 더 이상 나눌 수 없다.</p>
<h2 id="dbmsdatabase-management-system">DBMS(DataBase Management System)</h2>
<ul>
<li><p>데이터베이스를 관리하고 활용하기위한 프로그램</p>
</li>
<li><p>문서작성을 할 때 워드프로세서 프로그램을 사용하는 것처럼 데이터베이스를 다룰 때에도 DBMS를 사용</p>
</li>
<li><p>대표적인 DBMS는 오라클, MySQL, MS SQL Server, DB2, Maria DB, H2, HSQLDB 등이 있다.</p>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>