SELECT * FROM DBA_PROFILES;

PROFILE|RESOURCE_NAME            |RESOURCE_TYPE|LIMIT    |
-------+-------------------------+-------------+---------+
DEFAULT|COMPOSITE_LIMIT          |KERNEL       |UNLIMITED|
DEFAULT|SESSIONS_PER_USER        |KERNEL       |UNLIMITED|
DEFAULT|CPU_PER_SESSION          |KERNEL       |UNLIMITED|
DEFAULT|CPU_PER_CALL             |KERNEL       |UNLIMITED|
DEFAULT|LOGICAL_READS_PER_SESSION|KERNEL       |UNLIMITED|
DEFAULT|LOGICAL_READS_PER_CALL   |KERNEL       |UNLIMITED|
DEFAULT|IDLE_TIME                |KERNEL       |UNLIMITED|
DEFAULT|CONNECT_TIME             |KERNEL       |UNLIMITED|
DEFAULT|PRIVATE_SGA              |KERNEL       |UNLIMITED|
DEFAULT|FAILED_LOGIN_ATTEMPTS    |PASSWORD     |10       |
DEFAULT|PASSWORD_LIFE_TIME       |PASSWORD     |180      |
DEFAULT|PASSWORD_REUSE_TIME      |PASSWORD     |UNLIMITED|
DEFAULT|PASSWORD_REUSE_MAX       |PASSWORD     |UNLIMITED|
DEFAULT|PASSWORD_VERIFY_FUNCTION |PASSWORD     |NULL     |
DEFAULT|PASSWORD_LOCK_TIME       |PASSWORD     |1        |
DEFAULT|PASSWORD_GRACE_TIME      |PASSWORD     |7        |

PASSWORD_LIFE_TIME 에 설정된 값이 다가와서 뜨는 경고

 

개인적으로 사용하는 로컬에선  불필요하므로 변경하는 것이 편리하다.

ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED;

 

 

'개발 > 오라클 SQL' 카테고리의 다른 글

ORDER BY 다루기  (0) 2022.12.26
NULL을 다룰 때 주의할 점  (0) 2022.12.23
문자열 다루기 핵심 TRANSLATE  (1) 2022.11.25
IN, NOT IN, EXISTS, NOT EXISTS  (0) 2022.11.10
마이바티스 null 체크  (0) 2022.09.28

아래 결과를 이해하는 것이 목표

SELECT DATA
     , TRANSLATE (DATA, ' 1234567890', ' ') AS "숫자만제거"
     , TRANSLATE (DATA, '1234567890'||DATA, '1234567890') AS "숫자만남기기"
     , TRANSLATE (LOWER(DATA), ' abcdefghijklmnopqrstuvwxyz' , ' ' ) AS "문자만제거"
     , TRANSLATE (LOWER(DATA), 'abcdefghijklmnopqrstuvwxyz'||DATA , 'abcdefghijklmnopqrstuvwxyz' ) AS "문자만남기기"
FROM (SELECT EMPNO||ENAME||HIREDATE AS DATA  
        FROM EMP )

 

 

DATA 숫자만제거 숫자만남기기 문자만제거 문자만남기기
7698BLAKE81/05/01 BLAKE// 7698810501 769881/05/01 blake
7782CLARK81/06/09 CLARK// 7782810609 778281/06/09 clark
7566JONES81/04/02 JONES// 7566810402 756681/04/02 jones
7902FORD81/12/03 FORD// 7902811203 790281/12/03 ford
7369SMITH80/12/17 SMITH// 7369801217 736980/12/17 smith
7499ALLEN81/02/20 ALLEN// 7499810220 749981/02/20 allen
7521WARD81/02/22 WARD// 7521810222 752181/02/22 ward
7654MARTIN81/09/28 MARTIN// 7654810928 765481/09/28 martin
7844TURNER81/09/08 TURNER// 7844810908 784481/09/08 turner
7900JAMES81/12/03 JAMES// 7900811203 790081/12/03 james
7934MILLER82/01/23 MILLER// 7934820123 793482/01/23 miller
7876ADAMS87/05/23 ADAMS// 7876870523 787687/05/23 adams
7788SCOTT87/04/19 SCOTT// 7788870419 778887/04/19 scott
1111YODA81/11/17 YODA// 1111811117 111181/11/17 yoda
7839KING81/11/17 KING// 7839811117 783981/11/17 king

 

