You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 17 Next »


조건분기/집합연산/윈도우함수/갱신



조건분기

SQL의 조건 분기는 특수한 값을 반환합니다.

SQL CASE식
SELECT name,address,
	CASE WHEN address = '서울' THEN '경기'
		 WHEN address = '창원' THEN '경남'
		 ELSE 'NA'
	END AS district
FROM Address;

위 코드는 기존 데이터를 교환하여 표현했다 라고 말할수 있습니다. 물론 기존 데이터를 수정한다란 의미는 아닙니다.


그러면, 위와같은 코드처리를 JPA에서는 어떻게 할까요?

JPA
@Entity
public class Address {
@Id
@GeneratedValue
@Column(name = "ADDRESS_ID")
private Integer id;

private String name;
private String address;

//Customer Property ( NotAccess DB)
public String getDistrict() {
String result="NA";
switch(address) {
case "서울": result = "경기";
break;
case "창원": result = "경남";
break;
}
return result;
}
//Setter,Getter 생략
}

이와 같은 단순 교환식은 JPA의Entity의 Get 퍼로퍼티 정의를 통해 기존 데이터 변환이 가능합니다.

DB를 접근하지 않는 getProperty는 우선, write할 멤버가 없기때문에 상태를 보관할 멤버변수가 없어도 되며

주석으로 구분하여 DB를 접근하지 않는 퍼로퍼티로 공간 분리를 합니다.

  

 이렇게 사용했을 때 장점은 다음과 같습니다.

  • district를 얻기위해 , 데이터베이스에게 일을 시키지 않습니다. 어플리케이션이 분산해서 연산할수 있음을 의미합니다.
  • 조회가 100개가되었다고 해서, getDistrict() 를 호출하지 않으면(사용하는곳이없으면), 어플리케이션내에서 변환 연산조차 발생하지 않습니다. 
  • 다양한 자바 함수사용이 가능합니다.

위와같이 변환할수있다고 하더라도, SQL문의 CASE문은 여러요소에사용되는 식이기때문에,

모든 케이스에서 JAVA의 CASE문으로 변환할수 있다란 의미는 아닙니다. - 식과 문장의차이

이것은 단지 데이터베이스의 Entity모델내에, 자연스럽게 기존값을 특수변환 처리를 하는 로직을 이질성없이 결합할수 있다란것이며

SQL에서 조건문처리는 JAVA의 CASE문과 작동방식은 동일하지만 아주 큰차이가 있습니다.

SQL문에서 CASE는 식입니다. SQL문은 절차식 언어가 아니기때문에 반환값이 항상있으며 다양한 요소에 결합이 될수 있습니다.

JAVA에서 CASE는 문장입니다. 문장의 중간에 포함될수 있으며 절차식 흐름을 변경만하고 값자체를 반환하지는 않습니다.

절차식에 익숙해져 있다고하면 SQL에서는 성능좋은식을 만들기가 어렵습니다.

어플리케이션 내에서도 람다식을 활용하여 리스트와같은 데이터를 필터처리한다면 SQL과 비슷한 사고방식으로 데이터가공처리가 될것입니다. 


집한연산

UNION

단일 테이블내의 WHERE 구에서 합집합을 OR,교집합을 AND가 담당하였습니다.

두 테이블의 합집합을 구할때는 UNION을 연산자를 사용합니다.


샘플링 데이터는 JPA를 사용하여 만들었습니다.

  • Address → Address2 로 엔티티복사
  • Address는 100개의 랜덤 주소록생성( 각 프로필은 모두 유니크함 )
  • Address2는 짝수번째 남자 데이터만 복제해서, 여성이 존재하지 않음
  • 3의배수일때는  Address2에 철수만 존재하게함 
  • 샘플 데이터 Address 100개 , Address 67개


Address 데이터를 만드는것은 앞장에서 이미 설명이 되었으며

Address2는 Address데이터에서, 데이터만 약간 변형이 되었습니다.

순차적으로 기술되었기때문에 이해하는데 어려움은 없을듯 보입니다.

public void InsertAddressData(){		
	String[] addArray = {
			"서울","대구","부산","창원","마산","제주"				
	};			
	for(int i=0; i<100;i++) {
		Boolean isAddRepo2 = false;
		double random = Math.random();
	    double x = random*100;
	    int y = (int)x + 1; //Add 1 to change the range to 1 - 100 instead of 0 - 99
	    
		Address address = new Address();
		Address2 address2 = new Address2();
		
		address.setAge(y);
		address.setAddress( addArray[i%6] );
		address.setPhoneNbr(String.format("010-3333-1%04d", i));			
		
		if( i%2 == 0 ) {
			address.setName(String.format("민수%d", i+1));
			address.setSex("남");
			//Address2에는 민수가 중복존재함
			isAddRepo2 = true;
			address2.setName(String.format("민수%d", i+1));
			address2.setSex("남");
			address2.setAge(y);
			address2.setAddress( addArray[i%6] );
			address2.setPhoneNbr(String.format("010-3333-1%04d", i));				
		}else {
			address.setName(String.format("영희%d", i+1));
			address.setSex("여");
			//Address2에는 여자가 없음
			isAddRepo2 = false;
		}
		
		if( i%3 == 0) {
			//Address2에는 철수만 있음
			isAddRepo2 = true;
			address2.setName(String.format("철수%d", i+1));
			address2.setSex("남");
			address2.setAge(y);
			address2.setAddress( addArray[i%6] );
			//철수의 전화번호는 다름
			address2.setPhoneNbr(String.format("010-2222-1%04d", i));
		}					
		addressRepo.save(address);
		if(isAddRepo2==true) addressRepo2.save(address2);		
	}					
}
Address1Address2

