Hive에서도 아래와 같이 'EXPLAIN' 명령문으로 쿼리 실행계획을 확인할 수 있습니다.

hive> EXPLAIN 쿼리문;

  Hive의 실행계획은 다른 DBMS에 비해 실행계획의 가독성이 떨어진다는 점이 참으로 안타까운데요.
  쿼리 수행 작업을 여러 Stage로 나눠 실행하고, 각 작업 단계에서도 Map과 Reduce 태스크로 일감을 분할해 처리하는 hive의 특성상 장문의 실행계획은 다른 대안이 없는 결과라고 생각합니다.

  또 하나 아쉬운 점은, 파티션 테이블의 스캔 정보가 명확하지 않다는 점입니다.

  오라클의 실행계획을 예를 들어 살펴보면, 'PARTITION RANGE SINGLE'이라는 Operation으로 하나의 파티션만 액세스 된다는 것을 쉽게 알 수 있습니다.

 

  하지만, Hive에서의 실행계획에서는 PARTITION이라는 단어조차 찾아볼 수 없습니다.

  그럼, 내가 작성한 쿼리문이 '테이블의 전체 데이터를 액세스 하는지?' 아니면 '조건에 맞는 파티션만 액세스(이하 Partition puruning)를 하는지?' 확인할 수 있는 방법은 없는 건가요?

  방법은 두 쿼리 문의 실행계획 비교입니다.

 

  첫 번째, WHERE 조건절 없이 전체 테이블 데이터를 SELECT 하는 쿼리의 실행계획을 확인해봅니다.

hive> EXPLAIN SELECT UID FROM ACT_TXT_DD;

  위 실행계획에서 ACT_TXT_DD 테이블의 통계정보(Statistics)를 살펴봅니다.
  로우 수(Num rows)가 1,199,025건이고 데이터 크기(Data size)가 2,053,929,825 Byte이네요.

 

  두 번째, WHERE 조건절에 파티션 컬럼의 필터링 조건이 명시된 SELECT문의 실행계획을 확인해봅니다.

hive> EXPLAIN SELECT UID FROM ACT_TXT_DD WHERE BASE_DT='20200101';

  파티션 컬럼 BASE_DT의 값이 '20200101'인 데이터만 추출하는 SELECT문의 실행계획은 아래와 같습니다.


  통계정보(Statistics) 상의 로우 수(Num rows)는 101,835건이고 데이터 크기(Data size)는 174,443,355 Byte입니다.

  위 WHERE 조건절이 없는 SELECT문의 실행계획과 비교해서 로우 수와 데이터 크기가 적어진 것을 확인할 수 있습니다.
  Partition puruning으로 테이블 스캔 범위가 적어져 위와 같은 통계정보를 출력하게 된 것입니다.

 

  WHERE 조건절에 파티션 키 칼럼 필터링 조건을 입력했더라도, 형 변환 문제나 UDF 사용으로 인해 Partition puruning이 적용되지 않을 수도 있습니다.
  HQL 수행 속도가 매우 느리다면 위와 같은 방법으로 Partition puruning 여부를 확인해 보는 것이 HQL 최적화의 한 방법이 되겠습니다.

 

 * 성능 개선율 : (튜닝 전 - 튜닝 후) / 튜닝 후

 

  비효율적인 PL/SQL(Function)을 제거하여 성능을 높인 쿼리 예제를 살펴보도록 하겠습니다.
  이 포스트에 사용된 예제는 부하를 유발하는 PL/SQL를 보여주기 위해 임의로 작성된 쿼리입니다. 최대한 가독성을 높이기 위해 짧게 작성하고자 노력했으며, 이로 인하여 조금은 억지스러운 면도 있지만 설명을 위한 예제이니 참고 바랍니다.

[ 튜닝 전 ]

 

  먼저 튜닝 전 쿼리를 살펴보도록 하겠습니다.

SELECT SALE_DT
     , PROD_NO
     , FNC_GET_PROD_NM(PROD_NO) AS PROD_NM
  FROM PROD_SALE
 WHERE SALE_DT = '20191201';

  PROD_SALE 테이블 데이터 중 SALE_DT 값이 '20191201'인 데이터의 SALE_DT, PROD_NO, PROD_NM 값을 추출하고 있습니다. 이 중 PROD_NM 값은 FNC_GET_PROD_NM 함수를 호출한 결과 값을 출력합니다.

  위 쿼리에서 사용된 함수 FNC_GET_PROD_NM의 내용은 아래와 같습니다.

