-- ═══════════════════════════════════════════════════════════════════════════
-- 수집통계 수동 조회 (MariaDB) — 하루치 먼저 모으고 그 안에서만 집계
--
-- 원본 Python: kis_trader/web/feed_collect_stats.py  → build_feed_collect_stats()
-- 웹 API:       GET /api/feed_collect_stats?date=YYYY-MM-DD&heavy=0|1
--
-- DB: 192.168.0.141:3306/kis_quant_db  (환경변수 DB_* 와 동일)
--
-- 사용법 (mysql 클라이언트):
--   mysql -h 192.168.0.141 -u <USER> -p kis_quant_db < scripts/feed_collect_stats_manual.sql
-- 또는 HeidiSQL/DBeaver 에서 @day8 만 바꿔 구간 실행
--
-- 2026-08-28 기준 대략적 행수 (전체 테이블 420만 vs 하루):
--   ws_ticks        ~34만/일
--   ws_orderbook    ~18만/일
--   ls_ws_ticks     ~69만/일  (전체 560만)
--   ls_ws_orderbook ~64만/일
-- ═══════════════════════════════════════════════════════════════════════════

USE kis_quant_db;

SET @day8   = '20260828';                          -- ← 여기만 바꾸면 됨
SET @day    = CONCAT(LEFT(@day8,4),'-',SUBSTR(@day8,5,2),'-',SUBSTR(@day8,7,2));
SET @like   = CONCAT(@day8, '%');
SET @t0     = CONCAT(@day8, '000000');
SET @t1     = CONCAT(@day8, '235959');
SET @ts0    = CONCAT(@day, ' 00:00:00');
SET @ts1    = CONCAT(@day, ' 23:59:59');
SET @fb_age = 3;   -- LIVE_FEED_FALLBACK_MAX_AGE_SEC (DB env, 기본 3)
SET @soft   = 10;  -- FEED_STATS_DISCONNECT_SOFT_SEC
SET @hard   = 60;  -- FEED_STATS_DISCONNECT_HARD_SEC
SET @cap    = 1800; -- FEED_STATS_DISCONNECT_CAP_SEC

-- ─────────────────────────────────────────────────────────────────────────
-- A. 빠른 요약 (1~3초) — 웹 탭 상단 숫자와 동일
-- ─────────────────────────────────────────────────────────────────────────

SELECT '=== A1 ws_ticks 벤더별 ===' AS section;
SELECT source AS k, COUNT(*) AS n, COUNT(DISTINCT code) AS codes
FROM ws_ticks
WHERE tick_time >= @t0 AND tick_time <= @t1
GROUP BY source
ORDER BY n DESC;

SELECT '=== A2 ws_orderbook 벤더별 ===' AS section;
SELECT source AS k, COUNT(*) AS n, COUNT(DISTINCT code) AS codes
FROM ws_orderbook
WHERE snap_time >= @t0 AND snap_time <= @t1
GROUP BY source
ORDER BY n DESC;

SELECT '=== A3 ls_ws_ticks (DATETIME 범위, 인덱스 idx_ls_tick_ts) ===' AS section;
SELECT COUNT(*) AS n, COUNT(DISTINCT code) AS codes
FROM ls_ws_ticks
WHERE ts >= @ts0 AND ts <= @ts1;

SELECT '=== A4 ls_ws_orderbook ===' AS section;
SELECT COUNT(*) AS n, COUNT(DISTINCT code) AS codes
FROM ls_ws_orderbook
WHERE snap_time >= @t0 AND snap_time <= @t1;

SELECT '=== A5 filter_eval (호가 source=filter_eval) ===' AS section;
SELECT COALESCE(strategy,'') AS k, COUNT(*) AS n, COUNT(DISTINCT code) AS c
FROM ws_orderbook
WHERE source = 'filter_eval'
  AND snap_time >= @t0 AND snap_time <= @t1
GROUP BY strategy
ORDER BY n DESC;

