<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Powerful_Jin.log</title>
        <link>https://velog.io/</link>
        <description>안되면 되게 하고 싶은데 그러기까지 시간이 좀 걸리는 개발자</description>
        <lastBuildDate>Mon, 17 Jun 2024 07:56:21 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Powerful_Jin.log</title>
            <url>https://velog.velcdn.com/images/weirdcode_93/profile/9ea952c3-9942-43c3-9c57-8c1ed7ed5620/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Powerful_Jin.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/weirdcode_93" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[DB세상에 모두 좋은 것은 없다 - 두번째 포스트]]></title>
            <link>https://velog.io/@weirdcode_93/DB%EC%84%B8%EC%83%81%EC%97%90-%EB%AA%A8%EB%91%90-%EC%A2%8B%EC%9D%80-%EA%B2%83%EC%9D%80-%EC%97%86%EB%8B%A4-%EB%91%90%EB%B2%88%EC%A7%B8-%ED%8F%AC%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@weirdcode_93/DB%EC%84%B8%EC%83%81%EC%97%90-%EB%AA%A8%EB%91%90-%EC%A2%8B%EC%9D%80-%EA%B2%83%EC%9D%80-%EC%97%86%EB%8B%A4-%EB%91%90%EB%B2%88%EC%A7%B8-%ED%8F%AC%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Mon, 17 Jun 2024 07:56:21 GMT</pubDate>
            <description><![CDATA[<h1 id="산타-할아버지가-솔플이-가능한-이유">산타 할아버지가 솔플이 가능한 이유</h1>
<p>????: 무슬림, 힌두교, 기타 종교 아이들 재끼고 크리스찬 아이들만! 우는 아이들 재끼고 웃는 아이들만! 늦게 자는 아이들 재끼고 미라클 모닝 하는 아이들만 필터링하니 몇 명 안남 더군요?...</p>
<blockquote>
<p>그렇다 그는 전세계 모든 어린이들에게 선물을 줄 필요가 없었기 때문이다. 바로 <strong>&quot;선택도&quot;</strong>를 바탕으로 선물을 주는 쿼리를 짜고 실행하셨기 때문이지 후훗; 
산타할아버지는 사실 개쩌는 DB엔지니어였다는 거지</p>
</blockquote>
<p>그렇다 시작부터 그냥 대충 개소리를 느려놓으며 <strong>밀도와 선택도</strong>에 대한 이야기와 그 지표로 인덱스를 설정하는 방법에 대해 이야기할 것이다.</p>
<p><img src="https://velog.velcdn.com/images/weirdcode_93/post/9858d3b2-0a5f-47e6-ac93-aa5ec64af1c2/image.png" alt=""></p>
<h2 id="1-밀도density">1. 밀도(density)</h2>
<h3 id="1-정의">1) 정의</h3>
<blockquote>
<p>밀도는 테이블의 특정 컬럼이나 인덱스 키 값에 대한 고유 값의 분포를 나타내며, 쿼리 최적화 및 성능 튜닝에 중요한 역할을 합니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/weirdcode_93/post/9f8cf281-6572-4616-a55f-0783a662a1b5/image.png" alt="">
인덱스 키 값의 중복정도를 나타내기 때문에 계산법은 아래와 같다</p>
<h3 id="2-계산법">2) 계산법</h3>
<p>1 / 고유 값의 수
예) 탑, 미드, 정글, 원딜, 서포터 5개
포지션에 대한 밀도 1 / 5 = 0.2
이름에 대한 밀도 1 / 16 = 0.0625</p>
<p>밀도가 높을수록 효율이 적고
밀도가 낮을수록 효율이 높음</p>
<p>즉, 인덱스를 설정할 때에 밀도를 고려한다면 위와 같은 테이블은 이름 컬럼에 인덱스를 설정하는 것이 좋다.</p>
<p><img src="https://velog.velcdn.com/images/weirdcode_93/post/45c8e8d3-b84f-4d11-b95a-3a5c04d5fee4/image.png" alt=""></p>
<h2 id="2-선택도selectivity">2. 선택도(selectivity)</h2>
<h3 id="1-정의-1">1) 정의</h3>
<blockquote>
<p>특정 값이 전체 데이터에서 차지하는 비율, 쿼리 조건이 얼마나 <strong>선택적</strong>인지(즉, 얼마나 적은 수의 행을 반환하는지)를 의미한다. <strong>분포도</strong>라고도 불리어 진다.</p>
</blockquote>
<h3 id="2-계산법-1">2) 계산법</h3>
<p>위와 같은 테이블을 예시로 하였을 때 아래와 같다.
조건을 만족하는 행의 수 / 전체 행의 수</p>
<p>포지션에 대한 선택도
서포터: 6 / 16 = 0.375
원딜: 2 / 16 = 0.125
탑: 3 / 16 = 0.1875
정글: 4 / 16 = 0.25
미드: 2 / 16 = 0.125</p>
<p>이름에 대한 선택도는 모두
 1 / 16 = 0.0625
 <strong>예시에서 알 수 있듯 컬럼자체가 아닌 쿼리의 조건을 기준으로 계산을 한다는 것이 Point!</strong></p>
