저널 파일(Journal File)은 컴퓨터 시스템에서 변경 사항을 기록하는 로그 파일의 일종으로, 주로 데이터베이스, 파일 시스템, 애플리케이션에서 사용됩니다.

1. 주요 역할

  • 데이터 무결성 유지: 시스템 충돌, 전원 장애 등의 상황에서도 데이터가 손실되지 않도록 변경 사항을 기록함.
  • 복구 기능 제공: 장애 발생 시 저널 파일을 이용해 데이터베이스나 파일 시스템을 일관된 상태로 복구 가능.
  • 추적 및 감사 로그: 변경 내역을 기록하여 시스템 운영 및 문제 해결에 활용.

2. 주요 용도

  • 데이터베이스: SQLite, MySQL, PostgreSQL 등에서는 트랜잭션 로그 또는 저널 파일을 활용해 데이터 일관성을 유지.
  • 파일 시스템: NTFS, ext4 등 저널링 파일 시스템에서는 파일 변경 사항을 기록하여 갑작스러운 오류 발생 시 복구를 지원.
  • 응용 프로그램: 일부 소프트웨어는 실행 중 변경 내용을 기록하여 예기치 않은 종료 후 복원 기능 제공.

3. 데이터베이스에서의 저널 파일 예시 (SQLite)

SQLite에서는 WAL (Write-Ahead Logging) 또는 Rollback Journal 방식의 저널 파일을 사용하여 데이터 변경 사항을 저장함.

  • 파일명-journal: 롤백 저널 방식 사용 시 생성됨.
  • 파일명-wal: WAL 모드 사용 시 생성됨.

✅ 요약

저널 파일은 시스템의 변경 사항을 기록하여 장애 발생 시 데이터를 보호하고 복구하는 중요한 역할을 합니다. 특히 데이터베이스와 파일 시스템에서 널리 사용됩니다.

 

REQUEST)
isam 파일과 Journal File 은 어떤 차이가 있나요?

 

ChatGPT의 말:

ISAM(Indexed Sequential Access Method) 파일과 Journal File은 각각 다른 목적과 방식으로 데이터 저장 및 관리에 사용됩니다.

1. ISAM 파일 vs. Journal File 개요

구분ISAM 파일Journal 파일
주요 목적 데이터 저장 및 검색 (빠른 인덱스 기반 조회) 변경 사항을 기록하여 데이터 복구 및 무결성 유지
사용처 데이터베이스, 파일 시스템 데이터베이스, 파일 시스템, 애플리케이션
주요 기능 - 인덱스를 활용한 빠른 데이터 조회
- 순차 및 랜덤 접근 지원
- 변경된 데이터를 기록
- 장애 발생 시 복구 및 롤백 지원
데이터 저장 방식 정적인 데이터 저장 및 인덱싱 트랜잭션의 변경 사항을 로그 형태로 기록
파일 생성 방식 데이터 파일(.ISD), 인덱스 파일(.ISM) 트랜잭션 발생 시 자동 생성 (예: db.journal, db-wal)

2. ISAM 파일이란?

**ISAM(Indexed Sequential Access Method)**은 데이터베이스 및 파일 시스템에서 인덱스 기반으로 데이터를 검색하고 저장하는 방법입니다.
주로 빠른 조회 성능이 중요한 경우 사용됩니다.

ISAM 파일의 특징

  • 데이터 파일(.ISD)과 인덱스 파일(.ISM)로 구성됨.
  • 인덱스를 활용하여 빠른 검색과 정렬된 데이터 조회 가능.
  • 순차 검색(Sequential Access)과 랜덤 검색(Random Access) 모두 지원.
  • MySQL의 MyISAM 스토리지 엔진이 대표적인 예.

ISAM 파일 사용 예

  • MySQL MyISAM 엔진: *.MYD(데이터), *.MYI(인덱스) 파일 사용.
  • IBM DB2, VSAM 등에서도 유사한 방식 사용.

3. Journal 파일이란?

Journal 파일은 시스템에서 변경된 데이터를 기록하여 장애 발생 시 복구 및 무결성 유지에 초점을 맞춘 파일입니다.

Journal 파일의 특징

  • 트랜잭션 로그 형식으로 데이터 변경 사항 기록.
  • 장애 발생 시, Redo (복구) 또는 Undo (롤백) 가능.
  • 파일 시스템(NTFS, ext4) 및 데이터베이스(SQLite, PostgreSQL)에서 사용됨.

Journal 파일 사용 예

  • 데이터베이스
    • SQLite: *.journal, *-wal 파일 사용.
    • PostgreSQL: pg_wal(Write-Ahead Logging).
  • 파일 시스템
    • NTFS, ext4 파일 시스템의 저널링 기능.

4. 주요 차이점 정리

구분ISAM 파일Journal 파일
기능 인덱스를 활용한 빠른 데이터 조회 트랜잭션 변경 사항을 기록하여 복구 가능
목적 검색 성능 최적화 데이터 일관성 및 복구
파일 구성 데이터 파일 + 인덱스 파일 변경 사항을 기록하는 로그 파일
장애 복구 데이터 손실 시 별도 복구 필요 장애 발생 시 복구 가능

✅ 결론

  • ISAM 파일데이터 조회 성능을 높이기 위한 구조이며,
  • Journal 파일데이터 변경 사항을 기록하여 복구를 지원하는 역할을 합니다.

즉, ISAM은 "빠른 검색"에 초점, Journal 파일은 "데이터 보호 및 복구"에 초점을 맞춘 개념입니다.

  • (x1...) 표시는 SQLite가 인덱스를 생성하는 과정에서 발생하는 출력일 가능성이 높음.
 
4o

나의 말:

WITH MaxTransaction AS ( SELECT MainNumber, SubNumber, ComplexName, AreaSquareMeters, TransactionAmount, ContractYearMonth, Floor, ROW_NUMBER() OVER ( PARTITION BY MainNumber, SubNumber, ComplexName, AreaSquareMeters ORDER BY TransactionAmount DESC, ContractYearMonth DESC, Floor DESC ) AS RowNum FROM ANESTATEAPTALL WHERE (TransactionType = '중개거래' OR NOT EXISTS (SELECT 1 FROM ANESTATEAPTALL WHERE TransactionType = '중개거래')) AND CityCounty LIKE '인천광역시%' ), MinTransaction AS ( SELECT MainNumber, SubNumber, ComplexName, AreaSquareMeters, TransactionAmount, ContractYearMonth, Floor, ROW_NUMBER() OVER ( PARTITION BY MainNumber, SubNumber, ComplexName, AreaSquareMeters ORDER BY TransactionAmount ASC, ContractYearMonth ASC, Floor ASC ) AS RowNum FROM ANESTATEAPTALL WHERE (TransactionType = '중개거래' OR NOT EXISTS (SELECT 1 FROM ANESTATEAPTALL WHERE TransactionType = '중개거래')) AND CityCounty LIKE '인천광역시%' ) SELECT ROW_NUMBER() OVER () AS DescendingNumber, m.MainNumber, m.SubNumber, m.ComplexName, m.AreaSquareMeters AS 평형대, m.TransactionAmount AS MAX_금액, m.ContractYearMonth AS MAX_매매일자, m.Floor AS MAX_층수, n.TransactionAmount AS MIN_금액, n.ContractYearMonth AS MIN_매매일자, n.Floor AS MIN_층수, (m.TransactionAmount - n.TransactionAmount) AS AliasCapitalGain, '032' FROM MaxTransaction m JOIN MinTransaction n ON m.MainNumber = n.MainNumber AND m.SubNumber = n.SubNumber AND m.ComplexName = n.ComplexName AND m.AreaSquareMeters = n.AreaSquareMeters WHERE m.RowNum = 1 AND n.RowNum = 1 ORDER BY AliasCapitalGain ASC LIMIT 60; 위의 SQL를 보고 INDEX를 생성하고 싶습니다. sqlite3.exe 로 생성하고자 합니다. 생성하는 파일은 C:\Users\B210145_BK\Downloads\EstateDotNet\EstateStatisticsNodeSearch\RedisTickServer 디렉터리 2025-02-20 오후 12:18 414,756,864 database_all_total.db
 