CREATE OR REPLACE FUNCTION FNC_GET_PROD_NM (V_PRODNO IN NUMBER) 
          RETURN VARCHAR2
IS
V_PROD_NM VARCHAR2(100);
V_PROD_STTS_CD VARCHAR2(10);
V_RES_NM VARCHAR2(100);
BEGIN
SELECT PROD_NM, PROD_STTS_CD
    INTO V_PROD_NM, V_PROD_STTS_CD
     FROM PROD 
    WHERE PROD_NO = V_PRODNO ;

IF V_PROD_STTS_CD = '10' THEN
  V_RES_NM := '[판매중] ' || V_PROD_NM;
ELSIF V_PROD_STTS_CD IN ('30','40') THEN
 V_RES_NM := '[판매종료] ' || V_PROD_NM;
ELSIF V_PROD_STTS_CD IN ('50','60') THEN
 V_RES_NM := '[판매예정] ' || V_PROD_NM;
ELSE
 V_RES_NM := '[기타] ' || V_PROD_NM;
END IF;

RETURN V_RES_NM; 
END; 

  FNC_GET_PROD_NM 함수는 입력받은 인자 값 V_PRODNO에 매칭 되는 상품명(더 정확하는 상품 상태 값과 상품명)을 반환하는 함수입니다. 
  PROD 테이블에서 PROD_NO 컬럼의 값이 입력받은 인자 값 V_PRODNO와 일치되는 데이터의 PROD_NM과 PROD_STTS_CD 값을 추출하여 V_PROD_NM과 V_PROD_STTS_CD 변수에 각각 입력합니다. IF문을 이용해 V_PROD_STTS_CD 값에 따라 상품상태값과 상품명(V_PROD_NM)을 합친 문자열을 V_RES_NM에 대입하고, 마지막에 V_RES_NM 값을 반환하게 됩니다.

  위 쿼리의 AUTOTRACE 결과를 살펴보겠습니다.

  AUTOTRACE의 Statistics를 보면 recursive calls이 과도하게 발생한 것을 확인할 수 있습니다. recursive calls는 오라클 내부적으로 필요한 작업이나 SQL문을 수행할 때 발생합니다. 예를 들어, 데이터를 Insert시 공간이 부족하면 recursive calls로 공간을 동적으로 할당하는 작업을 하게 됩니다. 여기서는 PL/SQL 내의 SQL문을 수행을 위해 recursive calls를 호출하였습니다.

  SELECT절에 사용된 PL/SQL의 실행 횟수는 SELECT 되는 데이터의 건수와 비슷합니다. SELECT되는 각 로우의 값을 입력 인자 값으로 해서 PL/SQL을 수행하게 됩니다. PL/SQL 결과가 캐시에 저장되어 있다면 PL/SQL 수행 없이 캐쉬 내 결과 값을 반환하게 되므로 항상 SELECT 되는 데이터 건수와 동일한 것은 아닙니다.

  PROD_SALE 테이블에서 SALE_DT 값이 '20191201'인 데이터는 251,896건입니다. 251,896건의 recursive calls이 발생했으니 데이터 건수만큼 함수가 호출되었음을 확인할 수 있습니다.
  여기서 가장 큰 문제는 FNC_GET_PROD_NM 함수를 호출할 때마다 함수 내부의 쿼리문을 반복해서 실행하게 된다는 것입니다. SQL 최적화를 위해서는 블럭 액세스를 줄여야 하는데, 함수내 쿼리문을 실행하기 위해 PROD 테이블을 여러 번 액세스하게되어 블럭 액세스(consistent gets)가 과도하게 증가하였습니다.

  위 쿼리를 최적화하기 위해서는 과도한 블럭 액세스를 유발하는 함수를 제거해야 합니다.

 

 

[ 튜닝 후 ]

 

  아래는 위 튜닝 전 쿼리에서 함수 제거를 위해 함수 내 PROD 테이블 조회를 조인으로 풀어낸 쿼리입니다.

SELECT S.SALE_DT
     , S.PROD_NO
     , CASE WHEN PROD_STTS_CD = '10' THEN '[판매중] ' || P.PROD_NM
            WHEN PROD_STTS_CD IN ('30','40') THEN '[판매중] ' || P.PROD_NM
            WHEN PROD_STTS_CD IN ('50','60') THEN '[판매중] ' || P.PROD_NM
            ELSE '[기타] ' || P.PROD_NM END  AS PROD_NM
  FROM PROD_SALE S
     , PROD P
 WHERE S.PROD_NO = P.PROD_NO
   AND SALE_DT = '20191201'