<p>선택도가 높을수록(비율은 낮을수록) 효율이 높고
선택도가 낮을수록(비율은 높을수록) 효율이 낮음</p>
<p><strong>즉, 선택도를 고려하더라도 위와 같은 테이블은 이름 컬럼에 인덱스를 설정하는 것이 좋다.</strong></p>
<blockquote>
<p>그럼 여기서 드는 의문은 <strong>&quot;밀도와 선택도가 다르게 나온다면 그때는 어떤 지표를 따라서 인덱스를 설정하여야 하는가?&quot;</strong></p>
</blockquote>
<h2 id="3-밀도--선택도">3. 밀도 &lt; 선택도</h2>
<h3 id="1-인덱스와-선택도의-관계">1) 인덱스와 선택도의 관계</h3>
<p>예를 들어 아래와 같은 테이블이 있다고 가정해보자.
<img src="https://velog.velcdn.com/images/weirdcode_93/post/04203f81-5818-460d-9add-893f08eb8b85/image.png" alt=""></p>
<p>밀도 분석:
Department 열의 밀도: 1 / 고유 값의 수
(Sales, HR, IT) 고유 값의 수 = 3
밀도 = 0.333</p>
<p>Title 열의 밀도: 1 / 고유 값의 수
(Manager, Salesperson, Assistant, Developer) 고유 값의 수 = 4
밀도 = 0.25</p>
<p>선택도 분석:
Department = &#39;Sales&#39; 쿼리의 선택도: Sales의 행 수 / 전체 행 수
Sales의 행 수 = 3
전체 행 수 = 10
선택도 = 0.3</p>
<p>Title = &#39;Developer&#39; 쿼리의 선택도: Developer의 행 수 / 전체 행 수
Developer의 행 수 = 4
전체 행 수 = 10
선택도 = 0.4</p>
<p>만일 밀도만을 생각하여 Title 컬럼에 인덱스를 설정하였다면 발생하는 문제점</p>
<blockquote>
<ol>
<li>Title = &#39;Developer&#39; 쿼리의 선택도는 0.4, 이는 전체 행의 40%를 차지한다.(물론 Department컬럼에 Sales도 딱히 좋지 못함 0.3이므로) 통상적으로 <strong>선택도가 10%</strong>가 넘어간다면 인덱스를 통한 방식보다 <strong>테이블 스캔</strong>이 더 빠르다.</li>
<li>쿼리 최적화기라는 쿼리의 효율을 높히기 위해 실행 계획을 세우는 SQL Server 구성 요소가 있는데 이 친구가 해당 인덱스가 비효율적인 것을 인지하지 못하고 잘못된 쿼리를 선택할 수 있다.</li>
</ol>
</blockquote>
<p><strong>결론은 인덱스를 설정할 때는 밀도와 함께 선택도, 쿼리 패턴, 데이터 업데이트 빈도 등을 종합적으로 고려해야 한다.</strong></p>
<h3 id="2-이상적인-인덱스-컬럼은">2) 이상적인 인덱스 컬럼은?</h3>
<p><img src="https://velog.velcdn.com/images/weirdcode_93/post/c5c4ee4e-abdd-4c3a-8b93-42faaaf13cbb/image.png" alt=""></p>
<p>이 과정에서 우리는 한 가지 알 수 있는 것이 생겼다. 인덱스는 다른 어떠한 컬럼과도 아무런 연관이 없는 것이 좋다는 것이다. 그렇지만 이러한 방법 역시 만병통치약은 아니란 것이 앞선 포스트에서 얘기하였듯이 컬럼 하나를 따로 뽑아야 할 것이기 때문에 저장 공간을 차지하기 때문에 별도의 유지비용이 든다. 그리고 다른 컬럼들이 무수히 더 추가되기 시작하면 저 하나의 ID컬럼 만으로는 감당이 안될 수도 있는 괴물 같은 쿼리가 만들어 질 수도 있다.</p>
<p>다시 한번 되새기자.</p>
<h3 id="db세상에-모두-좋은-것은-없다">DB세상에 모두 좋은 것은 없다.</h3>
<p>만약 위와 같은 상황들이 생긴다면 DB를 많이 다뤄본 고수님들은 아시겠지만 테이블을 나누어 관리하거나 상황에 맞게 여러가지 대처를 할 것이다. 그래서 정원혁 선생님께서도 뭐 이런 기타 여러가지 대응을 하기 위해서는 <strong>비즈니스 로직을 이해</strong>하고 <strong>공부</strong>하는 것이 최우선이라고 하였다!!</p>
<p>이상, 다음 포스트는 인덱스를 설정한 테이블에 DML을 쓰게되면 벌어지는 일에 대해 작성해보겠다!
오늘도 DB 골머리를 앓고 있을 개발자들 파이팅!!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DB세상에서 모두 좋은 것은 없다.]]></title>
            <link>https://velog.io/@weirdcode_93/DB%EC%84%B8%EC%83%81%EC%97%90%EC%84%9C-%EB%AA%A8%EB%91%90-%EC%A2%8B%EC%9D%80-%EA%B2%83%EC%9D%80-%EC%97%86%EB%8B%A4</link>
            <guid>https://velog.io/@weirdcode_93/DB%EC%84%B8%EC%83%81%EC%97%90%EC%84%9C-%EB%AA%A8%EB%91%90-%EC%A2%8B%EC%9D%80-%EA%B2%83%EC%9D%80-%EC%97%86%EB%8B%A4</guid>
            <pubDate>Tue, 11 Jun 2024 01:27:28 GMT</pubDate>
            <description><![CDATA[<h3 id="feat-디플러스-대표-정원혁-선생님의-sql-server-강의를-듣고서-선생님이-하신-말씀-중에-가장-뜻-깊은-얘기를-이번-포스트의-제목으로-삼아-보았다">feat. 디플러스 대표 정원혁 선생님의 SQL Server 강의를 듣고서... 선생님이 하신 말씀 중에 가장 뜻 깊은 얘기를 이번 포스트의 제목으로 삼아 보았다.</h3>
<p>현재 여러 IT 회사를 다니면서 DB에 대한 문제해결 및 솔루션을 제시해주는 일을 하고 다니신다고 하셨다.
<img src="https://velog.velcdn.com/images/weirdcode_93/post/d826fbfb-88e8-4ea4-96b4-3e93b51a7bfc/image.png" alt=""></p>
<h1 id="아이유가-단발이-이쁘니까-나도-이쁠꺼얌">아이유가 단발이 이쁘니까 나도 이쁠꺼얌!</h1>
<h3 id="그렇다-단발이-모두에게-좋은-스타일은-아니다-이처럼-db세상에도-모두-좋은-것은-없다-이를-테면-인덱스랄까">그렇다. 단발이 모두에게 좋은 스타일은 아니다... 이처럼 DB세상에도 모두 좋은 것은 없다... 이를 테면 인덱스랄까;</h3>
<p><del>와 근데 아이유 진짜 졸라 이뿌다...</del></p>
<h2 id="1-인덱스란">1. 인덱스란</h2>
<h3 id="1-정의">1) 정의</h3>
<blockquote>
<p>데이터베이스 테이블에서 하나 이상의 컬럼에 대한 값의 순서와 위치를 미리 저장한 데이터 구조로, <strong>빠르고 효율적인 검색 작업</strong>을 지원합니다. 인덱스는 <strong>책의 색인처럼 작동</strong>하며, 특정 데이터를 빠르게 찾을 수 있도록 도와줍니다.</p>
</blockquote>
<p>정의와 동시에 인덱스의 장점이라고 볼 수 있는 내용이다. 테이블의 검색 성능을 향상시키고 책의 색인(목차)처럼 데이터가 정렬되어 있어서 데이터의 순서와 위치를 미리 저장해 두고 찾아 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/weirdcode_93/post/b6abf38e-ab4b-425a-8a49-57f4e431ba19/image.png" alt=""></p>
<h3 id="2-단점">2) 단점</h3>
<blockquote>
<p>1, 저장 공간 사용량이 증가한다.
2, DML 성능 저하의 가능성이 있다.
3, 인덱스가 많아질수록 데이터 구조가 복잡해진다.</p>
</blockquote>
<p>크게 꼽자면 위의 세 가지가 가장 큰 단점이라고 볼 수 있다. 자세한 내용은 아래 인덱스에는 어떠한 종류들이 있고 어떠한 장·단점과 적재적소에 써야하는지 알아보도록 하자.</p>
<h3 id="우선-그-전에-우리는-데이터베이스에서-데이터의-구조가-테이블에-어떠한-형태로-들어가-있는지-파악하여야-한다">&quot;우선, 그 전에 우리는 데이터베이스에서 데이터의 구조가 테이블에 어떠한 형태로 들어가 있는지 파악하여야 한다.&quot;</h3>
<p><img src="https://velog.velcdn.com/images/weirdcode_93/post/bb01f25a-c8a0-447b-b74d-7e37d868e21d/image.png" alt=""></p>
<h2 id="2-인덱스-구조feat-b-tree">2. 인덱스 구조(feat. B-Tree)</h2>
<blockquote>
<p>인덱스는 B-Tree(Balanced-Tree) 구조로 구성되어 있다. 말 그대로 <strong>뿌리, 가지, 잎</strong> 순으로 균형잡힌 구조를 띄는데 이는 마치 나무를 거꾸로 뒤집어 놓은 모양이라고 생각하면 편할거 같다. 여기서 실제 데이터는 <strong>Data Level</strong>에 존재하게 된다.</p>
</blockquote>
<p>실제로 테이블 인덱스가 담겨 있는 말단 페이지 하나를 Leaf Page 혹은 Leaf Node라고 하고 본인은 이하 본문 부터 Page라는 용어로 통일하도록 하겠다.
<del>해서 이 인덱스 구조에서 실제 데이터가 어느 페이지에 있는지 그리고 해당 데이터를 물리적 or 논리적으로 어떻게 검색하는지 에 따라 인덱스의 종류가 나뉘어지느...ㄴ 솰라솰라 ㄴ어ㅜ얼ㄴ풔</del></p>
<h3 id="결론-인덱스는-크게-보면-두-가지다">결론, 인덱스는 크게 보면 두 가지다.</h3>
<h2 id="3-인덱스의-종류">3. 인덱스의 종류</h2>
<h3 id="1-클러스터-인덱스-clustered-index">1) 클러스터 인덱스 (Clustered Index)</h3>
<p><img src="https://velog.velcdn.com/images/weirdcode_93/post/b017931c-449a-417f-a580-7a25e779bd4c/image.png" alt="">
사진출처) <a href="https://gwang920.github.io/database/clusterednonclustered/">https://gwang920.github.io/database/clusterednonclustered/</a></p>
<blockquote>
<p>클러스터 인덱스의 가장 큰 특징은 그림과 같이 인덱스의 정보를 저장하는 가장 말단 끝 레벨인 <strong>Leaf Level</strong>에 테이블의 <strong>데이터(Data Page)</strong>가 <strong>함께 저장</strong>되어진다는 것이다.</p>
</blockquote>
<p>클러스터 인덱스가 설정되어 있는 테이블은 조회시 <strong>Table Scan</strong>이 아닌 <strong>Index Seek</strong>나 <strong>Index Scan</strong>이 발생하여 빠른 시간안에 찾고자 하는 데이터를 불러올 수 있다. (단, WHERE절에서 컬럼을 Index로 잡았다면)</p>
<p>데이터들의 주소 정보(키값)를 담고 있는 최종 레벨이 즉 Leaf Page가 Data Page와 동일하기 때문에 테이블 스캔을 할 필요 없이 인덱스 시크나 스캔만으로 빠르게 데이터를 훑어 찾아낼 수 있는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/weirdcode_93/post/ec0f4c27-3f83-419c-b1ee-ad406670b885/image.png" alt=""></p>
<h3 id="그래서-인덱스-씨크고-스캔이고-그게-뭔데">그래서 인덱스 씨크고 스캔이고 그게 뭔데?</h3>
<p>본인도 정원혁 선생님께서 테이블을 만들고 쿼리를 실행해주시면서 알려줬을 때 벙쪘었다... 따라서 3만건 정도의 테이블을 만들고 나서 실험해보니 알게되었달까?</p>
<h3 id="2-클러스터-인덱스-실행-테스트---index-seek">2) 클러스터 인덱스 실행 테스트 - Index Seek</h3>
<pre><code>CREATE TABLE Orders (
    OrderID INT PRIMARY KEY,
    CustomerID INT,
    OrderDate DATE,
    OrderAmount DECIMAL(10, 2)
);</code></pre><p>Orders 라는 테이블을 하나 만든다 참고로 PK를 지정하게 되면 테이블에 자동적으로 클러스터드 인덱스가 생성된다.</p>
<pre><code>DECLARE @i INT = 1;