ChatGPT의 말:

📌 SQLite에서 성능 최적화를 위한 INDEX 생성

현재 쿼리는 ANESTATEAPTALL 테이블에서 최고가/최저가 거래를 찾고, 이를 비교하여 차익을 계산하는 작업을 수행합니다.
특히 WHERE, JOIN, ORDER BY에 자주 사용되는 컬럼에 대한 인덱스를 추가하면 성능을 향상시킬 수 있습니다.


🔍 쿼리에서 중요한 조건

  1. WHERE 절에서 검색 조건이 사용되는 컬럼
    • TransactionType
    • CityCounty
  2. PARTITION BY 및 JOIN에 사용되는 컬럼 (동일한 기준으로 매칭 필요)
    • MainNumber
    • SubNumber
    • ComplexName
    • AreaSquareMeters
  3. ORDER BY에 사용되는 컬럼 (최고/최저 금액을 찾기 위한 정렬)
    • TransactionAmount
    • ContractYearMonth
    • Floor

INDEX 생성 쿼리 (SQLite)

아래 인덱스를 생성하여 쿼리 성능을 향상시킬 수 있습니다.

sql
복사편집
-- 1. 거래 유형(TransactionType)과 지역(CityCounty)에 대한 필터링 최적화 CREATE INDEX idx_anestateaptall_citycounty_transaction ON ANESTATEAPTALL (CityCounty, TransactionType); -- 2. PARTITION BY 및 JOIN에 사용되는 컬럼 최적화 CREATE INDEX idx_anestateaptall_partition ON ANESTATEAPTALL (MainNumber, SubNumber, ComplexName, AreaSquareMeters); -- 3. 최대 및 최소 거래 금액 검색 최적화 (ORDER BY 에 사용되는 컬럼 포함) CREATE INDEX idx_anestateaptall_transaction_sort ON ANESTATEAPTALL (TransactionAmount DESC, ContractYearMonth DESC, Floor DESC);

📌 인덱스 최적화 이유

  • 첫 번째 인덱스 (idx_anestateaptall_citycounty_transaction)
    → CityCounty LIKE '인천광역시%' 검색을 빠르게 수행
    → TransactionType 필터링을 최적화
  • 두 번째 인덱스 (idx_anestateaptall_partition)
    → PARTITION BY 및 JOIN에 사용되는 컬럼을 효율적으로 조회
  • 세 번째 인덱스 (idx_anestateaptall_transaction_sort)
    → ORDER BY TransactionAmount DESC, ContractYearMonth DESC, Floor DESC 최적화

🚀 인덱스 생성 방법 (sqlite3.exe)

  1. sqlite3 실행
  2. nginx
    복사편집
    sqlite3 "C:\Users\B210145_BK\Downloads\EstateDotNet\EstateStatisticsNodeSearch\RedisTickServer\database_all_total.db"
  3. 인덱스 생성 SQL 실행
  4. pgsql
    복사편집
    CREATE INDEX idx_anestateaptall_citycounty_transaction ON ANESTATEAPTALL (CityCounty, TransactionType); CREATE INDEX idx_anestateaptall_partition ON ANESTATEAPTALL (MainNumber, SubNumber, ComplexName, AreaSquareMeters); CREATE INDEX idx_anestateaptall_transaction_sort ON ANESTATEAPTALL (TransactionAmount DESC, ContractYearMonth DESC, Floor DESC);
  5. 인덱스 확인
  6. csharp
    복사편집
    PRAGMA index_list('ANESTATEAPTALL');
  7. 쿼리 실행 후 성능 확인
  8. pgsql
    복사편집
    EXPLAIN QUERY PLAN <위의 SELECT 문>;

📈 추가 최적화 가능성

  1. 거래 타입이 항상 '중개거래' 또는 없는 경우만 검색됨
    → TransactionType이 NULL일 가능성이 있다면,
    CREATE INDEX idx_anestateaptall_transactiontype ON ANESTATEAPTALL (TransactionType);
    추가 고려 가능
  2. 매매가 상승/하락 정렬 속도 향상
    → AliasCapitalGain 기준으로 ORDER BY 성능이 필요하다면,
    CREATE INDEX idx_anestateaptall_capitalgain ON ANESTATEAPTALL (TransactionAmount); 고려 가능

이렇게 인덱스를 추가하면 SELECT 속도가 빨라지고, 특히 최고가/최저가 검색 시 성능이 개선될 것입니다.
적용 후 쿼리 실행 속도가 어느 정도 개선되는지 확인해보면 좋을 것 같네요! 🚀

 
4o

나의 말:

C:\Users\B210145_BK\Downloads\sqlite-tools-win-x64-3480000>sqlite3 "C:\Users\B210145_BK\Downloads\EstateDotNet\EstateStatisticsNodeSearch\RedisTickServer\database_all_total.db" SQLite version 3.48.0 2025-01-14 11:05:00 Enter ".help" for usage hints. sqlite> CREATE INDEX idx_anestateaptall_partition ON ANESTATEAPTALL (MainNumber, SubNumber, ComplexName, AreaSquareMeters); C:\Users\B210145_BK\Downloads\sqlite-tools-win-x64-3480000> 프롬프트로 바로 빠져나와 버린다면?
 

ChatGPT의 말:

프롬프트에서 SQLite를 실행하고 CREATE INDEX 명령을 실행한 후 에러 메시지 없이 바로 종료되는 경우, 몇 가지 원인을 확인해야 합니다.


🔍 가능한 원인 및 해결 방법

1. SQLite 실행 중 오류로 인해 비정상 종료 (Crash)

  • 해결 방법:
    SQLite를 다시 실행하고, 데이터베이스가 정상적으로 열리는지 확인합니다.
    sh
    복사편집
    sqlite3 "C:\Users\B210145_BK\Downloads\EstateDotNet\EstateStatisticsNodeSearch\RedisTickServer\database_all_total.db"
    정상적으로 열리면 .tables 명령어를 입력하여 테이블 목록을 확인해 보세요.
    sql
    복사편집
    .tables
    → 만약 SQLite가 바로 종료된다면, 데이터베이스 파일이 손상되었을 가능성이 있습니다.
    (아래 "데이터베이스 손상 가능성" 해결 방법 참고)