;

  PROD_SALE 테이블의 데이터는 튜닝 전 쿼리처럼 SALE_DT 값이 '20191201'인 데이터만 추출합니다. 그리고 FNC_GET_PROD_NM 함수 대신에 ROD_SALE 테이블과 PROD 테이블을 PROD_NO 키를 이용해 조인을 하고 있습니다. SELECT절의 PROD_NM 항목은 함수 내 연산처럼 PROD_STTS_CD 값에 따라 상태 값을 CASE문을 이용해 분기하여 PROD_STTS_CD에 따른 상품상태값과 상품명(PROD_NM)을 합친 결과로 출력됩니다.

  위 쿼리의 AUTOTRACE 결과 값은 아래와 같습니다.

  PROD_SALE테이블과 PROD테이블을 조인하는 형태로 쿼리를 수정하여 블럭 액세스가 5.3%로(797,850블럭에서 42,664블럭) 감소되었습니다.

  실행계획을 보면 PROD_SALE 테이블과 PROD 테이블을 FULL TABLE SCAN 하여 HASH 조인했음을 알 수 있습니다. 두 테이블 중 크기가 상대적으로 작은 PROD 테이블이 BUILD 테이블이 되어 해쉬 테이블을 만들고 PROD_SALE 테이블이 PROBE 테이블이 되어 해시 조인했음을 확인할 수 있습니다. 

 

  해시 조인을 수행하였기 때문에 튜닝 후 쿼리에서는 PROD 테이블을 한 번만 액세스 하였습니다.
  튜닝 전 쿼리에서는 함수 실행을 위해 SELECT 건수만큼 PROD 테이블을 액세스 하여 과도한 블럭 액세스를 유발하였는데, 쿼리를 조인으로 풀어 이러한 과도한 블럭 액세스를 제거한 것입니다.

 

  V2X 관련 비즈니스 도메인 분석이 필요하여 정리하였던 자료 공유합니다.


 

 

[출처] TTA ICT 표준화전략맵 Ver. 2018

'New Tech. > Autonomous Driving' 카테고리의 다른 글

ECU / CAN / LIN / OBD  (0) 2020.04.16
자율주행차 정의 및 관련 센서  (0) 2020.04.16
자율주행 관련 용어  (0) 2020.04.11

  자율주행 관련 비즈니스 도메인 분석이 필요하여 정리하였던 자료 공유합니다.

 

ECU(Engine Control Unit) 개념
 - 현대자동차 Avante MD에 장착되는 ECU

[출처] http://blog.naver.com/diguyz/220940649758

 

 

 

'New Tech. > Autonomous Driving' 카테고리의 다른 글

V2X (Vehicle-to-Everything)  (0) 2020.04.16
자율주행차 정의 및 관련 센서  (0) 2020.04.16
자율주행 관련 용어  (0) 2020.04.11

자율주행 관련 비즈니스 도메인 분석이 필요하여 정리하였던 자료 공유합니다.

 

 

자율주행 프로세스

[출처] https://news.hmgjournal.com/Tech/Item/adas-01

 

자율주행차 센서 적용 예 ( Audi A8 )

[출처] netcarshow.com

 

'New Tech. > Autonomous Driving' 카테고리의 다른 글

V2X (Vehicle-to-Everything)  (0) 2020.04.16
ECU / CAN / LIN / OBD  (0) 2020.04.16
자율주행 관련 용어  (0) 2020.04.11

 

  공간 연산 함수를 이해하기에 앞서 아래 포스트를 먼저 참고하실 것을 권장드립니다.

 

공간 데이터 타입(Spatial Data Type)

Spatial(공간) DB가 필요한 이유? 혹시 아래 나열된 서비스나 시스템을 사용해보신 적이 있으신가요? 버스정보시스템(버스 실시간 도착 알림) 차량 네비게이션 시스템 지하철 앱 지도 앱 차량 호출 서비스 가까운..

sparkdia.tistory.com

 

공간 관계 함수(Spatial Relation Functions)

이전 포스팅에서는 공간 데이터 타입에 대해 알아봤습니다. 공간 데이터 타입(Spatial Data Type) Spatial(공간) DB가 필요한 이유? 혹시 아래 나열된 서비스나 시스템을 사용해보신 적이 있으신가요? 버스정보시..