WHILE @i &lt;= 30000
BEGIN
    INSERT INTO Orders (OrderID, CustomerID, OrderDate, OrderAmount)
    VALUES (
        @i,
        (ABS(CHECKSUM(NEWID())) % 1000) + 1, -- 1 ~ 1000 사이의 임의의 CustomerID
        DATEADD(DAY, @i % 365, &#39;2023-01-01&#39;), -- 임의의 OrderDate (2023년 내의 날짜)
        ROUND((ABS(CHECKSUM(NEWID())) % 10000) / 100.0, 2) -- 임의의 OrderAmount (0.00 ~ 100.00)
    );

    SET @i = @i + 1;
END;</code></pre><p>이렇게 3만건의 임의의 데이터를 만들어 넣어 주었다 여기까지 GPT형의 도움을 받아 빠르게 진행했다. 고마워요 GPT형~ 정원혁 선생님께서는 AI를 잘 활용하는 것이 더욱 미래지향적인 개발자라고 하셨다. 라고 변명변명~</p>
<p>각설하고 여기서 다음 두 개의 쿼리를 실행해보자</p>
<pre><code>-- 특정 OrderID에 대한 조회
SELECT * FROM Orders WHERE OrderID = 15000;

-- 특정 범위의 OrderDate에 대한 조회
SELECT * FROM Orders WHERE OrderDate BETWEEN &#39;2023-06-01&#39; AND &#39;2023-06-30&#39;;</code></pre><p><img src="https://velog.velcdn.com/images/weirdcode_93/post/90c92634-bca1-420a-b8e8-1b7d9ad6d11c/image.png" alt=""></p>
<p>우선, 해당 버튼을 클릭하여 실행 계획 창을 활성화 시켜주고 첫번째 쿼리를 실행하게 되면 아래와 같은 결과를 얻을 수 있다.
<img src="https://velog.velcdn.com/images/weirdcode_93/post/94c405f1-45b0-42e3-8bf0-cfab25e50a7e/image.png" alt="">
좌측은 쿼리 실행 메시지이고 우측은 실행 내용이다. 
 페이지 I/O가 2회 발생하였으며 이외에 동원된 자원은 없다. 여기서 &quot;실제 읽기&quot;란 디스크를 실제로 읽었다는 뜻이고 &quot;미리 읽기 읽기&quot; 란 (Read-Ahead Reads)로 순차적으로 데이터를 읽어야 하는 경우에 읽어야할 페이지를 미리 읽어 들여와 디스크에 I/O 지연을 줄여 성능을 높이는 방식이란 정도로만 알고 있으면 좋겠다. 사실 나도 잘 모른다...;;
 아무튼 3만건의 데이터 중 단일 데이터를 찾는데 3만건을 모두 훑지 않고 단 2회의 디스크 읽기로 찾아낸 셈이다. 난 감탄을 금치 못하였다. 
 이처럼 Index Seek는 인덱스의 특정 부분만 선택적으로 읽기 때문에 높은 선택도를 필요로 한다. 선택도는 쉽게 말해 해당 테이블에서 내가 찾는 행이 얼마나 &quot;고유한가&quot; 에 대한 얘긴데 자세한 얘기는 다음 포스트를 통해 정리하도록 하겠다. 
 성능이 아주 효율적으로 일어난 Index Seek의 예시이다.</p>
<h3 id="3-클러스터-인덱스-실행-테스트---index-scan">3) 클러스터 인덱스 실행 테스트 - Index Scan</h3>
<p>그렇다면 두번째 특정 범위를 검색하는 OrderDate에 대한 검색을 해보겠다.</p>
<pre><code>-- 특정 범위의 OrderDate에 대한 조회
SELECT * FROM Orders WHERE OrderDate BETWEEN &#39;2023-06-01&#39; AND &#39;2023-06-30&#39;;</code></pre><p><img src="https://velog.velcdn.com/images/weirdcode_93/post/52c56f89-3ec9-47cb-ba33-65277fb06570/image.png" alt="">
 페이지 I/O가 총 실제읽기 1회 + 미리 읽기 101회로 총 102회가 발생하였으며 앞선 Index Seek와는 다르게 미약하게나마 경과 시간이 더 걸렸다는 점을 알 수 있다.
 스캔 수1이 눈에 띄는 데 이는 OrderDate의 해당하는 범위에 인덱스를 스캔하였다는 뜻이다. 그로 인해 데이터를 순차적으로 읽어들여서 확인하여야하는 경우가 되어 버렸고 미리 읽기가 101회 발생, Index Scan이 일어났다고 볼 수 있는 것이다.
 다시 말해 낮은 선택도에 의해 쿼리 실행 속도가 조금은 느려졌다고 말할 수 있을 것이다. 물론 방금까지의 예시에서 느려진 정도로는 아무런 문제가 되지 않는다. 
 선생님께서는 선택도가 테이블의 전체행에서 <strong>1% 미만</strong>이면 쿼리 실행에 문제가 없을 거라고 하셨다. 클러스터 인덱스를 활용할 때에는 이 선택도에 대한 것을 잘 고려하여 써야 할 것이다.</p>