SELECT '=== A6 ls_ws_candles ===' AS section;
SELECT COUNT(*) AS n, COUNT(DISTINCT code) AS codes
FROM ls_ws_candles
WHERE datetime LIKE @like;


-- ─────────────────────────────────────────────────────────────────────────
-- B. 하루치 TEMP TABLE (한 번만) — 이후 C/D/E는 이 테이블만 스캔 (~34만)
--    세션 끊기면 TEMP 사라짐. 다시 B부터 실행.
-- ─────────────────────────────────────────────────────────────────────────

DROP TEMPORARY TABLE IF EXISTS tmp_fs_ticks;
CREATE TEMPORARY TABLE tmp_fs_ticks (
  id BIGINT,
  code VARCHAR(32),
  tick_time VARCHAR(14),
  tick_time_raw VARCHAR(32),
  source VARCHAR(16),
  recv_ts VARCHAR(30),
  KEY (source, code),
  KEY (recv_ts)
) AS
SELECT id, code, tick_time, tick_time_raw, source, recv_ts
FROM ws_ticks
WHERE tick_time >= @t0 AND tick_time <= @t1;

SELECT '=== B tmp_fs_ticks 적재 ===' AS section;
SELECT COUNT(*) AS n, COUNT(DISTINCT code) AS codes, COUNT(DISTINCT source) AS srcs
FROM tmp_fs_ticks;


DROP TEMPORARY TABLE IF EXISTS tmp_fs_ob;
CREATE TEMPORARY TABLE tmp_fs_ob (
  id BIGINT,
  code VARCHAR(32),
  snap_time VARCHAR(14),
  source VARCHAR(16),
  recv_ts VARCHAR(30),
  reject_code VARCHAR(64),
  strategy VARCHAR(64),
  KEY (source, code)
) AS
SELECT id, code, snap_time, source, recv_ts, reject_code, strategy
FROM ws_orderbook
WHERE snap_time >= @t0 AND snap_time <= @t1;

SELECT '=== B tmp_fs_ob 적재 ===' AS section;
SELECT COUNT(*) AS n, COUNT(DISTINCT code) AS codes FROM tmp_fs_ob;


-- ─────────────────────────────────────────────────────────────────────────
-- C. 폴백나이(usable) — feed_collect_stats._feed_age_cut_stats 동일 축
--    (웹 기본 조회에서 제일 무거움: TIMESTAMPDIFF 전행)
-- ─────────────────────────────────────────────────────────────────────────

SELECT '=== C1 ws_ticks lag by source (tmp) ===' AS section;
SELECT source AS src,
       COUNT(*) AS total,
       SUM(CASE WHEN lag_sec IS NOT NULL AND lag_sec <= @fb_age THEN 1 ELSE 0 END) AS pass_n,
       SUM(CASE WHEN lag_sec IS NOT NULL AND lag_sec > @fb_age THEN 1 ELSE 0 END) AS fail_n,
       SUM(CASE WHEN lag_sec IS NULL THEN 1 ELSE 0 END) AS unknown_n
FROM (
  SELECT source,
         TIMESTAMPDIFF(SECOND,
           STR_TO_DATE(
             CASE
               WHEN CHAR_LENGTH(IFNULL(tick_time,'')) >= 14 THEN LEFT(tick_time, 14)
               WHEN CHAR_LENGTH(IFNULL(tick_time_raw,'')) >= 14 THEN LEFT(tick_time_raw, 14)
               WHEN CHAR_LENGTH(IFNULL(tick_time_raw,'')) >= 6 THEN
                 CONCAT(COALESCE(NULLIF(LEFT(IFNULL(tick_time,''), 8), ''), DATE_FORMAT(recv_ts, '%Y%m%d')),
                        RIGHT(tick_time_raw, 6))
               ELSE NULL
             END,
             '%Y%m%d%H%i%s'),
           recv_ts) AS lag_sec
  FROM tmp_fs_ticks
) t
GROUP BY source;

