데이터 소스 간 데이터 이동. 이는 데이터 통합 프로젝트의 핵심 활동 중 하나입니다. 전통적으로 데이터 이동과 관련된 기법은 데이터 웨어하우스, BI 및 분석. 최근에는 빅 데이터, 데이터 레이크, 하둡이 이 분야에서 자주 사용됩니다.
이 항목에서는 이러한 종류의 시나리오에서 데이터를 대규모로 조작하기 위해 Couchbase N1QL 언어를 사용하는 방법에 대해 설명합니다.
먼저, 데이터 이동을 수행할 때 두 가지 고전적인 접근 방식을 기억해 보겠습니다:
ETL (추출-변환-로드). 이 모델을 사용하면 데이터는 다음과 같습니다. 추출 (원본 데이터 소스에서 가져옴), 변형 (데이터가 대상 시스템에 맞게 다시 포맷됨) 및 로드 (대상 데이터 저장소에서).
ELT (추출-로드-변환). 이 모델을 사용하면 데이터는 다음과 같습니다. 추출 (원본 데이터 소스에서 가져옴), 로드 를 대상 시스템에 동일한 형식으로 저장합니다. 그런 다음 변환 를 사용하여 원하는 데이터 형식을 얻습니다.
우리는 다음 사항에 집중할 것입니다. ELT 연습을 해보겠습니다. 관계형 데이터베이스에서 간단한 내보내기를 수행하고 데이터를 Couchbase에 로드해 보겠습니다. 입력 데이터 소스로는 인사 부서를 모델링하는 고전적인 HR 스키마 예제가 내장된 Oracle 데이터베이스를 사용합니다.
이것이 소스 데이터 모델입니다:

첫 번째 단계에서는 동일한 구조로 데이터를 로드합니다. 이 초기 마이그레이션을 수행하는 데 사용할 수 있는 무료 도구가 있습니다. 여기. 마지막에는 이 테이블 모델을 매핑하는 JSON 문서가 있습니다:

예를 들어 위치 문서는 다음과 같이 표시됩니다:
|
1 2 3 4 5 6 7 8 9 10 |
{ "street_address": "2017 Shinjuku-ku", "city": "Tokyo", "state_province": "Tokyo Prefecture", "postal_code": "1689", "type": "locations", "location_id": 1200, "country_id": "JP" } |
이것은 쉬운 첫 단계였습니다. 그러나 이러한 테이블과 문서 간의 매핑은 NoSQL 세계에서는 종종 잘못된 설계입니다. NoSQL에서는 참조된 데이터를 포함하는 보다 직접적인 액세스 경로를 위해 데이터의 정규화를 해제하는 경우가 빈번합니다. 목표는 데이터베이스 상호 작용과 조인을 최소화하여 최상의 성능을 찾는 것입니다.
직원의 전체 작업 기록에 자주 액세스해야 하는 사용 사례가 있다고 가정해 보겠습니다. 디자인을 다음과 같이 변경하기로 결정합니다:

위치의 경우, 국가 및 지역에 대한 참조 데이터를 단일 위치 문서에 조인하고 있습니다.
직원 문서의 경우 부서 데이터를 포함하고 전체 업무 기록 또는 각 직원이 포함된 배열을 포함할 것입니다. JSON의 이러한 배열 지원은 관계형 세계에서 외래 키 참조 및 조인에 비해 크게 개선된 기능입니다.
작업 문서의 경우 원래 테이블 구조를 유지합니다.
그래서 우리는 추출 그리고 로드 이제 우리는 변환 이 모델을 완성하기 위해 ELT 예제. 이 작업을 어떻게 할 수 있을까요? N1QL을 사용할 때입니다.
N1QL은 데이터 액세스 및 데이터 조작을 위해 Couchbase에 포함된 SQL과 유사한 언어입니다. 이 예에서는 두 개의 버킷을 사용하겠습니다: 원래 Oracle HR 스키마에 매핑되는 HR과 대상 문서 모델을 보유할 HR_DNORM입니다.
이미 HR 스키마를 로드했습니다. 다음 단계는 HR_DNORM이라는 버킷을 만드는 것입니다. 그런 다음 이 새 버킷에 기본 인덱스를 생성합니다:
|
1 |
CREATE PRIMARY INDEX ON HR_DNORM |
이제 위치 문서를 만들 차례입니다. 이 문서는 원본 위치, 국가 및 지역 문서로 구성됩니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
INSERT INTO HR_DNORM (key _k, value _v) SELECT meta().id _k, { "type":"location", "city":loc.city, "postal_code":loc.postal_code, "state_province":IFNULL(loc.state_province, null), "street_address":loc.street_address, "country_name":ct.country_name, "region_name":rg.region_name } as _v FROM HR loc JOIN HR ct ON KEYS "countries::" || loc.country_id JOIN HR rg ON KEYS "regions::" || TO_STRING(ct.region_id) WHERE loc.type="locations" |
몇 가지 주목해야 할 사항이 있습니다:
- 여기서는 삽입을 위해 SELECT 문의 투영을 사용하고 있습니다. 이 예제에서 원본 데이터는 다른 버킷에서 가져온 것입니다.
- JOIN은 원래 버킷에서 국가 및 지역을 참조하는 데 사용됩니다.
- state_province 필드에 명시적으로 null 값을 설정하는 데 사용되는 IFNULL 함수
- 키 참조를 위해 숫자 필드에 적용된 TO_STRING 함수
원본 샘플은 이렇게 됩니다:
|
1 2 3 4 5 6 7 8 9 |
{ "city": "Tokyo", "country_name": "Japan", "postal_code": "1689", "region_name": "Asia", "state_province": "Tokyo Prefecture", "street_address": "2017 Shinjuku-ku", "type": "location" } |
참조 위치_id 및 국가_id를 제거했습니다.
이제 직원 문서를 작성할 차례입니다. 여러 단계로 진행하겠습니다. 첫 번째는 부서 및 실제 직무 정보를 포함하여 원래 HR 버킷에서 직원을 만드는 것입니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
INSERT INTO HR_DNORM (key _k, value _v) SELECT meta().id _k, { "type":"employees", "employee_id": emp. employee_id, "first_name": emp.first_name, "last_name": emp.last_name, "phone_number": emp.phone_number, "email": emp.email, "hire_date": emp.hire_date, "salary": emp.salary, "commission_pct": IFNULL(emp.commission_pct, null), "manager_id": IFNULL(emp.manager_id, null), "job_id": emp.job_id, "job_title": job.job_title, "department" : { "name" : dpt.department_name, "manager_id" : dpt.manager_id, "department_id" : dpt.department_id } } as _v FROM HR emp JOIN HR job ON KEYS "jobs::" || emp.job_id JOIN HR dpt ON KEYS "departments::" || TO_STRING(emp.department_id) WHERE emp.type="employees" RETURNING META().id; |
둘째, 임시 구조를 사용하여 작업 기록 배열을 구축합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
INSERT INTO HR_DNORM (key _k, value job_history) SELECT "job_history::" || TO_STRING(jobh.employee_id) AS _k, { "jobs" : ARRAY_AGG( { "start_date": jobh.start_date, "end_date": jobh.end_date, "job_id": jobh.job_id, "department_id": jobh.department_id } ) } AS job_history FROM HR jobh WHERE jobh.type="job_history" GROUP BY jobh.employee_id RETURNING META().id; |
이제 job_history 배열을 추가하여 직원 문서를 쉽게 업데이트할 수 있습니다:
|
1 2 3 4 5 6 7 8 9 |
UPDATE HR_DNORM emp SET job_history=( SELECT RAW jobs FROM HR_DNORM jobh USE KEYS "job_history::" || SUBSTR(meta(emp).id, 11) )[0] WHERE emp.type="employees" RETURNING meta().id |
직원 문서는 이렇게 생겼습니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
{ "commission_pct": null, "department": { "department_id": 10, "manager_id": 200, "name": "Administration" }, "email": "JWHALEN", "employee_id": 200, "first_name": "Jennifer", "hire_date": "2003-09-16T22:00:00Z", "job_history": [ { "department_id": 80, "end_date": "2007-12-31T23:00:00Z", "job_id": "SA_REP", "start_date": "2006-12-31T23:00:00Z" }, { "department_id": 90, "end_date": "2001-06-16T22:00:00Z", "job_id": "AD_ASST", "start_date": "1995-09-16T22:00:00Z" }, { "department_id": 90, "end_date": "2006-12-30T23:00:00Z", "job_id": "AC_ACCOUNT", "start_date": "2002-06-30T22:00:00Z" } ], "job_id": "AD_ASST", "job_title": "Administration Assistant", "last_name": "Whalen", "manager_id": 101, "phone_number": "515.123.4444", "salary": 4400, "type": "employees" } |
이전 직책의 job_history 배열에 주목하세요.
이제 임시 job_history 문서를 삭제할 수 있습니다:
|
1 2 |
DELETE FROM HR_DNORM emp WHERE meta().id LIKE "job_history::%" |
마지막 단계로 원본 작업 문서를 삽입합니다:
|
1 2 3 4 |
INSERT INTO HR_DNORM (key _k, value _v) SELECT meta().id _k, _v FROM HR _v WHERE _v.type="jobs" |
끝났습니다. 이것은 간단한 예이지만 N1QL 데이터 조작이 강력할 수 있음을 보여줍니다. 행복한 데이터 마이그레이션!