TRANSLATE 만 이해하면 문자열 다루는 함수는 거의 다 이해한 것이나 다름이 없다.

 

 

 

 

TRANSLATE( 데이터, 검색문자 , 치환문자)

 

문제

SELECT TRANSLATE ('abcde', '1234567890', ' ')  FROM DUAL

정답은?

'abcde' 그대로 출력된다. '1234567890' 로 검색해서 일치하는 것이 없기 때문이다.

 

문제

SELECT TRANSLATE ('12345', '1234567890', '12345') A
     , TRANSLATE ('12345', '1234567890', '67890') B
  FROM dual

A,B 결과는?

여기서 중요한 것은 A도 똑같지만 특히 B가 왜 '67890'이라는 결과가 나왔는지다

 

'1234567890' 에서 '67890' 이 있으니까 '67890' 나왔다고 생각했다면 틀렸다.

SELECT TRANSLATE ('12345', '1234567890', '12345') A
     , TRANSLATE ('12345', '1234567890', '67890') B
     , TRANSLATE ('12345', '1234567890', '0000067890') C
  FROM dual

 C가 어떤 결과가 나올 것 같은가?

답은 '00000' 이다. 

즉, 앞서 A 도 '12345' 가 '12345' 가 그대로 출력된게 아니라 치환되어 '12345'가 된것으로 값이 잘 맞아 떨어져 그대로 출력하게 된 것 처럼 보인 것이다.

즉, A, B 다 치환된 결과이다.

 

 

'1234567890'  검색문자가 어떻게 치환되는지 알아보자

문제

SELECT TRANSLATE ('54321', '1234567890', '12345') A
     , TRANSLATE ('54321', '1234567890', '67890') B
     , TRANSLATE ('54321', '1234567890', '0000067890') C
  FROM dual

단순히 데이터를 '12345'에서 '54321' 로 변경했다. 결과가 어떻게 나올까?

아마도 B에서 복잡하게 느껴지는 사람이 많을 것 같다. 따라서 B를 기준으로 설명하겠다.

여기서 핵심은 검색문자와 치환문자 변경 규칙은 위치, 즉 인덱스에 정확히 대응된다는 것을 염두하자

 

1회차 TRANSLATE ('54321', '1234567890', '67890')   ===>  '04321'

데이터 첫번째 문자 '5' 를 검색문자에 같은 '5'가 존재한다. 치환문자에 같은 위치 '0' 으로 치환된다.

2회차 TRANSLATE ('04321', '1234567890', '67890')   ===>  '09321'

데이터 두번째 문자 '4' 를 검색문자에 같은 '4'가 존재한다. 치환문자에 같은 위치 '9' 으로 치환된다.

똑같이 5회차까지 진행되서 '09876' 이 나온 것이다. 

SELECT DATA
     , TRANSLATE (DATA, ' 1234567890', ' ') AS "숫자만제거"
     , TRANSLATE (DATA, '1234567890'||DATA, '1234567890') AS "숫자만남기기"
     , TRANSLATE (LOWER(DATA), ' abcdefghijklmnopqrstuvwxyz' , ' ' ) AS "문자만제거"
     , TRANSLATE (LOWER(DATA), 'abcdefghijklmnopqrstuvwxyz'||DATA , 'abcdefghijklmnopqrstuvwxyz' ) AS "문자만남기기"
FROM (SELECT EMPNO||ENAME||HIREDATE AS DATA  
        FROM EMP )

 

DATA 숫자만제거 숫자만남기기 문자만제거 문자만남기기
7698BLAKE81/05/01 BLAKE// 7698810501 769881/05/01 blake
7782CLARK81/06/09 CLARK// 7782810609 778281/06/09 clark
7566JONES81/04/02 JONES// 7566810402 756681/04/02 jones
7902FORD81/12/03 FORD// 7902811203 790281/12/03 ford
7369SMITH80/12/17 SMITH// 7369801217 736980/12/17 smith
7499ALLEN81/02/20 ALLEN// 7499810220 749981/02/20 allen
7521WARD81/02/22 WARD// 7521810222 752181/02/22 ward
7654MARTIN81/09/28 MARTIN// 7654810928 765481/09/28 martin
7844TURNER81/09/08 TURNER// 7844810908 784481/09/08 turner
7900JAMES81/12/03 JAMES// 7900811203 790081/12/03 james
7934MILLER82/01/23 MILLER// 7934820123 793482/01/23 miller
7876ADAMS87/05/23 ADAMS// 7876870523 787687/05/23 adams
7788SCOTT87/04/19 SCOTT// 7788870419 778887/04/19 scott
1111YODA81/11/17 YODA// 1111811117 111181/11/17 yoda
7839KING81/11/17 KING// 7839811117 783981/11/17 king

 

 