sparkdia.tistory.com

 

  공간 연산 함수는 두 공간 객체의 연산 결과로 새로운 공간 객체를 반환해주는 함수입니다.

  MySQL에서 제공해주는 공간 연산 함수 중에서 자주 사용되는 함수는 아래와 같습니다

공간 연산 함수 설명
ST_Intersection (g1 Geometry, g2 Geometry)
 : Geometry
g1과 g2의 교집합인 공간 객체를 반환
ST_Union (g1 Geometry, g2 Geometry)
 : Geometry
g1과 g2의 합집합인 공간 객체를 반환
ST_Difference (g1 Geometry, g2 Geometry)
 : Geometry
g1과 g2의 차집합인 공간 객체를 반환
ST_Buffer (g1 Geometry, d Double )
 : Geometry
g1에서 d 거리만큼 확장된 공간 객체를 반환
ST_Envelope (g1 Geometry)
 : Polygon
g1을 포함하는 최소 MBR인 Polygon을 반환
ST_StartPoint (l1 LineString)
 : Point
l1의 첫 번째 Point를 반환
ST_EndPoint (l1 LineString)
 : Point
l1의 마지막 Point를 반환
ST_PointN (l1 LineString)
 : Point
l1의 n 번째 Point를 반환

 

  MySQL에서 공간 연산 함수의 테스트를 위해 아래 테스트 데이터를 사용자 변수에 저장하도록 하겠습니다.

SET @p0 = POINT(0,0);

SET @p1 = POINT(1,1);

SET @p2 = POINT(2,2);

SET @l1 = ST_GeomFromText('LineString(1 1, 3 3, 5 5, 10 10, 14 14)');

SET @l2 = ST_GeomFromText('LineString(2 2, 8 8)');

SET @b1 = ST_GeomFromText('Polygon((1 1,1 10,10 10,10 1,1 1))');

 

  위에서 정의한 사용자 변수를 가지고 아래와 같이 공간 연산 함수를 테스트해보았습니다.

SELECT ST_AsText(ST_Intersection(@p0, @p1)), ST_AsText(ST_Intersection(@b1, @p2));

SELECT ST_AsText(ST_Union(@p0, @p1)), ST_AsText(ST_Union(@l1, @p1));

SELECT ST_AsText(ST_Difference(@p0, @p1)), ST_AsText(ST_Difference(@l1, @p2));

SELECT ST_AsText(ST_Buffer(@b1,0));

SELECT ST_AsText(ST_Envelope(@l2));

SELECT ST_AsText(ST_StartPoint(@l1));

SELECT ST_AsText(ST_EndPoint(@l1));

SELECT ST_AsText(ST_PointN(@l1,3));

  테스트 결과 화면은 아래와 같습니다.

 

 

  이전 포스팅에서는 공간 데이터 타입에 대해 알아봤습니다.

 

공간 데이터 타입(Spatial Data Type)

Spatial(공간) DB가 필요한 이유? 혹시 아래 나열된 서비스나 시스템을 사용해보신 적이 있으신가요? 버스정보시스템(버스 실시간 도착 알림) 차량 네비게이션 시스템 지하철 앱 지도 앱 차량 호출 서비스 가까운..

sparkdia.tistory.com

 

  이제 공간 데이터 타입으로 저장된 공간 데이터를 이용해 연산을 할 수 있는 공간 함수에 대해 알아보겠습니다.

  공간 함수는 공간 관계 함수와 공간 연산 함수로 구분 질 수 있는데, 이 중에서 공간 관계 함수는 두 공간 객체 간의 관계를 일반 데이터 타입(Boolean 또는 숫자)으로 반환해주는 함수입니다.

 

  MySQL에서 제공해주는 공간 관계 함수 중에서 자주 사용되는 함수는 아래와 같습니다.

 

공간 관계 함수 설명
ST_Equals (g1 Geometry, g2 Geometry)
 : Boolean
g1과 g2가 동일하면 True를 반환하고 상이하다면 False를 반환 
ST_Disjoint (g1 Geometry, g2 Geometry)   
 : Boolean
g1과 g2가 겹치는 곳 없다면 True를 반환하고, 겹치는 곳이 있으면 False를 반환 
ST_Within (g1 Geometry, g2 Geometry)
  : Boolean
