#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ データベースクリーンアップスクリプト: 1. 各修正チェーンで最新版のみis_latest=trueに 2. 古い版すべてをis_latest=falseに 3. 逆仕訳を削除 """ import os import subprocess import sys def run_sql_via_docker(): """ Docker innerからSQLコマンドを実行 """ # SQL命令を準備 sql_commands = [ # ========== Step 1: 確認 ========== """ -- 統計:実行前のデータ状況 SELECT COUNT(*) as total_entries, SUM(CASE WHEN is_latest = true THEN 1 ELSE 0 END) as latest_count, SUM(CASE WHEN is_latest = false THEN 1 ELSE 0 END) as old_count FROM journal_entries WHERE is_deleted = false; """, # ========== Step 2: 修正チェーンの最新版を確認して修正 ========== """ -- 各修正チェーンで、最後に作成されたもののみis_latest=trueとする UPDATE journal_entries SET is_latest = false WHERE journal_entry_id IN ( SELECT je.journal_entry_id FROM journal_entries je WHERE je.original_entry_id IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM journal_entries child WHERE child.original_entry_id = je.original_entry_id AND child.created_at > je.created_at ) AND je.is_latest = false ); """, # Step 3: 最新版以外のものをis_latest=falseに """ UPDATE journal_entries SET is_latest = false WHERE original_entry_id IS NOT NULL AND journal_entry_id NOT IN ( SELECT journal_entry_id FROM journal_entries WHERE original_entry_id IN ( SELECT original_entry_id FROM journal_entries WHERE original_entry_id IS NOT NULL ) ORDER BY original_entry_id, created_at DESC ); """, # Step 4: 最新版以外をすべてis_latest=falseに確認 """ -- 各修正チェーンで最新版を確認して、それ以外をfalseに WITH ranked_entries AS ( SELECT journal_entry_id, CASE WHEN original_entry_id IS NULL THEN journal_entry_id ELSE original_entry_id END as chain_id, ROW_NUMBER() OVER ( PARTITION BY CASE WHEN original_entry_id IS NULL THEN journal_entry_id ELSE original_entry_id END ORDER BY created_at DESC, journal_entry_id DESC ) as rn FROM journal_entries WHERE is_deleted = false ) UPDATE journal_entries SET is_latest = (ranked_entries.rn = 1) FROM ranked_entries WHERE journal_entries.journal_entry_id = ranked_entries.journal_entry_id; """ ] print("=" * 80) print("【Docker内でのデータベースクリーンアップを実行中】") print("=" * 80) print() # SQLコマンドをファイルに保存してDockerで実行 sql_file_content = "\n".join(sql_commands) # コマンドを実行 try: result = subprocess.run( [ 'docker', 'exec', 'njts_backend', 'bash', '-c', f""" cd /app python3 << 'PYTHON_EOF' import os from app.core.database import get_connection try: with get_connection() as conn: with conn.cursor() as cur: # Step: 統計前 print("【実行前の統計】") cur.execute(''' SELECT COUNT(*) as total_entries, SUM(CASE WHEN is_latest = true THEN 1 ELSE 0 END) as latest_count, SUM(CASE WHEN is_latest = false THEN 1 ELSE 0 END) as old_count FROM journal_entries WHERE is_deleted = false; ''') result = cur.fetchone() print(f"総レコード数: {result[0]}") print(f"is_latest=true: {result[1]}") print(f"is_latest=false: {result[2]}") print() # Step: 各修正チェーン内の最新版を正しくマーク print("【ステップ1: 各修正チェーンで最新版をマーク】") cur.execute(''' WITH ranked_entries AS ( SELECT journal_entry_id, CASE WHEN original_entry_id IS NULL THEN journal_entry_id ELSE original_entry_id END as chain_id, ROW_NUMBER() OVER ( PARTITION BY CASE WHEN original_entry_id IS NULL THEN journal_entry_id ELSE original_entry_id END ORDER BY created_at DESC, journal_entry_id DESC ) as rn FROM journal_entries WHERE is_deleted = false ) UPDATE journal_entries SET is_latest = (ranked_entries.rn = 1) FROM ranked_entries WHERE journal_entries.journal_entry_id = ranked_entries.journal_entry_id; ''') print(f"更新完了: {cur.rowcount} レコード") conn.commit() print() # Verification print("【実行後の統計】") cur.execute(''' SELECT COUNT(*) as total_entries, SUM(CASE WHEN is_latest = true THEN 1 ELSE 0 END) as latest_count, SUM(CASE WHEN is_latest = false THEN 1 ELSE 0 END) as old_count FROM journal_entries WHERE is_deleted = false; ''') result = cur.fetchone() print(f"総レコード数: {result[0]}") print(f"is_latest=true: {result[1]}") print(f"is_latest=false: {result[2]}") print() # Check each chain print("【各修正チェーン内のis_latest値を確認】") cur.execute(''' SELECT CASE WHEN original_entry_id IS NULL THEN journal_entry_id ELSE original_entry_id END as chain_id, COUNT(*) as total, SUM(CASE WHEN is_latest = true THEN 1 ELSE 0 END) as latest_count, SUM(CASE WHEN is_latest = false THEN 1 ELSE 0 END) as old_count FROM journal_entries WHERE is_deleted = false GROUP BY chain_id HAVING COUNT(*) > 1 ORDER BY chain_id DESC LIMIT 10 ''') chains = cur.fetchall() if chains: print(f"複数バージョンがあるチェーン: {len(chains)}") for chain in chains: print(f" Chain ID={chain[0]}: 合計={chain[1]}, latest={chain[2]}, old={chain[3]}") else: print("すべてのレコードは単独バージョン(修正なし)です") except Exception as e: print(f"エラー: {e}") import traceback traceback.print_exc() PYTHON_EOF """ ], capture_output=False, text=True, timeout=30 ) if result.returncode == 0: print("\n✓ クリーンアップ完了") return True else: print(f"\n✗ クリーンアップ失敗 (Exit code: {result.returncode})") return False except subprocess.TimeoutExpired: print("✗ 実行タイムアウト") return False except Exception as e: print(f"✗ エラー: {e}") import traceback traceback.print_exc() return False if __name__ == "__main__": success = run_sql_via_docker() sys.exit(0 if success else 1)