"숫자만제거"의 결과가 저렇게 왜 저렇게 나왔을까?

TRANSLATE (DATA, ' 1234567890', ' ')  핵심은 공백 활용과 치환할 문자가 없음을 이해하는 것이다.

검색문자 맨 앞에 ' ' 공백이 존재한다. 치환문자는 ' ' 공백만 존재한다. 

즉, 공백이 존재하면 공백으로 치환한다. 이후 검색문자 '1234567890'은 치환할 문자가 없으므로 null처리(제거)가 된다.

검색문자 이외 철자나, 특수문자는 그대로 남아 BLAKE// .... KING// 이런 결과가 나온 것이다.

 

"숫자만남기기"의 결과를 살펴보자

(DATA, '1234567890'||DATA, '1234567890')   핵심은 데이터 활용(||) 하는 것이다

원리는 이전 치환할 문자가 없다는 것을 이용한다.

검색문자에서 '1234567890'||DATA 중 '1234567890'는 치환문자 '1234567890' 대응되어 먼저 처리된다. 

"7698BLAKE81/05/01" 를 예를 들자면 치환문자  '1234567890' 0까지 처리된 직후 결과는 "숫자만제거" 결과인 "BLAKE//" 상태일 것이다. 이후 치환할 문자가 없으니 null처리 되어 숫자만 제외하고 다 null처리된 것이다.

 

문자 처리도 숫자처리와 원리는 완벽히 같다. 다만 대소문자 구분을 한다는 것을 알리기 위해 LOWER함수만 추가 썼을 뿐 모든 면에서 동일하다.

 

 

만약 당장 이해하기 어렵다면, 위 표를 보고 공식처럼 대입해서 사용하면 된다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'개발 > 오라클 SQL' 카테고리의 다른 글

NULL을 다룰 때 주의할 점  (0) 2022.12.23
오라클 비밀번호 만료  (0) 2022.11.27
IN, NOT IN, EXISTS, NOT EXISTS  (0) 2022.11.10
마이바티스 null 체크  (0) 2022.09.28
정규식을 통한 Mybatis Sql Injection 처리  (0) 2022.06.29

IN, EXISTS 

비슷한 듯, 동작방식이 다른 두 연산에 대해 알아보자.

 

select DISTINCT deptno from dept

select DISTINCT deptno from dept

 

select DISTINCT deptno from emp

 

IN 동작방식

근본적으로 IN 은 OR 동작과 같다.

 

select DISTINCT deptno
  from dept
 where deptno in (select DISTINCT deptno from emp)

 

위 쿼리에 서브 쿼리 결과는 아래와 같다.

select DISTINCT deptno from emp

즉, 위 쿼리는 아래와 같은 결과를 도출한다.

 SELECT *
  FROM DEPT
 WHERE DEPTNO IN 
 (30
,NULL
,20
,10)

 

실행결과는 둘 다 같다.

EXISTS  

 select  *
  from dept d
 where  EXISTS  (select deptno from emp e WHERE e.DEPTNO = d.DEPTNO)

위 쿼리 또한 같은 결과를 도출한다.

 

IN과 차이는 EXISTS는 서브쿼리가 행을 반환하면 TRUE를 도출한다. 즉, 직접적으로 값 비교를 하지 않는다.

 

NOT IN, NOT EXISTS

단순히 위 쿼리 2개에 NOT만 붙이면 어떻게 될까?

select distinct deptno
  from dept
 where deptno not in (select distinct deptno from emp)

아마 위와 같은 결과를 기대했을 것이다.

하지만 아래와 같은 결과가 나온다.

 SELECT  DISTINCT DEPTNO
  FROM DEPT D
 WHERE NOT EXISTS  (SELECT DISTINCT DEPTNO FROM EMP E WHERE E.DEPTNO = D.DEPTNO)

NOT EXISTS는 정상적으로 기대한 값이 나온다. 