SELECT '=== C2 전량 fail 분(종목×분) ===' AS section;
SELECT COUNT(*) AS empty_minutes
FROM (
  SELECT code, LEFT(tick_time, 12) AS mk
  FROM (
    SELECT code, tick_time,
           TIMESTAMPDIFF(SECOND,
             STR_TO_DATE(LEFT(tick_time, 14), '%Y%m%d%H%i%s'),
             recv_ts) AS lag_sec
    FROM tmp_fs_ticks
  ) x
  GROUP BY code, LEFT(tick_time, 12)
  HAVING SUM(CASE WHEN lag_sec IS NULL OR lag_sec <= @fb_age THEN 1 ELSE 0 END) = 0
     AND COUNT(*) > 0
) z;

SELECT '=== C3 ws_orderbook lag (filter_eval 제외, tmp) ===' AS section;
SELECT source AS src,
       COUNT(*) AS total,
       SUM(CASE WHEN lag_sec IS NOT NULL AND lag_sec <= @fb_age THEN 1 ELSE 0 END) AS pass_n,
       SUM(CASE WHEN lag_sec IS NOT NULL AND lag_sec > @fb_age THEN 1 ELSE 0 END) AS fail_n,
       SUM(CASE WHEN lag_sec IS NULL THEN 1 ELSE 0 END) AS unknown_n
FROM (
  SELECT source,
         TIMESTAMPDIFF(SECOND,
           STR_TO_DATE(
             CASE WHEN CHAR_LENGTH(snap_time) >= 14 THEN LEFT(snap_time, 14)
                  WHEN CHAR_LENGTH(snap_time) >= 6 THEN CONCAT(@day8, RIGHT(snap_time, 6))
                  ELSE NULL END,
             '%Y%m%d%H%i%s'),
           recv_ts) AS lag_sec
  FROM tmp_fs_ob
  WHERE source <> 'filter_eval'
) t
GROUP BY source;


-- ─────────────────────────────────────────────────────────────────────────
-- D. 끊김(disconnect) — recv_ts LAG (장중 09:00~15:30)
--    feed_collect_stats._feed_disconnect_stats
-- ─────────────────────────────────────────────────────────────────────────

SELECT '=== D ws_ticks recv_ts gap (tmp, 장중) ===' AS section;
SELECT source AS vendor,
       COUNT(*) AS pair_n,
       SUM(CASE WHEN gap_sec >= @soft THEN 1 ELSE 0 END) AS soft_n,
       SUM(CASE WHEN gap_sec >= @hard THEN 1 ELSE 0 END) AS hard_n,
       MAX(gap_sec) AS max_gap,
       ROUND(AVG(CASE WHEN gap_sec >= @soft THEN gap_sec END), 1) AS avg_soft_gap
FROM (
  SELECT source,
         TIMESTAMPDIFF(SECOND,
           LAG(recv_ts) OVER (PARTITION BY source, code ORDER BY recv_ts, id),
           recv_ts) AS gap_sec
  FROM tmp_fs_ticks
  WHERE TIME(recv_ts) BETWEEN '09:00:00' AND '15:30:00'
) t
WHERE gap_sec IS NOT NULL AND gap_sec > 0 AND gap_sec < @cap
GROUP BY source;


-- ─────────────────────────────────────────────────────────────────────────
-- E. LS 틱 (별도 테이블 — ts 범위가 인덱스 탐)
-- ─────────────────────────────────────────────────────────────────────────

SELECT '=== E1 ls_ws_ticks lag ===' AS section;
SELECT COUNT(*) AS total,
       SUM(CASE WHEN lag_sec IS NOT NULL AND lag_sec <= @fb_age THEN 1 ELSE 0 END) AS pass_n,
       SUM(CASE WHEN lag_sec IS NOT NULL AND lag_sec > @fb_age THEN 1 ELSE 0 END) AS fail_n,
       SUM(CASE WHEN lag_sec IS NULL THEN 1 ELSE 0 END) AS unknown_n
