문서 편집 권한이 없습니다. 다음 이유를 확인해주세요:
요청한 명령은 다음 권한을 가진 사용자에게 제한됩니다: 사용자.
= Oracle 테이블 분석기 설치 및 사용 가이드 = == 프로그램 구성== ===파일 구조=== <source> OracleTableAnalyzer/ ├── oracle_table_analyzer.py # Python 백엔드 스크립트 ├── oracle_analyzer.au3 # AutoIt GUI 프로그램 ├── oracle_analyzer_config.ini # 설정 파일 (자동 생성) ├── requirements.txt # Python 라이브러리 의존성 └── README.md # 사용 설명서 </source> == 2. 설치 및 설정 == === 2.1 Python 환경 설정 === ==== 필수 라이브러리 설치 ==== <source>a # cx_Oracle 라이브러리 설치 pip install cx_Oracle # 추가 라이브러리 설치 pip install pandas numpy openpyxl </source> ==== requirements.txt 파일 생성 ==== <source> cx_Oracle>=8.3.0 pandas>=1.5.0 numpy>=1.21.0 openpyxl>=3.0.0 </source> ==== Oracle Instant Client 설치 ==== 1. [Oracle Instant Client 다운로드](https://www.oracle.com/database/technologies/instant-client/downloads.html) 2. 적절한 디렉토리에 압축 해제 3. 환경 변수 설정: <source> PATH에 Instant Client 경로 추가 예: C:\oracle\instantclient_21_8 </source> === 2.2 AutoIt 설정 === ==== AutoIt 설치 ==== 1. [AutoIt 공식 사이트](https://www.autoitscript.com/)에서 AutoIt 다운로드 2. SciTE 에디터와 함께 설치 3. 필요한 Include 파일 확인: - Json.au3 (JSON 처리용) - 기본 GUI 라이브러리들 ==== JSON 처리 라이브러리 (Json.au3) ==== <source lang=autoit> ; Json.au3 파일을 AutoIt Include 디렉토리에 복사 ; 또는 다음 링크에서 다운로드: ; https://www.autoitscript.com/forum/topic/148114-a-non-strict-json-udf/ </source> == 3. 프로그램 실행 방법 == === 3.1 GUI 프로그램 실행 === 1. **AutoIt 스크립트 컴파일** (선택사항) <source lang=autoit> Aut2Exe.exe를 사용하여 .au3 파일을 .exe로 컴파일 </source> 2. **직접 실행** <source> oracle_analyzer.au3 파일을 더블클릭하여 실행 </source> === 3.2 Python 스크립트 독립 실행 === <source lang=python> # 기본 실행 python oracle_table_analyzer.py "user/password@host:1521/service" "table_name" # 옵션 포함 실행 python oracle_table_analyzer.py "hr/password@localhost:1521/ORCL" "employees" \ --owner HR \ --include-constraints \ --include-indexes \ --include-statistics \ --output json \ --save-to-file employees_analysis.json </source> == 4. 사용법 상세 가이드 == === 4.1 GUI 프로그램 사용 === ==== 연결 정보 입력 ==== 1. **사용자명**: Oracle 사용자 계정 2. **비밀번호**: 해당 계정의 비밀번호 3. **호스트**: Oracle 서버 주소 (기본값: localhost) 4. **포트**: Oracle 포트 번호 (기본값: 1521) 5. **서비스명**: Oracle 서비스명 또는 SID ==== 테이블 분석 설정 == 1. **스키마**: 조회할 스키마명 (비워두면 현재 사용자) 2. **테이블명**: 분석할 테이블명 (필수) 3. **포함 정보**: - 제약조건: Primary Key, Foreign Key, Check 제약조건 - 인덱스: 테이블의 모든 인덱스 정보 - 통계정보: 테이블 통계 (행 수, 블록 수 등) ==== 실행 순서 == 1. 연결 정보 입력 2. "연결 테스트" 클릭하여 DB 연결 확인 3. 테이블명 및 옵션 설정 4. "테이블 분석 실행" 클릭 5. 결과를 탭별로 확인 6. 필요시 CSV/JSON으로 내보내기 === 4.2 Python 명령행 사용 === ==== 기본 문법 == <source lang=python> python oracle_table_analyzer.py [연결문자열] [테이블명] [옵션들] </source> ==== 주요 옵션들 == - `--owner SCHEMA`: 스키마명 지정 - `--output {json,csv,table}`: 출력 형식 - `--include-constraints`: 제약조건 포함 - `--include-indexes`: 인덱스 정보 포함 - `--include-statistics`: 통계정보 포함 - `--save-to-file FILE`: 결과를 파일로 저장 ==== 실행 예제들 == <source lang=python> # 1. 기본 컬럼 정보만 조회 python oracle_table_analyzer.py "hr/password@localhost:1521/ORCL" "employees" # 2. 모든 정보 포함하여 JSON으로 저장 python oracle_table_analyzer.py "hr/password@localhost:1521/ORCL" "employees" \ --include-constraints --include-indexes --include-statistics \ --output json --save-to-file employees_full.json # 3. 다른 스키마의 테이블 분석 python oracle_table_analyzer.py "system/password@localhost:1521/ORCL" "employees" \ --owner HR --output csv # 4. 테이블 형식으로 출력 (AutoIt에서 파싱하기 쉬운 형태) python oracle_table_analyzer.py "hr/password@localhost:1521/ORCL" "departments" \ --output table </source> == 5. 실무 활용 시나리오 == === 5.1 일상적인 테이블 구조 확인 === <source lang=python> # 새로 추가된 테이블의 구조 빠른 확인 python oracle_table_analyzer.py "dba/password@prod:1521/PROD" "new_customer_table" \ --include-constraints --output table </source> === 5.2 데이터 모델링 문서 작성용 === <source lang=python> # 전체 스키마의 주요 테이블들 문서화 for table in employees departments locations jobs; do python oracle_table_analyzer.py "hr/password@localhost:1521/ORCL" "$table" \ --include-constraints --include-indexes --include-statistics \ --save-to-file "${table}_structure.json" done </source> === 5.3 성능 분석용 통계 수집 === <source lang=python> # 큰 테이블들의 통계 정보 확인 python oracle_table_analyzer.py "dba/password@prod:1521/PROD" "large_transaction_table" \ --include-statistics --output json </source> === 5.4 마이그레이션 준비 === <source lang=python> # 마이그레이션 대상 테이블들의 구조 백업 python oracle_table_analyzer.py "old_system/password@old_db:1521/OLDSYS" "customer_master" \ --include-constraints --include-indexes \ --save-to-file migration_customer_structure.json </source> == 6. 고급 활용법 == === 6.1 배치 스크립트 작성 === ==== Windows 배치 파일 (analyze_tables.bat) == </source>batch @echo off set DB_CONN=hr/password@localhost:1521/ORCL set PYTHON_SCRIPT=oracle_table_analyzer.py echo 테이블 구조 분석 시작... for %%T in (employees departments locations jobs job_history) do ( echo 분석 중: %%T python %PYTHON_SCRIPT% "%DB_CONN%" "%%T" --include-constraints --include-indexes --save-to-file "%%T_structure.json" ) echo 모든 테이블 분석 완료! pause </source> ==== Linux/Unix 쉘 스크립트 (analyze_tables.sh) == <source lang=python> #!/bin/bash DB_CONN="hr/password@localhost:1521/ORCL" PYTHON_SCRIPT="oracle_table_analyzer.py" OUTPUT_DIR="./table_structures" mkdir -p $OUTPUT_DIR tables=("employees" "departments" "locations" "jobs" "job_history") for table in "${tables[@]}"; do echo "분석 중: $table" python $PYTHON_SCRIPT "$DB_CONN" "$table" \ --include-constraints \ --include-indexes \ --include-statistics \ --save-to-file "$OUTPUT_DIR/${table}_structure.json" done echo "모든 테이블 분석 완료!" </source> === 6.2 AutoIt 자동화 스크립트 === ==== 여러 테이블 자동 분석 == </source>autoit ; 여러 테이블을 순차적으로 분석하는 함수 Func AnalyzeMultipleTables() Local $aTables = StringSplit("EMPLOYEES,DEPARTMENTS,LOCATIONS,JOBS", ",") For $i = 1 To $aTables[0] GUICtrlSetData($idTableName, $aTables[$i]) AnalyzeTable() Sleep(2000) ; 2초 대기 ; 결과를 개별 파일로 저장 ExportToJSON(@ScriptDir & "\" & $aTables[$i] & "_structure.json") Next EndFunc </source> == 7. 문제 해결 가이드 == === 7.1 자주 발생하는 오류들 === ==== cx_Oracle 설치 문제 == </source> 오류: ModuleNotFoundError: No module named 'cx_Oracle' 해결: pip install cx_Oracle </source> ==== Oracle Client 문제 == </source> 오류: DPI-1047: Cannot locate a 64-bit Oracle Client library 해결: Oracle Instant Client 설치 및 PATH 환경변수 설정 </source> ==== 인코딩 문제 == </source> 오류: UnicodeDecodeError 해결: 환경변수 설정 set NLS_LANG=KOREAN_KOREA.AL32UTF8 </source> ==== 권한 문제 == </source> 오류: ORA-00942: table or view does not exist 해결: 적절한 권한 부여 GRANT SELECT ON dba_tab_columns TO username; GRANT SELECT ON dba_constraints TO username; </source> === 7.2 성능 최적화 === ==== 대용량 테이블 처리 == </source>python # 큰 테이블의 경우 통계 정보만 조회 python oracle_table_analyzer.py "conn_string" "large_table" \ --include-statistics --output json </source> ==== 네트워크 최적화 == </source>python # 로컬 네트워크가 아닌 경우 필요한 정보만 조회 python oracle_table_analyzer.py "conn_string" "remote_table" \ --output csv # JSON보다 가벼움 </source> == 8. 확장 및 커스터마이징 == === 8.1 추가 기능 구현 아이디어 === 1. **DDL 생성 기능** 2. **테이블 비교 기능** 3. **데이터 프로파일링** 4. **성능 메트릭 수집** 5. **웹 인터페이스 제공** === 8.2 설정 파일 커스터마이징 === ==== oracle_analyzer_config.ini 예제 == <source> [Connection] User=hr Host=localhost Port=1521 Service=ORCL [Settings] PythonPath=C:\Python39\python.exe DefaultTimeout=30 MaxResults=1000 [Display] FontName=Consolas FontSize=10 GridLines=1 </source> == 파이썬 프로그램 (오라클 컬럼 정보 조회) == <source lang=python> #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Oracle 테이블 컬럼 정보 조회 프로그램 AutoIt에서 호출되어 테이블 구조를 분석하고 결과를 반환 """ import cx_Oracle import sys import json import csv import argparse from datetime import datetime from typing import List, Dict, Optional import os class OracleTableAnalyzer: """Oracle 테이블 구조 분석 클래스""" def __init__(self, connection_string: str): """ 초기화 Args: connection_string: Oracle 연결 문자열 """ self.connection_string = connection_string self.connection = None self.cursor = None def connect(self): """Oracle 데이터베이스 연결""" try: self.connection = cx_Oracle.connect(self.connection_string) self.cursor = self.connection.cursor() return True except cx_Oracle.Error as e: print(f"ERROR|연결 실패: {str(e)}") return False def disconnect(self): """데이터베이스 연결 해제""" if self.cursor: self.cursor.close() if self.connection: self.connection.close() def get_table_columns(self, table_name: str, owner: str = None) -> List[Dict]: """ 테이블 컬럼 정보 조회 Args: table_name: 테이블명 owner: 스키마명 (None이면 현재 사용자) Returns: 컬럼 정보 리스트 """ try: if owner: # 다른 스키마의 테이블 조회 sql = """ SELECT column_name, data_type, data_length, data_precision, data_scale, nullable, data_default, column_id, hidden_column, virtual_column, char_length FROM all_tab_columns WHERE table_name = UPPER(:table_name) AND owner = UPPER(:owner) ORDER BY column_id """ params = {'table_name': table_name, 'owner': owner} else: # 현재 사용자 스키마의 테이블 조회 sql = """ SELECT column_name, data_type, data_length, data_precision, data_scale, nullable, data_default, column_id, hidden_column, virtual_column, char_length FROM user_tab_columns WHERE table_name = UPPER(:table_name) ORDER BY column_id """ params = {'table_name': table_name} self.cursor.execute(sql, params) rows = self.cursor.fetchall() if not rows: return [] columns = [] for row in rows: column_info = { 'column_name': row[0], 'data_type': row[1], 'data_length': row[2], 'data_precision': row[3], 'data_scale': row[4], 'nullable': row[5], 'data_default': row[6].read() if row[6] else None, 'column_id': row[7], 'hidden_column': row[8], 'virtual_column': row[9], 'char_length': row[10] if len(row) > 10 else None } # 데이터 타입 포맷팅 column_info['formatted_type'] = self._format_data_type(column_info) columns.append(column_info) return columns except cx_Oracle.Error as e: print(f"ERROR|쿼리 실행 실패: {str(e)}") return [] def get_table_constraints(self, table_name: str, owner: str = None) -> List[Dict]: """테이블 제약조건 정보 조회""" try: if owner: sql = """ SELECT c.constraint_name, c.constraint_type, c.status, cc.column_name, cc.position, c.r_constraint_name, rc.table_name as ref_table, rcc.column_name as ref_column FROM all_constraints c LEFT JOIN all_cons_columns cc ON c.constraint_name = cc.constraint_name AND c.owner = cc.owner LEFT JOIN all_constraints rc ON c.r_constraint_name = rc.constraint_name LEFT JOIN all_cons_columns rcc ON rc.constraint_name = rcc.constraint_name AND rc.owner = rcc.owner WHERE c.table_name = UPPER(:table_name) AND c.owner = UPPER(:owner) ORDER BY c.constraint_type, c.constraint_name, cc.position """ params = {'table_name': table_name, 'owner': owner} else: sql = """ SELECT c.constraint_name, c.constraint_type, c.status, cc.column_name, cc.position, c.r_constraint_name, rc.table_name as ref_table, rcc.column_name as ref_column FROM user_constraints c LEFT JOIN user_cons_columns cc ON c.constraint_name = cc.constraint_name LEFT JOIN user_constraints rc ON c.r_constraint_name = rc.constraint_name LEFT JOIN user_cons_columns rcc ON rc.constraint_name = rcc.constraint_name WHERE c.table_name = UPPER(:table_name) ORDER BY c.constraint_type, c.constraint_name, cc.position """ params = {'table_name': table_name} self.cursor.execute(sql, params) rows = self.cursor.fetchall() constraints = [] for row in rows: constraint_info = { 'constraint_name': row[0], 'constraint_type': row[1], 'constraint_type_desc': self._get_constraint_type_desc(row[1]), 'status': row[2], 'column_name': row[3], 'position': row[4], 'ref_constraint': row[5], 'ref_table': row[6], 'ref_column': row[7] } constraints.append(constraint_info) return constraints except cx_Oracle.Error as e: print(f"ERROR|제약조건 조회 실패: {str(e)}") return [] def get_table_indexes(self, table_name: str, owner: str = None) -> List[Dict]: """테이블 인덱스 정보 조회""" try: if owner: sql = """ SELECT i.index_name, i.index_type, i.uniqueness, i.status, ic.column_name, ic.column_position, ic.descend FROM all_indexes i JOIN all_ind_columns ic ON i.index_name = ic.index_name AND i.owner = ic.index_owner WHERE i.table_name = UPPER(:table_name) AND i.owner = UPPER(:owner) ORDER BY i.index_name, ic.column_position """ params = {'table_name': table_name, 'owner': owner} else: sql = """ SELECT i.index_name, i.index_type, i.uniqueness, i.status, ic.column_name, ic.column_position, ic.descend FROM user_indexes i JOIN user_ind_columns ic ON i.index_name = ic.index_name WHERE i.table_name = UPPER(:table_name) ORDER BY i.index_name, ic.column_position """ params = {'table_name': table_name} self.cursor.execute(sql, params) rows = self.cursor.fetchall() indexes = [] for row in rows: index_info = { 'index_name': row[0], 'index_type': row[1], 'uniqueness': row[2], 'status': row[3], 'column_name': row[4], 'column_position': row[5], 'descend': row[6] } indexes.append(index_info) return indexes except cx_Oracle.Error as e: print(f"ERROR|인덱스 조회 실패: {str(e)}") return [] def get_table_statistics(self, table_name: str, owner: str = None) -> Dict: """테이블 통계 정보 조회""" try: if owner: sql = """ SELECT num_rows, blocks, empty_blocks, avg_space, chain_cnt, avg_row_len, sample_size, last_analyzed, table_name FROM all_tables WHERE table_name = UPPER(:table_name) AND owner = UPPER(:owner) """ params = {'table_name': table_name, 'owner': owner} else: sql = """ SELECT num_rows, blocks, empty_blocks, avg_space, chain_cnt, avg_row_len, sample_size, last_analyzed, table_name FROM user_tables WHERE table_name = UPPER(:table_name) """ params = {'table_name': table_name} self.cursor.execute(sql, params) row = self.cursor.fetchone() if row: return { 'num_rows': row[0], 'blocks': row[1], 'empty_blocks': row[2], 'avg_space': row[3], 'chain_cnt': row[4], 'avg_row_len': row[5], 'sample_size': row[6], 'last_analyzed': row[7].strftime('%Y-%m-%d %H:%M:%S') if row[7] else None, 'table_name': row[8] } else: return {} except cx_Oracle.Error as e: print(f"ERROR|통계 정보 조회 실패: {str(e)}") return {} def _format_data_type(self, column_info: Dict) -> str: """데이터 타입 포맷팅""" data_type = column_info['data_type'] if data_type in ['VARCHAR2', 'CHAR', 'NVARCHAR2', 'NCHAR']: if column_info['char_length']: return f"{data_type}({column_info['char_length']})" else: return f"{data_type}({column_info['data_length']})" elif data_type == 'NUMBER': if column_info['data_precision'] and column_info['data_scale'] is not None: return f"NUMBER({column_info['data_precision']},{column_info['data_scale']})" elif column_info['data_precision']: return f"NUMBER({column_info['data_precision']})" else: return "NUMBER" elif data_type in ['RAW', 'LONG RAW']: return f"{data_type}({column_info['data_length']})" else: return data_type def _get_constraint_type_desc(self, constraint_type: str) -> str: """제약조건 타입 설명""" type_map = { 'P': 'Primary Key', 'U': 'Unique', 'R': 'Foreign Key', 'C': 'Check', 'V': 'View Check', 'O': 'Read Only' } return type_map.get(constraint_type, constraint_type) def main(): """메인 함수""" parser = argparse.ArgumentParser(description='Oracle 테이블 컬럼 정보 조회') parser.add_argument('connection_string', help='Oracle 연결 문자열 (user/password@host:port/service)') parser.add_argument('table_name', help='조회할 테이블명') parser.add_argument('--owner', help='스키마명 (선택사항)') parser.add_argument('--output', choices=['json', 'csv', 'table'], default='json', help='출력 형식') parser.add_argument('--include-constraints', action='store_true', help='제약조건 정보 포함') parser.add_argument('--include-indexes', action='store_true', help='인덱스 정보 포함') parser.add_argument('--include-statistics', action='store_true', help='테이블 통계 포함') parser.add_argument('--save-to-file', help='결과를 파일로 저장') args = parser.parse_args() try: # Oracle 연결 analyzer = OracleTableAnalyzer(args.connection_string) if not analyzer.connect(): sys.exit(1) # 테이블 컬럼 정보 조회 columns = analyzer.get_table_columns(args.table_name, args.owner) if not columns: print(f"ERROR|테이블 '{args.table_name}'을 찾을 수 없습니다.") sys.exit(1) result = { 'table_name': args.table_name.upper(), 'owner': args.owner.upper() if args.owner else None, 'columns': columns, 'query_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S') } # 추가 정보 조회 if args.include_constraints: result['constraints'] = analyzer.get_table_constraints(args.table_name, args.owner) if args.include_indexes: result['indexes'] = analyzer.get_table_indexes(args.table_name, args.owner) if args.include_statistics: result['statistics'] = analyzer.get_table_statistics(args.table_name, args.owner) # 결과 출력 if args.output == 'json': output = json.dumps(result, indent=2, ensure_ascii=False, default=str) print(f"SUCCESS|{output}") elif args.output == 'csv': output_lines = [] output_lines.append("COLUMN_NAME,DATA_TYPE,NULLABLE,DEFAULT_VALUE,COLUMN_ID") for col in columns: line = f"{col['column_name']},{col['formatted_type']},{col['nullable']},{col['data_default'] or ''},{col['column_id']}" output_lines.append(line) print(f"SUCCESS|{chr(10).join(output_lines)}") elif args.output == 'table': # 테이블 형태 출력 (AutoIt ListView용) output_lines = [] output_lines.append("COLUMN_NAME|DATA_TYPE|NULLABLE|DEFAULT|POSITION") for col in columns: line = f"{col['column_name']}|{col['formatted_type']}|{col['nullable']}|{col['data_default'] or ''}|{col['column_id']}" output_lines.append(line) print(f"SUCCESS|{chr(10).join(output_lines)}") # 파일 저장 if args.save_to_file: with open(args.save_to_file, 'w', encoding='utf-8') as f: if args.output == 'json': json.dump(result, f, indent=2, ensure_ascii=False, default=str) else: f.write(output) print(f"INFO|결과를 {args.save_to_file}에 저장했습니다.") except Exception as e: print(f"ERROR|예기치 않은 오류: {str(e)}") sys.exit(1) finally: if 'analyzer' in locals(): analyzer.disconnect() if __name__ == "__main__": main() </source> == autoit - 파이썬 호출용 GUI화면 == <source lang=autoit> #Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Icon=oracle.ico #AutoIt3Wrapper_Res_Comment=Oracle Table Analyzer #AutoIt3Wrapper_Res_Description=Oracle 테이블 구조 분석 도구 #AutoIt3Wrapper_Res_Fileversion=1.0.0.0 #AutoIt3Wrapper_Res_ProductVersion=1.0.0.0 #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** #include <ButtonConstants.au3> #include <EditConstants.au3> #include <GUIConstantsEx.au3> #include <ListViewConstants.au3> #include <StaticConstants.au3> #include <TabConstants.au3> #include <WindowsConstants.au3> #include <GuiListView.au3> #include <File.au3> #include <Array.au3> #include <Json.au3> ; ================================================================ ; Oracle 테이블 분석기 - AutoIt GUI 프로그램 ; ================================================================ ; 전역 변수 Global $hMainGUI, $hStatusBar Global $g_sConnectionString = "" Global $g_sPythonPath = "python.exe" ; Python 실행 파일 경로 Global $g_sScriptPath = @ScriptDir & "\oracle_table_analyzer.py" ; Python 스크립트 경로 ; GUI 컨트롤 변수들 Global $idUser, $idPassword, $idHost, $idPort, $idService, $idOwner, $idTableName Global $idBtnConnect, $idBtnAnalyze, $idBtnExportCSV, $idBtnExportJSON, $idBtnClear Global $idChkConstraints, $idChkIndexes, $idChkStatistics Global $idTabMain, $idTabColumns, $idTabConstraints, $idTabIndexes, $idTabStatistics Global $idListColumns, $idListConstraints, $idListIndexes, $idEditStatistics Global $idProgressBar ; 메인 함수 Main() Func Main() ; GUI 생성 CreateMainGUI() ; 설정 파일 로드 LoadConfig() ; GUI 표시 GUISetState(@SW_SHOW, $hMainGUI) ; 메인 루프 While 1 Local $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE ExitProgram() Case $idBtnConnect TestConnection() Case $idBtnAnalyze AnalyzeTable() Case $idBtnExportCSV ExportToCSV() Case $idBtnExportJSON ExportToJSON() Case $idBtnClear ClearResults() EndSwitch WEnd EndFunc Func CreateMainGUI() ; 메인 윈도우 생성 $hMainGUI = GUICreate("Oracle 테이블 분석기 v1.0", 1000, 700, -1, -1, $WS_OVERLAPPEDWINDOW) ; 연결 정보 그룹 GUICtrlCreateGroup("데이터베이스 연결 정보", 10, 10, 480, 150) GUICtrlCreateLabel("사용자명:", 20, 35, 60, 20) $idUser = GUICtrlCreateInput("", 85, 32, 100, 20) GUICtrlCreateLabel("비밀번호:", 200, 35, 60, 20) $idPassword = GUICtrlCreateInput("", 265, 32, 100, 20, $ES_PASSWORD) GUICtrlCreateLabel("호스트:", 380, 35, 40, 20) $idHost = GUICtrlCreateInput("localhost", 425, 32, 60, 20) GUICtrlCreateLabel("포트:", 20, 65, 30, 20) $idPort = GUICtrlCreateInput("1521", 55, 62, 50, 20) GUICtrlCreateLabel("서비스명:", 120, 65, 60, 20) $idService = GUICtrlCreateInput("ORCL", 180, 62, 80, 20) $idBtnConnect = GUICtrlCreateButton("연결 테스트", 280, 62, 80, 25) GUICtrlCreateLabel("스키마:", 20, 95, 50, 20) $idOwner = GUICtrlCreateInput("", 75, 92, 100, 20) GUICtrlCreateLabel("테이블명:", 190, 95, 60, 20) $idTableName = GUICtrlCreateInput("", 255, 92, 120, 20) ; 옵션 체크박스 GUICtrlCreateLabel("포함 정보:", 20, 125, 60, 20) $idChkConstraints = GUICtrlCreateCheckbox("제약조건", 85, 122, 70, 20) $idChkIndexes = GUICtrlCreateCheckbox("인덱스", 160, 122, 60, 20) $idChkStatistics = GUICtrlCreateCheckbox("통계정보", 225, 122, 70, 20) GUICtrlCreateGroup("", -99, -99, 1, 1) ; 그룹 종료 ; 실행 버튼들 GUICtrlCreateGroup("작업", 500, 10, 480, 150) $idBtnAnalyze = GUICtrlCreateButton("테이블 분석 실행", 520, 40, 120, 35) GUICtrlSetFont($idBtnAnalyze, 10, 600) $idBtnExportCSV = GUICtrlCreateButton("CSV 내보내기", 660, 40, 100, 25) $idBtnExportJSON = GUICtrlCreateButton("JSON 내보내기", 770, 40, 100, 25) $idBtnClear = GUICtrlCreateButton("결과 지우기", 880, 40, 80, 25) ; 진행률 표시 GUICtrlCreateLabel("진행 상황:", 520, 85, 60, 20) $idProgressBar = GUICtrlCreateProgress(585, 82, 300, 20) ; 상태 정보 GUICtrlCreateLabel("상태:", 520, 115, 30, 20) $hStatusBar = GUICtrlCreateLabel("준비", 555, 115, 400, 20) GUICtrlSetColor($hStatusBar, 0x0000FF) GUICtrlCreateGroup("", -99, -99, 1, 1) ; 그룹 종료 ; 탭 컨트롤 생성 $idTabMain = GUICtrlCreateTab(10, 170, 980, 500) ; 컬럼 정보 탭 $idTabColumns = GUICtrlCreateTabItem("컬럼 정보") $idListColumns = GUICtrlCreateListView("순서|컬럼명|데이터타입|Null허용|기본값|가상컬럼|숨김컬럼", 20, 200, 960, 450, $LVS_REPORT + $LVS_GRIDLINES + $LVS_FULLROWSELECT) _GUICtrlListView_SetExtendedListViewStyle($idListColumns, $LVS_EX_GRIDLINES + $LVS_EX_FULLROWSELECT + $LVS_EX_SUBITEMIMAGES) ; 컬럼 너비 설정 _GUICtrlListView_SetColumnWidth($idListColumns, 0, 50) ; 순서 _GUICtrlListView_SetColumnWidth($idListColumns, 1, 150) ; 컬럼명 _GUICtrlListView_SetColumnWidth($idListColumns, 2, 120) ; 데이터타입 _GUICtrlListView_SetColumnWidth($idListColumns, 3, 80) ; Null허용 _GUICtrlListView_SetColumnWidth($idListColumns, 4, 150) ; 기본값 _GUICtrlListView_SetColumnWidth($idListColumns, 5, 80) ; 가상컬럼 _GUICtrlListView_SetColumnWidth($idListColumns, 6, 80) ; 숨김컬럼 ; 제약조건 탭 $idTabConstraints = GUICtrlCreateTabItem("제약조건") $idListConstraints = GUICtrlCreateListView("제약조건명|타입|상태|컬럼명|참조테이블|참조컬럼", 20, 200, 960, 450, $LVS_REPORT + $LVS_GRIDLINES + $LVS_FULLROWSELECT) _GUICtrlListView_SetExtendedListViewStyle($idListConstraints, $LVS_EX_GRIDLINES + $LVS_EX_FULLROWSELECT) _GUICtrlListView_SetColumnWidth($idListConstraints, 0, 200) ; 제약조건명 _GUICtrlListView_SetColumnWidth($idListConstraints, 1, 120) ; 타입 _GUICtrlListView_SetColumnWidth($idListConstraints, 2, 80) ; 상태 _GUICtrlListView_SetColumnWidth($idListConstraints, 3, 150) ; 컬럼명 _GUICtrlListView_SetColumnWidth($idListConstraints, 4, 150) ; 참조테이블 _GUICtrlListView_SetColumnWidth($idListConstraints, 5, 150) ; 참조컬럼 ; 인덱스 탭 $idTabIndexes = GUICtrlCreateTabItem("인덱스") $idListIndexes = GUICtrlCreateListView("인덱스명|타입|유일성|상태|컬럼명|순서|정렬", 20, 200, 960, 450, $LVS_REPORT + $LVS_GRIDLINES + $LVS_FULLROWSELECT) _GUICtrlListView_SetExtendedListViewStyle($idListIndexes, $LVS_EX_GRIDLINES + $LVS_EX_FULLROWSELECT) _GUICtrlListView_SetColumnWidth($idListIndexes, 0, 200) ; 인덱스명 _GUICtrlListView_SetColumnWidth($idListIndexes, 1, 100) ; 타입 _GUICtrlListView_SetColumnWidth($idListIndexes, 2, 80) ; 유일성 _GUICtrlListView_SetColumnWidth($idListIndexes, 3, 80) ; 상태 _GUICtrlListView_SetColumnWidth($idListIndexes, 4, 150) ; 컬럼명 _GUICtrlListView_SetColumnWidth($idListIndexes, 5, 60) ; 순서 _GUICtrlListView_SetColumnWidth($idListIndexes, 6, 60) ; 정렬 ; 통계정보 탭 $idTabStatistics = GUICtrlCreateTabItem("통계정보") $idEditStatistics = GUICtrlCreateEdit("", 20, 200, 960, 450, $ES_MULTILINE + $ES_READONLY + $WS_VSCROLL) GUICtrlSetFont($idEditStatistics, 10, 400, 0, "Consolas") GUICtrlCreateTabItem("") ; 탭 종료 EndFunc Func TestConnection() UpdateStatus("데이터베이스 연결을 테스트하고 있습니다...") GUICtrlSetData($idProgressBar, 30) Local $sConnectionString = BuildConnectionString() ; 간단한 연결 테스트를 위한 Python 스크립트 실행 Local $sCommand = '"' & $g_sPythonPath & '" -c "' & _ 'import cx_Oracle; ' & _ 'conn = cx_Oracle.connect(""' & $sConnectionString & '""); ' & _ 'print(""SUCCESS""); ' & _ 'conn.close()"' Local $iPID = Run($sCommand, @WorkingDir, @SW_HIDE, $STDIN_CHILD + $STDOUT_CHILD + $STDERR_CHILD) ; 실행 결과 읽기 Local $sOutput = "" While ProcessExists($iPID) $sOutput &= StdoutRead($iPID) Sleep(100) WEnd $sOutput &= StdoutRead($iPID) GUICtrlSetData($idProgressBar, 100) If StringInStr($sOutput, "SUCCESS") Then UpdateStatus("데이터베이스 연결 성공!") GUICtrlSetColor($hStatusBar, 0x008000) ; 녹색 $g_sConnectionString = $sConnectionString ; 설정 저장 SaveConfig() Else UpdateStatus("데이터베이스 연결 실패: " & StringStripWS($sOutput, 3)) GUICtrlSetColor($hStatusBar, 0xFF0000) ; 빨간색 EndIf Sleep(2000) GUICtrlSetData($idProgressBar, 0) EndFunc Func AnalyzeTable() Local $sTableName = GUICtrlRead($idTableName) Local $sOwner = GUICtrlRead($idOwner) If $sTableName = "" Then MsgBox(64, "알림", "테이블명을 입력해주세요.") Return EndIf If $g_sConnectionString = "" Then MsgBox(64, "알림", "먼저 데이터베이스 연결을 확인해주세요.") Return EndIf UpdateStatus("테이블 '" & $sTableName & "' 분석 중...") GUICtrlSetData($idProgressBar, 20) ; Python 스크립트 실행 명령 구성 Local $sCommand = '"' & $g_sPythonPath & '" "' & $g_sScriptPath & '" "' & $g_sConnectionString & '" "' & $sTableName & '"' ; 옵션 추가 If $sOwner <> "" Then $sCommand &= " --owner " & $sOwner EndIf If GUICtrlRead($idChkConstraints) = 1 Then $sCommand &= " --include-constraints" EndIf If GUICtrlRead($idChkIndexes) = 1 Then $sCommand &= " --include-indexes" EndIf If GUICtrlRead($idChkStatistics) = 1 Then $sCommand &= " --include-statistics" EndIf $sCommand &= " --output json" GUICtrlSetData($idProgressBar, 50) ; Python 스크립트 실행 Local $iPID = Run($sCommand, @WorkingDir, @SW_HIDE, $STDIN_CHILD + $STDOUT_CHILD + $STDERR_CHILD) Local $sOutput = "" While ProcessExists($iPID) $sOutput &= StdoutRead($iPID) Sleep(100) WEnd $sOutput &= StdoutRead($iPID) GUICtrlSetData($idProgressBar, 80) ; 결과 처리 If StringLeft($sOutput, 7) = "SUCCESS" Then Local $sJsonData = StringTrimLeft($sOutput, 8) ; "SUCCESS|" 제거 ProcessResults($sJsonData) UpdateStatus("테이블 분석 완료!") GUICtrlSetColor($hStatusBar, 0x008000) ElseIf StringLeft($sOutput, 5) = "ERROR" Then Local $sError = StringTrimLeft($sOutput, 6) ; "ERROR|" 제거 UpdateStatus("오류: " & $sError) GUICtrlSetColor($hStatusBar, 0xFF0000) MsgBox(16, "오류", $sError) Else UpdateStatus("알 수 없는 오류가 발생했습니다.") GUICtrlSetColor($hStatusBar, 0xFF0000) MsgBox(16, "오류", "Python 스크립트 실행 결과를 해석할 수 없습니다:" & @CRLF & $sOutput) EndIf GUICtrlSetData($idProgressBar, 100) Sleep(1000) GUICtrlSetData($idProgressBar, 0) EndFunc Func ProcessResults($sJsonData) ; JSON 파싱 (간단한 방법 - 실제로는 JSON 라이브러리 사용 권장) Local $oJson ; JSON 파싱 시도 Try $oJson = Json_Decode($sJsonData) Catch ; JSON 파싱 실패 시 수동 파싱 MsgBox(16, "오류", "JSON 결과를 파싱할 수 없습니다.") Return EndTry ; 컬럼 정보 표시 DisplayColumns($sJsonData) ; 제약조건 정보 표시 (옵션이 체크되어 있고 데이터가 있는 경우) If GUICtrlRead($idChkConstraints) = 1 Then DisplayConstraints($sJsonData) EndIf ; 인덱스 정보 표시 If GUICtrlRead($idChkIndexes) = 1 Then DisplayIndexes($sJsonData) EndIf ; 통계정보 표시 If GUICtrlRead($idChkStatistics) = 1 Then DisplayStatistics($sJsonData) EndIf EndFunc Func DisplayColumns($sJsonData) ; 기존 데이터 지우기 _GUICtrlListView_DeleteAllItems($idListColumns) ; JSON에서 컬럼 정보 추출 (간단한 문자열 파싱 방법) Local $aColumns = StringRegExp($sJsonData, '"column_name":\s*"([^"]+)"', 3) Local $aDataTypes = StringRegExp($sJsonData, '"formatted_type":\s*"([^"]+)"', 3) Local $aNullable = StringRegExp($sJsonData, '"nullable":\s*"([^"]+)"', 3) Local $aDefaults = StringRegExp($sJsonData, '"data_default":\s*"?([^",}]+)"?', 3) Local $aColumnIds = StringRegExp($sJsonData, '"column_id":\s*([0-9]+)', 3) ; ListView에 데이터 추가 For $i = 0 To UBound($aColumns) - 1 Local $sDefault = "" If $i < UBound($aDefaults) Then $sDefault = $aDefaults[$i] Local $sNullable = "" If $i < UBound($aNullable) Then $sNullable = $aNullable[$i] Local $sColumnId = "" If $i < UBound($aColumnIds) Then $sColumnId = $aColumnIds[$i] Local $sDataType = "" If $i < UBound($aDataTypes) Then $sDataType = $aDataTypes[$i] _GUICtrlListView_AddItem($idListColumns, $sColumnId & "|" & $aColumns[$i] & "|" & $sDataType & "|" & $sNullable & "|" & $sDefault & "| | ") Next ; 컬럼 탭을 활성화 GUICtrlSetState($idTabColumns, $GUI_SHOW) EndFunc Func DisplayConstraints($sJsonData) _GUICtrlListView_DeleteAllItems($idListConstraints) ; 제약조건 정보 파싱 및 표시 (간단한 구현) ; 실제로는 더 정교한 JSON 파싱이 필요 Local $aConstraints = StringRegExp($sJsonData, '"constraint_name":\s*"([^"]+)"', 3) If UBound($aConstraints) > 0 Then For $i = 0 To UBound($aConstraints) - 1 _GUICtrlListView_AddItem($idListConstraints, $aConstraints[$i] & "|||||") Next EndIf EndFunc Func DisplayIndexes($sJsonData) _GUICtrlListView_DeleteAllItems($idListIndexes) ; 인덱스 정보 파싱 및 표시 Local $aIndexes = StringRegExp($sJsonData, '"index_name":\s*"([^"]+)"', 3) If UBound($aIndexes) > 0 Then For $i = 0 To UBound($aIndexes) - 1 _GUICtrlListView_AddItem($idListIndexes, $aIndexes[$i] & "||||||") Next EndIf EndFunc Func DisplayStatistics($sJsonData) ; 통계정보를 텍스트 형태로 표시 Local $sStats = "테이블 통계 정보:" & @CRLF & @CRLF ; JSON에서 통계 정보 추출 Local $aStats = StringRegExp($sJsonData, '"statistics":\s*{([^}]+)}', 3) If UBound($aStats) > 0 Then $sStats &= StringReplace($aStats[0], '","', @CRLF) $sStats = StringReplace($sStats, '"', "") $sStats = StringReplace($sStats, ":", ": ") Else $sStats &= "통계 정보가 없습니다." EndIf GUICtrlSetData($idEditStatistics, $sStats) EndFunc Func ExportToCSV() Local $sFileName = FileSaveDialog("CSV로 저장", @DesktopDir, "CSV 파일 (*.csv)", $FD_PATHMUSTEXIST) If $sFileName <> "" Then ; 현재 표시된 컬럼 정보를 CSV로 저장 Local $sCSV = "순서,컬럼명,데이터타입,Null허용,기본값" & @CRLF Local $iItemCount = _GUICtrlListView_GetItemCount($idListColumns) For $i = 0 To $iItemCount - 1 Local $sLine = "" For $j = 0 To 4 ; 처음 5개 컬럼만 If $j > 0 Then $sLine &= "," $sLine &= '"' & _GUICtrlListView_GetItemText($idListColumns, $i, $j) & '"' Next $sCSV &= $sLine & @CRLF Next FileWrite($sFileName, $sCSV) UpdateStatus("CSV 파일로 저장 완료: " & $sFileName) EndIf EndFunc Func ExportToJSON() Local $sFileName = FileSaveDialog("JSON으로 저장", @DesktopDir, "JSON 파일 (*.json)", $FD_PATHMUSTEXIST) If $sFileName <> "" Then ; Python 스크립트를 다시 실행하여 JSON 파일로 직접 저장 Local $sTableName = GUICtrlRead($idTableName) Local $sOwner = GUICtrlRead($idOwner) Local $sCommand = '"' & $g_sPythonPath & '" "' & $g_sScriptPath & '" "' & $g_sConnectionString & '" "' & $sTableName & '" --save-to-file "' & $sFileName & '"' If $sOwner <> "" Then $sCommand &= " --owner " & $sOwner RunWait($sCommand, @WorkingDir, @SW_HIDE) UpdateStatus("JSON 파일로 저장 완료: " & $sFileName) EndIf EndFunc Func ClearResults() _GUICtrlListView_DeleteAllItems($idListColumns) _GUICtrlListView_DeleteAllItems($idListConstraints) _GUICtrlListView_DeleteAllItems($idListIndexes) GUICtrlSetData($idEditStatistics, "") UpdateStatus("결과를 지웠습니다.") EndFunc Func BuildConnectionString() Local $sUser = GUICtrlRead($idUser) Local $sPassword = GUICtrlRead($idPassword) Local $sHost = GUICtrlRead($idHost) Local $sPort = GUICtrlRead($idPort) Local $sService = GUICtrlRead($idService) Return $sUser & "/" & $sPassword & "@" & $sHost & ":" & $sPort & "/" & $sService EndFunc Func UpdateStatus($sMessage) GUICtrlSetData($hStatusBar, $sMessage) GUICtrlSetColor($hStatusBar, 0x000000) ; 검은색으로 리셋 EndFunc Func SaveConfig() ; 연결 정보를 설정 파일에 저장 (비밀번호 제외) Local $sConfigFile = @ScriptDir & "\oracle_analyzer_config.ini" IniWrite($sConfigFile, "Connection", "User", GUICtrlRead($idUser)) IniWrite($sConfigFile, "Connection", "Host", GUICtrlRead($idHost)) IniWrite($sConfigFile, "Connection", "Port", GUICtrlRead($idPort)) IniWrite($sConfigFile, "Connection", "Service", GUICtrlRead($idService)) IniWrite($sConfigFile, "Settings", "PythonPath", $g_sPythonPath) EndFunc Func LoadConfig() ; 설정 파일에서 연결 정보 로드 Local $sConfigFile = @ScriptDir & "\oracle_analyzer_config.ini" If FileExists($sConfigFile) Then GUICtrlSetData($idUser, IniRead($sConfigFile, "Connection", "User", "")) GUICtrlSetData($idHost, IniRead($sConfigFile, "Connection", "Host", "localhost")) GUICtrlSetData($idPort, IniRead($sConfigFile, "Connection", "Port", "1521")) GUICtrlSetData($idService, IniRead($sConfigFile, "Connection", "Service", "ORCL")) $g_sPythonPath = IniRead($sConfigFile, "Settings", "PythonPath", "python.exe") EndIf EndFunc Func ExitProgram() ; 프로그램 종료 전 설정 저장 SaveConfig() Exit EndFunc ; 추가 유틸리티 함수들 Func GenerateDDL() ; DDL 생성 기능 (향후 구현) Local $sTableName = GUICtrlRead($idTableName) MsgBox(64, "정보", "DDL 생성 기능은 향후 버전에서 제공될 예정입니다." & @CRLF & "테이블: " & $sTableName) EndFunc Func SearchTables() ; 테이블 검색 기능 (향후 구현) Local $sSearchTerm = InputBox("테이블 검색", "검색할 테이블명 패턴을 입력하세요:", "") If $sSearchTerm <> "" Then MsgBox(64, "정보", "테이블 검색 기능은 향후 버전에서 제공될 예정입니다." & @CRLF & "검색어: " & $sSearchTerm) EndIf EndFunc ; 메뉴 추가 함수 (선택사항) Func CreateMenu() Local $hMenu = GUICtrlCreateMenu("파일") GUICtrlCreateMenuItem("설정 저장", $hMenu) GUICtrlCreateMenuItem("설정 불러오기", $hMenu) GUICtrlCreateMenuItem("-", $hMenu) GUICtrlCreateMenuItem("종료", $hMenu) Local $hToolMenu = GUICtrlCreateMenu("도구") GUICtrlCreateMenuItem("DDL 생성", $hToolMenu) GUICtrlCreateMenuItem("테이블 검색", $hToolMenu) Local $hHelpMenu = GUICtrlCreateMenu("도움말") GUICtrlCreateMenuItem("사용법", $hHelpMenu) GUICtrlCreateMenuItem("정보", $hHelpMenu) EndFunc </source> [[category:autoit]]
문서 저장 미리 보기 차이 보기 취소