-- ============================================================
-- Step 1: diagnosis_history 테이블 생성
-- 실행 위치: Supabase SQL Editor → New query → Run
-- ============================================================
CREATE TABLE IF NOT EXISTS diagnosis_history (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
diagnosed_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
file_name TEXT NOT NULL,
total_rows INTEGER DEFAULT 0,
total_cols INTEGER DEFAULT 0,
quality_score NUMERIC(5,2),
error_rate NUMERIC(5,2),
issues_count INTEGER DEFAULT 0,
issues JSONB,
summary JSONB
);
-- 조회 성능용 인덱스
CREATE INDEX IF NOT EXISTS idx_dh_diagnosed_at
ON diagnosis_history (diagnosed_at DESC);
CREATE INDEX IF NOT EXISTS idx_dh_file_name
ON diagnosis_history (file_name);
-- 결과 확인
SELECT 'diagnosis_history 테이블 생성 완료' AS result;
result --------------------------------- diagnosis_history 테이블 생성 완료
-- ============================================================
-- Step 2: diagnosis_history RLS 설정
-- ============================================================
-- RLS 활성화
ALTER TABLE diagnosis_history ENABLE ROW LEVEL SECURITY;
-- 익명 사용자 INSERT 허용 (진단 결과 저장)
CREATE POLICY "anon_dh_insert" ON diagnosis_history
FOR INSERT TO anon WITH CHECK (true);
-- 익명 사용자 SELECT 허용 (이력 조회)
CREATE POLICY "anon_dh_select" ON diagnosis_history
FOR SELECT TO anon USING (true);
-- 익명 사용자 DELETE 허용 (이력 삭제)
CREATE POLICY "anon_dh_delete" ON diagnosis_history
FOR DELETE TO anon USING (true);
-- 결과 확인
SELECT policyname, cmd
FROM pg_policies
WHERE tablename = 'diagnosis_history'
ORDER BY cmd;
policyname | cmd ---------------------+-------- anon_dh_delete | DELETE anon_dh_insert | INSERT anon_dh_select | SELECT
-- ============================================================ -- Step 3: diagnosis_history 연결 테스트 -- ============================================================ -- 레코드 수 확인 (신규: 0건 이어야 함) SELECT COUNT(*) AS record_count FROM diagnosis_history; -- 컬럼 구조 확인 SELECT column_name, data_type, column_default FROM information_schema.columns WHERE table_name = 'diagnosis_history' ORDER BY ordinal_position;
record_count
------------
0
column_name | data_type
--------------+-----------------------------
id | uuid
diagnosed_at | timestamp with time zone
file_name | text
total_rows | integer
... (총 10개 컬럼)
-- ============================================================
-- Step 4: keyword_type_map 테이블 생성
-- ============================================================
CREATE TABLE IF NOT EXISTS keyword_type_map (
id SERIAL PRIMARY KEY,
suffix TEXT NOT NULL UNIQUE,
type_code TEXT NOT NULL,
suffix_len INT GENERATED ALWAYS AS (char_length(suffix)) STORED,
priority INT DEFAULT 0,
is_active BOOLEAN DEFAULT TRUE,
description TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- suffix_len DESC 정렬 인덱스 (긴 접미사 우선 매칭)
CREATE INDEX IF NOT EXISTS idx_ktm_suffix_len
ON keyword_type_map (suffix_len DESC, priority DESC);
-- type_code 조회 인덱스
CREATE INDEX IF NOT EXISTS idx_ktm_type_code
ON keyword_type_map (type_code);
-- 결과 확인
SELECT 'keyword_type_map 테이블 생성 완료' AS result;
result ----------------------------------- keyword_type_map 테이블 생성 완료
-- ============================================================
-- Step 5: keyword_type_map RLS 설정
-- ============================================================
ALTER TABLE keyword_type_map ENABLE ROW LEVEL SECURITY;
CREATE POLICY "anon_ktm_select" ON keyword_type_map
FOR SELECT TO anon USING (true);
CREATE POLICY "anon_ktm_insert" ON keyword_type_map
FOR INSERT TO anon WITH CHECK (true);
CREATE POLICY "anon_ktm_update" ON keyword_type_map
FOR UPDATE TO anon USING (true);
CREATE POLICY "anon_ktm_delete" ON keyword_type_map
FOR DELETE TO anon USING (true);
-- 결과 확인
SELECT policyname, cmd
FROM pg_policies
WHERE tablename = 'keyword_type_map'
ORDER BY cmd;
policyname | cmd ---------------------+-------- anon_ktm_delete | DELETE anon_ktm_insert | INSERT anon_ktm_select | SELECT anon_ktm_update | UPDATE
-- ============================================================
-- Step 6: keyword_type_map 기초 데이터 INSERT (100개)
-- analyzer.js suffixMap → DB 마이그레이션
-- ============================================================
INSERT INTO keyword_type_map
(suffix, type_code, priority, description)
VALUES
-- ── len 11 ──────────────────────────────────────────────────
('ipaddress', 'number_id_ip', 0, 'IP address (영문)'),
-- ── len 9 ───────────────────────────────────────────────────
('timestamp', 'datetime_ymdhms', 0, 'Unix timestamp (영문)'),
('longitude', 'coordinate_longitude', 0, '경도 (영문)'),
('latitude', 'coordinate_latitude', 0, '위도 (영문)'),
-- ── len 8 ───────────────────────────────────────────────────
('datetime', 'datetime_ymdhms', 0, '날짜시간 복합 (영문)'),
('yearmonth', 'date_year_month', 0, '년월 (영문)'),
('category', 'code_category', 0, '카테고리 (영문)'),
('selected', 'code_boolean', 0, '선택 여부 (영문)'),
('approved', 'code_boolean', 0, '승인 여부 (영문)'),
('completed', 'code_boolean', 0, '완료 여부 (영문)'),
('website', 'text_general', 0, '웹사이트 (영문)'),
('address', 'text_general', 0, '주소 (영문)'),
-- ── len 7 ───────────────────────────────────────────────────
('주민등록번호', 'number_id_resident', 10, '주민등록번호'),
('enabled', 'code_boolean', 0, '활성화 여부 (영문)'),
('checked', 'code_boolean', 0, '체크 여부 (영문)'),
('visible', 'code_boolean', 0, '표시 여부 (영문)'),
('zipcode', 'number_id_postal', 0, '우편번호 (영문)'),
('mobile', 'phone', 0, '휴대폰 (영문)'),
-- ── len 6 ───────────────────────────────────────────────────
('yyyymmdd', 'date_ymd', 0, 'YYYYMMDD 형식'),
('active', 'code_boolean', 0, '활성 여부 (영문)'),
('postal', 'number_id_postal', 0, '우편번호 약자 (영문)'),
('status', 'code_boolean', 0, '상태값 (영문)'),
('title', 'text_general', 0, '제목 (영문)'),
-- ── len 5 ───────────────────────────────────────────────────
('타임스탬프', 'datetime_ymdhms', 5, '타임스탬프'),
('사업자번호', 'number_id_business', 10, '사업자등록번호'),
('이메일주소', 'email', 5, '이메일 전체 명칭'),
('yyyymm', 'date_year_month', 0, 'YYYYMM 형식'),
('phone', 'phone', 0, '전화번호 (영문)'),
('email', 'email', 0, '이메일 (영문)'),
('웹사이트', 'text_general', 0, '웹사이트'),
-- ── len 4 ───────────────────────────────────────────────────
('생년월일', 'date_ymd', 5, '생년월일'),
('년월일시', 'datetime_ymdh', 5, '년월일시'),
('등록시간', 'datetime_ymdhms', 5, '등록 시간'),
('생성시간', 'datetime_ymdhms', 5, '생성 시간'),
('접수시간', 'datetime_ymdhm', 5, '접수 시간'),
('처리시간', 'datetime_ymdhm', 5, '처리 시간'),
('수정시간', 'datetime_ymdhm', 5, '수정 시간'),
('변경시간', 'datetime_ymdhm', 5, '변경 시간'),
('법인번호', 'number_id_corporate', 5, '법인등록번호'),
('우편번호', 'number_id_postal', 5, '우편번호'),
('전화번호', 'phone', 5, '전화번호 전체 명칭'),
('팩스번호', 'phone', 5, '팩스번호'),
('ip주소', 'number_id_ip', 5, 'IP 주소'),
('기관코드', 'code_category', 5, '기관 코드'),
('시설코드', 'code_category', 5, '시설 코드'),
('업체코드', 'code_category', 5, '업체 코드'),
('전자메일', 'email', 5, '이메일 별칭'),
('code', 'code_category', 0, '코드 (영문)'),
('type', 'code_category', 0, '타입/유형 (영문)'),
('flag', 'code_boolean', 0, '플래그 (영문)'),
('mail', 'email', 0, '메일 (영문)'),
('date', 'date_ymd', 0, '날짜 (영문)'),
('time', 'time_hms', 0, '시간 (영문)'),
('year', 'date_year', 0, '연도 (영문)'),
('yyyy', 'date_year', 0, '연도 YYYY'),
('link', 'text_general', 0, '링크 (영문)'),
('name', 'text_general', 0, '이름 (영문)'),
-- ── len 3 ───────────────────────────────────────────────────
('년월일', 'date_ymd', 5, '년월일'),
('시분초', 'time_hms', 5, '시분초'),
('이메일', 'email', 5, '이메일 약칭'),
('연락처', 'phone', 5, '연락처'),
('휴대폰', 'phone', 5, '휴대폰 번호'),
('x좌표', 'coordinate_longitude', 5, 'X좌표=경도'),
('y좌표', 'coordinate_latitude', 5, 'Y좌표=위도'),
('소재지', 'text_general', 5, '소재지 주소'),
('url', 'text_general', 0, 'URL (영문)'),
('tel', 'phone', 0, '전화 약자 (영문)'),
('hh', 'time_hour', 0, '시(hour) 약자'),
('lng', 'coordinate_longitude', 0, '경도 약자 (영문)'),
('lon', 'coordinate_longitude', 0, '경도 전체 (영문)'),
('lat', 'coordinate_latitude', 0, '위도 약자 (영문)'),
('mm', 'date_month', 0, '월(month) 약자'),
('dd', 'date_day', 0, '일(day) 약자'),
('ss', 'time_second', 0, '초(second) 약자'),
('hhmm', 'time_hm', 0, '시분 HHMM'),
('mmdd', 'date_month_day', 0, '월일 MMDD'),
('month', 'date_month', 0, '월 (영문)'),
('hour', 'time_hour', 0, '시 (영문)'),
-- ── len 2 ───────────────────────────────────────────────────
('일시', 'datetime_ymdhm', 10, '"등록일시","처리일시" 등'),
('시각', 'datetime_ymdhm', 5, '시각'),
('일자', 'date_ymd', 5, '"등록일자" 등'),
('년월', 'date_year_month', 5, '년월'),
('년도', 'date_year', 5, '년도'),
('연도', 'date_year', 5, '연도'),
('날짜', 'date_ymd', 5, '날짜'),
('시분', 'time_hm', 5, '시분'),
('월일', 'date_month_day', 5, '월일'),
('번호', 'number_id_general', 0, '번호 fallback'),
('전화', 'phone', 5, '전화'),
('경도', 'coordinate_longitude', 5, '경도'),
('위도', 'coordinate_latitude', 5, '위도'),
('코드', 'code_category', 5, '코드'),
('cd', 'code_category', 0, '코드 약자 (영문)'),
('구분', 'code_category', 5, '구분 코드'),
('여부', 'code_boolean', 5, '여부'),
('유무', 'code_boolean', 5, '유무'),
('상태', 'code_boolean', 5, '상태'),
('가능', 'code_boolean', 5, '가능 여부'),
('승인', 'code_boolean', 5, '승인 여부'),
('완료', 'code_boolean', 5, '완료 여부'),
('사용', 'code_boolean', 5, '사용 여부'),
('활성', 'code_boolean', 5, '활성 여부'),
('표시', 'code_boolean', 5, '표시 여부'),
('선택', 'code_boolean', 5, '선택 여부'),
('동의', 'code_boolean', 5, '동의 여부'),
('확인', 'code_boolean', 5, '확인 여부'),
('존재', 'code_boolean', 5, '존재 여부'),
('소개', 'text_general', 5, '소개 텍스트'),
('내용', 'text_general', 5, '내용 텍스트'),
('설명', 'text_general', 5, '설명 텍스트'),
('주소', 'text_general', 5, '주소 텍스트'),
('위치', 'text_general', 5, '위치 설명'),
('제목', 'text_general', 5, '제목'),
('이름', 'text_general', 5, '이름'),
('성명', 'text_general', 5, '성명'),
('명칭', 'text_general', 5, '명칭'),
('비고', 'text_general', 5, '비고'),
('메모', 'text_general', 5, '메모'),
('사유', 'text_general', 5, '사유'),
('목적', 'text_general', 5, '목적'),
('내역', 'text_general', 5, '내역'),
('is_', 'code_boolean', 0, 'is_ 접두형 boolean'),
('has_', 'code_boolean', 0, 'has_ 접두형 boolean'),
('can_', 'code_boolean', 0, 'can_ 접두형 boolean'),
-- ── len 1 (반드시 최하단 — 가장 낮은 우선순위) ──────────────
('일', 'date_ymd', 1, '일(day) 단독 접미사'),
('월', 'date_month', 1, '월(month) 단독 접미사'),
('년', 'date_year', 1, '년(year) 단독 접미사'),
('초', 'time_second', 1, '초(second) 단독 접미사')
ON CONFLICT (suffix) DO NOTHING;
-- 입력 건수 확인
SELECT COUNT(*) AS inserted_count FROM keyword_type_map;
inserted_count
--------------
100 ← 약 100 (중복 제외)
-- ============================================================
-- Step 7: keyword_type_map 최종 검증
-- ============================================================
-- 1) 총 건수 확인 (약 100개)
SELECT COUNT(*) AS total_keywords FROM keyword_type_map;
-- 2) type_code 별 분포
SELECT type_code,
COUNT(*) AS cnt
FROM keyword_type_map
GROUP BY type_code
ORDER BY cnt DESC;
-- 3) 길이 내림차순 상위 20개 (suffixMap 정렬 기준 확인)
SELECT suffix, type_code, suffix_len, priority
FROM keyword_type_map
ORDER BY suffix_len DESC, priority DESC
LIMIT 20;
-- 4) 비활성 키워드 확인 (없어야 정상)
SELECT COUNT(*) AS inactive_count
FROM keyword_type_map
WHERE is_active = FALSE;
total_keywords ≈ 100 inactive_count = 0 최상위: ipaddress(len=9) → number_id_ip
① 테이블 생성 + RLS (8-A/B)
-- ============================================================
-- Step 8-A: user_column_rules 테이블 생성
-- ============================================================
CREATE TABLE IF NOT EXISTS user_column_rules (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
column_name TEXT NOT NULL,
rule_type TEXT NOT NULL
CHECK (rule_type IN ('code_list', 'numeric_range', 'time_order')),
rule_value JSONB NOT NULL,
is_active BOOLEAN DEFAULT TRUE,
description TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_ucr_column_name ON user_column_rules (column_name);
CREATE INDEX IF NOT EXISTS idx_ucr_rule_type ON user_column_rules (rule_type);
-- ============================================================
-- Step 8-B: RLS 설정
-- ============================================================
ALTER TABLE user_column_rules ENABLE ROW LEVEL SECURITY;
CREATE POLICY "anon_ucr_select" ON user_column_rules FOR SELECT TO anon USING (true);
CREATE POLICY "anon_ucr_insert" ON user_column_rules FOR INSERT TO anon WITH CHECK (true);
CREATE POLICY "anon_ucr_update" ON user_column_rules FOR UPDATE TO anon USING (true);
CREATE POLICY "anon_ucr_delete" ON user_column_rules FOR DELETE TO anon USING (true);
SELECT 'user_column_rules 생성 완료' AS result;
② 샘플 데이터 INSERT (8-C, 선택 사항)
-- ============================================================
-- Step 8-C: 샘플 데이터 (선택 사항)
-- ============================================================
INSERT INTO user_column_rules (column_name, rule_type, rule_value, description)
VALUES
-- 코드 목록 예시 (R-5)
('처리상태', 'code_list',
'{"allowed": ["접수", "처리중", "완료", "반려"]}',
'처리상태 허용 코드'),
('성별코드', 'code_list',
'{"allowed": ["M", "F", "남", "여"]}',
'성별 허용 코드'),
-- 수치 범위 예시 (R-7)
('나이', 'numeric_range', '{"min": 0, "max": 150}', '나이 허용 범위'),
('점수', 'numeric_range', '{"min": 0, "max": 100}', '점수 0~100점'),
('위도', 'numeric_range', '{"min": 33.0, "max": 38.9}', '한국 위도 범위'),
('경도', 'numeric_range', '{"min": 124.6,"max": 131.9}','한국 경도 범위')
ON CONFLICT DO NOTHING;
SELECT column_name, rule_type, description
FROM user_column_rules ORDER BY rule_type, column_name;
result ----------------------------- user_column_rules 생성 완료
-- ============================================================
-- Step 9: diag_criteria_settings 테이블 생성
-- 실행 위치: Supabase SQL Editor → New query → Run
-- ============================================================
CREATE TABLE IF NOT EXISTS diag_criteria_settings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
profile_name TEXT NOT NULL DEFAULT 'default',
type_code TEXT NOT NULL,
criteria_data JSONB NOT NULL DEFAULT '{}',
is_custom BOOLEAN NOT NULL DEFAULT FALSE,
custom_meta JSONB NOT NULL DEFAULT '{}',
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 프로파일 + 타입코드 복합 유니크 인덱스 (UPSERT용)
CREATE UNIQUE INDEX IF NOT EXISTS uq_profile_typecode
ON diag_criteria_settings (profile_name, type_code);
-- 프로파일명 조회 인덱스
CREATE INDEX IF NOT EXISTS idx_dcs_profile_name
ON diag_criteria_settings (profile_name);
-- 커스텀 타입 조회 인덱스
CREATE INDEX IF NOT EXISTS idx_dcs_is_custom
ON diag_criteria_settings (is_custom)
WHERE is_custom = TRUE;
-- updated_at 자동 갱신 트리거
CREATE OR REPLACE FUNCTION update_dcs_updated_at()
RETURNS TRIGGER AS $$
BEGIN NEW.updated_at = NOW(); RETURN NEW; END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_dcs_updated_at ON diag_criteria_settings;
CREATE TRIGGER trg_dcs_updated_at
BEFORE UPDATE ON diag_criteria_settings
FOR EACH ROW EXECUTE FUNCTION update_dcs_updated_at();
-- 결과 확인
SELECT 'diag_criteria_settings 테이블 생성 완료' AS result;
result ------------------------------------------- diag_criteria_settings 테이블 생성 완료
-- ============================================================
-- Step 10: diag_criteria_settings RLS 설정
-- ============================================================
ALTER TABLE diag_criteria_settings ENABLE ROW LEVEL SECURITY;
CREATE POLICY "anon_dcs_select" ON diag_criteria_settings
FOR SELECT TO anon USING (true);
CREATE POLICY "anon_dcs_insert" ON diag_criteria_settings
FOR INSERT TO anon WITH CHECK (true);
CREATE POLICY "anon_dcs_update" ON diag_criteria_settings
FOR UPDATE TO anon USING (true);
CREATE POLICY "anon_dcs_delete" ON diag_criteria_settings
FOR DELETE TO anon USING (true);
-- RLS 정책 확인
SELECT policyname, cmd, roles
FROM pg_policies
WHERE tablename = 'diag_criteria_settings'
ORDER BY cmd;
policyname | cmd | roles
---------------------|--------|-------
anon_dcs_delete | DELETE | {anon}
anon_dcs_insert | INSERT | {anon}
anon_dcs_select | SELECT | {anon}
anon_dcs_update | UPDATE | {anon}
-- ============================================================
-- Step 11: 기본 프로파일(default) 초기 데이터 INSERT
-- ============================================================
INSERT INTO diag_criteria_settings
(profile_name, type_code, criteria_data, is_custom, custom_meta)
VALUES
-- 📅 날짜/시간
('default','date_ymd',
'{"yearMin":1900,"yearMax":2100,"allowFuture":true,"formatConsistency":true,"errorLevel":"error"}',
FALSE,'{}'),
('default','date_year_month',
'{"yearMin":1900,"yearMax":2100,"allowFuture":true,"errorLevel":"error"}',
FALSE,'{}'),
('default','date_year',
'{"yearMin":1900,"yearMax":2100,"errorLevel":"error"}',
FALSE,'{}'),
('default','datetime_ymdhms',
'{"yearMin":1900,"yearMax":2100,"allowFuture":true,"formatConsistency":true,"errorLevel":"error"}',
FALSE,'{}'),
('default','datetime_ymdhm',
'{"yearMin":1900,"yearMax":2100,"allowFuture":true,"errorLevel":"error"}',
FALSE,'{}'),
('default','time_hms',
'{"formatConsistency":true,"errorLevel":"error"}',
FALSE,'{}'),
('default','time_hm',
'{"errorLevel":"error"}',
FALSE,'{}'),
-- 🔢 번호
('default','phone',
'{"allowDash":true,"allowNoDash":true,"allowIntl":false,"formatConsistency":true,"errorLevel":"error"}',
FALSE,'{}'),
('default','number_id_postal',
'{"allowNew":true,"allowOld":false,"errorLevel":"error"}',
FALSE,'{}'),
('default','number_id_business',
'{"allowDash":true,"allowNoDash":true,"errorLevel":"error"}',
FALSE,'{}'),
('default','vehicle_number',
'{"allowNew":true,"allowOld":true,"errorLevel":"error"}',
FALSE,'{}'),
-- 🔢 숫자
('default','number',
'{"valueMin":null,"valueMax":null,"allowNegative":true,"allowComma":true,"errorLevel":"error"}',
FALSE,'{}'),
('default','number_decimal',
'{"valueMin":null,"valueMax":null,"decimalPlaces":null,"allowNegative":true,"errorLevel":"error"}',
FALSE,'{}'),
('default','currency',
'{"allowSymbols":true,"allowComma":true,"valueMin":null,"valueMax":null,"errorLevel":"error"}',
FALSE,'{}'),
('default','percentage',
'{"valueMin":0,"valueMax":100,"allowOver100":false,"errorLevel":"error"}',
FALSE,'{}'),
-- 🏷️ 코드
('default','code_boolean',
'{"caseSensitive":false,"allowKorean":true,"allowNumeric":true,"errorLevel":"error"}',
FALSE,'{}'),
('default','code_category',
'{"caseSensitive":false,"trimWhitespace":true,"errorLevel":"warning"}',
FALSE,'{}'),
('default','code_sequence',
'{"checkSequential":false,"allowLeadingZero":true,"errorLevel":"warning"}',
FALSE,'{}'),
-- 📍 좌표
('default','coordinate_latitude',
'{"latMin":-90,"latMax":90,"koreaOnly":false,"decimalPlaces":4,"errorLevel":"error"}',
FALSE,'{}'),
('default','coordinate_longitude',
'{"lonMin":-180,"lonMax":180,"koreaOnly":false,"decimalPlaces":4,"errorLevel":"error"}',
FALSE,'{}'),
-- 📝 텍스트
('default','email',
'{"allowedDomains":"","errorLevel":"error"}',
FALSE,'{}'),
('default','text',
'{"minLength":0,"maxLength":0,"allowSpecialChar":true,"trimCheck":true,"errorLevel":"warning"}',
FALSE,'{}')
ON CONFLICT (profile_name, type_code)
DO UPDATE SET
criteria_data = EXCLUDED.criteria_data,
updated_at = NOW();
-- 삽입 건수 확인
SELECT profile_name, COUNT(*) AS type_count
FROM diag_criteria_settings
GROUP BY profile_name
ORDER BY profile_name;
profile_name | type_count -------------|------------ default | 22
-- ============================================================
-- Step 12: diag_criteria_settings 최종 검증
-- ============================================================
-- 1) 테이블 컬럼 구조 확인 (9개 컬럼)
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_name = 'diag_criteria_settings'
ORDER BY ordinal_position;
-- 2) 프로파일별 데이터 건수
SELECT profile_name,
COUNT(*) AS total,
SUM(CASE WHEN is_custom THEN 1 ELSE 0 END) AS custom_count
FROM diag_criteria_settings
GROUP BY profile_name
ORDER BY profile_name;
-- 3) 인덱스 목록 확인
SELECT indexname
FROM pg_indexes
WHERE tablename = 'diag_criteria_settings'
ORDER BY indexname;
-- 4) 샘플 데이터 확인 (date_ymd 기본값)
SELECT profile_name, type_code, criteria_data, is_custom
FROM diag_criteria_settings
WHERE type_code IN ('date_ymd', 'phone', 'coordinate_latitude')
ORDER BY profile_name, type_code;
컬럼 수 : 9개 default 건수 : 22건 (custom_count = 0) 인덱스 : uq_profile_typecode, idx_dcs_profile_name, idx_dcs_is_custom date_ymd : yearMin=1900, yearMax=2100
diagnosis_history, keyword_type_map,
user_column_rules, diag_criteria_settings
4개 테이블이 모두 준비되었습니다.
시스템 헤더에서 🟢 DB 연결됨 상태를 확인하세요.
진단 실행 시 결과가 자동으로 Supabase에 저장됩니다.