kcg-monitoring/database/migration/003_ships_osint_satellites.sql
htlee 0c78ad8bb8 feat(db): ships/osint/satellites 테이블 생성 + 샘플 데이터 적재
- ship_positions: AIS/signal-batch 선박 위치 이력 (PostGIS)
- osint_feeds: GDELT/Google News/CENTCOM 피드 (UNIQUE 중복방지)
- satellite_tle: CelesTrak TLE 위성 궤도 데이터
- 샘플: 중동 함정 16척, 한국 해역 8척, OSINT 17건, 위성 11기
2026-03-18 02:59:54 +09:00

80 lines
4.4 KiB
SQL

-- 003: 선박 위치 이력 + OSINT 피드 + 위성 TLE 테이블
-- 리플레이 및 분석용 시계열 데이터 저장
SET search_path TO kcg, public;
-- ═══════════════════════════════════════════
-- 선박 위치 이력 (AIS / signal-batch)
-- ═══════════════════════════════════════════
CREATE TABLE IF NOT EXISTS ship_positions (
id BIGSERIAL PRIMARY KEY,
mmsi VARCHAR(9) NOT NULL,
name VARCHAR(128),
position geometry(Point, 4326) NOT NULL,
heading DOUBLE PRECISION,
speed DOUBLE PRECISION, -- knots
course DOUBLE PRECISION, -- COG
category VARCHAR(16), -- warship, carrier, destroyer, tanker, cargo, ...
flag VARCHAR(4), -- ISO 국가코드
typecode VARCHAR(16),
type_desc VARCHAR(128),
imo VARCHAR(16),
call_sign VARCHAR(16),
status VARCHAR(64), -- Under way, Anchored, ...
destination VARCHAR(128),
draught DOUBLE PRECISION,
length DOUBLE PRECISION,
width DOUBLE PRECISION,
source VARCHAR(16) NOT NULL, -- spg, signal-batch, sample
region VARCHAR(16) NOT NULL, -- middleeast, korea
collected_at TIMESTAMP NOT NULL DEFAULT NOW(),
last_seen TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_ship_pos_geom ON ship_positions USING GIST (position);
CREATE INDEX IF NOT EXISTS idx_ship_pos_collected ON ship_positions (collected_at);
CREATE INDEX IF NOT EXISTS idx_ship_pos_region_time ON ship_positions (region, collected_at);
CREATE INDEX IF NOT EXISTS idx_ship_pos_mmsi ON ship_positions (mmsi, collected_at);
-- ═══════════════════════════════════════════
-- OSINT 피드 (GDELT, Google News, CENTCOM)
-- ═══════════════════════════════════════════
CREATE TABLE IF NOT EXISTS osint_feeds (
id BIGSERIAL PRIMARY KEY,
title TEXT NOT NULL,
source VARCHAR(64) NOT NULL, -- gdelt, google-news-ko, google-news-en, centcom
source_url TEXT,
category VARCHAR(32), -- military, oil, diplomacy, shipping, nuclear, ...
language VARCHAR(8), -- ko, en
focus VARCHAR(16), -- iran, korea
image_url TEXT,
position geometry(Point, 4326), -- nullable (위치 추출 가능 시)
published_at TIMESTAMP, -- 원본 기사 발행 시각
collected_at TIMESTAMP NOT NULL DEFAULT NOW(),
UNIQUE(source, source_url) -- 중복 방지
);
CREATE INDEX IF NOT EXISTS idx_osint_feeds_collected ON osint_feeds (collected_at);
CREATE INDEX IF NOT EXISTS idx_osint_feeds_focus ON osint_feeds (focus, collected_at);
CREATE INDEX IF NOT EXISTS idx_osint_feeds_category ON osint_feeds (category);
CREATE INDEX IF NOT EXISTS idx_osint_feeds_geom ON osint_feeds USING GIST (position);
-- ═══════════════════════════════════════════
-- 위성 TLE (CelesTrak)
-- ═══════════════════════════════════════════
CREATE TABLE IF NOT EXISTS satellite_tle (
id BIGSERIAL PRIMARY KEY,
norad_id INTEGER NOT NULL,
name VARCHAR(128) NOT NULL,
tle_line1 VARCHAR(70) NOT NULL,
tle_line2 VARCHAR(70) NOT NULL,
category VARCHAR(20), -- reconnaissance, communications, navigation, weather, other
tle_group VARCHAR(32), -- military, gps-ops, geo, weather, stations
epoch TIMESTAMP, -- TLE epoch (궤도 기준 시각)
collected_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_satellite_tle_norad ON satellite_tle (norad_id, collected_at);
CREATE INDEX IF NOT EXISTS idx_satellite_tle_collected ON satellite_tle (collected_at);
CREATE INDEX IF NOT EXISTS idx_satellite_tle_category ON satellite_tle (category);