왜 이런 결과가 나오는 것일까?

 SELECT DISTINCT DEPTNO
  FROM DEPT
 WHERE DEPTNO NOT IN 
 (30
--,NULL
,20
,10)

위 쿼리에서 봤던 풀이에서 NULL만 주석 처리하고 결과를 보면 같은 결과가 나온다.

위 결과가 도출된 이유는 아래와 같다.

SELECT DISTINCT DEPTNO FROM DEPT;
SELECT DISTINCT DEPTNO FROM EMP;

각 테이블의 결과를 데카르트 곱을 하게 된다.

10을 예로 들면

(10=10, 10=20, 10=30, 10=NULL)

 

IN은 근본적으로 OR연산이니

(10=10 OR 10=20 OR 10=30 OR 10=NULL)

 

(TRUE OR FALSE OR FALSE  OR NULL)

 

OR연산에서 하나라도 TRUE가 있으면 FALSE가 몇 개던 TRUE이다.

(TURE OR NULL)

 

여기가 중요하다 OR은 NULL과 연산해도 TRUE를 도출한다.

(TURE)

 

즉, 각 값마다 데카르트 곱을 할 때 NULL이 껴 있느면 그냥 IN일때는 TRUE를 반환하지만

NOT IN 일 경우 TRUE에 NOT이 되어 전부 FALSE가 되는 것이다.

 

 SELECT  DISTINCT DEPTNO
  FROM DEPT D
 WHERE NOT EXISTS  (SELECT DISTINCT DEPTNO FROM EMP E WHERE E.DEPTNO = D.DEPTNO)

EXISTS의 경우에는 각 행 단위로 행 결과만 존재 여부로 TRUE, FALSE를 리턴하기 때문에 정상적으로 값이 도출되는 것이다.

 

 

이래나 저래나 당장 이해하기 버겁다면, NOT IN만 조심하면 된다는 것을 기억하면 된다.

아니 NOT IN에 NULL이 끼어있으면 값이 제대로 도출이 안된다는 것만 기억하면 된다.

 

이를 방지하기 위해선 위 처럼 NOT EXISTS를 사용하거나

기존 쿼리 구조를 변경하기 어려운 경우라면 NULL처리를 적당해 해주면 된다.

SELECT DISTINCT DEPTNO FROM DEPT
 WHERE DEPTNO NOT IN (SELECT DISTINCT  NVL(DEPTNO, 0) FROM EMP)

굳이 NVL아니더라도 DECODE, COALESCE.... 등 편한대로 처리하면 된다.

 

 

 

 

 

 

 

 

-- #{var} 라고 가정
<if test="var != null && var != '' ">
</if>

논리연산자 동작 방식을 알고 있으면 소소하게 도움되는게 있다.

예를 들어 위 null 체크에서  and 연산을 하고 있다.

and 연산은 진리표를 보면 둘 다 참이여야 참이된다.

즉, 하나라도 거짓이면 참이 될 수 없다.

따라서 앞에서 거짓이 나오면, 뒤에 연산은 이루어지지 않는다 (성능)

만약 var != ''  앞에 왔고, null이라면 익셉션이 발생한다.

 

 

역으로 or연산자는 하나라도 true이면 전부 true이므로, 

성능상에 이점을 가져가기 위해서는 true일 확률이 높은 것을 앞에 배치하는 것이 좋다.

 

 

 

 

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class MyFile {
	static int fileCount = 0;
	static int dirCount = 0;
	
	public static void main(String[] args) throws IOException {
    //폴더 경로
		String src = "C:\\Users\\kks\\Documents\\0000\\abc";
		String dst = "C:\\Users\\kks\\Documents\\0000\\def";
		String tmpDir = "C:\\Users\\kks\\Documents\\0000\\temp";
				
		if(new File(src).isDirectory()&& new File(tmpDir).isDirectory()) {
			fileMove(src, tmpDir, false);
			fileMove(tmpDir, dst, true);
		}
		System.out.println("총 파일 : "+ fileCount);
		System.out.println("총 폴더 : "+ dirCount);
		
	}
	
	private static void fileMove(String src, String dst , boolean srcDelete) throws IOException {
		File srcFile = new File(src);
		FileInputStream in = null;
		BufferedInputStream bis = null;
		
		FileOutputStream out = null;
		BufferedOutputStream bos = null;
		
		File[] files = srcFile.listFiles();
		for(File file : files) {
			File dstFile = new File(dst+"\\"+file.getName());
			if(srcDelete) {
            // JVM 종료 시 삭제 
				file.deleteOnExit();
			}
			
			if(file.isDirectory()) {
				dirCount++;
				dstFile.mkdir();
				fileMove(file.getAbsolutePath(), dst+"\\"+file.getName(), srcDelete);
				
			}else {
				fileCount++;
				in = new FileInputStream(file);
				bis = new BufferedInputStream(in);
				
				out = new FileOutputStream(dstFile);
				bos = new BufferedOutputStream(out);
				
				int data = 0;
				while ((data = bis.read())!= -1 ) {
					bos.write(data);
				}
				bos.close();
				bis.close();
			}
		}
	}
}

 