g1가 g2 영역 안에 포함된 경우 True를 반환하고 그렇지 않은 경우 False를 반환
ST_Overlaps (g1 Geometry, g2 Geometry) 
 : Boolean
g1과 g2 영역 중 교집합 영역이 존재하는 경우 True를 반환하고 존재하지 않는 경우 False를 반환
ST_Intersects (g1 Geometry, g2 Geometry)  
 : Boolean
g1과 g2 영역 간에 교집합이 존재하는 경우 True를 반환하고 그렇지 않은 경우 False를 반환
ST_Contains (g1 Geometry, g2 Geometry)
 : Boolean
g2가 g1 영역 안에 포함된 경우 True를 반환하고 그렇지 않은 경우 False를 반환
ST_Touches (g1 Geometry, g2 Geometry)
 : Boolean
g1과 g2가 경계 영역에서만 겹치는 경우 결과 값으로 True를 반환하며 경계 영역 외에서 겹치거나 겹치는 곳이 없다면 False를 반환
ST_Distance (g1 Geometry, g2 Geometry)
 : Double
g1과 g2간의 거리를 반환

 

  공간 관계 함수의 테스트를 위해 아래 테스트 데이터를 사용자 변수에 저장하도록 하겠습니다.

SET @p0 = POINT(0,0);

SET @p1 = POINT(1,1);

SET @p2 = POINT(2,2);

SET @l1 = ST_GeomFromText('LineString(1 1, 3 3)');

SET @l2 = ST_GeomFromText('LineString(2 2, 4 4)');

SET @b1 = ST_GeomFromText('Polygon((1 1,1 10,10 10,10 1,1 1))');

 

  위에서 정의한 사용자 변수를 가지고 아래와 같이 공간 관계 함수를 테스트해보았습니다.

SELECT ST_Equals(@p1, @p1), ST_Equals(@p1, @l1);

SELECT ST_Disjoint(@b1, @p0), ST_Disjoint(@b1, @p2);

SELECT ST_Within(@p2, @b1), ST_Within(@b1, @p2);

SELECT Overlaps(@l1, @l2), Overlaps(@l1, @p1);

SELECT ST_Intersects(@b1, @p2), ST_Intersects(@b1, @p1);

SELECT ST_Contains(@b1, @p2), ST_Contains(@p2, @b1);

SELECT ST_Touches(@b1, @p1), ST_Touches(@b1, @p2);

SELECT ST_Distance(@p1, @p2);

  테스트 결과 화면은 아래와 같습니다.

 

이어서, 다음 포스트에서는 공간 연산 함수에 대해 설명드리도록 하겠습니다.

 

 

공간 연산 함수(Spatial Operator Functions)

공간 연산 함수를 이해하기에 앞서 아래 포스트를 먼저 참고하실 것을 권장드립니다. 공간 데이터 타입(Spatial Data Type) Spatial(공간) DB가 필요한 이유? 혹시 아래 나열된 서비스나 시스템을 사용해보신 적이..

sparkdia.tistory.com

 

 

  이전 포스팅에서 공간 데이터베이스의 필요성에 대해 RDBMS와 비교 설명을 해보았습니다.

 

Spatial DB(공간 데이터베이스)가 필요한 이유?

혹시 아래 나열된 서비스나 시스템을 사용해보신 적이 있으신가요? 버스정보시스템(버스 실시간 도착 알림) 차량 네비게이션 시스템 지하철 앱 지도 앱 차량 호출 서비스 가까운 상점 찾기 지역 쿠폰 수신 SNS 등..