<h3 id="4-넌클러스터-인덱스-non-clustered-index">4) 넌클러스터 인덱스 (Non Clustered Index)</h3>
<p><img src="https://velog.velcdn.com/images/weirdcode_93/post/fd64a3d2-363d-4792-a2cc-a64f751bd338/image.png" alt="">
사진출처) <a href="https://gwang920.github.io/database/clusterednonclustered/">https://gwang920.github.io/database/clusterednonclustered/</a></p>
<blockquote>
<p>넌클러스터 인덱스는 <strong>Leaf Level과 Data Level이 분리</strong>되어 있고 Leaf Level에서 Data Page의 <strong>데이터 주소 정보</strong> 들고 있다.</p>
</blockquote>
<p>그림에서 Leaf Level의 201페이지에 1번 행을 보면 102:1이라는 주소 정보를 담고 있다. 해당 정보를 찾아 Data Page로 이동하면 102페이지에 1번행이 한국이라는 값을 얻을 수 있는 것이다.</p>
<h4 id="단-넌클러스터-인덱스는-반드시-클러스터-인덱스를-만들고-만들어야-한다">단, 넌클러스터 인덱스는 반드시 클러스터 인덱스를 만들고 만들어야 한다.</h4>
<p>클러스터 인덱스와 넌클러스터 인덱스를 만드는 쿼리와 방법은 <a href="https://learn.microsoft.com/ko-kr/sql/relational-databases/indexes/create-clustered-indexes?view=sql-server-ver16">공식 문서</a>가 있긴 하나...</p>
<p>선생님 피셜 공식문서는 설명이 개떡같다고 하셔..ㅅ ㄷ..;; 그래서 괜찮은 블로그를 소개해 볼까 한다.</p>
<blockquote>
<p>는 개뿔 <strong>GPT 형아</strong> 한테 물어보면 잘 알려준답...;</p>
</blockquote>
<h3 id="5-넌클러스터-인덱스-실행-테스트---look-up">5) 넌클러스터 인덱스 실행 테스트 - Look Up</h3>
<pre><code>-- 특정 CustomerID 대한 조회
SELECT * FROM Orders WHERE CustomerID = 500;</code></pre><p>이번엔 넌클러스터 인덱스로 설정한 CustomerID를 특정해서 조회해 본 결과
<img src="https://velog.velcdn.com/images/weirdcode_93/post/0a52bc22-64d2-40a1-b6d6-9937d22f270f/image.png" alt=""></p>
<h1 id="">?????????????????????????????????????</h1>
<p>웬 쌩뚱맞은 <strong>중첩 루프 조인</strong>과 <strong>룩업</strong>이라는 것이 나를 반겨 주었다... 아니 반겨준게 맞는건가 당황스럽다;
그런데 생각보다 이러한 과정이 일어난 이유는 간단했다.</p>
<blockquote>
<p>1, Index Seek =&gt; idx_CustomerID 인덱스를 활용하여 CustomerID = &#39;500&#39;으로 높은 선택도를 주었기에 Index Seek가 발생, 해당하는 OrderID를 조회한 임의에 테이블이 만들어짐
2, Look Up =&gt; Index Seek를 통해 찾은 OrderID로 나머지 데이터들을 실제 테이블에서 가져와 임의에 테이블이 만들어짐
3, 조인을 통해 두 임시 테이블을 내부 조인을 걸어 최종 쿼리 결과를 도출해냄</p>
</blockquote>
<p><em><strong>룩업을 통해 실제 테이블에서 데이터를 가져온다라는 뜻이 곧 Leaf Level과 Data Level이 분리되어 있다는 반증인 것이다!</strong></em></p>
<p>참고, 중첩 루프 조인은 조인중에 가장 흔해 빠진 방식이며 선행 테이블에서 도출해낸 검색결과를 반복해서 후행테이블에서도 검색하는 방식이다. 자세한 내용은 <a href="https://seokrae.gitbook.io/sr/database/database/2021-07-17-db-toc/2021-08-07-db-join">해당 블로그</a> 참조.</p>
<h3 id="6-넌클러스터-인덱스-실행-테스트---index-scan">6) 넌클러스터 인덱스 실행 테스트 - Index Scan</h3>
<pre><code>-- 특정 범위의 OrderDate에 대한 조회
SELECT * FROM Orders WHERE OrderDate BETWEEN &#39;2023-06-01&#39; AND &#39;2023-06-30&#39;;</code></pre><p>자 이번엔 앞서 클러스터 인덱스에서 테스트했던 특정 범위에 대한 조회를 해보겠다.
<img src="https://velog.velcdn.com/images/weirdcode_93/post/89f06f6d-edce-42ec-a864-fc0a26f4dbb5/image.png" alt="">
앞선 클러스터 인덱스의 Index Scan과 동일한 결과가 도출되었다. 이유는 OrderDate라는 컬럼은 클러스터와 넌클러스터 어느 인덱스로도 설정되지 않은 것이였고 데이터 조회를 위해 스캔이 불가피하다는 것이 동일한 결과를 불러온 것이다. </p>
<blockquote>
<p><strong>결론, 선택도가 높은 좁은 범위의 검색은 Look Up이 발생하고 선택도가 낮은 넓은 범위에서는 Index Scan이 발생한다.</strong></p>
</blockquote>
<p>선생님께서는 말씀하셨다. 테이블의 전체행에서 10%정도의 Size면 넌클러스터 인덱스를 활용해 볼만한 가치가 있다고 하셨다.</p>
<h2 id="4-비교-분석">4. 비교 분석</h2>
<p><img src="https://velog.velcdn.com/images/weirdcode_93/post/4c595c89-12cd-4106-acd1-4f4ff5ae1ff0/image.png" alt=""></p>
<h4 id="●-클러스터-인덱스는-리프-레벨과-데이터-레벨이-동일한-위치에-있고-넌클러스터-인덱스는-그렇지-않다">● 클러스터 인덱스는 리프 레벨과 데이터 레벨이 동일한 위치에 있고 넌클러스터 인덱스는 그렇지 않다.</h4>
<h4 id="●-클러스터-인덱스는-그로-인해-물리적인-성격으로-띄고-넌클러스터-인덱스는-look-up을-통해-데이터-페이지에-접근하기-때문에-논리적인-성격을-띈다">● 클러스터 인덱스는 그로 인해 물리적인 성격으로 띄고 넌클러스터 인덱스는 Look Up을 통해 데이터 페이지에 접근하기 때문에 논리적인 성격을 띈다.</h4>
<h4 id="●-size는-각각-1-10내의-조회량으로-예상될-때-활용하는-것이-효율적이다">● Size는 각각 1%, 10%내의 조회량으로 예상될 때 활용하는 것이 효율적이다.</h4>
<h4 id="●-높은-선택도를-가정하였을-때-스캔-방식은-각각-index-seek와-look-up으로-나뉜다">● 높은 선택도를 가정하였을 때 스캔 방식은 각각 Index Seek와 Look Up으로 나뉜다.</h4>
<h4 id="●-클러스터-인덱스는-인덱스-정보를-담을-별도의-공간이-필요하므로-저장-공간에-있어서는-넌클러스터-인덱스보다-불리하다">● 클러스터 인덱스는 인덱스 정보를 담을 별도의 공간이 필요하므로 저장 공간에 있어서는 넌클러스터 인덱스보다 불리하다.</h4>
<p>이상 지금까지 클러스터, 넌클러스터 인덱스에 대한 강의 내용을 공부하고 풀어보았다. 이처럼 인덱스란 장단점이 있기에 모든 테이블에 적용할 수 없고 비즈니스 로직을 잘 헤아려 보아야 한다. <strong>DB세상에 모두 좋은 것은 없다</strong> 라는 말을 단편적으로 이해해보는 시간을 가져 보았다. 아직 배운 내용이 엄청 많고 그에 비해 포스트해야 될 내용도 엄청 많다. 다음에는 이 인덱스를 만든 테이블에 <strong>DML</strong>를 쓰면 어떠한 영향을 미치는지, <strong>밀도와 선택도</strong>에 대한 이야기, <strong>Search Argument (SARG)</strong>를 통한 WHERE절 처리 방법 등을 정리해보겠다! <strong>DB세상에 모두 좋은 것은 없다</strong> 라는 말을 더욱이 깊게 느껴 질 수 있는 내용이 될 듯하다.</p>
<p>DB때문에 스트레스 받고있는 영혼들에게 조금이나마 도움이 되길 바라며 다들 파이팅!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[PTC Thingworx 커스텀CSS 갸꿀팁]]></title>
            <link>https://velog.io/@weirdcode_93/PTC-Thingworx-%EC%BB%A4%EC%8A%A4%ED%85%80CSS-%EA%BF%80</link>
            <guid>https://velog.io/@weirdcode_93/PTC-Thingworx-%EC%BB%A4%EC%8A%A4%ED%85%80CSS-%EA%BF%80</guid>
            <pubDate>Tue, 27 Feb 2024 03:14:35 GMT</pubDate>
            <description><![CDATA[<h1 id="ptc-thingworx의-custom-css-꿀팁-메모">PTC Thingworx의 custom css 꿀팁 메모</h1>
<h4 id="thingworx-web-component-sdk---polymer--문서-참고">ThingWorx Web Component SDK - Polymer =&gt; 문서 참고</h4>
<p><a href="https://support.ptc.com/help/thingworx_hc/visual_sdk/Web_Components/ptcs-style-unit/">Thingworx 컴포너트 SDK 문서 링크</a>
<img src="https://velog.velcdn.com/images/weirdcode_93/post/87df3404-e15a-4bc5-808b-50d5e2832364/image.png" alt=""></p>
<h4 id="thingworx에서는-위의-빨간-밑줄-같이-ptc전용-컴포넌트가-나올때는-일반적인-css로-셀렉팅을-하여-css문법을-사용할-수-없다">Thingworx에서는 위의 빨간 밑줄 같이 PTC전용 컴포넌트가 나올때는 일반적인 CSS로 셀렉팅을 하여 CSS문법을 사용할 수 없다.</h4>
<blockquote>
<p>ptcs-confirmation-dialog::part(dialog) {
    border-radius : 27px;
}
ptcs-confirmation-dialog::part(primary-button), ptcs-confirmation-dialog::part(cancel-button){
    border-radius : 27px;
    width: 160px;
    height: 54px;
    margin-left:6px;<br>}</p>
</blockquote>
<p>아래와 같이 컴포넘트를 아무 기호없이 선언하여 파트를 찾아가면서 대 =&gt; 소로 들어가여 셀렉팅을 하여야 한다. 역시 <a href="https://support.ptc.com/help/thingworx_hc/visual_sdk/Web_Components/ptcs-style-unit/">컴포넌트 SDK 문서</a>를 보면 아래와 같이
<img src="https://velog.velcdn.com/images/weirdcode_93/post/179c79d6-7119-4b58-88db-9bb024676d7e/image.png" alt="">
<img src="https://velog.velcdn.com/images/weirdcode_93/post/442577b6-7c5a-4e89-9a95-0b077307de8b/image.png" alt="">
part부분과 subcomponents 부분을 확인하고 셀렉터를 작성하여 css를 작성할 수 있다.</p>
<p>한가지 예외가 있다면 바로 wc라고 하는 shadow DOM 컴포넌트를 지정하는 구분자가 나오는데 개발자 노트에서 내가 css를 작성해야할 셀렉터에 해당 구분자가 있다면 일반적인 방법으론 css를 작성할 수 없다.</p>
<p>따라서 이제 사장된 방법이라고 하는 @support라고 하는 문법을 써서 css 를 작성해야 한다.</p>
<blockquote>
<p>@supports (ptcs-style-unit: &quot;PTCS-GRID:grid-control:data-filter&quot;){
    [part~=top-bar]{
        padding: 0 !important;
    }
}</p>
</blockquote>
<p>위는 그 방법의 예시이다.</p>
<p>ptcs-style-unit이라는 컴포넌트인데 이 컴포넌트는 @support 문법으로 작성하여야지 css 작성이 가능하다.
<img src="https://velog.velcdn.com/images/weirdcode_93/post/cd4047c7-9e9a-43bb-a63a-a29ecc56c735/image.png" alt="">
 위의 컴포넌트의 css를 작성할 때 쓴 예시이다. wc 구분자로 나타나 있는 &quot;PTCS-GRID:grid-control:data-filter&quot;를 해당 위치에 넣고 아래에는 개발자 노트에 나와있는 part명들을 작성하여 css 작성을 마무리하면 된다. 간혹 적용하여도 바뀌지 않을 때가 있는데 이는 내가 작성하고 있는 custom css보다 상위에 css 파일에서 작성된 문법이 이미 존재하여 적용이 되지 않는 것이므로 !important를 써 css 적용 우선순위를 높여주면 된다.</p>
]]></description>
        </item>
    </channel>
</rss>