Ostatnio aktywny 1756818083

shp2pgsql_local.sh Surowy
1#!/bin/bash
2
3DB="yourDb" # 실제 데이터베이스 이름으로 변경
4USER="postgres" # 실제 PostgreSQL 사용자명으로 변경
5SCHEMA="yourSchema" # 스키마 이름
6TABLE="yourTable" # 생성할 테이블 이름
7
8# 대소문자 구분 문제 해결을 위해 소문자로 변환
9SCHEMA_LOWER=$(echo "$SCHEMA" | tr '[:upper:]' '[:lower:]')
10TABLE_LOWER=$(echo "$TABLE" | tr '[:upper:]' '[:lower:]')
11
12SRID_ORI=5186 # 원본 좌표계
13SRID_NEW=0 # 변환 좌표계 (0이면 좌표변환 안함)
14CHARSET="UTF-8" # 문자 인코딩
15
16SHP_DIR="./" # shp 파일들이 있는 디렉토리 경로
17MAX_JOBS=2 # 병렬 작업 수 (시스템 성능에 따라 조정)
18
19
20# 로그 파일 설정
21LOG_DIR="./" # 로그 디렉토리 (필요시 경로 변경)
22mkdir -p "$LOG_DIR" # 로그 디렉토리가 없으면 생성
23LOG_FILE="$LOG_DIR/shp_import_$(date '+%Y%m%d_%H%M%S').log"
24
25# 로그 함수 정의
26logger() {
27 echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
28}
29
30# job control 함수 (병렬 처리 제한)
31function 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 모드 전용)
43function 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# 시작 시간 기록
94START_TIME=$(date '+%Y-%m-%d %H:%M:%S')
95START_TIMESTAMP=$(date +%s)
96
97
98# SHP 디렉토리 및 파일 존재 확인
99if [ ! -d "$SHP_DIR" ]; then
100 logger "❌ 오류: SHP 디렉토리가 존재하지 않습니다: $SHP_DIR"
101 exit 1
102fi
103
104SHP_COUNT=$(find "$SHP_DIR" -name "*.shp" | wc -l)
105if [ $SHP_COUNT -eq 0 ]; then
106 logger "❌ 오류: SHP 파일이 없습니다: $SHP_DIR"
107 exit 1
108fi
109logger "📁 발견된 SHP 파일 수: $SHP_COUNT개"
110
111# 필수 변수 검증
112if [ -z "$DB" ]; then
113 logger "❌ 오류: DB 변수가 설정되지 않았습니다. 스크립트 상단에서 DB 변수를 설정해주세요."
114 exit 1
115fi
116
117if [ -z "$TABLE" ]; then
118 logger "❌ 오류: TABLE 변수가 설정되지 않았습니다. 스크립트 상단에서 TABLE 변수를 설정해주세요."
119 exit 1
120fi
121
122logger "📋 설정된 변수:"
123logger " - 데이터베이스: $DB"
124logger " - 사용자: $USER"
125logger " - 스키마: $SCHEMA"
126logger " - 테이블: $TABLE"
127
128# PostgreSQL 연결 테스트
129if ! psql -q -U "$USER" -d "$DB" -c "SELECT 1;" >/dev/null 2>&1; then
130 logger "❌ 오류: PostgreSQL 연결 실패. 데이터베이스 연결을 확인해주세요."
131 exit 1
132fi
133
134# 스키마 존재 확인 및 생성
135SCHEMA_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
137if [ "$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
144fi
145
146# PostGIS 확장 확인
147POSTGIS_EXISTS=$(psql -q -U "$USER" -d "$DB" -t -c "SELECT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'postgis');" 2>/dev/null | xargs)
148
149if [ "$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
157fi
158
159# 좌표계 설정 확인 및 로깅
160if [ "$SRID_NEW" -ne 0 ]; then
161 logger "🗺️ 좌표변환 모드: $SRID_ORI$SRID_NEW"
162else
163 logger "🗺️ 원본좌표계 사용: $SRID_ORI"
164fi
165
166logger "=================================="
167logger "🚀 Import 시작: $START_TIME"
168logger "=================================="
169
170# 성능 최적화 설정 로깅
171logger "⚡ 성능 최적화 설정:"
172logger " - COPY 모드: ✅ 활성화 (INSERT 대비 10-100배 빠름)"
173logger " - 병렬 작업 수: $MAX_JOBS개"
174
175# 테이블 존재 여부 확인
176TABLE_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 파일을 배열로 수집 (공백이 포함된 파일명도 안전하게 처리)
179mapfile -t SHP_FILES < <(find "$SHP_DIR" -name "*.shp" | sort)
180TOTAL_FILES=${#SHP_FILES[@]}
181
182# 배열이 비어있거나 null인 경우 체크
183if [ ${#SHP_FILES[@]} -eq 0 ] || [ -z "${SHP_FILES[0]}" ]; then
184 logger "❌ 오류: 유효한 SHP 파일을 찾을 수 없습니다."
185 exit 1
186fi
187
188if [ "$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 작업 완료"
202else
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
262fi
263
264# 병렬 처리 완료 확인 및 요약
265logger "🔄 병렬 처리 완료 확인 중..."
266logger "📊 처리된 파일 수: $TOTAL_FILES개"
267
268# 데이터 입력 완료 시간 기록
269DATA_END_TIME=$(date '+%Y-%m-%d %H:%M:%S')
270DATA_END_TIMESTAMP=$(date +%s)
271DATA_DURATION=$((DATA_END_TIMESTAMP - START_TIMESTAMP))
272
273# 데이터 입력 시간을 시:분:초 형식으로 변환
274DATA_HOURS=$((DATA_DURATION / 3600))
275DATA_MINUTES=$(((DATA_DURATION % 3600) / 60))
276DATA_SECONDS=$((DATA_DURATION % 60))
277
278# 1보다 작으면 0으로 표시
279if [ $DATA_HOURS -lt 1 ]; then DATA_HOURS=0; fi
280if [ $DATA_MINUTES -lt 1 ]; then DATA_MINUTES=0; fi
281
282# 3. (선택) 인덱스 재생성 (권장: 한 번에 생성)
283INDEX_START_TIME=$(date '+%Y-%m-%d %H:%M:%S')
284INDEX_START_TIMESTAMP=$(date +%s)
285
286logger "🔍 공간 인덱스 생성 시작: $INDEX_START_TIME"
287
288# 공간 인덱스 생성 (PostGIS 확장이 활성화되어 있어야 함)
289INDEX_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);")
290INDEX_EXIT_CODE=$?
291
292if [ $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 "🔄 인덱스 생성에 실패했지만 스크립트는 계속 진행됩니다."
300fi
301
302# 인덱스 재생성 완료 시간 기록
303INDEX_END_TIME=$(date '+%Y-%m-%d %H:%M:%S')
304INDEX_END_TIMESTAMP=$(date +%s)
305INDEX_DURATION=$((INDEX_END_TIMESTAMP - INDEX_START_TIMESTAMP))
306
307# 인덱스 재생성 시간을 시:분:초 형식으로 변환
308INDEX_HOURS=$((INDEX_DURATION / 3600))
309INDEX_MINUTES=$(((INDEX_DURATION % 3600) / 60))
310INDEX_SECONDS=$((INDEX_DURATION % 60))
311
312# 1보다 작으면 0으로 표시
313if [ $INDEX_HOURS -lt 1 ]; then INDEX_HOURS=0; fi
314if [ $INDEX_MINUTES -lt 1 ]; then INDEX_MINUTES=0; fi
315
316logger "=================================="
317logger "✅ 작업 완료"
318logger "📋 대상테이블: $SCHEMA_LOWER.$TABLE_LOWER"
319if [ "$SRID_NEW" -ne 0 ]; then
320 logger "🗺️ 좌표계: $SRID_ORI$SRID_NEW (변환됨)"
321else
322 logger "🗺️ 좌표계: $SRID_ORI (원본)"
323fi
324logger "⏱️ 데이터 입력 소요시간: ${DATA_HOURS}시간 ${DATA_MINUTES}${DATA_SECONDS}"
325logger "⏱️ 공간 인덱스 소요시간: ${INDEX_HOURS}시간 ${INDEX_MINUTES}${INDEX_SECONDS}"
326logger "=================================="