FROM (
  SELECT TIMESTAMPDIFF(SECOND,
    STR_TO_DATE(
      CASE WHEN CHAR_LENGTH(REGEXP_REPLACE(IFNULL(chetime,''), '[^0-9]', '')) >= 14
           THEN LEFT(REGEXP_REPLACE(chetime, '[^0-9]', ''), 14)
           WHEN CHAR_LENGTH(REGEXP_REPLACE(IFNULL(chetime,''), '[^0-9]', '')) >= 6
           THEN CONCAT(DATE_FORMAT(ts,'%Y%m%d'), RIGHT(REGEXP_REPLACE(chetime, '[^0-9]', ''), 6))
           ELSE DATE_FORMAT(ts,'%Y%m%d%H%i%s') END,
      '%Y%m%d%H%i%s'),
    ts) AS lag_sec
  FROM ls_ws_ticks
  WHERE ts >= @ts0 AND ts <= @ts1
) t;

SELECT '=== E2 ls_ws_ticks disconnect (장중) ===' AS section;
SELECT COUNT(*) AS pair_n,
       SUM(CASE WHEN gap_sec >= @soft THEN 1 ELSE 0 END) AS soft_n,
       SUM(CASE WHEN gap_sec >= @hard THEN 1 ELSE 0 END) AS hard_n,
       MAX(gap_sec) AS max_gap
FROM (
  SELECT TIMESTAMPDIFF(SECOND,
           LAG(ts) OVER (PARTITION BY code ORDER BY ts, id),
           ts) AS gap_sec
  FROM ls_ws_ticks
  WHERE ts >= @ts0 AND ts <= @ts1
    AND TIME(ts) BETWEEN '09:00:00' AND '15:30:00'
) t
WHERE gap_sec IS NOT NULL AND gap_sec > 0 AND gap_sec < @cap;


-- ─────────────────────────────────────────────────────────────────────────
-- F. 상세(heavy=1) — DISTINCT 종목 교집합 (웹 「상세 집계」)
-- ─────────────────────────────────────────────────────────────────────────

SELECT '=== F 틱/호가 종목 교집합 (kis vs kiwoom) ===' AS section;
SELECT
  (SELECT COUNT(DISTINCT code) FROM tmp_fs_ticks WHERE source='kis') AS kis_tick_codes,
  (SELECT COUNT(DISTINCT code) FROM tmp_fs_ticks WHERE source='kiwoom') AS kw_tick_codes,
  (SELECT COUNT(DISTINCT a.code)
   FROM (SELECT DISTINCT code FROM tmp_fs_ticks WHERE source='kis') a
   JOIN (SELECT DISTINCT code FROM tmp_fs_ticks WHERE source='kiwoom') b ON a.code=b.code) AS both_tick,
  (SELECT COUNT(DISTINCT code) FROM tmp_fs_ob WHERE source='kis_h0stasp0') AS kis_ob_codes,
  (SELECT COUNT(DISTINCT code) FROM tmp_fs_ob WHERE source='kiwoom_0d') AS kw_ob_codes;


-- ─────────────────────────────────────────────────────────────────────────
-- G. 테이블 전체 vs 하루 (왜 느린지 확인용)
-- ─────────────────────────────────────────────────────────────────────────

SELECT '=== G 전체 테이블 규모 ===' AS section;
SELECT 'ws_ticks' AS tbl, COUNT(*) AS total_rows FROM ws_ticks
UNION ALL
SELECT 'ws_orderbook', COUNT(*) FROM ws_orderbook
UNION ALL
SELECT 'ls_ws_ticks', COUNT(*) FROM ls_ws_ticks
UNION ALL
SELECT 'ls_ws_orderbook', COUNT(*) FROM ls_ws_orderbook;