sparkdia.tistory.com

 

  그럼, 본격적으로 공간 데이터베이스에 대해 알아보겠습니다.

 

  공간 데이터베이스. 

  쉽게 설명하면 X, Y 좌표로 구성된 공간 데이터를 저장하고 연산할 수 있는 기능을 제공해주는 데이터베이스라고 생각하시면 됩니다. 다른 말로, 공간 데이터의 저장 형태를 구분하는 공간 데이터 타입과 공간 데이터를 연산할 수 있는 공간 함수가 제공되는 데이터베이스라고 표현 가능합니다.

  우리가 일반적으로 사용하고 있는 RDBMS인 Oracle, MariaDB, MySQL, PostgreSQL 등에서 공간 데이터를 처리하기 위한 기능을 제공하고 있습니다. 이 솔루션을 이용해 공간 데이터베이스를 생성/관리할 수 있는 겁니다. 심지어 Hive에서도 관련 Library를 설치하면 공간 데이터를 저장하고 공간 연산이 가능합니다.

  그럼, 공간 데이터 타입과 공간 함수를 알면 공간 데이터베이스를 정복하게 되는걸까요? 저는 그렇다고 생각합니다. 가장 기본적인 공간 데이터 타입과 공간 함수를 알면 쉽게 공간 데이터를 처리할 수 있으니까요. 가장 기본적인 기초만 알면 그 후의 응용문제는 쉽게 풀어갈 수 있다고 생각합니다.

  참고로, 공간 데이터 타입과 함수는 Open Geospatial Consortium(OGC, www.ogc.org)에서 표준을 정의하고 있습니다. 아래 링크를 클릭하시면 표준 문서를 다운로드 하실 수 있습니다.
   > 표준 문서 다운로드 : http://portal.opengeospatial.org/files/?artifact_id=25354

 

  이제 공간 데이터베이스의 기초인 공간 데이터 타입에 대해 알아보겠습니다. 여기서는 MySQL에서 제공하는 공간 데이터 타입을 기준으로 설명을 진행할 것입니다.

  자주 사용되는 공간 데이터 타입은 아래와 같습니다.

공간 데이터 타입

 

공간데이터타입 정의 SQL 예
Point 좌표 공간에서 한 지점의 위치를 표시 POINT (10 10)
LineString 다수의 Point를 연결해주는 선분 LINESTRING (10 10, 20 25, 15 40)
Polygon 다수의 선분들이 연결되어 닫혀 있는 상태인 다각형 POLYGON ((10 10, 10 20, 20 20, 20 10, 10 10))
Multi-Point 다수 개의 Point 집합 MULTIPOINT (10 10, 30 20)
Multi-LineString 다수 개의 LineString 집합 MULTILINESTRING ((10 10, 20 20), (20 15, 30 40))
Multi-Polygon 다수 개의 Polygon 집합 MULTIPOLYGON ((( 10 10, 15 10, 20 15, 20 25, 15 20, 10 10 )) , (( 40 25, 50 40, 35 35, 25 10, 40 25 )) )
GeomCollection 모든 공간 데이터들의 집합 GEOMETRYCOLLECTION ( POINT (10 10), LINESTRING (20 20, 30 40), POINT (30 15) )

 

 

  MySQL에서 공간 데이터 타입의 컬럼을 추가하고, 공간 데이터를 입력해보도록 하겠습니다.

CREATE TABLE GEO_TAB
( 
  GID  INT(10),
  PT   POINT,
  LS   LINESTRING
);

  위 SQL은 테스트용 테이블 GEO_TAB을 생성하는 DDL문입니다. GEO_TAB 테이블은 일반 INT형 GID 컬럼, POINT 공간데이터 타입인 PT 컬럼과 LINESTRING 공간데이터 타입인 LS 컬럼으로 구성됩니다.

  이제 위에서 생성한 GEO_TAB 테이블에 데이터를 입력해보도록 하겠습니다. 

SET @g1 = 'POINT(10 10)';
SET @g2 = GeomFromText( 'LINESTRING(5 5, 10 5, 15 10)' );
INSERT INTO GEO_TAB VALUES ( 1, GeomFromText(@g1), @g2 );

  데이터 입력 예제의 가독성을 위해 사용자 변수를 사용하여 데이터를 입력해보았습니다.
  사용자변수 g1에는 POINT형 공간 데이터를 입력하기 위한 공간 데이터 문자열을 저장합니다. 사용자 변수 g2에는 LINESTRING형 공간 데이터를 저장합니다.
  이 사용자 변수의 값을 GEO_TAB 테이블에 입력을하는데 공간 데이터의 입력도 일반 RDBMS처럼 INSERT문을 사용합니다.
  이 때, 공간 데이터 타입은 Binary 형태로 저장되기 때문에 문자열을 저장한 사용자 변수 g1은 공간 데이터 타입으로 변환해주는 GeomFromText 함수를 사용하여 Binary로 변환 후 저장해야 합니다.
(* GeomFromText 함수는 MySQl 8.0에서 Remove되었으며, ST_GeomFromText 함수를 사용하면 됩니다. )

  공간 데이터를 조회하기 위해서는 Binary 데이터를 텍스트로 변환하는 AsText함수를 사용하면 됩니다.
(* AsText 함수는 MySQl 8.0에서 Remove되었으며, ST_AsText 함수를 사용하면 됩니다. )