출발지 폴더, 임시 폴더, 목적지 폴더를 정해 파일을 옮기는 예제

폴더 내의 파일 중 폴더가 있으면 재귀 호출

'개발 > 자바(JAVA)' 카테고리의 다른 글

객체 지향 - 2  (0) 2023.01.27
객체 지향 - 1  (0) 2023.01.25
변수의 타입  (0) 2022.07.07
변수  (0) 2022.07.03
자바란?  (0) 2022.07.02

기본형 변수 타입

  • 문자형
    문자 : char 
  • 숫자
    정수 : byte, short, int, long
    실수 : float, double
  • 논리형
    boolean

총 8개

 

기본형과 참조형

기본형은 실제 값을 저장한다

8개가 존재한다.

성능상 이점 때문에 사용한다.

 

참조형은 어떤 데이터가 저장된주소 값을 저장한다.

기본형을 제외한 모든 것이다 참조형이다

배열, 클래스, 열거형 …

32bit JVM 쓰면 참조변수 크기는 32bit

64bit JVM 쓰면 참조변수 크기는 64bit이다.

 

 

기본형

정수형과 실수형이 존재 정수형의 대표는 int, 실수형의 대표는 double

종류\크기(byte) 1 2 4 8
논리형 boolean      
문자형   char    
정수형 byte short int(기본) long
실수형     float double(기본)

boolean 타입은 제어문이나 반복문에서 주로 사용된다.

char타입은 사실 아스키코드로 숫자에 문자가 매핑되어있다. 따라서 반복문도 가능하다

public class CharEx {
	public static void main(String[] args) {
		for(char a = 65; a < 91 ; a ++) {
			System.out.print(a+",");
		}
	}
}
// 결과 : A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,

숫자형 타입은 작은 데이터에서 큰 데이터로 자동 형변환이 된다. 

일부 예외도 있지만 크게 중요하지 않다.

 

'개발 > 자바(JAVA)' 카테고리의 다른 글

객체 지향 - 1  (0) 2023.01.25
파일 옮기기  (0) 2022.07.25
변수  (0) 2022.07.03
자바란?  (0) 2022.07.02
폴더, 파일 수 탐색  (0) 2022.05.27

하나의 타입, 하나의 값을 저장할 수 있는 메모리 공간

 

int var = 0;

int는 변수의 타입, var는 변수 메모리 공간의 이름

자바는 강타입 언어로 타입을 명확히 한다.

 

변수의 초기화

메모리는 자바 프로그램 뿐만아니라 모든 프로그램이 공유하는 h/w자원이다.

따라서 그 공간에 잔여 값이 남아 있을 수도 있다. 

 

변수 초기화란 변수 사용 전에 처음으로 값을 저장하는 것을 말한다.

	public class Var {
	      static int classVariable;
	      int instanceVariable;
	      
	      //main()도 메서드이므로 로컬영역이다.
	      public static void main(String[] args) {
	            VarEx1 instance = new VarEx1();
	            int localVariable;
	            System.out.println(classVariable);
	            System.out.println(instance.instanceVariable);
	//          System.out.println(localVariable);
	      }
}

기본형 인스턴스 변수와 클래스 변수는 각각 정해진 기본값으로 초기화가 자동으로 된다.

기본형 지역변수는 선언과 동시에 초기화를 반드시 해야한다.

 

 

두 변수의 값 교환하기

현실의 사고방식으로는 바꿔치지를 생각할 있지만 여기는 기계속이다. 불가능하다.

  1. 먼저 a temp 임시 저장한다
  2. a 자리에 b 값을 넣는다.
  3. b 자리에 temp 값을 넣는다.

물컵 잔에 내용물을 바꾼다고 생각하면 쉽다.

