shp2pgsql_local.sh
· 12 KiB · Bash
Raw
#!/bin/bash
DB="yourDb" # 실제 데이터베이스 이름으로 변경
USER="postgres" # 실제 PostgreSQL 사용자명으로 변경
SCHEMA="yourSchema" # 스키마 이름
TABLE="yourTable" # 생성할 테이블 이름
# 대소문자 구분 문제 해결을 위해 소문자로 변환
SCHEMA_LOWER=$(echo "$SCHEMA" | tr '[:upper:]' '[:lower:]')
TABLE_LOWER=$(echo "$TABLE" | tr '[:upper:]' '[:lower:]')
SRID_ORI=5186 # 원본 좌표계
SRID_NEW=0 # 변환 좌표계 (0이면 좌표변환 안함)
CHARSET="UTF-8" # 문자 인코딩
SHP_DIR="./" # shp 파일들이 있는 디렉토리 경로
MAX_JOBS=2 # 병렬 작업 수 (시스템 성능에 따라 조정)
# 로그 파일 설정
LOG_DIR="./" # 로그 디렉토리 (필요시 경로 변경)
mkdir -p "$LOG_DIR" # 로그 디렉토리가 없으면 생성
LOG_FILE="$LOG_DIR/shp_import_$(date '+%Y%m%d_%H%M%S').log"
# 로그 함수 정의
logger() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# job control 함수 (병렬 처리 제한)
function wait_for_jobs() {
local current_jobs
while true; do
current_jobs=$(jobs -rp | wc -l)
if [ "$current_jobs" -lt "$MAX_JOBS" ]; then
break
fi
sleep 0.5
done
}
# append 함수 정의 (COPY 모드 전용)
function import_append() {
SHP="$1"
BASENAME=$(basename "$SHP")
# 처리 시작 시간 기록
FILE_START_TIME=$(date '+%Y-%m-%d %H:%M:%S')
FILE_START_TIMESTAMP=$(date +%s)
logger "🚀 [시작] $BASENAME 처리 시작: $FILE_START_TIME"
# COPY 모드로 처리 (가장 빠름)
if [ "$SRID_NEW" -ne 0 ]; then
OUTPUT=$(shp2pgsql -D -a -W "$CHARSET" -s $SRID_ORI:$SRID_NEW "$SHP" $SCHEMA_LOWER.$TABLE_LOWER | psql -q -U "$USER" -d "$DB")
else
OUTPUT=$(shp2pgsql -D -a -W "$CHARSET" -s $SRID_ORI "$SHP" $SCHEMA_LOWER.$TABLE_LOWER | psql -q -U "$USER" -d "$DB")
fi
EXIT_CODE=$?
# 처리 종료 시간 기록
FILE_END_TIME=$(date '+%Y-%m-%d %H:%M:%S')
FILE_END_TIMESTAMP=$(date +%s)
FILE_DURATION=$((FILE_END_TIMESTAMP - FILE_START_TIMESTAMP))
# 파일별 소요시간을 시:분:초 형식으로 변환
FILE_HOURS=$((FILE_DURATION / 3600))
FILE_MINUTES=$(((FILE_DURATION % 3600) / 60))
FILE_SECONDS=$((FILE_DURATION % 60))
# 1보다 작으면 0으로 표시
if [ $FILE_HOURS -lt 1 ]; then FILE_HOURS=0; fi
if [ $FILE_MINUTES -lt 1 ]; then FILE_MINUTES=0; fi
if [ $EXIT_CODE -eq 0 ]; then
logger "✅ [완료] $BASENAME 처리 완료: $FILE_END_TIME (소요: ${FILE_HOURS}시간 ${FILE_MINUTES}분 ${FILE_SECONDS}초)"
else
logger "❌ [실패] $BASENAME 처리 실패: $FILE_END_TIME (소요: ${FILE_HOURS}시간 ${FILE_MINUTES}분 ${FILE_SECONDS}초)"
# 오류 내용을 로그에 기록
logger "⚠️ 오류 내용: $OUTPUT"
echo "=== $BASENAME 처리 오류 상세 내용 ===" >> "$LOG_FILE"
echo "$OUTPUT" >> "$LOG_FILE"
echo "=====================================" >> "$LOG_FILE"
logger "🔄 다음 파일 처리를 계속 진행합니다."
fi
}
# 시작 시간 기록
START_TIME=$(date '+%Y-%m-%d %H:%M:%S')
START_TIMESTAMP=$(date +%s)
# SHP 디렉토리 및 파일 존재 확인
if [ ! -d "$SHP_DIR" ]; then
logger "❌ 오류: SHP 디렉토리가 존재하지 않습니다: $SHP_DIR"
exit 1
fi
SHP_COUNT=$(find "$SHP_DIR" -name "*.shp" | wc -l)
if [ $SHP_COUNT -eq 0 ]; then
logger "❌ 오류: SHP 파일이 없습니다: $SHP_DIR"
exit 1
fi
logger "📁 발견된 SHP 파일 수: $SHP_COUNT개"
# 필수 변수 검증
if [ -z "$DB" ]; then
logger "❌ 오류: DB 변수가 설정되지 않았습니다. 스크립트 상단에서 DB 변수를 설정해주세요."
exit 1
fi
if [ -z "$TABLE" ]; then
logger "❌ 오류: TABLE 변수가 설정되지 않았습니다. 스크립트 상단에서 TABLE 변수를 설정해주세요."
exit 1
fi
logger "📋 설정된 변수:"
logger " - 데이터베이스: $DB"
logger " - 사용자: $USER"
logger " - 스키마: $SCHEMA"
logger " - 테이블: $TABLE"
# PostgreSQL 연결 테스트
if ! psql -q -U "$USER" -d "$DB" -c "SELECT 1;" >/dev/null 2>&1; then
logger "❌ 오류: PostgreSQL 연결 실패. 데이터베이스 연결을 확인해주세요."
exit 1
fi
# 스키마 존재 확인 및 생성
SCHEMA_EXISTS=$(psql -q -U "$USER" -d "$DB" -t -c "SELECT EXISTS (SELECT 1 FROM information_schema.schemata WHERE schema_name = '$SCHEMA_LOWER');" 2>/dev/null | xargs)
if [ "$SCHEMA_EXISTS" != "t" ]; then
logger "📋 스키마 $SCHEMA_LOWER 생성 중..."
SCHEMA_OUTPUT=$(psql -q -U "$USER" -d "$DB" -c "CREATE SCHEMA IF NOT EXISTS $SCHEMA_LOWER;")
if [ $? -ne 0 ]; then
logger "❌ 스키마 생성 실패: $SCHEMA_OUTPUT"
exit 1
fi
fi
# PostGIS 확장 확인
POSTGIS_EXISTS=$(psql -q -U "$USER" -d "$DB" -t -c "SELECT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'postgis');" 2>/dev/null | xargs)
if [ "$POSTGIS_EXISTS" != "t" ]; then
logger "⚠️ PostGIS 확장 설치 중..."
POSTGIS_OUTPUT=$(psql -q -U "$USER" -d "$DB" -c "CREATE EXTENSION IF NOT EXISTS postgis;")
if [ $? -ne 0 ]; then
logger "❌ PostGIS 확장 설치 실패: $POSTGIS_OUTPUT"
logger "⚠️ PostGIS 확장을 수동으로 설치해주세요."
exit 1
fi
fi
# 좌표계 설정 확인 및 로깅
if [ "$SRID_NEW" -ne 0 ]; then
logger "🗺️ 좌표변환 모드: $SRID_ORI → $SRID_NEW"
else
logger "🗺️ 원본좌표계 사용: $SRID_ORI"
fi
logger "=================================="
logger "🚀 Import 시작: $START_TIME"
logger "=================================="
# 성능 최적화 설정 로깅
logger "⚡ 성능 최적화 설정:"
logger " - COPY 모드: ✅ 활성화 (INSERT 대비 10-100배 빠름)"
logger " - 병렬 작업 수: $MAX_JOBS개"
# 테이블 존재 여부 확인
TABLE_EXISTS=$(psql -q -U "$USER" -d "$DB" -t -c "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = '$SCHEMA_LOWER' AND table_name = '$TABLE_LOWER');" 2>/dev/null | xargs)
# 모든 SHP 파일을 배열로 수집 (공백이 포함된 파일명도 안전하게 처리)
mapfile -t SHP_FILES < <(find "$SHP_DIR" -name "*.shp" | sort)
TOTAL_FILES=${#SHP_FILES[@]}
# 배열이 비어있거나 null인 경우 체크
if [ ${#SHP_FILES[@]} -eq 0 ] || [ -z "${SHP_FILES[0]}" ]; then
logger "❌ 오류: 유효한 SHP 파일을 찾을 수 없습니다."
exit 1
fi
if [ "$TABLE_EXISTS" = "t" ]; then
logger "📋 테이블 $SCHEMA_LOWER.$TABLE_LOWER이 이미 존재합니다. Append 모드로 진행합니다."
logger "🔄 총 $TOTAL_FILES개 파일을 append 모드로 처리합니다..."
# 모든 파일을 append 모드로 병렬 처리
for shp in "${SHP_FILES[@]}"; do
if [ -n "$shp" ]; then
wait_for_jobs
import_append "$shp" &
logger "🔄 백그라운드 작업 시작: $(basename "$shp")"
fi
done
wait
logger "✅ 모든 append 작업 완료"
else
logger "📋 테이블 $SCHEMA_LOWER.$TABLE_LOWER이 존재하지 않습니다. 새로 생성합니다."
logger "🔄 총 $TOTAL_FILES개 파일을 처리합니다..."
# 첫 번째 파일로 테이블 생성
if [ $TOTAL_FILES -gt 0 ]; then
FIRST_FILE="${SHP_FILES[0]}"
FIRST_BASENAME=$(basename "$FIRST_FILE")
# 테이블 생성 시작 시간 기록
TABLE_START_TIME=$(date '+%Y-%m-%d %H:%M:%S')
TABLE_START_TIMESTAMP=$(date +%s)
logger "🚀 [시작] 테이블 구조 생성 시작: $FIRST_BASENAME - $TABLE_START_TIME"
# 좌표변환 여부에 따라 shp2pgsql 명령어 결정 (테이블 구조만 생성, 데이터는 나중에)
if [ "$SRID_NEW" -ne 0 ]; then
OUTPUT=$(shp2pgsql -p -W "$CHARSET" -s $SRID_ORI:$SRID_NEW "$FIRST_FILE" $SCHEMA_LOWER.$TABLE_LOWER | psql -q -U "$USER" -d "$DB")
else
OUTPUT=$(shp2pgsql -p -W "$CHARSET" -s $SRID_ORI "$FIRST_FILE" $SCHEMA_LOWER.$TABLE_LOWER | psql -q -U "$USER" -d "$DB")
fi
EXIT_CODE=$?
# 테이블 생성 종료 시간 기록
TABLE_END_TIME=$(date '+%Y-%m-%d %H:%M:%S')
TABLE_END_TIMESTAMP=$(date +%s)
TABLE_DURATION=$((TABLE_END_TIMESTAMP - TABLE_START_TIMESTAMP))
# 테이블 생성 소요시간을 시:분:초 형식으로 변환
TABLE_HOURS=$((TABLE_DURATION / 3600))
TABLE_MINUTES=$(((TABLE_DURATION % 3600) / 60))
TABLE_SECONDS=$((TABLE_DURATION % 60))
# 1보다 작으면 0으로 표시
if [ $TABLE_HOURS -lt 1 ]; then TABLE_HOURS=0; fi
if [ $TABLE_MINUTES -lt 1 ]; then TABLE_MINUTES=0; fi
if [ $EXIT_CODE -eq 0 ]; then
logger "✅ [완료] 테이블 구조 생성 완료: $TABLE_END_TIME (소요: ${TABLE_HOURS}시간 ${TABLE_MINUTES}분 ${TABLE_SECONDS}초)"
# 모든 파일을 한꺼번에 병렬 처리로 데이터 적재 (첫 번째 파일 포함)
logger "🔄 모든 $TOTAL_FILES개 파일을 병렬 처리로 데이터 적재합니다..."
for shp in "${SHP_FILES[@]}"; do
if [ -n "$shp" ]; then
wait_for_jobs
import_append "$shp" &
logger "🔄 백그라운드 작업 시작: $(basename "$shp")"
fi
done
wait
logger "✅ 모든 데이터 적재 작업 완료"
else
logger "❌ [실패] 테이블 구조 생성 실패: $TABLE_END_TIME (소요: ${TABLE_HOURS}시간 ${TABLE_MINUTES}분 ${TABLE_SECONDS}초)"
echo "$OUTPUT" >> "$LOG_FILE"
exit 1
fi
else
logger "❌ 오류: 처리할 SHP 파일이 없습니다."
exit 1
fi
fi
# 병렬 처리 완료 확인 및 요약
logger "🔄 병렬 처리 완료 확인 중..."
logger "📊 처리된 파일 수: $TOTAL_FILES개"
# 데이터 입력 완료 시간 기록
DATA_END_TIME=$(date '+%Y-%m-%d %H:%M:%S')
DATA_END_TIMESTAMP=$(date +%s)
DATA_DURATION=$((DATA_END_TIMESTAMP - START_TIMESTAMP))
# 데이터 입력 시간을 시:분:초 형식으로 변환
DATA_HOURS=$((DATA_DURATION / 3600))
DATA_MINUTES=$(((DATA_DURATION % 3600) / 60))
DATA_SECONDS=$((DATA_DURATION % 60))
# 1보다 작으면 0으로 표시
if [ $DATA_HOURS -lt 1 ]; then DATA_HOURS=0; fi
if [ $DATA_MINUTES -lt 1 ]; then DATA_MINUTES=0; fi
# 3. (선택) 인덱스 재생성 (권장: 한 번에 생성)
INDEX_START_TIME=$(date '+%Y-%m-%d %H:%M:%S')
INDEX_START_TIMESTAMP=$(date +%s)
logger "🔍 공간 인덱스 생성 시작: $INDEX_START_TIME"
# 공간 인덱스 생성 (PostGIS 확장이 활성화되어 있어야 함)
INDEX_OUTPUT=$(psql -q -U "$USER" -d "$DB" -c "CREATE INDEX IF NOT EXISTS ${TABLE_LOWER}_geom_idx ON $SCHEMA_LOWER.$TABLE_LOWER USING GIST (geom);")
INDEX_EXIT_CODE=$?
if [ $INDEX_EXIT_CODE -ne 0 ]; then
# 오류 내용을 로그에 기록
logger "⚠️ 인덱스 생성 오류: $INDEX_OUTPUT"
echo "=== 공간 인덱스 생성 오류 상세 내용 ===" >> "$LOG_FILE"
echo "$INDEX_OUTPUT" >> "$LOG_FILE"
echo "=====================================" >> "$LOG_FILE"
logger "🔄 인덱스 생성에 실패했지만 스크립트는 계속 진행됩니다."
fi
# 인덱스 재생성 완료 시간 기록
INDEX_END_TIME=$(date '+%Y-%m-%d %H:%M:%S')
INDEX_END_TIMESTAMP=$(date +%s)
INDEX_DURATION=$((INDEX_END_TIMESTAMP - INDEX_START_TIMESTAMP))
# 인덱스 재생성 시간을 시:분:초 형식으로 변환
INDEX_HOURS=$((INDEX_DURATION / 3600))
INDEX_MINUTES=$(((INDEX_DURATION % 3600) / 60))
INDEX_SECONDS=$((INDEX_DURATION % 60))
# 1보다 작으면 0으로 표시
if [ $INDEX_HOURS -lt 1 ]; then INDEX_HOURS=0; fi
if [ $INDEX_MINUTES -lt 1 ]; then INDEX_MINUTES=0; fi
logger "=================================="
logger "✅ 작업 완료"
logger "📋 대상테이블: $SCHEMA_LOWER.$TABLE_LOWER"
if [ "$SRID_NEW" -ne 0 ]; then
logger "🗺️ 좌표계: $SRID_ORI → $SRID_NEW (변환됨)"
else
logger "🗺️ 좌표계: $SRID_ORI (원본)"
fi
logger "⏱️ 데이터 입력 소요시간: ${DATA_HOURS}시간 ${DATA_MINUTES}분 ${DATA_SECONDS}초"
logger "⏱️ 공간 인덱스 소요시간: ${INDEX_HOURS}시간 ${INDEX_MINUTES}분 ${INDEX_SECONDS}초"
logger "=================================="
| 1 | #!/bin/bash |
| 2 | |
| 3 | DB="yourDb" # 실제 데이터베이스 이름으로 변경 |
| 4 | USER="postgres" # 실제 PostgreSQL 사용자명으로 변경 |
| 5 | SCHEMA="yourSchema" # 스키마 이름 |
| 6 | TABLE="yourTable" # 생성할 테이블 이름 |
| 7 | |
| 8 | # 대소문자 구분 문제 해결을 위해 소문자로 변환 |
| 9 | SCHEMA_LOWER=$(echo "$SCHEMA" | tr '[:upper:]' '[:lower:]') |
| 10 | TABLE_LOWER=$(echo "$TABLE" | tr '[:upper:]' '[:lower:]') |
| 11 | |
| 12 | SRID_ORI=5186 # 원본 좌표계 |
| 13 | SRID_NEW=0 # 변환 좌표계 (0이면 좌표변환 안함) |
| 14 | CHARSET="UTF-8" # 문자 인코딩 |
| 15 | |
| 16 | SHP_DIR="./" # shp 파일들이 있는 디렉토리 경로 |
| 17 | MAX_JOBS=2 # 병렬 작업 수 (시스템 성능에 따라 조정) |
| 18 | |
| 19 | |
| 20 | # 로그 파일 설정 |
| 21 | LOG_DIR="./" # 로그 디렉토리 (필요시 경로 변경) |
| 22 | mkdir -p "$LOG_DIR" # 로그 디렉토리가 없으면 생성 |
| 23 | LOG_FILE="$LOG_DIR/shp_import_$(date '+%Y%m%d_%H%M%S').log" |
| 24 | |
| 25 | # 로그 함수 정의 |
| 26 | logger() { |
| 27 | echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" |
| 28 | } |
| 29 | |
| 30 | # job control 함수 (병렬 처리 제한) |
| 31 | function wait_for_jobs() { |
| 32 | local current_jobs |
| 33 | while true; do |
| 34 | current_jobs=$(jobs -rp | wc -l) |
| 35 | if [ "$current_jobs" -lt "$MAX_JOBS" ]; then |
| 36 | break |
| 37 | fi |
| 38 | sleep 0.5 |
| 39 | done |
| 40 | } |
| 41 | |
| 42 | # append 함수 정의 (COPY 모드 전용) |
| 43 | function import_append() { |
| 44 | SHP="$1" |
| 45 | BASENAME=$(basename "$SHP") |
| 46 | |
| 47 | # 처리 시작 시간 기록 |
| 48 | FILE_START_TIME=$(date '+%Y-%m-%d %H:%M:%S') |
| 49 | FILE_START_TIMESTAMP=$(date +%s) |
| 50 | |
| 51 | logger "🚀 [시작] $BASENAME 처리 시작: $FILE_START_TIME" |
| 52 | |
| 53 | # COPY 모드로 처리 (가장 빠름) |
| 54 | if [ "$SRID_NEW" -ne 0 ]; then |
| 55 | OUTPUT=$(shp2pgsql -D -a -W "$CHARSET" -s $SRID_ORI:$SRID_NEW "$SHP" $SCHEMA_LOWER.$TABLE_LOWER | psql -q -U "$USER" -d "$DB") |
| 56 | else |
| 57 | OUTPUT=$(shp2pgsql -D -a -W "$CHARSET" -s $SRID_ORI "$SHP" $SCHEMA_LOWER.$TABLE_LOWER | psql -q -U "$USER" -d "$DB") |
| 58 | fi |
| 59 | |
| 60 | EXIT_CODE=$? |
| 61 | |
| 62 | # 처리 종료 시간 기록 |
| 63 | FILE_END_TIME=$(date '+%Y-%m-%d %H:%M:%S') |
| 64 | FILE_END_TIMESTAMP=$(date +%s) |
| 65 | FILE_DURATION=$((FILE_END_TIMESTAMP - FILE_START_TIMESTAMP)) |
| 66 | |
| 67 | # 파일별 소요시간을 시:분:초 형식으로 변환 |
| 68 | FILE_HOURS=$((FILE_DURATION / 3600)) |
| 69 | FILE_MINUTES=$(((FILE_DURATION % 3600) / 60)) |
| 70 | FILE_SECONDS=$((FILE_DURATION % 60)) |
| 71 | |
| 72 | # 1보다 작으면 0으로 표시 |
| 73 | if [ $FILE_HOURS -lt 1 ]; then FILE_HOURS=0; fi |
| 74 | if [ $FILE_MINUTES -lt 1 ]; then FILE_MINUTES=0; fi |
| 75 | |
| 76 | if [ $EXIT_CODE -eq 0 ]; then |
| 77 | logger "✅ [완료] $BASENAME 처리 완료: $FILE_END_TIME (소요: ${FILE_HOURS}시간 ${FILE_MINUTES}분 ${FILE_SECONDS}초)" |
| 78 | else |
| 79 | logger "❌ [실패] $BASENAME 처리 실패: $FILE_END_TIME (소요: ${FILE_HOURS}시간 ${FILE_MINUTES}분 ${FILE_SECONDS}초)" |
| 80 | |
| 81 | # 오류 내용을 로그에 기록 |
| 82 | logger "⚠️ 오류 내용: $OUTPUT" |
| 83 | echo "=== $BASENAME 처리 오류 상세 내용 ===" >> "$LOG_FILE" |
| 84 | echo "$OUTPUT" >> "$LOG_FILE" |
| 85 | echo "=====================================" >> "$LOG_FILE" |
| 86 | |
| 87 | logger "🔄 다음 파일 처리를 계속 진행합니다." |
| 88 | fi |
| 89 | } |
| 90 | |
| 91 | |
| 92 | |
| 93 | # 시작 시간 기록 |
| 94 | START_TIME=$(date '+%Y-%m-%d %H:%M:%S') |
| 95 | START_TIMESTAMP=$(date +%s) |
| 96 | |
| 97 | |
| 98 | # SHP 디렉토리 및 파일 존재 확인 |
| 99 | if [ ! -d "$SHP_DIR" ]; then |
| 100 | logger "❌ 오류: SHP 디렉토리가 존재하지 않습니다: $SHP_DIR" |
| 101 | exit 1 |
| 102 | fi |
| 103 | |
| 104 | SHP_COUNT=$(find "$SHP_DIR" -name "*.shp" | wc -l) |
| 105 | if [ $SHP_COUNT -eq 0 ]; then |
| 106 | logger "❌ 오류: SHP 파일이 없습니다: $SHP_DIR" |
| 107 | exit 1 |
| 108 | fi |
| 109 | logger "📁 발견된 SHP 파일 수: $SHP_COUNT개" |
| 110 | |
| 111 | # 필수 변수 검증 |
| 112 | if [ -z "$DB" ]; then |
| 113 | logger "❌ 오류: DB 변수가 설정되지 않았습니다. 스크립트 상단에서 DB 변수를 설정해주세요." |
| 114 | exit 1 |
| 115 | fi |
| 116 | |
| 117 | if [ -z "$TABLE" ]; then |
| 118 | logger "❌ 오류: TABLE 변수가 설정되지 않았습니다. 스크립트 상단에서 TABLE 변수를 설정해주세요." |
| 119 | exit 1 |
| 120 | fi |
| 121 | |
| 122 | logger "📋 설정된 변수:" |
| 123 | logger " - 데이터베이스: $DB" |
| 124 | logger " - 사용자: $USER" |
| 125 | logger " - 스키마: $SCHEMA" |
| 126 | logger " - 테이블: $TABLE" |
| 127 | |
| 128 | # PostgreSQL 연결 테스트 |
| 129 | if ! psql -q -U "$USER" -d "$DB" -c "SELECT 1;" >/dev/null 2>&1; then |
| 130 | logger "❌ 오류: PostgreSQL 연결 실패. 데이터베이스 연결을 확인해주세요." |
| 131 | exit 1 |
| 132 | fi |
| 133 | |
| 134 | # 스키마 존재 확인 및 생성 |
| 135 | SCHEMA_EXISTS=$(psql -q -U "$USER" -d "$DB" -t -c "SELECT EXISTS (SELECT 1 FROM information_schema.schemata WHERE schema_name = '$SCHEMA_LOWER');" 2>/dev/null | xargs) |
| 136 | |
| 137 | if [ "$SCHEMA_EXISTS" != "t" ]; then |
| 138 | logger "📋 스키마 $SCHEMA_LOWER 생성 중..." |
| 139 | SCHEMA_OUTPUT=$(psql -q -U "$USER" -d "$DB" -c "CREATE SCHEMA IF NOT EXISTS $SCHEMA_LOWER;") |
| 140 | if [ $? -ne 0 ]; then |
| 141 | logger "❌ 스키마 생성 실패: $SCHEMA_OUTPUT" |
| 142 | exit 1 |
| 143 | fi |
| 144 | fi |
| 145 | |
| 146 | # PostGIS 확장 확인 |
| 147 | POSTGIS_EXISTS=$(psql -q -U "$USER" -d "$DB" -t -c "SELECT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'postgis');" 2>/dev/null | xargs) |
| 148 | |
| 149 | if [ "$POSTGIS_EXISTS" != "t" ]; then |
| 150 | logger "⚠️ PostGIS 확장 설치 중..." |
| 151 | POSTGIS_OUTPUT=$(psql -q -U "$USER" -d "$DB" -c "CREATE EXTENSION IF NOT EXISTS postgis;") |
| 152 | if [ $? -ne 0 ]; then |
| 153 | logger "❌ PostGIS 확장 설치 실패: $POSTGIS_OUTPUT" |
| 154 | logger "⚠️ PostGIS 확장을 수동으로 설치해주세요." |
| 155 | exit 1 |
| 156 | fi |
| 157 | fi |
| 158 | |
| 159 | # 좌표계 설정 확인 및 로깅 |
| 160 | if [ "$SRID_NEW" -ne 0 ]; then |
| 161 | logger "🗺️ 좌표변환 모드: $SRID_ORI → $SRID_NEW" |
| 162 | else |
| 163 | logger "🗺️ 원본좌표계 사용: $SRID_ORI" |
| 164 | fi |
| 165 | |
| 166 | logger "==================================" |
| 167 | logger "🚀 Import 시작: $START_TIME" |
| 168 | logger "==================================" |
| 169 | |
| 170 | # 성능 최적화 설정 로깅 |
| 171 | logger "⚡ 성능 최적화 설정:" |
| 172 | logger " - COPY 모드: ✅ 활성화 (INSERT 대비 10-100배 빠름)" |
| 173 | logger " - 병렬 작업 수: $MAX_JOBS개" |
| 174 | |
| 175 | # 테이블 존재 여부 확인 |
| 176 | TABLE_EXISTS=$(psql -q -U "$USER" -d "$DB" -t -c "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = '$SCHEMA_LOWER' AND table_name = '$TABLE_LOWER');" 2>/dev/null | xargs) |
| 177 | |
| 178 | # 모든 SHP 파일을 배열로 수집 (공백이 포함된 파일명도 안전하게 처리) |
| 179 | mapfile -t SHP_FILES < <(find "$SHP_DIR" -name "*.shp" | sort) |
| 180 | TOTAL_FILES=${#SHP_FILES[@]} |
| 181 | |
| 182 | # 배열이 비어있거나 null인 경우 체크 |
| 183 | if [ ${#SHP_FILES[@]} -eq 0 ] || [ -z "${SHP_FILES[0]}" ]; then |
| 184 | logger "❌ 오류: 유효한 SHP 파일을 찾을 수 없습니다." |
| 185 | exit 1 |
| 186 | fi |
| 187 | |
| 188 | if [ "$TABLE_EXISTS" = "t" ]; then |
| 189 | logger "📋 테이블 $SCHEMA_LOWER.$TABLE_LOWER이 이미 존재합니다. Append 모드로 진행합니다." |
| 190 | logger "🔄 총 $TOTAL_FILES개 파일을 append 모드로 처리합니다..." |
| 191 | |
| 192 | # 모든 파일을 append 모드로 병렬 처리 |
| 193 | for shp in "${SHP_FILES[@]}"; do |
| 194 | if [ -n "$shp" ]; then |
| 195 | wait_for_jobs |
| 196 | import_append "$shp" & |
| 197 | logger "🔄 백그라운드 작업 시작: $(basename "$shp")" |
| 198 | fi |
| 199 | done |
| 200 | wait |
| 201 | logger "✅ 모든 append 작업 완료" |
| 202 | else |
| 203 | logger "📋 테이블 $SCHEMA_LOWER.$TABLE_LOWER이 존재하지 않습니다. 새로 생성합니다." |
| 204 | logger "🔄 총 $TOTAL_FILES개 파일을 처리합니다..." |
| 205 | |
| 206 | # 첫 번째 파일로 테이블 생성 |
| 207 | if [ $TOTAL_FILES -gt 0 ]; then |
| 208 | FIRST_FILE="${SHP_FILES[0]}" |
| 209 | FIRST_BASENAME=$(basename "$FIRST_FILE") |
| 210 | |
| 211 | # 테이블 생성 시작 시간 기록 |
| 212 | TABLE_START_TIME=$(date '+%Y-%m-%d %H:%M:%S') |
| 213 | TABLE_START_TIMESTAMP=$(date +%s) |
| 214 | |
| 215 | logger "🚀 [시작] 테이블 구조 생성 시작: $FIRST_BASENAME - $TABLE_START_TIME" |
| 216 | |
| 217 | # 좌표변환 여부에 따라 shp2pgsql 명령어 결정 (테이블 구조만 생성, 데이터는 나중에) |
| 218 | if [ "$SRID_NEW" -ne 0 ]; then |
| 219 | OUTPUT=$(shp2pgsql -p -W "$CHARSET" -s $SRID_ORI:$SRID_NEW "$FIRST_FILE" $SCHEMA_LOWER.$TABLE_LOWER | psql -q -U "$USER" -d "$DB") |
| 220 | else |
| 221 | OUTPUT=$(shp2pgsql -p -W "$CHARSET" -s $SRID_ORI "$FIRST_FILE" $SCHEMA_LOWER.$TABLE_LOWER | psql -q -U "$USER" -d "$DB") |
| 222 | fi |
| 223 | EXIT_CODE=$? |
| 224 | |
| 225 | # 테이블 생성 종료 시간 기록 |
| 226 | TABLE_END_TIME=$(date '+%Y-%m-%d %H:%M:%S') |
| 227 | TABLE_END_TIMESTAMP=$(date +%s) |
| 228 | TABLE_DURATION=$((TABLE_END_TIMESTAMP - TABLE_START_TIMESTAMP)) |
| 229 | |
| 230 | # 테이블 생성 소요시간을 시:분:초 형식으로 변환 |
| 231 | TABLE_HOURS=$((TABLE_DURATION / 3600)) |
| 232 | TABLE_MINUTES=$(((TABLE_DURATION % 3600) / 60)) |
| 233 | TABLE_SECONDS=$((TABLE_DURATION % 60)) |
| 234 | |
| 235 | # 1보다 작으면 0으로 표시 |
| 236 | if [ $TABLE_HOURS -lt 1 ]; then TABLE_HOURS=0; fi |
| 237 | if [ $TABLE_MINUTES -lt 1 ]; then TABLE_MINUTES=0; fi |
| 238 | |
| 239 | if [ $EXIT_CODE -eq 0 ]; then |
| 240 | logger "✅ [완료] 테이블 구조 생성 완료: $TABLE_END_TIME (소요: ${TABLE_HOURS}시간 ${TABLE_MINUTES}분 ${TABLE_SECONDS}초)" |
| 241 | |
| 242 | # 모든 파일을 한꺼번에 병렬 처리로 데이터 적재 (첫 번째 파일 포함) |
| 243 | logger "🔄 모든 $TOTAL_FILES개 파일을 병렬 처리로 데이터 적재합니다..." |
| 244 | for shp in "${SHP_FILES[@]}"; do |
| 245 | if [ -n "$shp" ]; then |
| 246 | wait_for_jobs |
| 247 | import_append "$shp" & |
| 248 | logger "🔄 백그라운드 작업 시작: $(basename "$shp")" |
| 249 | fi |
| 250 | done |
| 251 | wait |
| 252 | logger "✅ 모든 데이터 적재 작업 완료" |
| 253 | else |
| 254 | logger "❌ [실패] 테이블 구조 생성 실패: $TABLE_END_TIME (소요: ${TABLE_HOURS}시간 ${TABLE_MINUTES}분 ${TABLE_SECONDS}초)" |
| 255 | echo "$OUTPUT" >> "$LOG_FILE" |
| 256 | exit 1 |
| 257 | fi |
| 258 | else |
| 259 | logger "❌ 오류: 처리할 SHP 파일이 없습니다." |
| 260 | exit 1 |
| 261 | fi |
| 262 | fi |
| 263 | |
| 264 | # 병렬 처리 완료 확인 및 요약 |
| 265 | logger "🔄 병렬 처리 완료 확인 중..." |
| 266 | logger "📊 처리된 파일 수: $TOTAL_FILES개" |
| 267 | |
| 268 | # 데이터 입력 완료 시간 기록 |
| 269 | DATA_END_TIME=$(date '+%Y-%m-%d %H:%M:%S') |
| 270 | DATA_END_TIMESTAMP=$(date +%s) |
| 271 | DATA_DURATION=$((DATA_END_TIMESTAMP - START_TIMESTAMP)) |
| 272 | |
| 273 | # 데이터 입력 시간을 시:분:초 형식으로 변환 |
| 274 | DATA_HOURS=$((DATA_DURATION / 3600)) |
| 275 | DATA_MINUTES=$(((DATA_DURATION % 3600) / 60)) |
| 276 | DATA_SECONDS=$((DATA_DURATION % 60)) |
| 277 | |
| 278 | # 1보다 작으면 0으로 표시 |
| 279 | if [ $DATA_HOURS -lt 1 ]; then DATA_HOURS=0; fi |
| 280 | if [ $DATA_MINUTES -lt 1 ]; then DATA_MINUTES=0; fi |
| 281 | |
| 282 | # 3. (선택) 인덱스 재생성 (권장: 한 번에 생성) |
| 283 | INDEX_START_TIME=$(date '+%Y-%m-%d %H:%M:%S') |
| 284 | INDEX_START_TIMESTAMP=$(date +%s) |
| 285 | |
| 286 | logger "🔍 공간 인덱스 생성 시작: $INDEX_START_TIME" |
| 287 | |
| 288 | # 공간 인덱스 생성 (PostGIS 확장이 활성화되어 있어야 함) |
| 289 | INDEX_OUTPUT=$(psql -q -U "$USER" -d "$DB" -c "CREATE INDEX IF NOT EXISTS ${TABLE_LOWER}_geom_idx ON $SCHEMA_LOWER.$TABLE_LOWER USING GIST (geom);") |
| 290 | INDEX_EXIT_CODE=$? |
| 291 | |
| 292 | if [ $INDEX_EXIT_CODE -ne 0 ]; then |
| 293 | # 오류 내용을 로그에 기록 |
| 294 | logger "⚠️ 인덱스 생성 오류: $INDEX_OUTPUT" |
| 295 | echo "=== 공간 인덱스 생성 오류 상세 내용 ===" >> "$LOG_FILE" |
| 296 | echo "$INDEX_OUTPUT" >> "$LOG_FILE" |
| 297 | echo "=====================================" >> "$LOG_FILE" |
| 298 | |
| 299 | logger "🔄 인덱스 생성에 실패했지만 스크립트는 계속 진행됩니다." |
| 300 | fi |
| 301 | |
| 302 | # 인덱스 재생성 완료 시간 기록 |
| 303 | INDEX_END_TIME=$(date '+%Y-%m-%d %H:%M:%S') |
| 304 | INDEX_END_TIMESTAMP=$(date +%s) |
| 305 | INDEX_DURATION=$((INDEX_END_TIMESTAMP - INDEX_START_TIMESTAMP)) |
| 306 | |
| 307 | # 인덱스 재생성 시간을 시:분:초 형식으로 변환 |
| 308 | INDEX_HOURS=$((INDEX_DURATION / 3600)) |
| 309 | INDEX_MINUTES=$(((INDEX_DURATION % 3600) / 60)) |
| 310 | INDEX_SECONDS=$((INDEX_DURATION % 60)) |
| 311 | |
| 312 | # 1보다 작으면 0으로 표시 |
| 313 | if [ $INDEX_HOURS -lt 1 ]; then INDEX_HOURS=0; fi |
| 314 | if [ $INDEX_MINUTES -lt 1 ]; then INDEX_MINUTES=0; fi |
| 315 | |
| 316 | logger "==================================" |
| 317 | logger "✅ 작업 완료" |
| 318 | logger "📋 대상테이블: $SCHEMA_LOWER.$TABLE_LOWER" |
| 319 | if [ "$SRID_NEW" -ne 0 ]; then |
| 320 | logger "🗺️ 좌표계: $SRID_ORI → $SRID_NEW (변환됨)" |
| 321 | else |
| 322 | logger "🗺️ 좌표계: $SRID_ORI (원본)" |
| 323 | fi |
| 324 | logger "⏱️ 데이터 입력 소요시간: ${DATA_HOURS}시간 ${DATA_MINUTES}분 ${DATA_SECONDS}초" |
| 325 | logger "⏱️ 공간 인덱스 소요시간: ${INDEX_HOURS}시간 ${INDEX_MINUTES}분 ${INDEX_SECONDS}초" |
| 326 | logger "==================================" |