SELECT GID, AsText(PT), AsText(LS) FROM GEO_TAB; 

 

  아래 그림은 MySQL에서 테스트한 결과 화면입니다.

 

 

  이어서, 아래 포스트에서 공간 관계 함수와 공간 연산 함수에 대해 설명드리도록 하겠습니다.

 

 

공간 관계 함수(Spatial Relation Functions)

이전 포스팅에서는 공간 데이터 타입에 대해 알아봤습니다. 공간 데이터 타입(Spatial Data Type) Spatial(공간) DB가 필요한 이유? 혹시 아래 나열된 서비스나 시스템을 사용해보신 적이 있으신가요? 버스정보시..

sparkdia.tistory.com

 

공간 연산 함수(Spatial Operator Functions)

공간 연산 함수를 이해하기에 앞서 아래 포스트를 먼저 참고하실 것을 권장드립니다. 공간 데이터 타입(Spatial Data Type) Spatial(공간) DB가 필요한 이유? 혹시 아래 나열된 서비스나 시스템을 사용해보신 적이..

sparkdia.tistory.com

 

 

  혹시 아래 나열된 서비스나 시스템을 사용해보신 적이 있으신가요?

버스정보시스템(버스 실시간 도착 알림)
차량 네비게이션 시스템
지하철 앱
지도 앱
차량 호출 서비스
가까운 상점 찾기
지역 쿠폰 수신
SNS 등록(장소 정보 입력)
...

 

  위 서비스/시스템을 한 번이라도 사용하신 적이 있다면 직/간접적으로 공간 데이터와 조우하신 겁니다.

  공간 데이터란 말 그대로 이 세상에 존재하는 위치 및 영역을 X, Y 좌표로 표현한 데이터입니다.
  예를 들어, 내가 지금 서 있는 위치(점), 지하철 노선(선), 건물 영역(다각형) 등을 X, Y 좌표(예: 경도, 위도)로 표현할 수 있습니다. 이러한 공간 데이터를 활용하여 위와 같은 위치 기반 서비스가 제공될 수 있는 겁니다.

  스마트 폰 없이는 살 수 없는 시대를 살아가고 있는 우리는 매일 공간 데이터를 소비하고 생산하며 살아가고 있습니다. 매일매일 생산되고 있는 대용량의 공간 데이터. 생산되는 데이터의 양이 증가될수록 데이터를 관리하는 기술 또한 발전이 필요합니다. 공간 데이터도 마찬가지입니다. 일반적인 데이터(숫자, 문자, 날짜)와는 표현 방식이나 연산 방식이 상이하기 때문에 별도의 저장/관리 기술이 필요로 합니다.


 

  우선, 일반 RDBMS에서 공간 데이터를 저장하고 연산하는 방법을 살펴보도록 하겠습니다.

  선이나 다각형 데이터는 연속된 점 좌표로 구성됩니다. 이러한 다수의 점 좌표를 일반 RDBMS에서 저장하는 방법은 두 가지가 존재합니다.
  1) 좌표 엔터티를 별도로 생성하고, 하나의 좌표를 하나의 로우로 저장. 좌표를 구별할 수 있는 별도의 식별자가 필요함.
  2) 점 좌표 구분자를 사용하여 하나의 문자열로 저장

  아래 ERD는 상점 정보와 상점이 위치한 건물의 정보를 저장하기 위한 엔터티를 보여주고 있습니다.
  왼쪽은 1) 번 방식으로 설계한 ERD이며, 오른쪽은 2) 번 방식으로 설계한 ERD입니다.

상점 엔터티와 건물 엔터티

  1)번 방식으로 설계한 엔터티 중에서 좌표 값을 저장하는 건물영역좌표에는 아래와 같이 좌표 정보가 저장될 것입니다.  

  위와 같이 좌표 값을 저장하기 위한 하위 엔터티를 생성하는 것은 추가적인 저장 공간을 필요로 합니다. 그리고 하나의 건물영역좌표가 다수의 로우로 표현되기에 다수의 블록에 분산되어 저장될 가능성이 높습니다. 이는 관련 테이블 액세스 시 블록 액세스가 증가되어 성능 부하가 존재할 수 있게 됩니다.

 

  2) 번 방식으로 설계한 엔터티 중에서 좌표 값을 저장하는 건물에는 아래와 같이 좌표 정보가 저장될 것입니다. 연속된 점 좌표를 콤마(,)를 이용하여 구분해 공간 데이터를 저장하고 있습니다.

  위 데이터를 이용해 공간 연산을 하기 위해서는 문자열 형태(x1 y1, x2 y2, … xn yn)인 좌표 값을 점 좌표(x, y)로 변환하는 작업이 선행되어야 합니다.

  아래 사용자 위치에서 가까운 순서대로'GAS STATION'목록을 조회하는 SQL (Oracle)을 살펴보겠습니다. 