100개

67개


순수 프로필정보를 가지고 중복체크 여부를 확인하기위한 샘플이며

합집합,교집합,차집합을 설명하기위해 id 값은 select에서 제외하였습니다.


합집합 구하기

SELECT address,age,name,phoneNbr,sex FROM db_example.address
UNION
SELECT address,age,name,phoneNbr,sex FROM db_example.address2;


100개와 67개의 합집합을 수행하여 167개가 나와야하는데 실제로는 134개의 결과만 나왔습니다.

이것은, 짝수번째(3의배수가 짝수번째가되는상황제외)에서 복제를 하였기때문에 중복을 제거한 합이기 때문입니다.

UNION에서 중복은 제거된다라는 속성은 중요한 내용이며, 중복을 무시할시 UNION ALL을 사용합니다.



그럼 UNION수행이 JPA에서는 어떻게 작동되는지 확인해보겠습니다.


JPA에서 UNION 사용하기

아쉽게도 JPQL에서 UNION쿼리를 지원하지 않으며, 유니온된 결과 역시Entity에 정의하지 않은

새로운 튜플이기 때문에, SQL Native 쿼리 방식을 사용해야하며

사용방식에 만족스럽지는 못하지만 맵핑에도 어려움이 있어서

오브젝트를 변환하는 전통적인 방법을 그대로 사용해해보겠습니다.

public interface AddressRepo2 extends CrudRepository<Address2, Long>{	
	@Query(value="SELECT address,age,name,phoneNbr,sex FROM db_example.address " + 
			"UNION " + 
			"SELECT address,age,name,phoneNbr,sex FROM db_example.address2 ",
			nativeQuery = true)
	List<?> makeUniOn();	//이 함수를 통해, UNION의 결과를 볼수가 있습니다.
}


List<?> addressUnion =  addressRepo2.makeUniOn();
ObjectMapper mapper = new ObjectMapper();
addressUnion.forEach( item ->{
	try {
		String jsonInString = mapper.writeValueAsString(item);
		System.out.println(jsonInString);				
	}catch(Exception e) {				
	}
});


결과:

사용방식에 만족스럽진 못하지만, 동일한 기능을, JPA Repository 인터페이스를 활용하여 수행하였습니다.


UNION과같이 JPQL이 지원하지 않는 명령문에서 JPA에서 바람직하게 어플리케이션의 엔티티와

맵핑하는 기능은 사용할수 없어보입니다.

어렵게 맵핑할수 있는 방식도 있지만 ( UNION JPA ENTITY ECLIPSE LINK 등으로 검색하면 됩니다.)

준비해야할 코드들이 너무 많습니다.


UNION 은 특성상 ,모든 테이블을 합쳐서 처리하는 배치성에서 많이 사용되며 편리합니다.

모든것을 간단하게 합할수 있지만 데이터량에 따라 메모리풀의 위험성을 가지고 있으며,

이부분에 대한 튜닝을 위해 어렵고,코드가 길어지는 SQL문 작성능력및 트랜젝션처리가 요구되기도 합니다.


JPA에서는 대량의 데이터를 안전하게 부분처리를 하는 패턴을 간단하게 설계할수 있으며

아래 아티컬을 참조합니다.

https://memorynotfound.com/hibernate-jpa-batch-insert-batch-update-example/


교집합 구하기

SELECT address,age,name,phoneNbr,sex FROM db_example.address a1
WHERE a1.name IN (SELECT name FROM db_example.address2);

SELECT a.address,a.age,a.name,a.phoneNbr,a.sex 
FROM db_example.address a, db_example.address2 b 
WHERE a.name = b.name;


SELECT address,age,name,phoneNbr,sex FROM db_example.address
INTERSECT
SELECT address,age,name,phoneNbr,sex FROM db_example.address2;

차집합 구하기

SELECT address,age,name,phoneNbr,sex FROM db_example.address a1
WHERE a1.name NOT IN (SELECT name FROM db_example.address2);


SELECT address,age,name,phoneNbr,sex FROM db_example.address
EXCEPT
SELECT address,age,name,phoneNbr,sex FROM db_example.address2;

합집합,교집합은 순서가 다르다고 결과가 달라지지 않습니다. ( 드라이븐 시작조건에 의해 성능이 달라질순 있어도 )

하지만 차집합은 빼는것이기때문에 교환법칙이 성립되지않기때문에 순서에 유의합니다.

교환법칙

A +B = B + A : 같음으로 교환법칙 성립

A - B = B - A : 같지 않다.


UNION VS JOINS

이후 JOIN을 별도로 설명을 하겠지만, UNION과 JOIN은 아래와 같은 큰차이가 있습니다.

JOINUNION

  • 두개의 필드가 모두 합해지는 병렬조합
  • 필드명이 모두 합해져서,동일 필드명 사용에 유의해야합니다.
  • 엔티티가 다른 관계도가 있는 두 테이블을 하나의 결과로 만들때 사용됩니다.
  • 상하(직렬)조합이며,
  • 합하려는 양쪽 필드의 개수가 동일해야함
  • 컬럼명은 첫번째 선택된 테이블의 컬럼명에 영향받음
  • 전체행 중복제거 연산(Distinct)이 수행되기 때문에 성능에 유의
  • 엔티티가 유사한 결과 테이블을 하나의 결과로 만들때 사용됩니다.








  • No labels