아니면 실제로 코드상으로 변수 2개로만 바꿀려고 시도해보자. 그러면 왜 temp가 필요한지 이해가 잘된다.

 

변수명 규칙

  • 대소문자 구분한다/ 길이제한 없다
  • 예약어를 사용하면 안된다
  • 숫자로 시작하면 안된다
  • 특수문자는 _ , $ 허용한다

예약어는 대부분 자바 문법상 사용하는 단어들이다. 따라서 외울 생각을 할필요가 없다

int, long, for, ....

다만 예약어가 포함되어 있다고 사용이 다 불가능하지 않다. intSum 이런 것은 된다. 

즉, 딱 그 예약어 단어 사용이 불가능하다.

추가 권장사항

  • 클래스 이름은 글자를 대문자로한다
  • 여러 단어 이름은 단어 글자를 대문자로 한다
  • 상수의 이름은 대문자로만 사용하며 단어 구분은 _ 한다

 

 

'개발 > 자바(JAVA)' 카테고리의 다른 글

파일 옮기기  (0) 2022.07.25
변수의 타입  (0) 2022.07.07
자바란?  (0) 2022.07.02
폴더, 파일 수 탐색  (0) 2022.05.27
File  (0) 2022.05.26

1996년 출시한 객체지향 프로그래밍 언어

 

자바의 특징

  • 운영체제에 독립적이다.
    JVM만 설치되면 된다. JVM은 거의 모든 운영체제별로 준비가 되어있다.
    따라서 하나의 코드로 모든 환경에서 사용이 가능하다.

  • 객체지향언어이다.
    코드의 재사용성, 유지보수가 편리하다
  • 자동 메모리 관리
    메모리를 관리해주는 가비지 컬렉터가 존재해 개발자는 메모리 관리에 힘을 쓰지 않고 개발에만 집중할 수 있다.
  • 네트워크와 분산처리를 지원한다.
  • 멀티쓰레드를 지원한다.
    멀티쓰레드는 운영체제에 따라 구현 방법도 상이하고, 처리방법도 다르지만 자바는 멀티쓰레드 관련 API를 제공한다.
    따라서 사용법만 알면 손쉽게 구현이 가능하다
  • 동적로딩을 지원한다.
    애플리케이션 실행 시 모든 클래스가 로딩되지 않고 필요한 시점에 로딩할 수 있다.

JVM(Java Virtual Machine)

모든 자바 코드를 실행하기 위한 프로그램

자바와 일반 소프트웨어 차이

  • 단점
    일반 애플리케이션은 OS 거치고 컴퓨터로 코드 전달따라서 속도가 느리다.
    하지만 극복을 위해 요즘엔 바이트코드(컴파일된 자바코드) 하드웨어의 기계어로 바로 변환해주는 JIT컴파일러와 향상된 최적화 기술이 적용되어서 속도 격차를 많이 줄였다.
  • 장점
    하나의 코드로 모든 플랫폼에서 실행이 가능하다.

'개발 > 자바(JAVA)' 카테고리의 다른 글

파일 옮기기  (0) 2022.07.25
변수의 타입  (0) 2022.07.07
변수  (0) 2022.07.03
폴더, 파일 수 탐색  (0) 2022.05.27
File  (0) 2022.05.26

스패로우 같은 취약점 점검 툴을 점검 결과로 

제가 있는 사이트에 기존 sql mapper 파일에서 500개 정도되는 Mybatis Sql Injection 취약점이 나왔습니다.

원인은 ${} 사용 때문입니다. 

${}를 전부 #{}로 바꿔줬습니다.

SELECT '${BIND1}' FROM TEST
WHERE ${BIND2} BETWEEN '${BIND3}' AND '${BIND4}'

저같은 경우는 notepad++ 로 수정했습니다. 

이클립스나 STS도 당연히 정규식을 지원합니다.

 

\$\{[^{}]*\}  

 

일반적으로 ${}  안쓰는 것이 좋습니다.

성능면에서도 #{} 가 더 좋습니다. DB에 Library Cache를 찾아서 실행하기 때문입니다.

SELECT #{BIND1} 
FROM DUAL

실제 DB질의는 "SELECT ? FROM DUAL" 로 나가고 

파라미터로 #{BIND1} 값이 나갑니다. 

 

아마도 날짜로만 쿼리 포스트는 마지막일 듯 합니다.

 