WITH BLDS AS (
     SELECT S.STORE_ID
          , S.STORE_NM
          , B.BLD_ID
          , B.BLD_ADDR
          , B.BLD_AREA_CRD
       FROM STORE S
          , BLD B
      WHERE S.STORE_TYPE = 'GAS STATION'
        AND S.BLD_ID = B.BLD_ID
)
SELECT STORE_ID
     , STORE_NM
     , BLD_ID
     , BLD_ADDR
     , DIST 
  FROM (
        SELECT STORE_ID
             , STORE_NM
             , BLD_ID
             , BLD_ADDR
             , DIST
             , ROW_NUMBER() OVER(PARTITION BY BLD_ID ORDER BY DIST) CRD_RN
          FROM (
                SELECT STORE_ID
                     , STORE_NM
                     , BLD_ID
                     , BLD_ADDR
                     , SQRT(ABS(POWER(REGEXP_SUBSTR(BLD_CRD, '[^ ]+', 1, 1) - :USER_X_CRD, 2))+ ABS(POWER(REGEXP_SUBSTR(BLD_CRD, '[^ ]+', 1, 2) - :USER_Y_CRD, 2))) AS DIST
                  FROM (
                        SELECT STORE_ID
                             , STORE_NM
                             , BLD_ID
                             , BLD_ADDR
                             , BLD_CRD_SEQ
                             , REGEXP_SUBSTR(BLD_AREA_CRD, '[^,]+', 1, BLD_CRD_SEQ) AS BLD_CRD
                          FROM BLDS
                             , (SELECT LEVEL AS BLD_CRD_SEQ
                                  FROM DUAL
                               CONNECT BY LEVEL <= ( SELECT MAX( LENGTH(BLD_AREA_CRD) - LENGTH(REPLACE(BLD_AREA_CRD, ',', ''))) + 1 FROM BLDS ) ) L
                     ) A
                 WHERE BLD_CRD IS NOT NULL
             ) B
     ) C
 WHERE C.CRD_RN = 1
 ORDER BY DIST;

  건물영역좌표(BLD_AREA_CRD)를 다수의 로우로 변경하기 위해 CONNECT BY LEVEL을 사용해 카테시안조인을 하였습니다. 그리고 점 좌표를 콤마(,) 기준으로 분리하기 위해 정규식인 REGEXP_SUBSTR 함수를 사용하였습니다.
  위와 같은 방식으로 쿼리를 작성하다 보니 가독성이 떨어지고 공간 연산을 위한 수식이 복잡해집니다.

 


  그럼 공간 데이터베이스에서 공간 데이터를 저장하고 연산하는 방법을 살펴보겠습니다.

  아래 ERD는 건물영역의 좌표 값을 POLYGON 데이터 타입인 건물Polygon 속성에 저장하는 건물 엔터티를 보여주고 있습니다.

  건물 엔터티에는 아래와 같이 데이터가 저장될 것입니다.

 

  그리고 두 공간 객체 간의 거리를 계산하는 ST_DISTANCE 함수를 사용하여, 사용자 위치에서 가까운 순서대로'GAS STATION'목록을 조회하는 SQL (MySQL)을 살펴보겠습니다. 

SELECT S.STORE_ID
     , S.STORE_NM
     , B.BLD_ID
     , B.BLD_ADDR
     , B.BLD_POLYGON
     , ST_DISTANCE(B.BLD_POLYGON, POINT(@USER_X_CRD, @USER_Y_CRD) ) DIST
  FROM STORE S
     , BLD B
 WHERE S.STORE_TYPE = 'GAS STATION'
   AND S.BLD_ID = B.BLD_ID
 ORDER BY DIST;

  일반 RDBMS에서의 예와 비교하여 쿼리가 단순해지고 가독성이 좋아진 것을 확인하실 수 있습니다. 또한, 다양한 공간 함수를 제공하여 복잡한 연산도 쉽게 처리가 가능합니다.

 

+ Recent posts