<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>shy_dd.log</title>
        <link>https://velog.io/</link>
        <description>데이터를 사랑하는 개발자</description>
        <lastBuildDate>Sun, 15 Jan 2023 11:44:54 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>shy_dd.log</title>
            <url>https://images.velog.io/images/shy_dd/profile/f5bf8cdb-46dc-484e-8e5f-1acf0003a08a/KakaoTalk_20220213_145016038.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. shy_dd.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/shy_dd" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[개발자, 대학원에 가다 - 2]]></title>
            <link>https://velog.io/@shy_dd/%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%8C%80%ED%95%99%EC%9B%90%EC%97%90-%EA%B0%80%EB%8B%A4-2</link>
            <guid>https://velog.io/@shy_dd/%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%8C%80%ED%95%99%EC%9B%90%EC%97%90-%EA%B0%80%EB%8B%A4-2</guid>
            <pubDate>Sun, 15 Jan 2023 11:44:54 GMT</pubDate>
            <description><![CDATA[<h2 id="원서-준비">원서 준비</h2>
<p><img src="https://velog.velcdn.com/images/shy_dd/post/296ced87-7f57-4ca5-8805-58c43720a980/image.png" alt=""></p>
<p>가고 싶은 대학원과 학과를 골랐다면, 학과 홈페이지에서 요구하는 서류를 제출할 차례이다.
물론 입시기간이 아니라면 조금 더 기다려야 하겠지만, 아침에 일어나는 새가 벌레를 먹듯이
미리미리 써둔다고 손해 볼 일은 없다.</p>
<p>위와 같이 각 학교별로 또는 학교별 학과별로 노션에 정리를 해두었고
핸드폰 달력에도 원서마감일의 D-Day를 기록해두면 좋다.</p>
<p>처음 대학원에 입학원서를 넣을 때는 으레 그렇듯, 꽤 많은 서류를 요구하는 것 같아 보이지만
아래 목록에서 본인에게 해당하는 서류의 목록을 추리면 생각보다 많지 않다는 것을 알 수 있다.
다만 회사에 지원할 때는 써보지 않은 &#39;학업계획서&#39;와 &#39;연구계획서&#39;에서 두려움이 엄습하고
숨이 턱 막히는 경험을 할 수 있다.</p>
<p>매우 감사하게도, 내가 희망하는 학과 사이트에서는 아래처럼 작성예시가 있었는데
커피 한잔 때리면서 마음을 다잡고 천천히 훑어보면
&#39;어!? 생각보다 쓸만한데?&#39;라는 느낌을 받을 수도 있을 것이다.</p>
<p>다만 경험상 회사에 지원할 때도 예시의 양식을 지키되, 눈에 띄는 점이 없다면 탈락했던 경우가 많았다.
예시를 참고하되, 다른 지원자들보다 무언가 &#39;더&#39; 보여주어야 한다는 압박감이 생기기 시작한다.</p>
<p><img src="https://velog.velcdn.com/images/shy_dd/post/b347200b-774c-4c85-b588-f8246866735d/image.png" alt=""></p>
<p>그나마 다행인 점은, 이제는 자기소개쯤은 눈감고도 작성 할 수 있고(그러면 떨어진다.)
학업계획서 또한 &lt;학과별 전공소개&gt; 페이지를 상세히 보면 
&#39;매 학기에 이런것을 배우니 나는 이렇게 공부하고 이러한 것을 얻어가겠다&#39; 하는 형식으로
작성하면 어렵지 않게 작성할 수 있다.</p>
<p>좋다, 여기까지 왔다면 벌써 무시무시한 원서 삼형제 중 가장 맏이만 남은 셈이다.
더군다나 이 글을 읽는 독자가 논문을 쓰지 않는 트랙을 밟는 다면 이쯤에서 흐믓한 
아빠미소를 한번 날리고 과감하게 지원버튼을 누르자!</p>
<p>안타깝게도 나는 논문트랙을 선택한 사람이다.
내가 회사를 다니면서 남몰래 기깔나는 아이디어라도 메모해뒀냐 하면 그건 아니고
전 글에서도 얘기했듯이 기왕이면 &#39;1종 운전면허&#39;를 원하는 사람이기 때문이기도 하지만
지금껏 살아오며 한 번도 나의 이름 석자를 내건 글을 써 본적이 없기 때문이다.</p>
<p>論文이라는 뉘앙스가 주는 압박감에 &#39;과연 내가 이걸 완성할 수 있을까? 그것도 회사를 다니면서?&#39;
라며 미리 겁먹을 필요는 없다. 졸업까지 2년반이나 남았는데 그 동안 뭐라도 쓰다보면 되지 않겠는가? 물론 3학기에 논문트랙이냐 아니냐를 선택할 수 있기 때문에, 첫 2학기까지는 시간적 여유가 있다 하지만 지레 겁 먹을 필요는 없다는 이야기이다.</p>
<p>논문을 쓴다는게 이렇게 어렵다면, 그것을 해냈을 때 얻을 수 있는 성취감은 얼마나 클 것이고
앞으로 살아가는데 나를 지탱해줄 하나의 커다란 기둥이 되어 줄 것이라 확신이 있는 만큼
&#39;논문 그게 <em><del>돈이 되겠어</del></em> 도움이 되겠어?&#39;하며 고민하기 보다는 일단 진행시켜 보자.</p>
<h2 id="면접">면접</h2>
<p>원서제출부터 서류합격까지는 일주일이 걸렸다.
또한, 서류합격으로부터 최종 합격까지는 일주일이 걸렸으니,
원서를 제출하고나서 이주간의 텀이 생긴다.
다시 말해서, <strong>서류합격부터 최종합격 통보를 받는 그 사이에 면접이 진행</strong>된다는 소리이며
아무래도 직장인이 다수인 사이버대학원 특성상, 면접은 <strong>해당 주말</strong>일 가능성이 높다.</p>
<h3 id="준비">준비</h3>
<p>원서를 넣고나면 면접 날짜가 잡히게 된다.
회사 면접이었다면, 기존의 경험과 데이터를 통해 뭐라도 준비해볼 텐데 
대학원 면접은 처음인지라 사실상 두손 놓고 있었다.</p>
<p>그렇게 하루이틀 지나가다 당일 아침에 부랴부랴 
<strong>1분 자기소개, 나의 장/단점, 지원 사유, 전공관련 지식, 졸업 후 계획</strong> 등
5가지 항목에 대해 예상 답변을 작성하고 정장을 꺼내두고 기다렸다.</p>
<p>면접은 오후 2시가 넘어서 예약이 잡혔는데, 전날에 근처 스터디카페 회의실이라도 예약하려 했지만
하루전에는 모든 방이 꽉차서 부득이하게 집에서 진행 했다.
만약 집안에서 면접을 진행하기 어려운 상황이라면 일주일 전에 미리 회의실 등을 예약해보자.
다만, 나 같은 경우에는 집에서 오전에 미리 환경을 세팅해두고 잠깐 낮잠을 자다가 옷을 갈아입고 면접에 임했다.</p>
<p>준비해둔 항목은 아래와 같다.
<em><strong>1. 면접 링크는 줌이었지만 구글미트로 미리 뒷 배경이나 화면의 선명도를 체크했다.
2. 카메라가 상체의 어디까지 나오는지, 돌발상황은 없는지 체크했다.
3. 듀얼모니터를 썼기 때문에 시선처리가 이상하지는 않은지 체크했다.
4. 방안의 밝기가 적절한지, 알람/소음을 차단 했는지 체크했다.</strong></em></p>
<h3 id="예비면접">예비면접</h3>
<p>오전 11시에는 예비면접을 진행한다.
예비면접이라고 해서 거창한 것은 아니고, 본인의 <strong>신분증</strong>을 지참하여
본인확인 절차를 진행하면 끝이다.
소리는 잘 들리는지, 마이크에 이상이 없는지를 확인 한뒤, 
카메라에 신분증을 보여준 후 종료하면 끝이다.</p>
<h3 id="본-면접">본 면접</h3>
<p>나의 순서는 마지막에서 두번째였는데, 각 회차당 면접 예상시간이 <strong>15~20분</strong>이었던대 반해
앞 차례에서 다소 지연이 되어 실제 면접 시간보다 40분정도 늦춰젔었다.
시간이 지연되기도 하였고 면접자가 얼마 남지 않은 상황이라 관리자분이 채팅으로 
마지막 회차와 함께 진행한다고 공지받았다.</p>
<p>다대다 면접은 오랜만이기도 하고, 전공 지식도 얄팍하여 5분내로 끝나는것이 아닌가 걱정되었다.
심지어 앞 회차는 약속된 시간을 넘길만큼 만은 질문세례를 받았다니 불안함이 더욱 엄슴했다.</p>
<p>교수님은 총 세분이서 들어오셨는데, 면접 분위기를 풀기 위해서 농담도 던지시면서 
밝게 유지하려 하셨으며 같이 면접에 참여하신 지원자분들 모두 연륜에서 뿜어져 
나오는 여유를 발휘하여 면접 분위기는 생각보다 부드러웠다.</p>
<p>전공지식에 대해서는 따로 질문이 들어오지는 않았다.
처음에는 준비했던대로 순서대로 1분간 자기소개를 했고, 
왜 지원했는지 현재는 어떤일을 하는지 등의 얘기가 오갔다.
&#39;아 이쯤이면 마무리겠구나&#39; 생각이 들때 즈음, 마지막에 나에대해서 질문이 들어왔는데 
<strong>논문을 쓰는 이유</strong>에 대해서였다. 
추측하건대 다른 지원자분들은 논문을 쓰지 않는 것으로 원서 작성시에 표기했나 보다. 
하지만 당황하지 않고 연구계획서에 작성한 내용에 대한 부가적인 답변을 드렸으나 
만족스러워 하시는 표정은 아니었기 때문에 면접이 종료된 후 저절로 한숨이 나왔다.</p>
<p>기억나는 상세 질문은 아래와 같다.
<em><strong>1. 연구계획서에 작성한 내용에 대해 말해달라.
2. 해당 연구를 하고자 하는 이유는?
3. 해당 연구가 우리 과목과 얼마나 밀접한 관계가 있는지?
4. 어떠한 방식으로 연구를 진행할 것인지?</strong></em></p>
<p>면접에서 합격문자를 받고나면 등록금을 지불할 시기가 온다. <del>가슴에 비수가 날아와 꽂힌다.</del>
생각보다 등록금 지불 기한이 짧기 때문에, 핸드폰 문자와 이메일을 꼭 확인하여 기간을 놓치는 불상사를 피하도록 하자.</p>
<p>장학금 등의 요건을 잘 살펴보고 난뒤, 등록금을 무사히 지불하고 나면 
축하한다, 드디어 &#39;학생&#39;이라는 또 다른 신분을 얻게 되는 것이다.</p>
<p>다음 글에서는 첫 오리엔테이션과 입학전까지 준비한 사항에 대해 이야기 하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[개발자, 대학원에 가다 - 1]]></title>
            <link>https://velog.io/@shy_dd/%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%8C%80%ED%95%99%EC%9B%90%EC%97%90-%EA%B0%80%EB%8B%A4-1</link>
            <guid>https://velog.io/@shy_dd/%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%8C%80%ED%95%99%EC%9B%90%EC%97%90-%EA%B0%80%EB%8B%A4-1</guid>
            <pubDate>Sat, 14 Jan 2023 13:59:21 GMT</pubDate>
            <description><![CDATA[<h2 id="저런-어쩌다가">저런... 어쩌다가...</h2>
<p>적어도 내가 아는 한, 내 주위 웹 개발자 중에서 대학원을 나온 사람은 없다.
더군다나 지금도 입에 풀칠하며 먹고 사는데에 지장이 없는데 왜 
굳이 대학원엘 가려하나라며 핀잔만 받았지 나의 무대뽀 계획에 공감해 주는 이 또한 없었다.</p>
<p>그런대 왜 이제와서 대학원에 가려 하느냐 묻는다면, 사실 나도 모르겠다.
나는 그저 &#39;1종 따두면 언젠가는 쓸모가 있겠지&#39;하며 
2종 운전면허에는 눈길도 안줬 듯이, 설령 지난 십여년간 운전대를 잡아본 적이 
단 두번에 그칠지라도, 면허는 역시 1종이지 하는 그런 사람일 뿐이다.</p>
<p>그래서 막연히 스물언저리 대학을 졸업하기 이전부터 
&#39;서른전에는 대학원에 가야지...&#39; 하며 살아왔기에 맘 속에만 간직한 여러 생각을
가타부타 여기에 끄적이기 보다는 그저 언젠가는 그 쓰임새가 있겠지 하며 말을 줄이겠다.</p>
<h2 id="어느-대학원에-가야-하는가">어느 대학원에 가야 하는가?</h2>
<p>대학원에도 종류가 있다.
학문에 뜻이 있다면 사표를 던지고 일반대학원에 들어갈 수도 있겠지만,
그럴 강단도, 위대한 포부도 없는 소시민인 나는 소위 야간대학원이라 부르는 
특수 대학원에 입학 하려 했었다. 
3년전인 20년도부터 특수대학원에 대해 알음알음 알아보다가 점점 일이 바빠지기도 했고,
아직은 학업보다 경력을 더 쌓아야 할 시기였다고 생각하여 지지부진하다 어느덧 22년을 맞이했다.</p>
<p>22년은 개인의 인생에서도 개발자로서도 굉장히 위태로운 시기였다.
여자처자 많은 상처를 받으며 더 이상 키보드 만지지 않기로 결정했고 연말에 퇴사를 하게 된다.
그래서 퇴사하였으니 일반대학원에 가기로 했느냐면 그건 아니다.
결론적으로 회사는 나갔지만 아직도 개발자로 먹고 살고 있으며, 
한 동안 또 새로운 회사에 적응하느라 
대학원 생각은 저 멀리 고이 접어둔 꿈으로 대하며 살았다.</p>
<p>서론이 길었는데, 현재는 사이버대학원에 이름을 올려 둔 상태이다.
앞에서 일반대학원이니 특수대학원이니 얘기하다가 이 무슨 삼천포인가 할 수도 있지만,
그 나름의 이유가 있다.</p>
<p>우선, 일반대학원은 나 같이 회사도 다니고 싶고 공부도 하고 싶은 
욕심쟁이들에겐 요원한 일이니 여기에 적지 않겠다. (사실 애초에 많이 찾아보지 않아 잘 모른다.)</p>
<h3 id="특수대학원">특수대학원</h3>
<p>자, 그래드맵 되시겠다.
나 같이 시간없고(핑계) 의욕없는(핑계) 사람에게는 가히 보물지도라 할 수 있겠다.
<a href="http://www.gradmap.co.kr/">http://www.gradmap.co.kr/</a>
<img src="https://velog.velcdn.com/images/shy_dd/post/4f332dde-9f79-421b-8633-c8fe495001d5/image.png" alt=""></p>
<p>전국 특수대학원 전공학과 분포 &gt; 8 (전기,전자,컴퓨터)를 눌러보면
우리나라에 이렇게 많은 대학이 있었나 싶을 정도로 스크롤바가 한없이 작아지는 모습을 볼 수 있다.
<img src="https://velog.velcdn.com/images/shy_dd/post/89a898f8-f69a-4d81-88f0-e6de0a5046fd/image.png" alt=""></p>
<p>그 중 한 곳을 클릭해보면, 경쟁률이라던지 등록금과 같은 정보가 눈에 띈다.
<img src="https://velog.velcdn.com/images/shy_dd/post/54eb31bc-8287-45f5-8c74-4641c139e9f7/image.png" alt=""></p>
<p>아래처럼 도표로 나타나는 정보를 Notion이나 엑셀에 잘 정리해보자.
<img src="https://velog.velcdn.com/images/shy_dd/post/66f81db4-015d-411e-bfb6-f0fe8b7cc6e1/image.png" alt=""></p>
<p>당시에 내가 특수대학원을 선택하지 않은 이유는
학비가 6 ~ 900만원이라는 꽤 높은 가격도 있지만, 코로나로 인해 오프라인 수업을 진행하기
어렵다는 문제도 있었다. (당연히 학교마다 다름)</p>
<p><img src="https://velog.velcdn.com/images/shy_dd/post/452a1cef-3c33-4cb8-833b-34bffb3e6bb0/image.png" alt=""></p>
<h3 id="사이버-대학원">사이버 대학원</h3>
<p>그렇다고 해서 일년 더 미루면 끝도 없이 미뤄질까봐, 어차피 온라인으로 할거 사이버 대학원에 가기로 결정했다. 생각해보면, 캠퍼스에 다닐때는 대면수업이라고 공부를 열심히 했던가?
중요한건 얼굴을 마주보냐 아니냐 보다는 개인의 공부에 대한 의지에 달렸다고 본다.</p>
<p>그래서 이번에는 그래드맵에서 사이버 대학원 탭에 들어갔다.
상대적으로 사이버 대학원은 숫자가 더 적다. 따라서 나처럼 배Ο킨라빈Ο스처럼
많은 선택지에 고민하다 뒷사람이 발구르는 소리에 화들짝 놀라는 사람에겐 오히려
더 나을 수 있다.</p>
<p>하지만 대학원은 시간이 지난다고 녹아내리는 것도 아니요, 뒷사람이 재촉할 일도 없으니
시간을 들여 차분히 둘러보면서 대학원 홈페이지까지 가보자.</p>
<p>아뿔싸! 대학원만 선택하면 되는 줄 알았는데 학과에서 다시한번 선택을 해야한다.
심지어 나같은 까막눈에게는 애플민트냐 민트초코냐 하는 둘 다 민트라는 공통분모만 보일 뿐
그게 그거 같아 보이기도 또 아니기도 참 아리송하기만 할 뿐이다.
<img src="https://velog.velcdn.com/images/shy_dd/post/d29fd3d4-6453-4b4a-9a66-6e3134550420/image.png" alt=""></p>
<p>그래도 차분히 훑어보다보면 이 학과가 어떤 목적으로 개설됐고,
무엇을 중시하며 여기에 가면 이러한 것들을 배울 수 있겠구나 하는 정보를 얻을 수 있다.</p>
<p>다음 글에서는 대학원 원서를 내기까지 어떠한 준비를 했고, 원서부터 첫 오리엔테이션까지의 
이야기를 하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[제45회 SQLP 후기]]></title>
            <link>https://velog.io/@shy_dd/%EC%A0%9C45%ED%9A%8C-SQLP-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@shy_dd/%EC%A0%9C45%ED%9A%8C-SQLP-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Sun, 26 Jun 2022 06:13:43 GMT</pubDate>
            <description><![CDATA[<p>지난 5월28일에 치뤄진 45회 SQLP 시험 후기를 남긴다.
결과는 6월3일에 문자로 미리 받았는데, 사실 시험을 보면서 불합격이 예상되긴 했다.</p>
<p>실기를 풀면서 어떻게 풀어야할지 머릿속으로 그려지지 않던 것도 있고,
요구사항을 제대로 이해한 것이 맞는지 확신이 서지 않기도 했다.
실기 2문제 중, 1번 문제는 힌트와 쿼리 수정까지 했고, 2번 문제는 기억상
힌트만 적으라고 되어있어서 쿼리는 건드리지 않았다.</p>
<p><img src="https://velog.velcdn.com/images/shy_dd/post/ac225c29-573e-4c1e-9cfc-b34263f2dd69/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/shy_dd/post/688e3c74-793e-41f8-a797-54c64510d42a/image.png" alt=""></p>
<p>그래도 지난 2월17일 부터 5월 27일까지 공부하면서
스터디 방에 인원이 무려 48명이나 늘어났고 개인적으로도 총 268시간의 공부시간을 채웠다.
퇴근 후 평균 3시간씩 공부한 셈인데, 주말에는 하루종일 앉아있어도
순 공부 시간으로는 7시간을 채우면 새벽 1시를 넘어가곤 했다.</p>
<p><img src="https://velog.velcdn.com/images/shy_dd/post/4b3c1d8e-3fe3-4a24-8480-3bd4714de47f/image.png" alt=""></p>
<p>첫 술에 배부를 수는 없기 때문에,
그저 첫 SQLP를 도전하면서 꾸준한 공부 습관을 들일 수 있던 것에 만족한다.</p>
<p>앞으로도 책을 다독하며 SQLP에 계속 도전해 보려 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS RDS (MariaDB) Query Trace]]></title>
            <link>https://velog.io/@shy_dd/AWS-RDS-MariaDB-Query-Trace</link>
            <guid>https://velog.io/@shy_dd/AWS-RDS-MariaDB-Query-Trace</guid>
            <pubDate>Wed, 13 Apr 2022 12:31:55 GMT</pubDate>
            <description><![CDATA[<p>RDS를 사용한다면 파라미터 그룹을 통해 Slow Query에 대한 정보를 수집 할 수 있다.</p>
<p>아래와 같이 파라미터의 값을 수정한다.</p>
<pre><code class="language-SQL">Slow_query_log = 1
log_output = FILE (또는 TABLE)
long_query_time = 10 (초)
optimizer_trace = enabled=on
optimizer_trace_max_mem_size = 1048576
</code></pre>
<p><img src="https://velog.velcdn.com/images/shy_dd/post/678869ad-8442-44a4-9e9f-e15bde8e4d75/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/shy_dd/post/740c5f30-5f7d-400d-9caa-7b01a361529a/image.png" alt=""></p>
<p>파라미터 탭에서 로그 탭으로 이동한 뒤,
slow라고 검색하면 날짜별 버전별 로그파일이 생성된다.
2kb이상인 파일을 찾아보면 위 파라미터 long_query_time에서
설정한 것과 같이 약 12초 이상 걸린 쿼리가 나타난다. (SELECT 이하)</p>
<p><img src="https://velog.velcdn.com/images/shy_dd/post/7cc017f8-034d-4e92-acc4-d5e01a702311/image.png" alt=""></p>
<pre><code class="language-SQL">1. EXPLAIN SELECT ~~~~ FROM ~~~~~;

2. SELECT * FROM information_schema.optimizer_trace LIMIT 1;</code></pre>
<p>EXPLAIN을 SELECT 상단에 추가하여 위 쿼리를 실행하면
OPTIMIZER에 의해 선택된 키와 검토된 ROW의 수 등의 정보가 나온다.</p>
<p>2번 쿼리를 실행해보면 상세 정보가 JSON으로 출력되는 것을 확인 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/shy_dd/post/6860e47c-9623-4f0b-a201-04475196cedf/image.png" alt="">
출처: <a href="https://mariadb.com/kb/en/optimizer-trace-guide/">https://mariadb.com/kb/en/optimizer-trace-guide/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[실행계획]]></title>
            <link>https://velog.io/@shy_dd/%EC%8B%A4%ED%96%89%EA%B3%84%ED%9A%8D</link>
            <guid>https://velog.io/@shy_dd/%EC%8B%A4%ED%96%89%EA%B3%84%ED%9A%8D</guid>
            <pubDate>Sat, 12 Mar 2022 13:16:37 GMT</pubDate>
            <description><![CDATA[<h2 id="plan-table">Plan Table</h2>
<p>아래와 같이 실행계획을 확인해보고자 한다.</p>
<pre><code class="language-sql">EXPLAIN PLAN FOR
  SELECT *
  FROM   emp e, dept d
  WHERE  e.deptno = d.deptno
  AND    e.ename  = &#39;SMITH&#39;;</code></pre>
<p><img src="https://images.velog.io/images/shy_dd/post/ef1aef5a-21fe-4287-8d1e-fb6ea4c1ed37/image.png" alt=""></p>
<p>위 실행계획을 확인하기 위해서 Plan table을 생성하려고 했는데 실패했다.</p>
<pre><code class="language-sql">@$ORACLE_HOME/rdbms/admin/utlxpls.sql
/* 또는 아래와 같이 ?로 $ORACLE_HOME을 대체한다 */
@?/rdbms/admin/utlxpls.sql</code></pre>
<p><img src="https://images.velog.io/images/shy_dd/post/cc0f0da5-6d9f-4e33-8787-d3e5c8046872/image.png" alt=""></p>
<p><del>아마 이전에 이미 생성해두고 까먹었던가?</del></p>
<pre><code class="language-sql">SELECT *
FROM all_synonyms
WHERE synonym_name = &#39;PLAN_TABLE&#39;;</code></pre>
<p><img src="https://images.velog.io/images/shy_dd/post/270d2317-f38c-492f-b8cd-f2d990a3c686/image.png" alt=""></p>
<p>원인을 찾아보던 중, 9i 버전 이상에서는 DBMS_XPLAN 패키지를 사용하라는 문구를 발견하였으니,
해당 패키지를 사용하여 보자.</p>
<blockquote>
<p>From Oracle 9i onward, you should display execution plans using the DBMS_XPLAN package.
-- <a href="https://oracle-base.com/articles/8i/explain-plan-usage">ORACLE-BASE</a></p>
</blockquote>
<p><br/><br/><br/></p>
<h2 id="version-check">Version Check</h2>
<p>현재 오라클 버전을 확인하여 해당 문서를 찾아 DBMS_XPLAN 사용법을 확인해보자.</p>
<pre><code class="language-sql">select * from v$version;</code></pre>
<p><img src="https://images.velog.io/images/shy_dd/post/ee732b5f-5a42-4efb-b8a0-fcfb8efefd9d/image.png" alt=""></p>
<p><br/><br/><br/></p>
<h2 id="dbms_xplan">DBMS_XPLAN</h2>
<p><a href="https://docs.oracle.com/database/121/ARPLS/d_xplan.htm#ARPLS70124">Oracle Database Online Documentaion 12c Release 1 &gt; DBMS_XPLAN</a></p>
<h3 id="사용예시">사용예시</h3>
<p>위 문서에 나온 예제를 따라 실행계획을 확인해보자.</p>
<pre><code class="language-sql">SET LINESIZE 130
SET PAGESIZE 0
SELECT * FROM table(DBMS_XPLAN.DISPLAY);</code></pre>
<p><img src="https://images.velog.io/images/shy_dd/post/49b3cf85-8897-47fe-9ff1-b27348f5cb48/image.png" alt=""></p>
<h3 id="파라미터-사용">파라미터 사용</h3>
<p>여기에는 3개의 파라미터를 사용할 수 있다.</p>
<ul>
<li>Plan Table 명</li>
<li>statement_id (null인 경우 가장 마지막 실행계획)</li>
<li>Format option</li>
</ul>
<pre><code class="language-sql">/* SET STATEMENT ID*/

EXPLAIN PLAN SET statement_id = &#39;SI0001&#39; FOR
SELECT * FROM emp WHERE ename = &#39;KING&#39;;

SELECT * FROM table(DBMS_XPLAN.DISPLAY(&#39;PLAN_TABLE&#39;, &#39;SI0001&#39;)); </code></pre>
<p><img src="https://images.velog.io/images/shy_dd/post/ecc03349-2a60-490f-8d8a-6285a32c4a2f/image.png" alt=""></p>
<p>statement_id를 찾을 수 없는 경우, 아래와 같은 에러가 발생한다.
<img src="https://images.velog.io/images/shy_dd/post/177a3aa6-382d-4673-b908-9628925cba11/image.png" alt=""></p>
<pre><code class="language-sql">/* SIMPLE */
SELECT * FROM table(DBMS_XPLAN.DISPLAY(&#39;PLAN_TABLE&#39;, null, &#39;BASIC&#39;)); </code></pre>
<p><img src="https://images.velog.io/images/shy_dd/post/dc666548-21c4-4594-9d23-01149a3acd10/image.png" alt=""></p>
<p><br/><br/><br/></p>
<h3 id="병렬처리-실행계획">병렬처리 실행계획</h3>
<p>다음은 병렬처리에 대한 실행계획이다.</p>
<pre><code class="language-sql">ALTER TABLE emp PARALLEL;

EXPLAIN PLAN for
SELECT /*+ parallel(e 4)*/ 
count(*), min(sal), sum(sal)
FROM emp e, dept d
WHERE e.deptno = d.deptno
AND e.ename    =&#39;hermann&#39;
ORDER BY e.empno;


SET LINESIZE 130
SET PAGESIZE 0
SELECT * FROM table(DBMS_XPLAN.DISPLAY); </code></pre>
<p><img src="https://images.velog.io/images/shy_dd/post/d8408b10-2f50-4a04-8fa7-92a8f7e66254/image.png" alt=""></p>
<p>실제 예상과는 다르게 병렬처리가 되지 않은 것으로 보이는데, 추후에 좀 더 알아봐야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Transaction Isolation Level]]></title>
            <link>https://velog.io/@shy_dd/Transaction-Isolation-Level</link>
            <guid>https://velog.io/@shy_dd/Transaction-Isolation-Level</guid>
            <pubDate>Tue, 08 Mar 2022 13:29:57 GMT</pubDate>
            <description><![CDATA[<p>ANSI/ISO SQL 표준(SQL92)에 정의된 Transaction Isolation Level
<img src="https://images.velog.io/images/shy_dd/post/44f70061-c126-42e4-bc0b-abde9ce8f3c6/image.png" alt=""><strong><em>-SQL 전문가 가이드, 한국데이터산업진흥원 p.787</em></strong></p>
<p>각 레벨의 대한 간략한 설명은 아래와 같다.</p>
<ul>
<li><strong>Read Uncommitted</strong>: 
트랜젝션에서 처리중인 <em><strong>아직</strong></em> 커밋되지 않은 데이터를 다른 트랜젝션이 읽을 수 있음</li>
<li><strong>Read Committed</strong>:
트랜젝션이 <em><strong>커밋된</strong></em> 데이터만 다른 트렌젝션이 읽을 수 있음</li>
<li><strong>Repeatable Read</strong>: 
트랜젝션 내에서 2번 쿼리를 수행 시, 
첫 번째 쿼리에 있던 레코드가 <em><strong>사라지거나</strong></em> 값이 _<strong>변경되는 것을 방지</strong>_함</li>
<li><strong>Serializable Read</strong>:
Repeatable Read에 추가적으로 기존에는 없던 <em><strong>새로운 데이터가 읽히지 않음</strong></em></li>
</ul>
<p>각 레벨별 발생할  수 있는 현상은 아래와 같다.</p>
<ul>
<li><strong>Dirty Read</strong>:
다른 트랜젝션에 의해 수정됐지만 <em><strong>아직</strong></em> 커밋은 되지 않은 데이터를 읽음</li>
<li><strong>Non-Repeatable Read</strong>:
동일 트랜잭션 내에서 2번의 쿼리를 수행할 때,
그 사이에 다른 트랜젝션에 의한 변경 또는 삭제가 일어 남으로써
두 개의 쿼리가 _<strong>다른 결과</strong>_를 나타냄</li>
<li><strong>Phantom Read</strong>:
동일 트랜잭션 내에서 2번의 쿼리를 수행할 때,
처음 수행한 쿼리에서는 존재 하지 않던 _<strong>유령 레코드</strong>_가 두 번째 쿼리에서 읽힘</li>
</ul>
<p><br><br>
<br>
아래와 같이 테이블을 생성 후 데이터를 입력한다.
왼쪽 Transaction 1과 오른쪽 Transaction2를 순차적으로 실행 하였을 때,
5번과 8번의 결과를 알아보자.</p>
<p><img src="https://images.velog.io/images/shy_dd/post/4ddf7915-8cb0-4077-b812-58838b696c12/image.png" alt=""></p>
<p>1번 진행 
<img src="https://images.velog.io/images/shy_dd/post/bd7371ac-7005-4b01-bbb6-571c924b784c/image.png" alt=""></p>
<p>2번 진행
<img src="https://images.velog.io/images/shy_dd/post/e8870e5e-342b-431f-a3a8-e5a9d1a2f7a0/image.png" alt=""></p>
<p>3번 진행
<img src="https://images.velog.io/images/shy_dd/post/dde2299d-df92-48ae-a82a-0ef464143c3f/image.png" alt=""></p>
<p>4번 진행
<img src="https://images.velog.io/images/shy_dd/post/b093283d-c560-4f5a-b4f2-9150d0f028ba/image.png" alt=""></p>
<p>5번 진행
<img src="https://images.velog.io/images/shy_dd/post/1208390a-f1ec-493c-b9f5-71dfb8eb6009/image.png" alt=""></p>
<p>6번 진행
<img src="https://images.velog.io/images/shy_dd/post/fde7f522-9b34-4e63-b1d9-7676e69e79a2/image.png" alt=""></p>
<p>7번 진행
<img src="https://images.velog.io/images/shy_dd/post/0aecb07d-1157-41be-b0c9-18c2051dc763/image.png" alt=""></p>
<ul>
<li>Commit을 하게되면 트랜젝션이 종료되면서 설정한 Isolation level이 해제된다.
이에 따라 기본 레벨인 Read Committed로<br>변경되면서 Transaction 2에서 커밋된 변경 값을 읽게 된다.</li>
</ul>
<p>8번 진행
<img src="https://images.velog.io/images/shy_dd/post/8256ca4f-bc5c-472f-b6d6-dd2a332149b0/image.png" alt=""></p>
<p><em><strong>-SQL 자격검정 실전문제, 한국데이터진흥원 p.155 #27</strong></em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Update 튜닝 - Truncate & Insert]]></title>
            <link>https://velog.io/@shy_dd/Update-%ED%8A%9C%EB%8B%9D-Truncate-Insert</link>
            <guid>https://velog.io/@shy_dd/Update-%ED%8A%9C%EB%8B%9D-Truncate-Insert</guid>
            <pubDate>Sun, 06 Mar 2022 14:19:45 GMT</pubDate>
            <description><![CDATA[<h3 id="대량데이터를-update하기-위한-튜닝-기법을-알아본다">대량데이터를 update하기 위한 튜닝 기법을 알아본다.</h3>
<p>먼저, 샘플 데이터를 구해야 하는데 이번 예시에서는
아래 두 사이트중 owid의 covid-19 csv파일을 이용해 본다.</p>
<p><a href="https://www.data.go.kr/index.do">공공데이터포털</a> </p>
<p>Github: <a href="https://github.com/owid/covid-19-data/blob/master/public/data/README.md">owid/covid-19-data</a></p>
<p>위 사이트에서 받아온 csv 파일을 import 해보면 다수의 컬럼이 존재하는데,
이 중 iso_code, continent, location, date, total_cases만 이용해본다.</p>
<p><img src="https://images.velog.io/images/shy_dd/post/2279f501-c16d-4125-aebc-679a6c9dec68/image.png" alt=""></p>
<p>date 컬럼의 경우 컬럼 생성규칙에 맞춰 오른쪽과 같이 date_col로 변경한다.
<img src="https://images.velog.io/images/shy_dd/post/e130d9e6-13e8-4929-b929-c5a07735b784/image.png" alt=""></p>
<p>import가 문제 없이 되었다면 아래와 같이 약 16만건의 데이터가 있음을 확인 할 수 있다.
<img src="https://images.velog.io/images/shy_dd/post/f69f9a80-ac4e-4ce6-962d-c6f623b55518/image.png" alt=""></p>
<p>여기서, 모든 데이터의 Continent값을 World로 update 해보자.
<img src="https://images.velog.io/images/shy_dd/post/8b9f6b22-f212-4776-a718-99656dcad681/image.png" alt=""></p>
<p>일반적인 UPDATE문을 실행하는 경우,
실행계획에서 보여주듯이 2초 후반대를 기록했다. (<em>생성된 인덱스 없음</em>)
<img src="https://images.velog.io/images/shy_dd/post/30e0bb06-1ba3-479c-9bae-afc7a56fedc1/image.png" alt=""></p>
<p>반면, 아래와 같이 세단계로 작업을 진행한다면 대량 데이터에 대해서 
좀 더 우수한 성능을 보일 수 있다.</p>
<ol>
<li>테이블 복제 (CTAS)</li>
<li>데이터 비움 (Truncate)</li>
<li>변경된 데이터 삽입 (Insert)
<img src="https://images.velog.io/images/shy_dd/post/932cb03d-de16-4880-a279-e37fa24e0bf5/image.png" alt=""></li>
</ol>
<p>물론, 기존에 생성된 테이블 COVID_TEMP에 제약조건 또는 인덱스가 있는 경우
Truncate Table 전에 drop 해줘야 하는 것을 잊지 말자.</p>
<p>또한 Insert 이후에 적절한 제약조건과 인덱스를 추가하는 것이 좋다.</p>
<p>이번 테스트에서 아쉬운 점은 데이터가 16만건이 아니라 수 백만 건이었다면
성능 비교 부분에서 좀 더 확실하게 알 수 있었을 것이다.</p>
<p>또한 인덱스가 없이 테스트 하였기 때문에, 적당한 인덱스를 주고서도 테스트 했다면
결과에 달라짐을 비교해 보는 것도 유의미 했을 것이다.</p>
<p><em><strong>참고:  SQL 전문가 가이드, 한국데이터산업진흥원</strong></em> <em><strong>p.704</strong></em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SQL Optimizer - 실행계획과 비용]]></title>
            <link>https://velog.io/@shy_dd/SQL-Optimizer-%EC%8B%A4%ED%96%89%EA%B3%84%ED%9A%8D%EA%B3%BC-%EB%B9%84%EC%9A%A9</link>
            <guid>https://velog.io/@shy_dd/SQL-Optimizer-%EC%8B%A4%ED%96%89%EA%B3%84%ED%9A%8D%EA%B3%BC-%EB%B9%84%EC%9A%A9</guid>
            <pubDate>Tue, 01 Mar 2022 12:48:04 GMT</pubDate>
            <description><![CDATA[<p>옵티마이저는 최적의 실행계획을 선택하는 엔진이다.
그래서 여러 실행계획 후보군에서 가장 비용이 적은 것을 선택한다.</p>
<p>이를 확인해보기 위해 <strong>SCOTT</strong>계정으로 접속하여 아래와 같이 테이블을 생성한다.</p>
<blockquote>
<p>-- table t
CREATE TABLE t AS 
SELECT d.no, e.*
FROM emp e
, (SELECT ROWNUM no FROM DUAL CONNECT BY LEVEL &lt;= 1000) d;</p>
</blockquote>
<p>마찬가지로, 아래의 두 인덱스를 생성한다.</p>
<blockquote>
<p>-- index : t_x01 
CREATE INDEX t_x01 ON t(deptno, no);
<br>-- index : t_x02
CREATE INDEX t_x02 ON t(deptno, job, no);</p>
</blockquote>
<p>아래와 같이 실행하게되면 에러가 발생할 수 있다.</p>
<blockquote>
<p>-- 통계정보 수집
EXEC dbms_stats.gather_table_stats(user, &#39;t&#39;);
<br>-- 실행계획 확인
SET AUTOTRACE TRACEONLY exp;
<img src="https://images.velog.io/images/shy_dd/post/a9582019-fa61-4099-a363-663e6e3a7648/image.png" alt=""></p>
</blockquote>
<p>팝업 메시지에서 알려주는대로 <strong>SCOTT</strong> 계정에 권한을 주기로 한다.</p>
<p><strong>Admin</strong> 계정 접속</p>
<blockquote>
<p>GRANT SELECT_CATALOG_ROLE to SCOTT;
GRANT SELECT ANY DICTIONARY to SCOTT;</p>
</blockquote>
<p>다시, <strong>SCOTT</strong>으로 돌아온다.
<img src="https://images.velog.io/images/shy_dd/post/f83d4a94-725b-4955-bc66-c13a30073d16/image.png" alt=""></p>
<p>F10을 누르면 해당 쿼리가 실행되기 이전의 실행계획을 볼 수 있고
F6을 누르면 실제 결과가 실행되고 난 후의 정보를 볼 수 있다.</p>
<p><img src="https://images.velog.io/images/shy_dd/post/5f255d9d-2672-4bac-a57d-6e68daef5a7a/image.png" alt=""></p>
<p>위 그림은 각 쿼리별 COST를 보여준다.
여기서 COST는  I/O 횟수 또는 예상 소요시간과 같은 비용을 말한다.
3개의 쿼리 중, 가장 상위의 쿼리를 실행했을 때 채택된 인덱스가 
COST가 가장 적은 t_x01인 것을 보면 옵티마이저는 COST가 가장
적은 실행계획을 최적의 계획으로 뽑는다는 것을 알 수 있다.
<br><br></p>
<p><em><strong>-SQL 전문가 가이드, 한국데이터산업진흥원 p.489</strong></em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[집계함수에서의 NULL처리]]></title>
            <link>https://velog.io/@shy_dd/%EC%A7%91%EA%B3%84%ED%95%A8%EC%88%98%EC%97%90%EC%84%9C%EC%9D%98-NULL%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@shy_dd/%EC%A7%91%EA%B3%84%ED%95%A8%EC%88%98%EC%97%90%EC%84%9C%EC%9D%98-NULL%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Mon, 21 Feb 2022 13:59:18 GMT</pubDate>
            <description><![CDATA[<p>보통 CASE() 또는 NVL()에서 NULL을 0으로 변환하여 처리하려 하는데,
이는 자원낭비를 야기함으로 지양하는 것이 좋다.</p>
<p>그 이유는 아래와 같은 두가지 특성에 기인한다.</p>
<ul>
<li>CASE문 사용시 ELSE를 생략하면 Default값은 NULL</li>
<li>집계함수에서 NULL은 계산에 포함시키지 않음</li>
</ul>
<p>아래 두 예시를 보면,</p>
<blockquote>
<p>SUM (CASE MONTH WHEN 1 THEN SAL ELSE 0 END) </p>
</blockquote>
<blockquote>
<p> SUM(NVL (SAL,0))</p>
</blockquote>
<p>첫번째 구문은 ELSE로 NULL인 경우를 0으로 변환한 뒤,
SUM()에서 불필요한 0을 계산에 포함시키기 때문에 비효율적이다.</p>
<p>두번째 구문 또한 NULL을 일부로 0으로 변환하여
SUM()에서 연산시키고 있다.</p>
<p>따라서 집계함수를 사용할 때는, null처리 시 불필요한 연산을 하고 있지 
않은지 다시 한번 주의를 기울여야 한다.</p>
<p><em><strong>-SQL 전문가 가이드, 한국데이터산업진흥원 p.215</strong></em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[상호베타적(Exclusive-OR) 관계]]></title>
            <link>https://velog.io/@shy_dd/%EC%83%81%ED%98%B8%EB%B2%A0%ED%83%80%EC%A0%81Exclusive-OR-%EA%B4%80%EA%B3%84</link>
            <guid>https://velog.io/@shy_dd/%EC%83%81%ED%98%B8%EB%B2%A0%ED%83%80%EC%A0%81Exclusive-OR-%EA%B4%80%EA%B3%84</guid>
            <pubDate>Sat, 19 Feb 2022 13:37:43 GMT</pubDate>
            <description><![CDATA[<h2 id="--정의">- 정의</h2>
<p>아래와 같이 &quot;주문&quot;테이블이 있다고 할 때,
주문 테이블의 개인 / 법인번호 컬럼에는 
부모 테이블 &quot;개인고객&quot; 또는 &quot;법인고객&quot;으로 부터
상속받은 값 중 하나가 들어가게 된다.</p>
<p>이러한 관계를 상호베타적 관계라고 한다.
<img src="https://images.velog.io/images/shy_dd/post/6ed61521-9af2-4452-87be-6ac988c3f4c6/image.png" alt=""></p>
<h2 id="--table-생성">- Table 생성</h2>
<blockquote>
<p>-- Table 개인고객
CREATE TABLE &quot;개인고객&quot; 
   (&quot;개인번호&quot; VARCHAR2(10 BYTE), &quot;개인고객명&quot; VARCHAR2(20 BYTE)); <br>
-- Index 개인고객_PK
CREATE UNIQUE INDEX &quot;개인고객_PK&quot; ON &quot;개인고객&quot; (&quot;개인번호&quot;); <br>
-- Constraints for Table 개인고객
ALTER TABLE &quot;개인고객&quot; ADD CONSTRAINT &quot;개인고객_PK&quot; PRIMARY KEY (&quot;개인번호&quot;);</p>
</blockquote>
<hr>
<blockquote>
<p>-- Table 법인고객
CREATE TABLE &quot;SQLPADMIN&quot;.&quot;법인고객&quot; 
(&quot;법인번호&quot; VARCHAR2(10 BYTE), &quot;법인명&quot; VARCHAR2(20 BYTE));<br>
-- Index 법인고객_PK
CREATE UNIQUE INDEX &quot;법인고객_PK&quot; ON &quot;법인고객&quot; (&quot;법인번호&quot;); <br>
-- Constraints for Table 법인고객
ALTER TABLE &quot;법인고객&quot; ADD CONSTRAINT &quot;법인고객_PK&quot; PRIMARY KEY (&quot;법인번호&quot;);</p>
</blockquote>
<hr>
<blockquote>
<p>-- Table 주문
CREATE TABLE &quot;주문&quot; 
(&quot;주문번호&quot; VARCHAR2(7 BYTE), &quot;고객구분코드&quot; VARCHAR2(20 BYTE),
&quot;개인법인번호&quot; VARCHAR2(10 BYTE)); <br>
-- Index 주문_PK
CREATE UNIQUE INDEX &quot;주문_PK&quot; ON &quot;주문&quot; (&quot;주문번호&quot;); <br>
--  Constraints for Table 주문
ALTER TABLE &quot;주문&quot; ADD CONSTRAINT &quot;주문_PK&quot; PRIMARY KEY (&quot;주문번호&quot;);</p>
</blockquote>
<h2 id="--데이터-추가">- 데이터 추가</h2>
<blockquote>
<p>INSERT INTO &quot;개인고객&quot; (&quot;개인번호&quot;, &quot;개인고객명&quot;) VALUES (&#39;1234&#39;, &#39;홍길동&#39;);
INSERT INTO &quot;개인고객&quot; (&quot;개인번호&quot;, &quot;개인고객명&quot;) VALUES (&#39;1356&#39;, &#39;곽두팔&#39;);
INSERT INTO &quot;개인고객&quot; (&quot;개인번호&quot;, &quot;개인고객명&quot;) VALUES (&#39;2556&#39;, &#39;최민림&#39;); <br>
INSERT INTO &quot;법인고객&quot; (&quot;법인번호&quot;, &quot;법인명&quot;) VALUES (&#39;1122334455&#39;, &#39;주)카밀&#39;);
INSERT INTO &quot;법인고객&quot; (&quot;법인번호&quot;, &quot;법인명&quot;) VALUES (&#39;2233445566&#39;, &#39;주)해달&#39;); <br>
INSERT INTO &quot;주문&quot; (&quot;주문번호&quot;, &quot;고객구분코드&quot;, &quot;개인법인번호&quot;) VALUES (&#39;1100001&#39;, &#39;01&#39;, &#39;1234&#39;);
INSERT INTO &quot;주문&quot; (&quot;주문번호&quot;, &quot;고객구분코드&quot;, &quot;개인법인번호&quot;) VALUES (&#39;1100002&#39;, &#39;02&#39;, &#39;1122334455&#39;);
INSERT INTO &quot;주문&quot; (&quot;주문번호&quot;, &quot;고객구분코드&quot;, &quot;개인법인번호&quot;) VALUES (&#39;1100003&#39;, &#39;01&#39;, &#39;1356&#39;);
INSERT INTO &quot;주문&quot; (&quot;주문번호&quot;, &quot;고객구분코드&quot;, &quot;개인법인번호&quot;) VALUES (&#39;1100004&#39;, &#39;01&#39;, &#39;2556&#39;);
INSERT INTO &quot;주문&quot; (&quot;주문번호&quot;, &quot;고객구분코드&quot;, &quot;개인법인번호&quot;) VALUES (&#39;1100005&#39;, &#39;02&#39;, &#39;2233445566&#39;);</p>
</blockquote>
<hr>
<h2 id="--조회">- 조회</h2>
<p>이제 데이터가 준비 되었다면 UNION ALL 과 COALESCE를 통해 개인고객명을 조회해보자.</p>
<blockquote>
<p>-- UNION ALL <br>
SELECT B.개인고객명
&nbsp;&nbsp;&nbsp;&nbsp;  FROM 주문 A, 개인고객 B
WHERE A.주문번호 = 1100001
&nbsp;&nbsp;&nbsp;&nbsp;AND A.고객구분코드 = &#39;01&#39;  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-- 고객구분코드 &#39;01&#39;: 개인고객
&nbsp;&nbsp;&nbsp;&nbsp;AND A.개인법인번호 = B.개인번호
<br>UNION ALL <br>
SELECT B.법인명
&nbsp;&nbsp;&nbsp;&nbsp;FROM 주문 A, 법인고객 B
WHERE A.주문번호 = 1100001
&nbsp;&nbsp;&nbsp;&nbsp;AND A.고객구분코드 = &#39;02&#39;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-- 고객구분코드 &#39;02&#39;: 법인고객
&nbsp;&nbsp;&nbsp;&nbsp;AND A.개인법인번호 = B.법인번호;</p>
</blockquote>
<p><img src="https://images.velog.io/images/shy_dd/post/c248737c-e167-4355-a040-b7d4de5a09ce/image.png" alt=""></p>
<blockquote>
<p>-- COALESCE
-- 개인번호와 법인번호가 중복되지 않는다는 전제하에 <br>
SELECT COALESCE(B.개인고객명, C.법인명) 고객명
&nbsp;&nbsp;&nbsp;&nbsp;FROM 주문 A
LEFT OUTER JOIN 개인고객 B
&nbsp;&nbsp;&nbsp;&nbsp;ON (A.개인법인번호 = B.개인번호)
LEFT OUTER JOIN 법인고객 C
&nbsp;&nbsp;&nbsp;&nbsp;ON (A.개인법인번호 = C.법인번호)
WHERE A.주문번호 = 1100001;</p>
</blockquote>
<p><img src="https://images.velog.io/images/shy_dd/post/9c13a8bf-d9f4-4b97-976c-bd0eb5419e44/image.png" alt=""></p>
<p>위에서 보듯이, 있는 데이터를 조회한다면 둘 다 1건의 데이터를 잘 조회하고 있음을 알 수 있다.</p>
<p>반대로, <em><strong>없는 데이터를 조회한다면 어떤 결과가 나올까?</strong></em></p>
<blockquote>
<p>-- UNION ALL <br>
SELECT B.개인고객명
&nbsp;&nbsp;&nbsp;&nbsp;  FROM 주문 A, 개인고객 B
WHERE A.주문번호 = 1100009&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-- &#39;1100001&#39; --&gt; &#39;1100009&#39;
&nbsp;&nbsp;&nbsp;&nbsp;AND A.고객구분코드 = &#39;01&#39;<br>&nbsp;&nbsp;&nbsp;&nbsp;AND A.개인법인번호 = B.개인번호
<br>UNION ALL <br>
SELECT B.법인명
&nbsp;&nbsp;&nbsp;&nbsp;FROM 주문 A, 법인고객 B
WHERE A.주문번호 = 1100009&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-- &#39;1100001&#39; --&gt; &#39;1100009&#39;
&nbsp;&nbsp;&nbsp;&nbsp;AND A.고객구분코드 = &#39;02&#39;
&nbsp;&nbsp;&nbsp;&nbsp;AND A.개인법인번호 = B.법인번호;</p>
</blockquote>
<p><img src="https://images.velog.io/images/shy_dd/post/a636e546-c0ad-4fbb-9cd2-b938d0f5359a/image.png" alt=""></p>
<blockquote>
<p>-- COALESCE
-- 개인번호와 법인번호가 중복되지 않는다는 전제하에 <br>
SELECT COALESCE(B.개인고객명, C.법인명) 고객명
&nbsp;&nbsp;&nbsp;&nbsp;FROM 주문 A
LEFT OUTER JOIN 개인고객 B
&nbsp;&nbsp;&nbsp;&nbsp;ON (A.개인법인번호 = B.개인번호)
LEFT OUTER JOIN 법인고객 C
&nbsp;&nbsp;&nbsp;&nbsp;ON (A.개인법인번호 = C.법인번호)
WHERE A.주문번호 = 1100009;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-- &#39;1100001&#39; --&gt; &#39;1100009&#39;</p>
</blockquote>
<p><img src="https://images.velog.io/images/shy_dd/post/c8afca52-64ba-4d0e-8bac-f4c72187cef1/image.png" alt=""></p>
<p>두 쿼리 모두 동일한 값을 리턴하는 듯 보이지만
&lt;<em>SQL 전문가 가이드, 한국데이터산업진흥원</em>&gt; p.103에 따르면,
UNION ALL은 JOIN 결과가 없는 경우 공집합 NO ROWS를 출력하는 반면
COALESCE + LEFT OUTER JOIN은 NULL ROWS라는 1건을 출력한다고 한다.</p>
<h2 id="--검증">- 검증</h2>
<p>실제로 조회된 결과가 &#39;공집합&#39;인지 &#39;Null&#39;인지 <strong>NVL</strong> 함수를 사용하여 확인해보자</p>
<blockquote>
<p>SELECT NVL(U.고객명, &#39;NULL&#39;) AS 결과
&nbsp;&nbsp;&nbsp;&nbsp;FROM(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SELECT B.개인고객명 AS 고객명
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FROM 주문 A, 개인고객 B
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WHERE A.주문번호 = 1100009<br>   &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AND A.고객구분코드 = &#39;01&#39;
   &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AND A.개인법인번호 = B.개인번호<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UNION ALL<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SELECT B.법인명 AS 고객명
   &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FROM 주문 A, 법인고객 B
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WHERE A.주문번호 = 1100009<br>   &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AND A.고객구분코드 = &#39;02&#39;
   &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AND A.개인법인번호 = B.법인번호
   &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;) U;</p>
</blockquote>
<p><img src="https://images.velog.io/images/shy_dd/post/cee3cc72-9932-4008-9b04-72ab2c497168/image.png" alt=""></p>
<p>공집합은 NULL과 다르기 때문에 NVL함수에 걸리지 않아 공집합이 그대로 출력됨을 알 수 있다.</p>
<blockquote>
<p>SELECT NVL(COALESCE(B.개인고객명, C.법인명),&#39;NULL&#39;) 고객명
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FROM 주문 A
LEFT OUTER JOIN 개인고객 B
   &nbsp;&nbsp;&nbsp;&nbsp;ON (A.개인법인번호 = B.개인번호)
LEFT OUTER JOIN 법인고객 C
   &nbsp;&nbsp;&nbsp;&nbsp;ON (A.개인법인번호 = C.법인번호)
WHERE A.주문번호 = 1100009;</p>
</blockquote>
<p><img src="https://images.velog.io/images/shy_dd/post/3e8a1dc6-5da4-423d-8d83-bf620c7922c1/image.png" alt=""></p>
<p>실제 실습결과 마지막 쿼리에서 &#39;NULL&#39;이 출력되어야 할것으로 보이는데,
데이터 세팅이나 쿼리에 문제가 있는지 다시 한번 살펴보고 이후 수정하도록 하겠다.</p>
<p><img src="https://images.velog.io/images/shy_dd/post/1bc2c998-824e-463d-ba5d-10f161fee7c9/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[로우체이닝]]></title>
            <link>https://velog.io/@shy_dd/%EB%A1%9C%EC%9A%B0%EC%B2%B4%EC%9D%B4%EB%8B%9D</link>
            <guid>https://velog.io/@shy_dd/%EB%A1%9C%EC%9A%B0%EC%B2%B4%EC%9D%B4%EB%8B%9D</guid>
            <pubDate>Fri, 18 Feb 2022 13:28:12 GMT</pubDate>
            <description><![CDATA[<blockquote>로우체이닝이 발생할 정도로 한 테이블에 많은 칼럼들이 존재할 경우
조회성능저하가 발생할 수 있다. 트랜잭션이 접근하는 칼럼유형을 분석하여 
1:1로 테이블을 분리하면 디스크 I/O가 줄어들어 조회 성능을 향상 시킬 수 있다.
<br>--SQL 자격검정 실전문제 p.30, Q.45</blockquote>

<p>맨 처음 신입으로 입사할 당시, 
하나의 프로젝트를 구상부터 설계까지 한달간 해보았는데
당시 그렸던 ERD를 기술팀장님께서 보시곤 
&quot;<em><strong>가장 비용이 크게 드는게 DB I/O</strong></em>&quot; 라고 하셨던게 기억에 남는다.</p>
<p>45번 문제를 보고 당시 기억이 떠올라
해당 문제에 관한 해설을 주석과 함께 옮긴다.</p>
<hr>
<p><strong>Case:</strong>
한 테이블에 많은 컬럼들이 존재</p>
<ul>
<li><strong>Cause:</strong>
데이터가 물리적으로 저장되는 디스크 상에 넓게 분포할
가능성이 커지게 되어 디스크 I/O가 대량으로 발생할 수 있다.</li>
</ul>
<blockquote>
<p>컴퓨터 하드디스크도 데이터가 불연속적으로 저장되어, 즉 파편화가 발생하여
이를 추후 디스크 조각모음을 통해 공간을 확보하고 읽기 성능을 향상시켜줄 필요가 있는데, 이와 유사한 경우를 말하는 것 같다.</p>
</blockquote>
<ul>
<li><p><strong>Effect:</strong>
성능 저하 발생 (가능성 증가)</p>
</li>
<li><p><strong>Solution:</strong></p>
<ol>
<li>트랜잭션이 접근하는 칼럼유형을 분석</li>
<li>자주 접근하는 칼럼, 상대적으로 접근 빈도가 낮은 칼럼 구분하여 1:1로 테이블을 분리</li>
</ol>
<p>--&gt; 디스크 I/O가 줄어들어 성능을 향상 시킬 수 있다. </p>
<blockquote>
<p>정규화의 중요성이 다시 한번 드러난다.</p>
</blockquote>
</li>
</ul>
  <p>
  테이블 내에서 칼럼의 위치를 조정하는 것은 
  데이터 주로 채워지는 칼럼을 앞 쪽에 위치시키고,
데이터가 채워지지 않고 주로 NULL 상태로 존재하는 칼럼들을 뒤쪽에 모아둠으로써 
로우의 길이를 어느정도 감소시킬 수 있으나, NULL 상태이던 칼럼에 나중에 데이터가 채워지게 될 경우 더 많은 로우체인이 발생할 수도 있기 때문에 바람직한 해결책이라고 보기에 부족하다.
  <p>
  무엇보다도 데이터가 채워지지 않고 NULL 상태로 존재하게 되는 칼럼들이 많이 나타나는 경우는,
너무 많은 엔터티들에 무리하게 동질성을 부여하여 통합을 수행했거나, 예측하기 어려운 미래 시점을 겨냥하여 과도하게 의욕적으로 속성을 확장한 경우 등에서 주로 나타나기 때문에, 자주 사용되는 칼럼들이나 현시점에서 주로 사용되는 칼럼들을 한데 모으고, 사용빈도가 낮은 칼럼들이나 미래 시점에 사용될 것으로 예상되는 나머지 칼럼들을 한데 모아 별도의 1:1 관계 엔터티로 분리하는 등의 데이터모델 설계 수정을 고려해 보는 것이 좋다.


]]></description>
        </item>
        <item>
            <title><![CDATA[SQLP를 뽀개는 그날까지...]]></title>
            <link>https://velog.io/@shy_dd/SQLP%EB%A5%BC-%EB%BD%80%EA%B0%9C%EB%8A%94-%EA%B7%B8%EB%82%A0%EA%B9%8C%EC%A7%80</link>
            <guid>https://velog.io/@shy_dd/SQLP%EB%A5%BC-%EB%BD%80%EA%B0%9C%EB%8A%94-%EA%B7%B8%EB%82%A0%EA%B9%8C%EC%A7%80</guid>
            <pubDate>Thu, 17 Feb 2022 12:58:20 GMT</pubDate>
            <description><![CDATA[<h2 id="-계기">-계기</h2>
<p>지난 2년간 백엔드 개발자로 지내면서 
Query를 잘 짜는게 얼마나 중요한지 절실히 느낀적이 많다.</p>
<p>특히, 며칠간 기울어 가는 달과 함께 머리 싸매며 Javascript로 짠 함수를 
사수님이 기존 쿼리에 단 몇줄을 추가하여 구현해 낼 뿐만 아니라, 
더 나은 퍼포먼스를 보이는 것을 종종 보았는데
나름 SQLD를 딴 개발자로서, SQLD를 첫 개발관련 자격증으로 갖고 있는 만큼
애정이 깊은데 SQL과 이렇게 서먹하게 지내서야 되겠는가 하는 생각이 들었다.</p>
<p>어제부터 3차 백신을 맞고 누워지내면서 곰곰히 생각했는데, 
기왕이면 끝가지 가보자! 하는 생각이 머리를 떠나지 않았다. 
_&#39;기왕이면&#39;_이란 마인드로 운전면허도 1종으로 따지 않았는가?</p>
<p>그래도 SQLP를 따면 1종면허보다는 요긴하게 쓰이겠지...하며
덜덜 떨리는 팔을 붙잡고 케케묵은 이삿짐 박스 밑에서 
&lt;SQL 자격검정 실전문제&gt;를 3년만에 펴보았다.</p>
<p>(SQLD도 2번만에 겨우 땄는데...SQLP는..??)
<img src="https://images.velog.io/images/shy_dd/post/ebf9f89f-bafa-4747-adb5-17f6ff71f976/image.png" alt=""></p>
<p>3년만에 꺼냈는데 마치 <del>새것같다</del>!
<img src="https://images.velog.io/images/shy_dd/post/f1cdf47d-d4c7-4afa-8b7b-c6461eb818e2/image.png" alt=""></p>
<h2 id="-준비">-준비</h2>
<p>우선 목표는 9월4일까지 합격하는 것으로!
<img src="https://images.velog.io/images/shy_dd/post/8be8a515-ac11-4758-ba2b-bd1d554992d2/image.png" alt=""></p>
<p>SQLD 같은 경우 위 책 한권과 인터넷에서 구한 정리노트만 봐도 됐는데,
SQLP는 그 악명?만큼 봐야할 책도 만만치 않다...
심지어 자료도 거의 없다...</p>
<p>이게 다 얼마여 ㅜㅜ..... <del>책 이름마저 무시무시하다</del></p>
<p>공부 계획은</p>
<ol>
<li>SQL 자격검정 실전문제</li>
<li>SQL 전문가 가이드 (2020)</li>
<li>국가공인 SQLP 자격검정 핵심노트 -1</li>
<li>국가공인 SQLP 자격검정 핵심노트 -2</li>
<li>오라클 성능 고도화 원리와 해법 1</li>
<li>오라클 성능 고도화 원리와 해법 2
그 후 부족한 부분은 친절한, 불친절한 책을 보는 것으로 생각하고 있다.
<img src="https://images.velog.io/images/shy_dd/post/c2e6f0dc-193d-4869-8e70-a472b0f5ecc7/image.png" alt=""></li>
</ol>
<br>

<p>공부의지를 불태우기 위하여 오랜만에 열*타를 켜봤다.
(아무도 공부를 안하고 있구만....^^)
<img src="https://images.velog.io/images/shy_dd/post/4d9cdb67-728c-4011-ae01-2834a8bc676b/image.png" alt=""></p>
<p>sqlp를 같이 공부하기 위해서 sqlp 그룹을 찾아보려 했는데...없다..!
...그리고 없으면 만들어 쓰는 보통의 개발자1
(혹시라도 같이 공부하실 분이있다면 언제든, 누구나 환영입니다 ~~)
<img src="https://images.velog.io/images/shy_dd/post/dd5837a0-1956-4307-a528-f3ff42983fee/image.png" alt=""></p>
<p>자! 이제 마음의 준비는 다 됐으니 진짜로 공부를 해보자!
했는데 잠깐... 나 오라클 안깔려있잖아...?
오라클 부터 깔아보자 했는데 
<img src="https://images.velog.io/images/shy_dd/post/e086888d-d858-4585-8fb0-35034d1942ca/image.png" alt="">
진짜로 눈물이 나는지 한번 해보자</p>
<h2 id="-실습">-실습</h2>
<pre><code>_설치 관련해서는 구글에 검색해보면 좋은글이 많기 때문에 여기서 다루지는 않겠습니다._</code></pre><p>도커 설치...
<img src="https://images.velog.io/images/shy_dd/post/329d2694-c31e-4ebf-8f0a-2dda433b8b19/image.png" alt="">
오라클 설치...
<img src="https://images.velog.io/images/shy_dd/post/bce0c59d-de58-4700-b365-c5c45639a1cc/image.png" alt=""></p>
<p>SQL Developer까지 잘 설치가 되었다!
<img src="https://images.velog.io/images/shy_dd/post/406ee161-c2a4-47b8-9efc-6d0d3084b553/image.png" alt=""></p>
<p>그나저나ㅜㅜ 도커를 설치했더니 내 오래된 노트북이 너무 힘들어 했다...
<del>살려주세요...!</del>
Vmmem 관련 설정도 검색하면 간단히 해결 가능하다!
<img src="https://images.velog.io/images/shy_dd/post/d4ffd30c-eee2-48fe-b86c-c354508c9a1e/image.png" alt=""></p>
<h2 id="-마치며">-마치며</h2>
<p>어쨌거나 Docker로 Oracle 설치는 정말 간단하게 끝났다!
이것으로 실습 준비까지 마쳤으니, 앞으로는 진짜 열심히 공부하는 것만 남았다.</p>
<p>오늘의 공부 내용:
정규화, View, 로우 마이그레이션/체이닝, 슈퍼타입/서브타입
<img src="https://images.velog.io/images/shy_dd/post/b4d6ed56-0f9e-42d8-a322-ca2b4b72c4c7/image.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>