SELECT 
	TO_DATE(:ST_YYYYMM, 'YYYYMM') "시작일"
	,ADD_MONTHS(TO_DATE(:ET_YYYYMM , 'YYYYMM'),1)-1 "마지막일"
FROM DUAL;

 

시작점과 끝점 구하기


 

SELECT 
	TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL "DAY"
	,ADD_MONTHS(TO_DATE(:ET_YYYYMM , 'YYYYMM'),1)-1 "마지막일"
	,COUNT(*) OVER()
FROM DUAL
CONNECT BY TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL <= ADD_MONTHS(TO_DATE(:ET_YYYYMM , 'YYYYMM'),1)-1;

 

시작점과 끝점 사이 ROW값 구하기


SELECT 
	TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL "DAY"
	,TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'D') "속한 요일"
	,TRUNC(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL, 'D') "속한 주"
	,TRUNC(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL, 'MM') "속한 달"
FROM DUAL
CONNECT BY TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL <= ADD_MONTHS(TO_DATE(:ET_YYYYMM , 'YYYYMM'),1)-1;

필요한 컬럼 값 구하기

 

 


 

SELECT 
	DECODE( TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'D') , 1, TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'DD')) "일"
	,DECODE( TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'D') , 2, TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'DD')) "월"
	,DECODE( TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'D') , 3, TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'DD')) "화"
	,DECODE( TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'D') , 4, TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'DD')) "수"
	,DECODE( TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'D') , 5, TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'DD')) "목"
	,DECODE( TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'D') , 6, TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'DD')) "금"
	,DECODE( TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'D') , 7, TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'DD')) "토"
	,TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL "DAY"
	,TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'D') "속한 요일"
	,TRUNC(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL, 'D') "속한 주"
	,TRUNC(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL, 'MM') "속한 달"
FROM DUAL
CONNECT BY TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL <= ADD_MONTHS(TO_DATE(:ET_YYYYMM , 'YYYYMM'),1)-1;

"DAY" 행을 DECODE로 강제로 주 단위로 찢기

 


SELECT 
	TO_CHAR(DECODE(MIN(TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'DD')),1, MIN(TRUNC(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL, 'MM'))),'MM') "달"
	,RANK() OVER(PARTITION BY  TRUNC(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL, 'MM') ORDER BY MIN(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL)) || '주차' "주차"
	,SUM(DECODE( TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'D') , 1, TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'DD'))) "일"
	,SUM(DECODE( TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'D') , 2, TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'DD'))) "월"
	,SUM(DECODE( TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'D') , 3, TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'DD'))) "화"
	,SUM(DECODE( TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'D') , 4, TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'DD'))) "수"
	,SUM(DECODE( TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'D') , 5, TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'DD'))) "목"
	,SUM(DECODE( TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'D') , 6, TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'DD'))) "금"
	,SUM(DECODE( TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'D') , 7, TO_CHAR(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL,'DD'))) "토"
FROM DUAL
CONNECT BY TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL <= ADD_MONTHS(TO_DATE(:ET_YYYYMM , 'YYYYMM'),1)-1
GROUP BY TRUNC(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL, 'MM'),TRUNC(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL, 'D')
ORDER BY MIN(TO_DATE(:ST_YYYYMM, 'YYYYMM')-1+LEVEL)
;

 

찢은 컬럼을 GROUP BY 표현식으로 하나의 행으로 합침 

부가적으로  몇 월, 몇 주차 표시


 

마무리

날짜를 기준으로 그룹화를 하는 것은 꽤나 실전적인 쿼리입니다.

조금만 생각해보면 답이 나옵니다. 내가 쇼핑몰을 개발하던, 공장 재고관리를 하던, ... 고객이 원하는 대부분의 통계는 날짜 더나아가 시간 정보로 보길 원하거든요. 

이렇게 시간기준으로 집계한 원본 소스값에  WHERE 절을 추가해서 검색 조건을 달게 됩니다.

'개발 > 오라클 SQL' 카테고리의 다른 글

문자열 다루기 핵심 TRANSLATE  (1) 2022.11.25
IN, NOT IN, EXISTS, NOT EXISTS  (0) 2022.11.10
마이바티스 null 체크  (0) 2022.09.28
정규식을 통한 Mybatis Sql Injection 처리  (0) 2022.06.29
달력 만드는 과정  (0) 2022.06.02

+ Recent posts