2. 테이블이 존재하지 않음 (ANESTATEAPTALL이 없음)

  • CREATE INDEX 실행 시 대상 테이블이 존재하지 않으면 SQLite가 에러 없이 종료될 수도 있음
  • 해결 방법:
    SQLite 실행 후 테이블 존재 여부를 먼저 확인
    sql
    복사편집
    .tables
    또는
    sql
    복사편집
    SELECT name FROM sqlite_master WHERE type='table' AND name='ANESTATEAPTALL';
    → 결과가 없다면, 테이블이 존재하지 않는 것이므로 인덱스 생성이 불가능합니다.

3. 기존에 동일한 인덱스가 이미 존재함

  • SQLite에서 동일한 이름의 인덱스를 다시 생성하려고 하면 충돌이 발생할 수 있음.
  • 해결 방법:
    먼저 인덱스가 존재하는지 확인합니다.
    sql
    복사편집
    SELECT name FROM sqlite_master WHERE type='index' AND name='idx_anestateaptall_partition';
    → 결과가 있다면 기존 인덱스를 삭제하고 다시 생성할 수 있습니다.
    sql
    복사편집
    DROP INDEX idx_anestateaptall_partition; CREATE INDEX idx_anestateaptall_partition ON ANESTATEAPTALL (MainNumber, SubNumber, ComplexName, AreaSquareMeters);

4. 데이터베이스 파일이 손상됨

  • SQLite 데이터베이스 (database_all_total.db)가 크거나 손상된 경우,
    특정 명령어 실행 시 비정상 종료될 가능성이 있음.
  • 해결 방법:
    데이터베이스 무결성 검사 실행
    sql
    복사편집
    PRAGMA integrity_check;
    만약 손상되었다는 메시지가 나온다면, 백업 후 복구해야 합니다.
    백업 후 새 DB로 데이터 옮기기
    sh
    복사편집
    sqlite3 database_all_total.db ".dump" > backup.sql sqlite3 new_database.db < backup.sql

5. 인덱스 생성 중 메모리 부족

  • 데이터베이스 크기가 크거나 인덱스가 많은 경우,
    메모리 부족으로 SQLite가 종료될 가능성이 있음.
  • 해결 방법:
    SQLite 실행 후 설정을 늘려봅니다.
    sql
    복사편집
    PRAGMA cache_size = 100000; PRAGMA temp_store = MEMORY; PRAGMA synchronous = OFF;
    그리고 다시 CREATE INDEX 실행.

🔥 추천 해결 순서

1️⃣ SQLite 다시 실행 → .tables 확인
2️⃣ 테이블 존재 확인 (SELECT name FROM sqlite_master ...)
3️⃣ 기존 인덱스 존재 확인 (SELECT name FROM sqlite_master WHERE type='index' ...)
4️⃣ PRAGMA integrity_check 실행하여 데이터베이스 무결성 확인
5️⃣ 메모리 설정 조정 후 다시 시도

위 과정으로도 해결되지 않으면, 데이터베이스 손상 가능성이 크므로 백업 후 복구하는 것이 가장 확실한 방법입니다! 🚀

 
4o

나의 말:

sqlite> CREATE INDEX idx_anestateaptall_citycounty_transaction ON ANESTATEAPTALL (CityCounty, TransactionType); (x1...> (x1...> (x1...> 위의 "(x1...>" 표시는 무슨뜻인가요?
 

ChatGPT의 말:

SQLite 프롬프트에서 CREATE INDEX 실행 시 (x1...> 같은 메시지가 출력되는 것은 인덱스 생성 중에 진행 상황을 표시하는 SQLite의 내부 출력 형식일 가능성이 높습니다.

이것은 특히 데이터베이스가 크거나, 인덱스 생성에 시간이 오래 걸릴 때 나타날 수 있습니다.


🔍 원인

  1. SQLite가 대량의 데이터를 처리 중
    • CREATE INDEX는 테이블의 모든 데이터를 스캔하여 정렬 후 저장해야 하므로 시간이 걸릴 수 있습니다.
    • 이 과정에서 SQLite는 내부적으로 페이지를 읽고 쓰면서 (x1...) 같은 진행 상태를 표시할 수 있습니다.
    • 만약 데이터베이스가 크다면 몇 분~수십 분까지 걸릴 수도 있습니다.
  2. 터미널 출력 방식에 따른 현상
    • 특정 환경 (예: Windows 명령 프롬프트, Git Bash 등)에서는 SQLite 내부에서 버퍼링된 메시지를 이상한 형식으로 출력할 수도 있습니다.
    • 예를 들어, SQLite는 압축된 로그 출력을 사용할 수도 있으며, 일부 환경에서 올바르게 해석되지 않을 수 있습니다.

해결 방법

1. 진행 중이라면 기다리기

  • SQLite에서 특별한 오류 없이 인덱스를 생성하는 중일 가능성이 높으므로,
    CPU 사용량을 확인하면서 조금 더 기다려보세요.
  • database_all_total.db가 크다면 시간이 오래 걸릴 수 있습니다.

2. 인덱스 생성이 끝났는지 확인

  • SQLite가 아직 실행 중이라면, 다른 명령을 입력해보세요.
  • sql
    복사편집
    .tables
  • 만약 반응이 없다면, 인덱스 생성이 아직 진행 중일 가능성이 높습니다.
  • 또는 인덱스 목록을 확인하여 정상적으로 생성되었는지 체크합니다.
  • sql
    복사편집
    SELECT name FROM sqlite_master WHERE type='index';

3. 강제 종료 후 다시 시도

  • 만약 너무 오래 걸리거나 반응이 없다면, SQLite를 강제 종료 (Ctrl + C)하고 다시 실행할 수 있습니다.
  • 이후, 이미 생성된 인덱스가 있다면 삭제 후 다시 생성합니다.
    sql
    복사편집
    DROP INDEX IF EXISTS idx_anestateaptall_citycounty_transaction; CREATE INDEX idx_anestateaptall_citycounty_transaction ON ANESTATEAPTALL (CityCounty, TransactionType);

4. SQLite 실행 모드 변경 (Windows)

  • SQLite를 실행할 때, 명령 프롬프트(cmd) 대신 PowerShell을 사용하면 출력을 다르게 볼 수 있습니다.
  • 또는 SQLite를 "quiet mode"로 실행하여 불필요한 메시지를 줄일 수도 있습니다.
    sh
    복사편집
    sqlite3 -batch "C:\Users\B210145_BK\Downloads\EstateDotNet\EstateStatisticsNodeSearch\RedisTickServer\database_all_total.db"

🚀 결론

  • (x1...) 표시는 SQLite가 인덱스를 생성하는 과정에서 발생하는 출력일 가능성이 높음.
  • DB가 크면 시간이 오래 걸릴 수 있으므로 조금 더 기다리거나, 진행 상태를 확인할 것.
  • 너무 오래 걸리면 강제 종료 후 다시 실행하거나, 메모리 설정을 조정하여 성능을 개선할 것.

필요한 경우, 인덱스를 더 작은 부분부터 생성하면서 성능 테스트를 해볼 수도 있습니다. 🚀

 

SQLite 테이블에 인덱스 생성하기(.feat sqlite3.exe)
 
4o

 

CREATE TABLE ESTATENAVERALLINFOATTACH ( DATACLASS VARCHAR(2), INFOMARKETCLASS VARCHAR(3), TRANSACTIONTIME VARCHAR(14), COMPLEXNO VARCHAR(15), COMPLEXNAME VARCHAR(40), CORTARNO VARCHAR(20), REALESTATETYPECODE VARCHAR(20), REALESTATETYPENAME VARCHAR(20), USEAPPROVEYMD VARCHAR(20), CORTARADDRESS VARCHAR(400), MINPRICEBYLETTER VARCHAR(20), MAXPRICEBYLETTER VARCHAR(20), MINLEASEPRICEBYLETTER VARCHAR(20), MAXLEASEPRICEBYLETTER VARCHAR(20), MINPRICE VARCHAR(20), MAXPRICE VARCHAR(20), MINLEASEPRICE VARCHAR(20), MAXLEASEPRICE VARCHAR(20), LNBRMNNM VARCHAR(20), LNBRSLNO VARCHAR(20), ENDTEXT VARCHAR(1) ) SELECT DataClass,InfoMarketClass,TransactionTime,ComplexNo,CortarNo,RealEstateTypeCode,RealEstateTypeName,UseApproveYmd,CortarAddress,MinPriceByLetter,MaxPriceByLetter,MinLeasePriceByLetter,MaxLeasePriceByLetter,MinPrice,MaxPrice,MinLeasePrice,MaxLeasePrice FROM ESTATENAVERALLINFOATTACH WHERE CORTARADDRESS = '인천시 연수구 송도동' AND LNBRMNNM = '110' AND LNBRSLNO = '0' LIMIT 1; SQLite 쿼리인데, 테이블조회를 빠르게 하려면 인덱스를 생성하고 싶습니다. sqlite3.exe 를 이용해서 인덱스를 생성하는 방법은?
 

ChatGPT의 말:

SQLite에서 테이블 조회 속도를 높이려면 적절한 인덱스를 생성해야 합니다.
특히, WHERE 조건에서 자주 사용되는 컬럼(CORTARADDRESS, LNBRMNNM, LNBRSLNO)에 인덱스를 설정하면 조회 속도가 빨라집니다.

1. 인덱스 생성 (sqlite3.exe에서 실행)

먼저 SQLite 명령줄에서 sqlite3.exe를 실행한 후, 아래 SQL을 입력하세요.

sql
복사편집
CREATE INDEX idx_cortar_lnbr ON ESTATENAVERALLINFOATTACH (CORTARADDRESS, LNBRMNNM, LNBRSLNO);

2. 인덱스 확인

인덱스가 잘 생성되었는지 확인하려면 아래 명령을 실행하세요.

sql
복사편집
PRAGMA index_list(ESTATENAVERALLINFOATTACH);

3. 인덱스 사용 확인 (실행 계획 확인)

쿼리 실행 전에 인덱스를 사용하는지 확인하려면 EXPLAIN QUERY PLAN을 사용하세요.

sql
복사편집
EXPLAIN QUERY PLAN SELECT DataClass, InfoMarketClass, TransactionTime, ComplexNo, CortarNo, RealEstateTypeCode, RealEstateTypeName, UseApproveYmd, CortarAddress, MinPriceByLetter, MaxPriceByLetter, MinLeasePriceByLetter, MaxLeasePriceByLetter, MinPrice, MaxPrice, MinLeasePrice, MaxLeasePrice FROM ESTATENAVERALLINFOATTACH WHERE CORTARADDRESS = '인천시 연수구 송도동' AND LNBRMNNM = '110' AND LNBRSLNO = '0' LIMIT 1;

결과에 "USING INDEX idx_cortar_lnbr"와 같은 문구가 있으면 인덱스가 적용된 것입니다.

추가 최적화

  • ANALYZE; 명령을 실행하면 SQLite가 쿼리 최적화를 위해 통계를 수집합니다.
  • VACUUM; 명령을 실행하면 데이터베이스 파일을 최적화하여 성능을 높일 수 있습니다.

필요하면 추가 인덱스를 생성할 수도 있으니, 쿼리 실행 계획을 확인해보고 최적화하세요! 🚀

 
4o

나의 말:

C:\Users\B210145_BK\Downloads\EstateDotNet\EstateStatisticsNodeSearch\RedisTickServer 디렉터리 2025-02-18 오후 03:36 11,718,656 naver_attach_estate_info_file.db 위의 파일입니다. sqlite3.exe 를 이용해서 인덱스생성 방법을 자세히 설명해주세요
 

ChatGPT의 말:

SQLite3에서 sqlite3.exe를 사용하여 인덱스를 생성하는 과정을 자세히 설명해 드리겠습니다.


1. SQLite3 실행하기

먼저 **명령 프롬프트(cmd)**를 열고 sqlite3.exe를 실행합니다.

  1. 명령 프롬프트(cmd) 실행
    • 윈도우 + R 키 → cmd 입력 후 실행
  2. SQLite3 실행
    SQLite3가 설치된 디렉터리로 이동한 후, 데이터베이스를 엽니다.
    (예: sqlite3.exe가 C:\sqlite3\에 설치된 경우)참고: sqlite3.exe의 경로가 C:\sqlite3\sqlite3.exe라면 C:\sqlite3\sqlite3.exe를 직접 실행해도 됩니다.
  3. sh
    복사편집
    cd C:\sqlite3\ sqlite3 "C:\Users\B210145_BK\Downloads\EstateDotNet\EstateStatisticsNodeSearch\RedisTickServer\naver_attach_estate_info_file.db"

2. 데이터베이스 확인

데이터베이스가 정상적으로 열렸는지 확인합니다.

sql
복사편집
.tables

이 명령을 실행하면 ESTATENAVERALLINFOATTACH 테이블이 목록에 표시되어야 합니다.


3. 기존 인덱스 확인

기존에 생성된 인덱스가 있는지 확인합니다.

sql
복사편집
PRAGMA index_list(ESTATENAVERALLINFOATTACH);

만약 적절한 인덱스가 없다면 새로운 인덱스를 생성합니다.


4. 인덱스 생성

이제 CORTARADDRESS, LNBRMNNM, LNBRSLNO 컬럼을 이용해 인덱스를 생성합니다.

sql
복사편집
CREATE INDEX idx_cortar_lnbr ON ESTATENAVERALLINFOATTACH (CORTARADDRESS, LNBRMNNM, LNBRSLNO);

이제 인덱스가 생성되었습니다.


5. 인덱스 생성 확인

인덱스가 잘 생성되었는지 확인하려면 다시 PRAGMA index_list를 실행합니다.

sql
복사편집
PRAGMA index_list(ESTATENAVERALLINFOATTACH);

또한, 생성된 인덱스의 세부 정보를 확인하려면 아래 명령을 실행하세요.

sql
복사편집
PRAGMA index_info(idx_cortar_lnbr);

6. 인덱스 사용 여부 확인

쿼리가 인덱스를 사용하는지 확인하려면 EXPLAIN QUERY PLAN을 실행하세요.

sql
복사편집
EXPLAIN QUERY PLAN SELECT DataClass, InfoMarketClass, TransactionTime, ComplexNo, CortarNo, RealEstateTypeCode, RealEstateTypeName, UseApproveYmd, CortarAddress, MinPriceByLetter, MaxPriceByLetter, MinLeasePriceByLetter, MaxLeasePriceByLetter, MinPrice, MaxPrice, MinLeasePrice, MaxLeasePrice FROM ESTATENAVERALLINFOATTACH WHERE CORTARADDRESS = '인천시 연수구 송도동' AND LNBRMNNM = '110' AND LNBRSLNO = '0' LIMIT 1;

실행 결과에 "USING INDEX idx_cortar_lnbr" 같은 문구가 포함되어 있으면 인덱스가 적용된 것입니다.


7. 최적화 (선택 사항)

인덱스를 생성한 후 성능을 더 높이려면 아래 명령을 실행하세요.

sql
복사편집
ANALYZE; VACUUM;
  • ANALYZE; → 통계 정보를 업데이트하여 SQLite의 쿼리 최적화를 돕습니다.
  • VACUUM; → 데이터베이스 파일을 정리하여 크기를 줄이고 성능을 개선합니다.

요약

  1. SQLite3 실행: sqlite3 "파일 경로"
  2. 테이블 확인: .tables
  3. 기존 인덱스 확인: PRAGMA index_list(테이블명);
  4. 새 인덱스 생성: CREATE INDEX idx_cortar_lnbr ON 테이블명(컬럼1, 컬럼2, 컬럼3);
  5. 인덱스 확인: PRAGMA index_list(테이블명);
  6. 인덱스 적용 여부 확인: EXPLAIN QUERY PLAN SELECT ...
  7. 최적화 실행: ANALYZE; 및 VACUUM;

이제 sqlite3.exe를 이용해 인덱스를 빠르게 생성하고 최적화할 수 있습니다! 🚀

 
4o

나의 말:

SELECT CityCounty,LotNumber,TransactionAmount,Building,Buyer,Seller,ConstructionYear,RoadName,CancellationReasonDate,TransactionType,BrokerLocation,RegistrationDate FROM ANESTATEAPTALL WHERE MainNumber = '17' AND SubNumber = '5' AND ComplexName = '송도더샵하버뷰(D13)' AND AreaSquareMeters = '118.459' LIMIT 1 C:\Users\B210145_BK\Downloads\EstateDotNet\EstateStatisticsNodeSearch\RedisTickServer 디렉터리 2025-02-19 오후 03:07 344,907,776 database_all_total.db CREATE TABLE ANESTATEAPTALL ( Nnumber INTEGER, RepeatNumber INTEGER, CityCounty TEXT, LotNumber TEXT, MainNumber TEXT, SubNumber TEXT, ComplexName TEXT, AreaSquareMeters REAL, ContractYearMonth TEXT, ContractDate TEXT, TransactionAmount REAL, Building TEXT, Floor INTEGER, Buyer TEXT, Seller TEXT, ConstructionYear INTEGER, RoadName TEXT, CancellationReasonDate TEXT, TransactionType TEXT, BrokerLocation TEXT, RegistrationDate TEXT, HousingType TEXT ) 위의 테이블도 인덱스를 생성해주세요
 

ChatGPT의 말:

SQLite3를 이용한 인덱스 생성 (ANESTATEAPTALL 테이블)

1. SQLite3 실행

명령 프롬프트(cmd)에서 SQLite3을 실행하여 database_all_total.db 파일을 엽니다.

sh
복사편집
cd C:\sqlite3\ sqlite3 "C:\Users\B210145_BK\Downloads\EstateDotNet\EstateStatisticsNodeSearch\RedisTickServer\database_all_total.db"

2. 기존 인덱스 확인

이미 인덱스가 존재하는지 확인합니다.

sql
복사편집
PRAGMA index_list(ANESTATEAPTALL);

만약 적절한 인덱스가 없다면 아래와 같이 생성합니다.


3. 인덱스 생성

조회 성능을 향상시키기 위해 WHERE 조건에 자주 사용되는 컬럼(MainNumber, SubNumber, ComplexName, AreaSquareMeters)에 대해 인덱스를 생성합니다.

sql
복사편집
CREATE INDEX idx_apt_search ON ANESTATEAPTALL (MainNumber, SubNumber, ComplexName, AreaSquareMeters);

이제 인덱스가 생성되었습니다.


4. 인덱스 생성 확인

인덱스가 잘 생성되었는지 확인합니다.

sql
복사편집
PRAGMA index_list(ANESTATEAPTALL);

생성된 인덱스의 세부 정보를 보려면:

sql
복사편집
PRAGMA index_info(idx_apt_search);

5. 인덱스 사용 여부 확인

인덱스가 실제로 사용되는지 확인하기 위해 EXPLAIN QUERY PLAN을 실행합니다.

sql
복사편집
EXPLAIN QUERY PLAN SELECT CityCounty, LotNumber, TransactionAmount, Building, Buyer, Seller, ConstructionYear, RoadName, CancellationReasonDate, TransactionType, BrokerLocation, RegistrationDate FROM ANESTATEAPTALL WHERE MainNumber = '17' AND SubNumber = '5' AND ComplexName = '송도더샵하버뷰(D13)' AND AreaSquareMeters = '118.459' LIMIT 1;

결과에 "USING INDEX idx_apt_search"가 포함되어 있으면 인덱스가 적용된 것입니다.


6. 데이터베이스 최적화

인덱스를 추가한 후 성능을 최적화하려면 다음 명령을 실행하세요.

sql
복사편집
ANALYZE; VACUUM;
  • ANALYZE; → 테이블과 인덱스의 통계를 업데이트하여 SQLite의 쿼리 최적화를 돕습니다.
  • VACUUM; → 데이터베이스 파일을 정리하여 크기를 줄이고 성능을 향상시킵니다.

요약

  1. SQLite3 실행: sqlite3 "database_all_total.db"
  2. 기존 인덱스 확인: PRAGMA index_list(ANESTATEAPTALL);
  3. 새 인덱스 생성:
    sql
    복사편집
    CREATE INDEX idx_apt_search ON ANESTATEAPTALL (MainNumber, SubNumber, ComplexName, AreaSquareMeters);
  4. 인덱스 확인: PRAGMA index_list(ANESTATEAPTALL);
  5. 인덱스 적용 여부 확인: EXPLAIN QUERY PLAN SELECT ...
  6. 최적화 실행: ANALYZE; 및 VACUUM;

이제 ANESTATEAPTALL 테이블에서 빠르게 데이터를 조회할 수 있습니다! 🚀

 
4o

나의 말:

sqlite> CREATE INDEX idx_apt_search ON ANESTATEAPTALL (MainNumber, SubNumber, ComplexName); sqlite> sqlite> sqlite> sqlite> sqlite> PRAGMA index_list(ANESTATEAPTALL); 0|idx_apt_search|0|c|0 sqlite> PRAGMA index_info(idx_apt_search); 0|4|MainNumber 1|5|SubNumber 2|6|ComplexName sqlite> sqlite> 종료하는 명령어는?
 

ChatGPT의 말:

SQLite3에서 종료하는 명령어는 다음 중 하나를 사용하면 됩니다.

1. .exit 또는 .quit 명령어 사용 (권장)

sql
복사편집
.exit

또는

sql
복사편집
.quit

이 둘 중 하나를 입력하면 SQLite3이 종료됩니다.


2. Ctrl + D 키 사용 (Linux/macOS)

Linux 또는 macOS에서 SQLite3을 실행 중이라면 Ctrl + D 키를 눌러 종료할 수도 있습니다.


3. Ctrl + C 키 사용 (강제 종료)

만약 SQLite가 응답하지 않거나 즉시 종료하고 싶다면 **Ctrl + C**를 누르면 강제 종료됩니다.

이제 안전하게 SQLite를 종료할 수 있습니다! 😊

SQLite Update - 전체
SQLite Update - 단일건

using System;


class Program
{
    static void Main()
    {
        string dbPath = "database_all_total.db"; // SQLite 데이터베이스 파일 경로
        string connectionString = $"Data Source={dbPath};Version=3;";

        using (var connection = new SQLiteConnection(connectionString))
        {
            connection.Open();

            // 한 번의 UPDATE로 변환 실행
            string updateQuery = @"
                UPDATE ANESTATEAPTALL
                SET 
                    MainNumber = CAST(MainNumber AS INTEGER),
                    SubNumber = CAST(SubNumber AS INTEGER);";

            using (var command = new SQLiteCommand(updateQuery, connection))
            {
                int rowsAffected = command.ExecuteNonQuery();
                Console.WriteLine($"업데이트 완료! 변경된 행: {rowsAffected}");
            }
        }
    }
}
using System;
using System.Text;

class Program
{
    static void Main()
    {
        string dbPath = "database_all_total.db"; // SQLite 데이터베이스 파일 경로
        string connectionString = $"Data Source={dbPath};Version=3;";

        using (var connection = new SQLiteConnection(connectionString))
        {
            connection.Open();

            string selectQuery = "SELECT MainNumber, SubNumber FROM ANESTATEAPTALL;";
            using (var selectCommand = new SQLiteCommand(selectQuery, connection))
            using (var reader = selectCommand.ExecuteReader())
            {
                StringBuilder updateQuery = new StringBuilder("UPDATE ANESTATEAPTALL SET ");

                // MainNumber 변환 SQL
                updateQuery.Append("MainNumber = CASE ");
                while (reader.Read())
                {
                    string mainNumber = reader["MainNumber"].ToString();
                    string subNumber = reader["SubNumber"].ToString();

                    int newMainNumber = ConvertToInt(mainNumber);
                    int newSubNumber = ConvertToInt(subNumber);

                    updateQuery.Append($"WHEN MainNumber = '{mainNumber}' THEN '{newMainNumber}' ");
                }
                updateQuery.Append("ELSE MainNumber END, ");

                // SubNumber 변환 SQL
                updateQuery.Append("SubNumber = CASE ");
                reader.Close();
                using (var reader2 = selectCommand.ExecuteReader())
                {
                    while (reader2.Read())
                    {
                        string mainNumber = reader2["MainNumber"].ToString();
                        string subNumber = reader2["SubNumber"].ToString();

                        int newSubNumber = ConvertToInt(subNumber);

                        updateQuery.Append($"WHEN SubNumber = '{subNumber}' THEN '{newSubNumber}' ");
                    }
                }
                updateQuery.Append("ELSE SubNumber END;");

                using (var updateCommand = new SQLiteCommand(updateQuery.ToString(), connection))
                {
                    updateCommand.ExecuteNonQuery();
                }

                Console.WriteLine("한 번에 업데이트 완료!");
            }
        }
    }

    static int ConvertToInt(string input)
    {
        return int.TryParse(input, out int result) ? result : 0;
    }
}

 

2020.01 - 2025.01 까지 국토교통부 실거래자료 입력후에 분석해본 통계결과
기자료)


1. 2020-2025 경기도 안양시 동안구 매매자료 download후에, SQLite에 입력후 조회
2. 2020-2025 서울시 마포구 매매자료 download후에, SQLite에 입력 후 조회
3. 2019-2025 서울시 전체 매매자료 download후에, SQLite에 입력 후 조회



프로그램화)
1. 입력부
2. 조회부

분석결과)

서울은 마포구와 강남구를 조사했는데, 모두 신고가가 최근에 이루어지고 있는것을 확인할수 있었고,
나머지 지역은 2021-2022 저금리시기, 급등기에 이루어진 거래였다는 점입니다.
최근은 똘똘한 한채로 신고가 매매가 이루어 지는것 같고, 그 지역은 서울과 수도권의 알짜단지인것 같습니다.

 

아파트실거래가_통계_매매_MAX_MIN_지역별(20250131).xlsx
0.03MB


다량 데이타 입력을 처리하는 배치프로그램(성능향상)

다량 데이타 입력을 처리하는 배치프로그램에서 아래의 사항을 고려하면, 보다 효과적으로 속도를 개선시킬서수 있습니다.


OCI 환경 초기화

배치 처리를 위한 바인드 변수 설정

 

Oracle Call Interface(OCI)를 통해 다량의 데이터를 입력할 때, 성능을 최적화하려면 다음과 같은 전략을 사용할 수 있습니다.

OCI를 사용하여 데이터베이스 연결을 초기화합니다.

OCI에서는 다중 바인드 변수와 배열 인터페이스를 사용하여 대량 데이터를 효율적으로 처리할 수 있습니다.예: 한 번에 1000건의 데이터를 삽입하는 코드

OCIEnv *env;
OCIError *err;
OCISvcCtx *svc;
OCIStmt *stmt;

OCIEnvCreate(&env, OCI_DEFAULT, NULL, NULL, NULL, NULL, 0, NULL);
OCIHandleAlloc(env, (void **)&err, OCI_HTYPE_ERROR, 0, NULL);
OCIHandleAlloc(env, (void **)&svc, OCI_HTYPE_SVCCTX, 0, NULL);

// 데이터베이스 연결
OCIHandleAlloc(env, (void **)&svc, OCI_HTYPE_SVCCTX, 0, NULL);
OCILogon(env, err, &svc, "username", strlen("username"), "password", strlen("password"), "dbname", strlen("dbname"));

 

#define BATCH_SIZE 1000
#define MAX_COLUMN_LENGTH 100

char col1[BATCH_SIZE][MAX_COLUMN_LENGTH];
int col2[BATCH_SIZE];

OCIStmt *stmt;
OCIHandleAlloc(env, (void **)&stmt, OCI_HTYPE_STMT, 0, NULL);

// SQL 준비
char *sql = "INSERT INTO my_table (column1, column2) VALUES (:1, :2)";
OCIStmtPrepare(stmt, err, (text *)sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);

// 바인드 변수 설정
OCIBind *bnd1 = NULL, *bnd2 = NULL;
OCIBindByPos(stmt, &bnd1, err, 1, (void *)col1, MAX_COLUMN_LENGTH, SQLT_STR, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
OCIBindByPos(stmt, &bnd2, err, 2, (void *)col2, sizeof(int), SQLT_INT, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);

// 데이터 채우기
for (int i = 0; i < BATCH_SIZE; i++) {
    snprintf(col1[i], MAX_COLUMN_LENGTH, "Value_%d", i);
    col2[i] = i;
}

// 데이터 배치 삽입
OCIBindArrayOfStruct(bnd1, err, MAX_COLUMN_LENGTH, 0, 0, 0);
OCIBindArrayOfStruct(bnd2, err, sizeof(int), 0, 0, 0);

OCIStmtExecute(svc, stmt, err, BATCH_SIZE, 0, NULL, NULL, OCI_DEFAULT);


부가적인 성능 최적화

  1. 네트워크 왕복 최소화:
    • 한 번에 최대한 많은 데이터를 삽입하여 네트워크 왕복 횟수를 줄입니다.
  2. PL/SQL 프로시저 활용:
    • PL/SQL 저장 프로시저를 작성하고, 데이터를 바인딩하여 호출하면 성능이 더욱 향상됩니다.
  3. 병렬 처리:
    • Oracle Parallel DML을 활성화하거나 데이터를 병렬로 분할하여 삽입합니다.
  4. COMMIT 빈도 조정:
    • 대량의 데이터를 삽입할 때 너무 자주 커밋하면 성능이 저하될 수 있으므로 적절한 빈도로 조정합니다.

T'way Holdings 의 <'> 특수문자 테이블에 입력하기

{
{"A0","01S","00000332","001034","20230418","KR7004870002","000332","A004870  ","?곗썾?댄??⑹뒪                            ","T'way Holdings                          ","00005","STK","ST","N","00","00","N","00","N","1","N","00","N","N","N","N","N","032303    ","N"," ","0","00000000646","1","00000000646","000000441839","000000000283734992.000","00000000839","00000000453","00000000450","0000500.000","00000000000","19770630","0000000109472167","N","        ","        ","        ","        ","000000000.000","000000054736083500.000","Y","00007","00007","00001","00007","00001","00","0","N","00000000000","00000000000","00000000000","00000000001","00000000001"," ","            ","KRW","410","N","Y","Y","Y","Y","Y","N","000000.000000","N","N","0","000070.000000","N","        ","0"," ","        ","  ","        ","        "," ","  ","      ","   ","  ","  ","      ","      ","     ","N","N","N","0000000000001547988.000","N","N","N"," ","}",
{"A0","01S","00000333","001034","20230418","KR7004890000","000333","A004890  ","?숈씪?곗뾽                                ","DIC                                     ","00005","STK","ST","N","00","00","N","00","N","1","N","00","N","N","N","N","N","032401    ","N"," ","0","00000218000","1","00000218000","000000001716","000000000374394000.000","00000283000","00000153000","00000165680","0005000.000","00000024000","20050630","0000000002425215","N","        ","        ","        ","        ","000000000.000","000000012126075000.000","Y","00007","00007","00001","00007","00001","00","0","N","00000000000","00000000000","00000000000","00000000001","00000000001"," ","            ","KRW","410","Y","Y","Y","Y","Y","Y","N","000000.000000","N","N","0","000076.000000","N","        ","0"," ","        ","  ","        ","        "," ","  ","      ","   ","  ","  ","      ","      ","     ","N","N","N","0000000000000024253.000","N","N","N"," ","}",
{"A0","01S","00000334","001034","20230418","KR7004910006","000334","A004910  ","議곌킅?섏씤??                             ","ChokwangPaint                           ","00003","STK","ST","N","00","00","N","00","N","1","N","00","N","N","N","N","N","032004    ","N"," ","0","00000007810","1","00000007810","000000040114","000000000313154770.000","00000010150","00000005470","00000005310","0000500.000","00000000000","19761227","0000000012800000","N","        ","        ","        ","        ","000000000.000","000000006400000000.000","Y","00007","00007","00001","00007","00001","00","0","N","00000000000","00000000000","00000000000","00000000001","00000000001"," ","            ","KRW","410","N","Y","Y","Y","Y","Y","N","000000.000000","N","N","0","000068.000000","N","        ","0"," ","        ","  ","        ","        "," ","  ","      ","   ","  ","  ","      ","      ","     ","N","N","N","0000000000000128041.000","N","N","N"," ","}",
{"A0","01S","00000335","001034","20230418","KR7004920005","000335","A004920  ","?⑥븘?댄뀒??                             ","CITECH                                  ","00003","STK","ST","N","00","00","N","00","N","1","N","00","N","N","N","N","N","033309    ","N"," ","0","00000001447","1","00000001447","000001789100","000000002627682556.000","00000001881","00000001013","00000000980","0000500.000","00000000000","19891130","0000000046403359","N","        ","        ","        ","        ","000000000.000","000000023201679500.000","Y","00007","00007","00001","00007","00001","00","0","N","00000000000","00000000000","00000000000","00000000001","00000000001"," ","            ","KRW","410","N","Y","Y","Y","Y","Y","N","000000.000000","N","N","0","000068.000000","N","        ","0"," ","        ","  ","        ","        "," ","  ","      ","   ","  ","  ","      ","      ","     ","N","N","N","0000000000000691086.000","N","N","N"," ","}",
{"A0","01S","00000336","001034","20230418","KR7004960001","000336","A004960  ","?쒖떊怨듭쁺                                ","HanshinConst                            ","00004","STK","ST","N","00","00","N","00","N","1","N","00","N","N","N","N","N","064101    ","N"," ","0","00000007980","1","00000007980","000000113531","000000000914082160.000","00000010370","00000005590","00000005580","0005000.000","00000000000","19760707","0000000011570702","N","        ","        ","        ","        ","000000000.000","000000057853510000.000","Y","00007","00007","00001","00007","00001","00","0","N","00000000000","00000000000","00000000000","00000000001","00000000001"," ","            ","KRW","410","N","Y","Y","Y","Y","Y","N","000000.000000","N","N","0","000070.000000","N","        ","0"," ","        ","  ","        ","        "," ","  ","      ","   ","  ","  ","      ","      ","     ","N","N","N","0000000000000125314.000","N","N","N"," ","}",
{"A0","01S","00000337","001034","20230418","KR7004970000","000337","A004970  ","?좊씪援먯뿭                                ","Silla                                   ","00004","STK","ST","N","00","00","N","00","N","1","N","00","N","N","N","N","N","010301    ","N"," ","0","00000010570","1","00000010570","000000006385","000000000067294450.000","00000013740","00000007400","00000007610","0000500.000","00000000000","19760630","0000000016000000","N","        ","        ","        ","        ","000000000.000","000000008000000000.000","Y","00007","00007","00001","00007","00001","00","0","N","00000000000","00000000000","00000000000","00000000001","00000000001"," ","            ","KRW","410","Y","Y","Y","Y","Y","Y","N","000000.000000","N","N","0","000072.000000","N","        ","0"," ","        ","  ","        ","        "," ","  ","      ","   ","  ","  ","      ","      ","     ","N","N","N","0000000000000160000.000","N","N","N"," ","}",

,,
,,
};


A001S0000033200103420230418KR7004870002000332A004870  티웨이홀딩스                            T'way Holdings                          00005STKSTN0000N00N1N00

https://www.investing.com/equities/tway-holdings
TWay Holdings (004870)



프로시져.테이블내의 13개의 필드에 대한 1000줄의 입력데이타를 처리

CREATE TABLE [EXTUREPLUSSTOCKFOREIGNERMASTER] (
[DATACLASS] VARCHAR2(2),
[INFOMARKETCLASS] VARCHAR2(3),
[STOCKCODE] VARCHAR2(12),
[SERIALNO] VARCHAR2(8),
[TRADEDATE] VARCHAR2(8),
[STOCKLIMITRATE] VARCHAR2(5),
[INDIVIDUALLIMITRATE] VARCHAR2(5),
[LISTINGSTOCKSIZE] VARCHAR2(15),
[ORDERPOSSIBILITYVOLUME] VARCHAR2(15),
[LIMITEXHAUSTCLASS] VARCHAR2(1),
[FILLER1] VARCHAR2(5),
[FILLER2] VARCHAR2(5),

[ENDTEXT] VARCHAR2(1)
);

구분자 방식("N:12345"와 "S:Hello World")을 적용하여 숫자와 문자열을 구분하려면, C/C++ 코드에서 구분자(N: 또는 S:)를 활용하여 데이터를 구분하고, Oracle 프로시저에서 이를 처리하도록 할 수 있습니다. 여기서는 두 가지 주요 방법을 설명하겠습니다:

  1. C/C++ 코드에서 데이터 전처리
  2. Oracle 프로시저에서 데이터 처리

1. C/C++ 코드에서 데이터 전처리

C/C++에서는 입력 데이터를 N:과 S:로 구분하여 Oracle 프로시저로 전달할 수 있도록 준비합니다. 이를 위해 inData 배열을 처리하고, 각 값에 대해 N:은 숫자, S:는 문자열로 판단하여 Oracle 프로시저에 전달합니다.

예제: C/C++ 코드에서 구분자 추가

 
#include <stdio.h>
#include <string.h>
#include <ctype.h>

char inData[1000][13][100];

void preprocessData(char rawData[1000][13][100]) {
    // 배열 초기화 및 데이터 전처리
    for (int i = 0; i < 1000; i++) {
        for (int j = 0; j < 13; j++) {
            if (isdigit(rawData[i][j][0])) { // 숫자인 경우
                snprintf(inData[i][j], 100, "N:%s", rawData[i][j]);
            } else { // 문자열인 경우
                snprintf(inData[i][j], 100, "S:%s", rawData[i][j]);
            }
        }
    }
}

// 예시 호출
int main() {
    char rawData[1000][13][100] = {
        {"12345", "Hello", "67890", "World", "1", "2", "3", "4", "5", "6", "7", "8", "9"},
        {"54321", "Test", "98765", "Example", "10", "20", "30", "40", "50", "60", "70", "80", "90"}
        // 기타 데이터 추가...
    };
    
    preprocessData(rawData);
    
    // 'inData' 배열을 Oracle 프로시저로 전달
}



위 코드에서 preprocessData 함수는 rawData 배열을 받아 각 값에 N: 또는 S:를 접두사로 추가하여 inData 배열에 저장합니다. 이렇게 하면 inData 배열은 숫자와 문자열을 구분할 수 있는 형식으로 변환됩니다.


2. Oracle 프로시저에서 데이터 처리

Oracle 프로시저에서는 전달된 데이터를 구분자(N: 또는 S:)에 따라 숫자와 문자열을 구분해서 처리할 수 있습니다. 이때 문자열의 앞 두 문자를 확인하여 숫자와 문자열을 판별하고, 그에 맞는 처리 로직을 수행합니다.

예제: Oracle 프로시저에서 구분자 처리

CREATE OR REPLACE PROCEDURE bulk_insert_proc (
    p_data IN SYS.ODCIVARCHAR2LIST -- 문자열 배열 타입
)
IS
    v_prefix VARCHAR2(2);
    v_value  VARCHAR2(4000);
BEGIN
    FOR i IN 1..p_data.COUNT / 13 LOOP
        FOR j IN 1..13 LOOP
            v_prefix := SUBSTR(p_data((i-1)*13 + j), 1, 2);  -- 앞의 구분자 확인
            v_value := SUBSTR(p_data((i-1)*13 + j), 3);      -- 구분자 이후의 값
            
            IF v_prefix = 'N:' THEN
                -- 숫자 처리: 여기서는 예를 들어 숫자 컬럼에 값을 삽입
                INSERT INTO EXTUREPLUSSTOCKFOREIGNERMASTER (number_column)
                VALUES (TO_NUMBER(v_value));
            ELSIF v_prefix = 'S:' THEN
                -- 문자열 처리: 문자열 컬럼에 값을 삽입
                INSERT INTO EXTUREPLUSSTOCKFOREIGNERMASTER (string_column)
                VALUES (v_value);
            END IF;
        END LOOP;
    END LOOP;
    
    COMMIT;
END;
/

프로시저 설명:

  1. 구분자 확인: SUBSTR 함수로 문자열의 처음 두 글자인 N: 또는 S:를 확인하여, 숫자인지 문자열인지를 구분합니다.
  2. 숫자 처리: N:으로 시작하는 값은 TO_NUMBER를 사용해 숫자로 변환하여 숫자 컬럼에 삽입합니다.
  3. 문자열 처리: S:로 시작하는 값은 그대로 문자열로 처리하여 문자열 컬럼에 삽입합니다.

3. C/C++에서 Oracle 프로시저 호출

C/C++ 코드에서 위와 같이 전처리된 inData 배열을 Oracle 프로시저로 전달할 수 있습니다. 이때 각 레코드는 13개의 값을 가지며, 각 값은 N: 또는 S:로 시작하는 형태로 전달됩니다.

예제: Oracle 프로시저 호출

#include <oci.h>

// OCI 핸들 및 환경 초기화
OCIEnv     *env;
OCIError   *err;
OCISvcCtx  *svc;
OCIStmt    *stmt;
OCIBind    *bind;
OCIArray   *array_data;

void bulkInsert(char inData[1000][13][100]) {
    // 환경 초기화
    OCIEnvCreate(&env, OCI_THREADED | OCI_OBJECT, NULL, NULL, NULL, NULL, 0, NULL);
    OCIHandleAlloc(env, (void **)&err, OCI_HTYPE_ERROR, 0, NULL);
    OCIHandleAlloc(env, (void **)&svc, OCI_HTYPE_SVCCTX, 0, NULL);

    // Oracle 연결
    OCILogon(env, err, &svc, (text *)"username", strlen("username"), (text *)"password", strlen("password"), (text *)"db", strlen("db"));

    // 배열 준비: 1000 레코드, 13 컬럼
    OCIDescriptorAlloc(env, (void **)&array_data, OCI_DTYPE_TABLE, 0, NULL);
    
    // 데이터를 배열에 삽입
    for (int i = 0; i < 1000; i++) {
        for (int j = 0; j < 13; j++) {
            OCIArrayAppend(array_data, (text *)inData[i][j], strlen(inData[i][j]), err);
        }
    }

    // 프로시저 호출 준비
    OCIStmtPrepare(stmt, err, (text *)"BEGIN bulk_insert_proc(:data); END;", strlen("BEGIN bulk_insert_proc(:data); END;"), OCI_NTV_SYNTAX, OCI_DEFAULT);

    // 바인딩
    OCIBindByName(stmt, &bind, err, (text *)":data", strlen(":data"), (void *)array_data, sizeof(array_data), SQLT_TAB, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);

    // 실행
    OCIStmtExecute(svc, stmt, err, 1, 0, NULL, NULL, OCI_DEFAULT);

    // 종료
    OCILogoff(svc, err);
    OCIHandleFree(stmt, OCI_HTYPE_STMT);
    OCIHandleFree(err, OCI_HTYPE_ERROR);
    OCIHandleFree(svc, OCI_HTYPE_SVCCTX);
    OCIEnvFree(env, OCI_DEFAULT);
}

결론

  • C/C++ 코드에서 데이터를 N:과 S:로 구분하여 Oracle 프로시저에 전달합니다.
  • Oracle 프로시저에서는 SUBSTR을 사용하여 구분자를 확인하고, 숫자와 문자열을 처리합니다.
  • N: 접두사는 숫자 컬럼에 삽입하고, S: 접두사는 문자열 컬럼에 삽입합니다.

+ Recent posts