{"id":"91f6203b-d925-4b5b-ba49-e37adcceeb43","entityType":"agent","slug":"clawhub-moniq888-sql-master","name":"SQL Master","canonicalUrl":"https://www.xpersona.co/agent/clawhub-moniq888-sql-master","canonicalPath":"/agent/clawhub-moniq888-sql-master","generatedAt":"2026-10-11T07:39:19.318Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T05:16:30.801Z","emptyReason":null},"description":"SQL 查询、数据获取智能体。覆盖 SQL 全链路能力：自然语言转生产级 SQL、慢查询诊断与执行计划分析、索引设计与优化、数仓建模、SQL 原理深度科普、查询结果可视化。支持 MySQL / PostgreSQL / Hive / Spark SQL / ClickHouse / BigQuery 多方言。触发...","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.1K downloads reported by the source. Last updated 10/11/2026.","installCommand":"clawhub skill install s1774kyr593ezfy7qtq3g4k9t983g3a1:sql-master","sourceUrl":"https://clawhub.ai/moniq888/sql-master","homepage":"https://clawhub.ai/moniq888/skills/sql-master","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/moniq888/sql-master","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/moniq888/skills/sql-master","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":45,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"SQL Master technical dossier on Xpersona with agent coverage, OPENCLEW support, and live trust metadata."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-11T05:16:30.801Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T05:16:30.801Z","emptyReason":null},"stars":null,"forks":null,"downloads":1149,"packageName":null,"latestVersion":"1.0.1","tractionLabel":"1.1K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T05:16:30.801Z","emptyReason":null},"lastUpdatedAt":"2026-10-11T05:16:30.801Z","lastCrawledAt":"2026-10-11T05:16:30.801Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-12T05:16:30.801Z","lastVerifiedAt":null,"highlights":[{"version":"1.0.1","createdAt":"2026-03-27T10:31:31.453Z","changelog":"Version 1.0.1 - 更新 Skill 协作工具名称，“report-generator” 统一更名为 “sql-report-generator” - 所有协作流程、范例代码和说明同步修改，保持命名一致性 - 其他功能、依赖及接口保持不变","fileCount":20,"zipByteSize":74033},{"version":"1.0.0","createdAt":"2026-03-27T05:20:10.029Z","changelog":"sql-master 1.0.0 - 首发上线，完整覆盖 SQL 查询/优化/分析/可视化的全链路能力。 - 新增统一 Pipeline 编排，三 Skill（sql-master、sql-dataviz、report-generator）端到端串联，支持自动从数据获取到报告生成。 - 支持 MySQL、PostgreSQL、Hive、Spark SQL、ClickHouse、BigQuery 多 SQL 方言。 - 增加数据库连接与执行、文件数据接入（CSV/Excel/JSON/Parquet/SQLite）、数据格式转换等实用功能。 - 拓展 SQL Pipeline 与数据库管理、数据可视化、AI 洞察、自动 HTML 报告等一体化数据分析场景。 - 内含丰富 SQL 原理、建模、优化、诊断、规范与最佳实践文档。","fileCount":19,"zipByteSize":72302}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s1774kyr593ezfy7qtq3g4k9t983g3a1:sql-master","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s1774kyr593ezfy7qtq3g4k9t983g3a1:sql-master` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/moniq888/sql-master before using production credentials."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-moniq888-sql-master/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-moniq888-sql-master/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-moniq888-sql-master/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-moniq888-sql-master/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-moniq888-sql-master/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-moniq888-sql-master/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-11T07:39:19.317Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-moniq888-sql-master/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-moniq888-sql-master/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-moniq888-sql-master/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-moniq888-sql-master/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T05:16:30.801Z","emptyReason":null},"readme":"Skill: SQL Master\n\nOwner: moniq888\n\nSummary: SQL 查询、数据获取智能体。覆盖 SQL 全链路能力：自然语言转生产级 SQL、慢查询诊断与执行计划分析、索引设计与优化、数仓建模、SQL 原理深度科普、查询结果可视化。支持 MySQL / PostgreSQL / Hive / Spark SQL / ClickHouse / BigQuery 多方言。触发...\n\nTags: latest:1.0.1\n\nVersion history:\n\nv1.0.1 | 2026-03-27T10:31:31.453Z | user\n\nVersion 1.0.1\n\n- 更新 Skill 协作工具名称，“report-generator” 统一更名为 “sql-report-generator”\n- 所有协作流程、范例代码和说明同步修改，保持命名一致性\n- 其他功能、依赖及接口保持不变\n\nv1.0.0 | 2026-03-27T05:20:10.029Z | user\n\nsql-master 1.0.0\n\n- 首发上线，完整覆盖 SQL 查询/优化/分析/可视化的全链路能力。\n- 新增统一 Pipeline 编排，三 Skill（sql-master、sql-dataviz、report-generator）端到端串联，支持自动从数据获取到报告生成。\n- 支持 MySQL、PostgreSQL、Hive、Spark SQL、ClickHouse、BigQuery 多 SQL 方言。\n- 增加数据库连接与执行、文件数据接入（CSV/Excel/JSON/Parquet/SQLite）、数据格式转换等实用功能。\n- 拓展 SQL Pipeline 与数据库管理、数据可视化、AI 洞察、自动 HTML 报告等一体化数据分析场景。\n- 内含丰富 SQL 原理、建模、优化、诊断、规范与最佳实践文档。\n\nArchive index:\n\nArchive v1.0.1: 20 files, 74033 bytes\n\nFiles: references/cli-quickref.md (6312b), references/data-warehouse.md (8271b), references/ddl-design.md (7684b), references/dialect-guide.md (6934b), references/hive-skew-advanced.md (19618b), references/index-design.md (5900b), references/query-optimization.md (8157b), references/sql-generation.md (7250b), references/sql-internals.md (9668b), references/sql-security.md (3849b), references/visualization-guide.md (7602b), requirements.txt (1136b), scripts/__init__.py (954b), scripts/database_connector.py (15639b), scripts/file_connector.py (21770b), scripts/pipeline.py (25842b), scripts/unified_pipeline.py (28200b), skill-card.md (3060b), SKILL.md (14515b), _meta.json (129b)\n\nFile v1.0.1:SKILL.md\n\n---\nname: sql-master\ndescription: SQL 查询、数据获取智能体。覆盖 SQL 全链路能力：自然语言转生产级 SQL、慢查询诊断与执行计划分析、索引设计与优化、数仓建模、SQL 原理深度科普、查询结果可视化。支持 MySQL / PostgreSQL / Hive / Spark SQL / ClickHouse / BigQuery 多方言。触发场景：(1) 写 SQL / 生成查询，(2) SQL 慢/优化/调优，(3) 执行计划分析 EXPLAIN，(4) 索引设计，(5) 数仓建模 / 分层设计，(6) SQL 原理问题（事务/锁/MVCC/Join算法等），(7) 表结构设计 DDL，(8) SQL 报错诊断，(9) 任何\"帮我写个查询\"、\"这个SQL为什么慢\"、\"怎么建索引\"类请求，(10) 查询结果可视化 / 出图 / 图表 / 数据展示。\n---\n\n# SQL Master — SQL 查询、数据获取智能体\n\n## ⚠️ 使用前必读\n\n本 Skill 需要 Python 依赖。**首次使用前必须安装依赖**：\n\n```bash\nskillhub_install install_skill sql-master\n```\n\n工具会自动检测 Python3 环境、pip 可用性，并安装所有依赖。\n\n### 依赖安装方式\n\n| 方式 | 命令 | 适用场景 |\n|------|------|---------|\n| **自动安装（推荐）** | `skillhub_install install_skill sql-master` | 一键安装，自动处理 |\n| **手动安装** | `pip install -r requirements.txt` | 熟悉 Python 环境的用户 |\n\n### 无依赖使用（受限模式）\n\n如果无法安装依赖，本 Skill 提供以下**降级能力**：\n\n✅ **可用功能**：\n- SQL 语句生成（纯文本输出，无需执行）\n- SQL 诊断与优化建议（基于文本分析）\n- 索引设计建议（基于规则引擎）\n- SQL 原理解释与科普\n- 执行计划分析（用户提供 EXPLAIN 结果）\n\n❌ **不可用功能**：\n- 数据库连接与 SQL 执行\n- 数据 Pipeline 处理\n- 本地文件数据获取（CSV/Excel 等）\n- 与 sql-dataviz / sql-report-generator 联动\n\n---\n\n## 🔗 Skill 协作关系\n\n本 Skill 与 **sql-dataviz**、**sql-report-generator** 组成完整的数据分析流水线：\n\n```\n┌─────────────┐     ┌──────────────┐     ┌────────────────────────┐\n│ sql-master  │ ──► │ sql-dataviz  │ ──► │ sql-report-generator   │\n│  (数据层)   │     │  (可视化层)  │     │  (报告层)              │\n└─────────────┘     └──────────────┘     └────────────────────────┘\n      │                   │                   │\n      ▼                   ▼                   ▼\n   SQL 查询           图表生成            HTML 报告\n   数据获取           PNG/HTML            AI 洞察\n   格式转换           Dashboard           数据表格\n```\n\n### 协作模式\n\n| 模式 | 组合 | 适用场景 |\n|------|------|---------|\n| **单独使用** | sql-master | 仅需 SQL 查询/生成/优化 |\n| **可视化** | sql-master + sql-dataviz | SQL 查询 → 图表输出 |\n| **完整流程** | sql-master + sql-dataviz + sql-report-generator | 完整数据分析报告 |\n\n### 🥇 最优使用方式：三 Skill 串联\n\n```python\nfrom scripts.unified_pipeline import UnifiedPipeline\n\nresult = (\n    UnifiedPipeline(\"销售分析\")\n    .from_file(\"sales.csv\")                                    # sql-master: 数据获取\n    .query(\"SELECT region, SUM(sales) as total FROM data GROUP BY region\")\n    .interactive_chart(\"bar\", x_col=\"region\", y_col=\"total\")   # sql-dataviz: 可视化\n    .insights(value_cols=[\"total\"])                            # AI 洞察\n    .report(title=\"销售报告\", output=\"report.html\")            # sql-report-generator: 报告\n)\n```\n\n### 决策指南\n\n```\n你需要什么？\n├─ 仅 SQL 查询/优化 → sql-master 单独使用\n├─ SQL + 图表 → sql-master + sql-dataviz\n├─ 图表 + 报告（无 SQL）→ sql-dataviz + sql-report-generator\n└─ 完整分析报告 → sql-master + sql-dataviz + sql-report-generator ✅ 推荐\n```\n\n---\n\n## 新增功能：统一 Pipeline 编排（三 Skill 端到端）\n\n### `scripts/unified_pipeline.py`\n\n打通 sql-master → sql-dataviz → sql-report-generator 的端到端自动化：\n\n```python\nfrom scripts.unified_pipeline import UnifiedPipeline, analyze_file\n\n# 完整 Pipeline\nresult = (\n    UnifiedPipeline(\"销售分析\")\n    .from_file(\"sales.csv\")                        # 数据源\n    .query(\"SELECT region, SUM(sales) as total FROM data GROUP BY region\")  # SQL\n    .interactive_chart(\"bar\", x_col=\"region\", y_col=\"total\", title=\"区域销售\")  # 交互图\n    .chart(\"line\", x_col=\"region\", y_col=\"total\")              # 静态图 (PNG)\n    .insights(value_cols=[\"total\"])                            # AI 洞察\n    .report(title=\"销售报告\", output=\"report.html\")           # 完整报告\n)\nprint(result.log())\n\n# 一键分析\nresult = analyze_file(\"sales.csv\", output=\"report.html\")\n```\n\n**支持的图表**：静态 PNG（60种）+ 交互式 HTML（12种）\n**支持的洞察**：异常检测 / 趋势 / 相关性 / TOP N / 分布 / 季节性 / 对比\n**支持的报告**：完整 HTML（图表 + 洞察 + 数据表格 + KPI 卡片）\n\n## 新增功能：数据库连接执行层 + 数据 Pipeline\n\n### 1. 数据库连接（scripts/database_connector.py）\n\n支持 SQLite / MySQL / PostgreSQL / SQL Server / ClickHouse / Oracle\n\n```python\nfrom scripts.database_connector import connect_sqlite, connect_mysql, connect_postgresql\n\n# SQLite（本地文件）\nconn = connect_sqlite(\"data/sales.db\")\nresult = conn.execute(\"SELECT region, SUM(amount) FROM sales GROUP BY region\")\nprint(result.df)           # DataFrame 访问\nprint(result.to_dict())   # dict 访问\nresult.to_csv(\"output.csv\")  # 导出 CSV\nresult.to_json(\"output.json\") # 导出 JSON\n\n# MySQL\nconn = connect_mysql(host=\"localhost\", port=3306, username=\"root\", password=\"xxx\", database=\"mydb\")\nresult = conn.execute(\"SELECT * FROM orders WHERE date >= '2024-01-01'\")\nprint(result.summary())   # 可读摘要\n\n# PostgreSQL\nconn = connect_postgresql(host=\"localhost\", database=\"mydb\", username=\"postgres\", password=\"xxx\")\ntables = conn.get_tables()  # 获取所有表名\nschema = conn.get_schema(\"orders\")  # 获取表结构\nconn.close()\n```\n\n### 2. 本地文件数据获取（scripts/file_connector.py）\n\n支持 CSV / Excel / JSON / Parquet / SQLite 等所有主流格式，自动 SQL 查询 + 格式转换\n\n```python\nfrom scripts.file_connector import load_file, load_directory\n\n# 加载本地文件\nfc = load_file(\"data/sales.csv\")        # 单个文件\nfc = load_directory(\"data/reports/\")     # 目录下所有文件\nfc = load_file(\"data/*.csv\")            # 通配符匹配\n\nprint(fc.shape)           # (10000, 12)\nprint(fc.columns)         # ['date', 'region', 'amount', ...]\nprint(fc.df.head())       # DataFrame\n\n# 用途一：SQL 查询（自动建 SQLite 内存表）\nresult = fc.query(\"SELECT region, SUM(amount) as total FROM data GROUP BY region ORDER BY total DESC\")\n\n# 用途二：格式转换\nfc.to_csv(\"output/sales_report.csv\")\nfc.to_excel(\"output/sales_report.xlsx\")\nfc.to_json(\"output/sales_report.json\")\nfc.to_parquet(\"output/sales_report.parquet\")\nfc.to_sqlite(\"output/sales.db\", table_name=\"sales\")\n\n# 用途三：传给 sql-dataviz 画图\nb64 = fc.to_dataviz(\"line\", x_col=\"month\", y_col=\"sales\", title=\"月度销售趋势\")\n```\n\n### 3. SQL Pipeline 流水线（scripts/pipeline.py）\n\n三大用途一气呵成：数据获取 → SQL 查询 → 格式转换 → 可视化 → HTML 报告\n\n```python\nfrom scripts.pipeline import SQLPipeline\n\n# 方式一：从文件开始\np = (\n    SQLPipeline()\n    .from_file(\"data/sales.csv\")\n    .query(\"SELECT region, SUM(amount) as total FROM data GROUP BY region\")\n    .to_csv(\"output/regional_sales.csv\")\n    .to_excel(\"output/regional_sales.xlsx\")\n)\n\n# 方式二：从数据库开始\np = SQLPipeline().from_db(dialect=\"sqlite\", database=\"data.db\")\np.query(\"SELECT * FROM sales WHERE amount > 1000\")\np.query(\"SELECT region, COUNT(*) FROM data GROUP BY region\")\n\n# 方式三：从 DataFrame 开始\nimport pandas as pd\ndf = pd.read_csv(\"data.csv\")\np = SQLPipeline().from_dataframe(df)\n\n# 管道操作\np.query(\"SELECT region, SUM(amount) as total FROM data GROUP BY region\")\np.transform(lambda df: df[df[\"total\"] > 1000])  # 过滤\np.to_dataviz(\"bar\", x_col=\"region\", y_col=\"total\", title=\"区域销售排行\")\np.to_report(title=\"销售分析报告\", output=\"output/report.html\")\np.log()   # 打印执行日志\n```\n\n**Pipeline 完整流程示例：**\n```python\n(\n    SQLPipeline()\n    .from_file(\"sales_2024.csv\")                        # 加载数据\n    .query(\"SELECT * FROM data WHERE region = '华东'\")  # SQL 筛选\n    .to_csv(\"output/east_sales.csv\")                   # 导出 CSV\n    .to_json(\"output/east_sales.json\")                 # 导出 JSON\n    .to_dataviz(\"line\", x_col=\"month\", y_col=\"sales\") # 生成折线图\n    .to_dataviz(\"pie\", x_col=\"product\", y_col=\"amount\") # 生成饼图\n    .to_report(title=\"华东区域销售报告\", output=\"output/report.html\")  # HTML 报告\n)\n```\n\n## 核心原则\n\n**生产级标准**：所有输出的 SQL 必须满足：\n- 注释完整（业务背景 + 性能预期 + 适用数据量级）\n- 明确标注数据库版本和方言\n- 主动提示 NULL 处理、空集合、边界条件\n- 给出多方案时说明各自 trade-off\n\n**分层回答**：同一问题，先给结论，再给原理，最后给深入扩展。自动识别用户水平（初学者/开发者/DBA），调整解释深度。\n\n**可复现**：生成的 SQL 必须附带最小可复现测试数据（DDL + INSERT），确保用户能直接验证。\n\n---\n\n## 功能模块导航\n\n| 场景 | 参考文件 |\n|------|---------|\n| 自然语言 → SQL 生成 | [references/sql-generation.md](references/sql-generation.md) |\n| 慢查询诊断 & 执行计划分析 | [references/query-optimization.md](references/query-optimization.md) |\n| 索引设计策略 | [references/index-design.md](references/index-design.md) |\n| 数仓建模 & 分层架构 | [references/data-warehouse.md](references/data-warehouse.md) |\n| Hive 数据倾斜深度（引擎原理/量化模型/极端场景） | [references/hive-skew-advanced.md](references/hive-skew-advanced.md) |\n| SQL 原理深度（事务/锁/MVCC/Join） | [references/sql-internals.md](references/sql-internals.md) |\n| 多方言差异速查 | [references/dialect-guide.md](references/dialect-guide.md) |\n| DDL 设计规范 | [references/ddl-design.md](references/ddl-design.md) |\n| SQL 安全规范（注入防护/参数化查询） | [references/sql-security.md](references/sql-security.md) |\n| CLI 实操速查（sqlite3/psql/mysql 连接与导入导出） | [references/cli-quickref.md](references/cli-quickref.md) |\n| 查询结果可视化（图表选型/Python 代码/设计原则） | [references/visualization-guide.md](references/visualization-guide.md) |\n\n---\n\n## 工作流程\n\n### 1. 意图识别\n收到请求后，先判断属于哪个场景：\n- **生成类**：用户描述业务需求，需要输出 SQL\n- **优化类**：用户提供现有 SQL 或 EXPLAIN，需要诊断和改写\n- **设计类**：表结构、索引、数仓架构设计\n- **科普类**：原理解释、概念问答\n- **诊断类**：报错信息分析\n- **可视化类**：将查询结果转化为图表 → 加载 [references/visualization-guide.md](references/visualization-guide.md)\n\n### 2. 上下文收集\n生成或优化 SQL 前，主动确认（如未提供）：\n- 数据库类型和版本\n- 关键表的 schema（列名、类型、索引）\n- 数据量级（行数、数据大小）\n- 查询频率和性能目标（P99 < Xms？）\n\n### 3. 输出规范\n\n**SQL 输出模板**：\n```sql\n-- ============================================================\n-- 业务说明：[描述这段 SQL 解决什么业务问题]\n-- 数据库：MySQL 8.0 / PostgreSQL 15 / ...\n-- 性能预期：[预计执行时间，适用数据量级]\n-- 注意事项：[NULL 处理、边界条件、已知限制]\n-- ============================================================\n\nSELECT ...\nFROM ...\nWHERE ...\n```\n\n**优化报告模板**：\n```\n## 问题诊断\n[执行计划中发现的问题，按严重程度排序]\n\n## 优化方案\n### 方案 A（推荐）\n[改写后的 SQL + 原因]\n\n### 方案 B（备选）\n[另一种思路 + 适用场景]\n\n## 预期收益\n[优化前 vs 优化后的性能对比估算]\n\n## 可复现测试\n[最小 DDL + 数据 + 验证步骤]\n```\n\n### 4. 加载参考文件\n根据意图识别结果，读取对应的 references/ 文件获取详细指导。\n\n---\n\n## 快速参考\n\n### 常见性能陷阱（立即识别）\n- `SELECT *` → 明确列名，避免回表\n- `WHERE` 列上有函数 → 索引失效\n- `OR` 连接不同列 → 考虑 UNION ALL\n- `!=` / `NOT IN` → 无法走索引\n- 隐式类型转换 → 索引失效\n- `LIMIT` 大偏移量 → 延迟关联优化\n- `COUNT(*)` vs `COUNT(col)` → NULL 语义差异\n\n### Join 算法选择直觉\n- 小表 JOIN 大表 → Nested Loop（小表驱动）\n- 两个大表等值 JOIN → Hash Join\n- 有序数据等值 JOIN → Merge Join\n- 数据倾斜 → 广播小表 / 加盐打散\n\n### 索引设计口诀\n**最左前缀、区分度高、覆盖查询、避免冗余**\n\n---\n\n## 强制规范（MUST DO / MUST NOT）\n\n借鉴 sql-pro 的约束清单，以下规则在任何情况下都必须遵守：\n\n### ✅ MUST DO\n- 优化前**必须先分析执行计划**（EXPLAIN / EXPLAIN ANALYZE）\n- 优先使用**集合操作**，避免逐行处理（游标/循环）\n- **尽早过滤**：WHERE 条件尽量前置，减少中间结果集\n- 存在性检查用 `EXISTS`，不用 `COUNT(*) > 0`\n- **显式处理 NULL**：IS NULL / IS NOT NULL / COALESCE / NULLIF\n- 为高频查询创建**覆盖索引**\n- 涉及安全场景时，必须使用**参数化查询**，详见 [references/sql-security.md](references/sql-security.md)\n- 跨数据库迁移时，必须标注**方言差异**，详见 [references/dialect-guide.md](references/dialect-guide.md)\n\n### ❌ MUST NOT\n- 不在 WHERE / JOIN 条件列上使用函数（导致索引失效）\n- 不用 `SELECT *`（回表开销 + 隐式依赖）\n- 不用字符串拼接构造 SQL（SQL 注入风险）\n- 不在大表上做无索引的全表扫描\n- 不用 `OFFSET` 大偏移量分页（改用游标/keyset 分页）\n- 不忽略隐式类型转换（导致索引失效 + 数据截断）\n- 不在生产环境直接运行未经 EXPLAIN 验证的复杂查询\n\nFile v1.0.1:_meta.json\n\n{\n  \"ownerId\": \"kn76k6338wpqydkxgdztg6vb9x83hz20\",\n  \"slug\": \"sql-master\",\n  \"version\": \"1.0.1\",\n  \"publishedAt\": 1774607491453\n}\n\nFile v1.0.1:references/cli-quickref.md\n\n# CLI 实操速查\n\n数据库命令行工具的常用操作速查，适合直接上手。\n\n---\n\n## SQLite\n\nSQLite 内置于 Python，零配置，适合本地开发和原型验证。\n\n### 连接与基本操作\n```bash\n# 打开/创建数据库\nsqlite3 mydb.sqlite\n\n# 单行查询（不进入交互模式）\nsqlite3 mydb.sqlite \"SELECT COUNT(*) FROM users;\"\n\n# 交互模式开启表头和列对齐\nsqlite3 -header -column mydb.sqlite\n```\n\n### 数据导入导出\n```bash\n# 导入 CSV\nsqlite3 mydb.sqlite \".mode csv\" \".import data.csv mytable\" \"SELECT COUNT(*) FROM mytable;\"\n\n# 导出为 CSV\nsqlite3 -header -csv mydb.sqlite \"SELECT * FROM orders;\" > orders.csv\n\n# 导出整个数据库为 SQL\nsqlite3 mydb.sqlite .dump > backup.sql\n\n# 从 SQL 文件恢复\nsqlite3 mydb.sqlite < backup.sql\n```\n\n### 常用 Meta 命令\n```\n.tables              -- 列出所有表\n.schema users        -- 查看表结构\n.indexes users       -- 查看索引\n.mode column         -- 列对齐显示\n.headers on          -- 显示列名\n.quit                -- 退出\n```\n\n### 关键 PRAGMA\n```sql\nPRAGMA journal_mode = WAL;        -- 提升并发写入性能\nPRAGMA synchronous = NORMAL;      -- 平衡安全与性能\nPRAGMA foreign_keys = ON;         -- 启用外键约束（默认关闭！）\nPRAGMA cache_size = -64000;       -- 设置缓存 64MB\nPRAGMA temp_store = MEMORY;       -- 临时表放内存\nPRAGMA integrity_check;           -- 数据库完整性检查\n```\n\n---\n\n## PostgreSQL\n\n### 连接\n```bash\n# 基本连接\npsql -h localhost -U myuser -d mydb\n\n# 连接字符串\npsql \"postgresql://user:pass@localhost:5432/mydb?sslmode=require\"\n\n# 单行查询\npsql -h localhost -U myuser -d mydb -c \"SELECT NOW();\"\n\n# 执行 SQL 文件\npsql -h localhost -U myuser -d mydb -f migration.sql\n\n# 列出所有数据库\npsql -l\n```\n\n### 数据导入导出\n```bash\n# 导出整个数据库\npg_dump -h localhost -U myuser mydb > backup.sql\n\n# 导出为自定义格式（推荐，支持并行恢复）\npg_dump -h localhost -U myuser -Fc mydb > backup.dump\n\n# 恢复\npsql -h localhost -U myuser mydb < backup.sql\npg_restore -h localhost -U myuser -d mydb backup.dump\n\n# 导出单表为 CSV\npsql -h localhost -U myuser -d mydb -c \"\\COPY orders TO 'orders.csv' CSV HEADER\"\n\n# 导入 CSV\npsql -h localhost -U myuser -d mydb -c \"\\COPY orders FROM 'orders.csv' CSV HEADER\"\n```\n\n### 常用 Meta 命令\n```\n\\l                   -- 列出数据库\n\\c mydb              -- 切换数据库\n\\dt                  -- 列出表\n\\d users             -- 查看表结构（含索引）\n\\di                  -- 列出索引\n\\df                  -- 列出函数\n\\timing              -- 显示查询耗时\n\\x                   -- 切换扩展显示模式（宽表友好）\n\\e                   -- 用编辑器编辑查询\n\\q                   -- 退出\n```\n\n### 性能诊断\n```sql\n-- 查看慢查询（需开启 pg_stat_statements）\nSELECT query, calls, mean_exec_time, total_exec_time\nFROM pg_stat_statements\nORDER BY mean_exec_time DESC\nLIMIT 10;\n\n-- 查看表大小\nSELECT relname, pg_size_pretty(pg_total_relation_size(relid))\nFROM pg_stat_user_tables\nORDER BY pg_total_relation_size(relid) DESC;\n\n-- 查看锁等待\nSELECT pid, wait_event_type, wait_event, query\nFROM pg_stat_activity\nWHERE wait_event IS NOT NULL;\n\n-- 终止慢查询\nSELECT pg_terminate_backend(pid)\nFROM pg_stat_activity\nWHERE query_start < NOW() - INTERVAL '5 minutes'\n  AND state = 'active';\n```\n\n---\n\n## MySQL / MariaDB\n\n### 连接\n```bash\n# 基本连接\nmysql -h localhost -u myuser -p mydb\n\n# 单行查询\nmysql -h localhost -u myuser -p mydb -e \"SELECT NOW();\"\n\n# 执行 SQL 文件\nmysql -h localhost -u myuser -p mydb < migration.sql\n\n# 不显示密码警告（脚本用）\nmysql --defaults-extra-file=~/.my.cnf mydb -e \"SELECT 1;\"\n```\n\n### ~/.my.cnf 配置（避免明文密码）\n```ini\n[client]\nhost=localhost\nuser=myuser\npassword=mypassword\n```\n\n### 数据导入导出\n```bash\n# 导出整个数据库\nmysqldump -h localhost -u myuser -p mydb > backup.sql\n\n# 导出单表\nmysqldump -h localhost -u myuser -p mydb orders > orders.sql\n\n# 恢复\nmysql -h localhost -u myuser -p mydb < backup.sql\n\n# 导出为 CSV（需 FILE 权限）\nmysql -h localhost -u myuser -p mydb -e \\\n  \"SELECT * FROM orders INTO OUTFILE '/tmp/orders.csv' FIELDS TERMINATED BY ',' ENCLOSED BY '\\\"' LINES TERMINATED BY '\\n';\"\n```\n\n### 常用 Meta 命令\n```sql\nSHOW DATABASES;\nUSE mydb;\nSHOW TABLES;\nDESCRIBE users;           -- 查看表结构\nSHOW CREATE TABLE users;  -- 查看建表语句（含索引）\nSHOW INDEX FROM users;    -- 查看索引详情\nSHOW PROCESSLIST;         -- 查看当前连接和查询\nSHOW VARIABLES LIKE 'innodb%';  -- 查看 InnoDB 配置\n```\n\n### 性能诊断\n```sql\n-- 查看慢查询日志状态\nSHOW VARIABLES LIKE 'slow_query%';\nSHOW VARIABLES LIKE 'long_query_time';\n\n-- 开启慢查询（临时）\nSET GLOBAL slow_query_log = 'ON';\nSET GLOBAL long_query_time = 1;  -- 超过 1 秒记录\n\n-- 查看 InnoDB 状态（锁、事务）\nSHOW ENGINE INNODB STATUS\\G\n\n-- 查看当前锁等待\nSELECT * FROM information_schema.INNODB_LOCK_WAITS;\n\n-- 终止慢查询\nKILL QUERY <pid>;\n```\n\n---\n\n## 通用技巧\n\n### EXPLAIN 快速解读\n```sql\n-- MySQL\nEXPLAIN SELECT ...;\nEXPLAIN FORMAT=JSON SELECT ...;   -- 更详细\n\n-- PostgreSQL\nEXPLAIN SELECT ...;\nEXPLAIN (ANALYZE, BUFFERS) SELECT ...;  -- 实际执行 + 缓存命中\n\n-- 关注指标\n-- type: ALL（全表扫描，危险）→ ref/eq_ref/const（索引，好）\n-- rows: 预估扫描行数，越小越好\n-- Extra: Using filesort / Using temporary（需优化）\n```\n\n### 快速生成测试数据\n```sql\n-- MySQL：生成 10000 行测试数据\nINSERT INTO test_table (name, value, created_at)\nSELECT\n  CONCAT('user_', seq),\n  FLOOR(RAND() * 1000),\n  DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 365) DAY)\nFROM (\n  SELECT @row := @row + 1 AS seq\n  FROM information_schema.columns, (SELECT @row := 0) r\n  LIMIT 10000\n) t;\n\n-- PostgreSQL：generate_series\nINSERT INTO test_table (name, value, created_at)\nSELECT\n  'user_' || i,\n  (RANDOM() * 1000)::INT,\n  NOW() - (RANDOM() * 365 || ' days')::INTERVAL\nFROM generate_series(1, 10000) AS i;\n\n-- SQLite\nWITH RECURSIVE cnt(x) AS (\n  SELECT 1 UNION ALL SELECT x+1 FROM cnt WHERE x < 10000\n)\nINSERT INTO test_table (name, value)\nSELECT 'user_' || x, ABS(RANDOM() % 1000) FROM cnt;\n```\n\nFile v1.0.1:references/data-warehouse.md\n\n# 数仓建模 & 分层架构\n\n## 数仓分层架构（标准）\n\n```\n原始数据\n    ↓\nODS（Operational Data Store）操作数据层\n    ↓\nDWD（Data Warehouse Detail）明细数据层\n    ↓\nDWS（Data Warehouse Summary）汇总数据层\n    ↓\nADS（Application Data Store）应用数据层\n    ↓\n报表 / BI / 应用\n```\n\n### 各层职责\n\n| 层 | 职责 | 特点 |\n|----|------|------|\n| ODS | 原始数据落地，不做业务加工 | 保留原始字段，全量或增量同步 |\n| DWD | 数据清洗、标准化、维度关联 | 1:1 对应业务事实，最细粒度 |\n| DWS | 按主题聚合，轻度汇总 | 按天/周/月聚合，宽表 |\n| ADS | 面向具体应用的指标 | 直接支撑报表，高度聚合 |\n\n---\n\n## 维度建模\n\n### 星型模型 vs 雪花模型\n\n```\n星型模型：\n  事实表 ← 直接关联 → 维度表（维度表不再关联其他维度表）\n  优点：查询简单，JOIN 少，性能好\n  缺点：维度表可能有冗余\n\n雪花模型：\n  事实表 ← 维度表 ← 子维度表（维度表继续规范化）\n  优点：存储空间小，无冗余\n  缺点：JOIN 多，查询复杂，性能差\n\n实践建议：OLAP 场景优先用星型模型\n```\n\n### 事实表设计\n```sql\n-- 事实表：记录业务事件，包含度量值和外键\nCREATE TABLE dwd_order_detail (\n    order_id        BIGINT          COMMENT '订单ID',\n    user_id         BIGINT          COMMENT '用户ID（关联用户维度）',\n    product_id      BIGINT          COMMENT '商品ID（关联商品维度）',\n    date_id         INT             COMMENT '日期ID（关联日期维度，格式 20240101）',\n    -- 度量值\n    quantity        INT             COMMENT '购买数量',\n    unit_price      DECIMAL(10,2)   COMMENT '单价',\n    discount_amount DECIMAL(10,2)   COMMENT '优惠金额',\n    actual_amount   DECIMAL(10,2)   COMMENT '实付金额',\n    -- 分区字段\n    dt              STRING          COMMENT '数据日期分区 yyyy-MM-dd'\n)\nCOMMENT '订单明细事实表'\nPARTITIONED BY (dt STRING)\nSTORED AS ORC;\n```\n\n### 维度表设计\n```sql\n-- 维度表：描述业务实体的属性\nCREATE TABLE dim_user (\n    user_id         BIGINT          COMMENT '用户ID',\n    username        STRING          COMMENT '用户名',\n    register_date   STRING          COMMENT '注册日期',\n    city            STRING          COMMENT '城市',\n    age_group       STRING          COMMENT '年龄段（18-24/25-34/...）',\n    user_level      STRING          COMMENT '用户等级（普通/银牌/金牌/钻石）',\n    -- SCD（缓慢变化维度）字段\n    start_date      STRING          COMMENT '该版本生效日期',\n    end_date        STRING          COMMENT '该版本失效日期（9999-12-31 表示当前有效）',\n    is_current      TINYINT         COMMENT '是否当前版本（1=是）'\n)\nCOMMENT '用户维度表'\nSTORED AS ORC;\n```\n\n---\n\n## 缓慢变化维度（SCD）\n\n### SCD Type 1：直接覆盖\n```sql\n-- 适用：不需要历史，只关心当前值\nUPDATE dim_user SET city = '上海' WHERE user_id = 1001;\n-- 缺点：历史数据丢失\n```\n\n### SCD Type 2：新增版本（最常用）\n```sql\n-- 适用：需要保留历史，分析不同时期的属性\n-- 用户从北京迁到上海时，新增一行，旧行标记失效\n\n-- 失效旧版本\nUPDATE dim_user\nSET end_date = '2024-01-14', is_current = 0\nWHERE user_id = 1001 AND is_current = 1;\n\n-- 插入新版本\nINSERT INTO dim_user VALUES\n(1001, '张三', '2020-01-01', '上海', '25-34', '金牌', '2024-01-15', '9999-12-31', 1);\n\n-- 查询某时间点的用户属性（点查）\nSELECT * FROM dim_user\nWHERE user_id = 1001\n  AND start_date <= '2024-01-10'\n  AND end_date > '2024-01-10';\n```\n\n---\n\n## 数仓常用 SQL 模式\n\n### 增量数据处理（每日 ETL）\n```sql\n-- 场景：每天增量同步订单数据到 DWD\n-- 策略：按分区覆盖写（INSERT OVERWRITE）\n\nINSERT OVERWRITE TABLE dwd_order_detail PARTITION (dt = '2024-01-15')\nSELECT\n    o.order_id,\n    o.user_id,\n    o.product_id,\n    DATE_FORMAT(o.create_time, '%Y%m%d')    AS date_id,\n    o.quantity,\n    o.unit_price,\n    COALESCE(o.discount_amount, 0)          AS discount_amount,\n    o.actual_amount,\n    '2024-01-15'                            AS dt\nFROM ods_orders o\nWHERE DATE(o.create_time) = '2024-01-15'\n  AND o.status != 'cancelled';\n```\n\n### 拉链表（全量历史快照）\n```sql\n-- 场景：记录每天的用户状态快照，支持任意时间点查询\n-- 比 SCD Type 2 更简单，适合数仓\n\n-- 每天生成当天快照\nINSERT INTO dws_user_snapshot PARTITION (dt = '2024-01-15')\nSELECT\n    user_id,\n    username,\n    status,\n    vip_level,\n    total_orders,\n    total_amount,\n    '2024-01-15' AS dt\nFROM (\n    -- 昨天快照 + 今天变更 = 今天快照\n    SELECT * FROM dws_user_snapshot WHERE dt = '2024-01-14'\n    UNION ALL\n    SELECT user_id, username, status, vip_level, ...\n    FROM ods_user_changes WHERE dt = '2024-01-15'\n) t\n-- 如果同一用户有多条，取最新的\nQUALIFY ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY dt DESC) = 1;\n```\n\n### 漏斗分析\n```sql\n-- 场景：分析用户从浏览 → 加购 → 下单 → 支付的转化漏斗\nWITH funnel AS (\n    SELECT\n        user_id,\n        MAX(CASE WHEN event = 'view'    THEN 1 ELSE 0 END) AS step1_view,\n        MAX(CASE WHEN event = 'add_cart' THEN 1 ELSE 0 END) AS step2_cart,\n        MAX(CASE WHEN event = 'order'   THEN 1 ELSE 0 END) AS step3_order,\n        MAX(CASE WHEN event = 'pay'     THEN 1 ELSE 0 END) AS step4_pay\n    FROM user_events\n    WHERE dt = '2024-01-15'\n    GROUP BY user_id\n)\nSELECT\n    SUM(step1_view)                                     AS view_cnt,\n    SUM(step2_cart)                                     AS cart_cnt,\n    SUM(step3_order)                                    AS order_cnt,\n    SUM(step4_pay)                                      AS pay_cnt,\n    ROUND(SUM(step2_cart) / SUM(step1_view) * 100, 2)  AS view_to_cart_rate,\n    ROUND(SUM(step3_order) / SUM(step2_cart) * 100, 2) AS cart_to_order_rate,\n    ROUND(SUM(step4_pay) / SUM(step3_order) * 100, 2)  AS order_to_pay_rate\nFROM funnel;\n```\n\n### 留存分析\n```sql\n-- 场景：计算用户次日留存、7日留存、30日留存\nWITH new_users AS (\n    -- 每天新注册用户\n    SELECT user_id, DATE(register_time) AS register_date\n    FROM users\n    WHERE register_time >= '2024-01-01'\n),\nactive_users AS (\n    -- 每天活跃用户\n    SELECT DISTINCT user_id, DATE(login_time) AS active_date\n    FROM user_logins\n)\nSELECT\n    n.register_date,\n    COUNT(DISTINCT n.user_id)                                           AS new_user_cnt,\n    COUNT(DISTINCT CASE WHEN DATEDIFF(a1.active_date, n.register_date) = 1  THEN n.user_id END) AS day1_retain,\n    COUNT(DISTINCT CASE WHEN DATEDIFF(a7.active_date, n.register_date) = 7  THEN n.user_id END) AS day7_retain,\n    COUNT(DISTINCT CASE WHEN DATEDIFF(a30.active_date, n.register_date) = 30 THEN n.user_id END) AS day30_retain\nFROM new_users n\nLEFT JOIN active_users a1  ON n.user_id = a1.user_id AND DATEDIFF(a1.active_date, n.register_date) = 1\nLEFT JOIN active_users a7  ON n.user_id = a7.user_id AND DATEDIFF(a7.active_date, n.register_date) = 7\nLEFT JOIN active_users a30 ON n.user_id = a30.user_id AND DATEDIFF(a30.active_date, n.register_date) = 30\nGROUP BY n.register_date\nORDER BY n.register_date;\n```\n\n---\n\n## 数据倾斜处理（Hive/Spark）\n\n```sql\n-- 症状：某些 Reduce 任务跑很久，其他早就完成了\n-- 原因：JOIN 或 GROUP BY 的 key 分布不均\n\n-- 方案 1：广播小表（Map Join）\nSELECT /*+ MAPJOIN(dim_user) */ o.*, u.username\nFROM dwd_orders o\nJOIN dim_user u ON o.user_id = u.user_id;\n\n-- 方案 2：加盐打散（处理 GROUP BY 倾斜）\n-- 第一步：加随机盐，局部聚合\nSELECT\n    CONCAT(user_id, '_', FLOOR(RAND() * 10)) AS salted_key,\n    SUM(amount) AS partial_sum\nFROM orders\nGROUP BY CONCAT(user_id, '_', FLOOR(RAND() * 10));\n\n-- 第二步：去盐，全局聚合\nSELECT\n    SPLIT(salted_key, '_')[0] AS user_id,\n    SUM(partial_sum) AS total_amount\nFROM (上面的结果)\nGROUP BY SPLIT(salted_key, '_')[0];\n\n-- 方案 3：Hive 配置（自动处理倾斜）\nSET hive.optimize.skewjoin = true;\nSET hive.skewjoin.key = 100000;  -- 超过此行数认为倾斜\n```\n\nFile v1.0.1:references/ddl-design.md\n\n# DDL 设计规范\n\n## 表设计原则\n\n### 命名规范\n```\n表名：小写 + 下划线，加业务前缀\n  ods_orders          原始订单表\n  dwd_order_detail    订单明细事实表\n  dim_user            用户维度表\n  dws_user_daily      用户日汇总表\n  ads_funnel_report   漏斗报表\n\n列名：小写 + 下划线，语义清晰\n  user_id（不用 uid）\n  create_time（不用 ctime）\n  is_deleted（布尔用 is_ 前缀）\n  order_status（不用 status，加业务前缀）\n```\n\n### 字段类型选择\n\n```sql\n-- 整数：按范围选最小类型（节省存储，提升缓存命中）\nTINYINT     -- 1字节，-128~127，适合状态码、等级\nSMALLINT    -- 2字节，适合年份、小范围数值\nINT         -- 4字节，适合普通 ID（<21亿）\nBIGINT      -- 8字节，适合雪花ID、大流水号\n\n-- 字符串\nCHAR(n)     -- 定长，适合固定长度（手机号、身份证）\nVARCHAR(n)  -- 变长，适合普通文本，n 不要设太大（影响内存分配）\nTEXT        -- 大文本，不能建普通索引，不能作为主键\n\n-- 金额：绝对不用 FLOAT/DOUBLE（精度丢失）\nDECIMAL(10,2)   -- 精确小数，10位总长，2位小数\n-- 或者存分（整数），避免小数运算\nBIGINT          -- 单位：分，1元 = 100\n\n-- 时间\nDATETIME        -- MySQL，不含时区，'2024-01-15 10:30:00'\nTIMESTAMP       -- MySQL，含时区转换，范围到2038年（慎用）\nTIMESTAMPTZ     -- PostgreSQL，推荐，含时区\n\n-- 布尔\nTINYINT(1)      -- MySQL（没有原生 BOOLEAN）\nBOOLEAN         -- PostgreSQL\n```\n\n---\n\n## 生产级建表模板\n\n### 业务表（MySQL）\n```sql\nCREATE TABLE `orders` (\n    `id`            BIGINT          NOT NULL AUTO_INCREMENT  COMMENT '主键ID',\n    `order_no`      VARCHAR(32)     NOT NULL                 COMMENT '订单号（业务唯一标识）',\n    `user_id`       BIGINT          NOT NULL                 COMMENT '用户ID',\n    `product_id`    BIGINT          NOT NULL                 COMMENT '商品ID',\n    `quantity`      INT             NOT NULL DEFAULT 1       COMMENT '购买数量',\n    `unit_price`    DECIMAL(10,2)   NOT NULL                 COMMENT '单价（元）',\n    `discount`      DECIMAL(10,2)   NOT NULL DEFAULT 0.00    COMMENT '优惠金额（元）',\n    `actual_amount` DECIMAL(10,2)   NOT NULL                 COMMENT '实付金额（元）',\n    `status`        TINYINT         NOT NULL DEFAULT 0       COMMENT '订单状态：0待支付 1已支付 2已发货 3已完成 4已取消',\n    `remark`        VARCHAR(500)    DEFAULT NULL             COMMENT '备注',\n    `is_deleted`    TINYINT(1)      NOT NULL DEFAULT 0       COMMENT '软删除：0正常 1已删除',\n    `create_time`   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP  COMMENT '创建时间',\n    `update_time`   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `uk_order_no` (`order_no`),\n    KEY `idx_user_id_status` (`user_id`, `status`),\n    KEY `idx_create_time` (`create_time`)\n) ENGINE=InnoDB\n  DEFAULT CHARSET=utf8mb4\n  COLLATE=utf8mb4_unicode_ci\n  COMMENT='订单表';\n```\n\n### 日志/流水表（大数据量）\n```sql\n-- 大流水表：分区 + 不建过多索引\nCREATE TABLE `user_behavior_logs` (\n    `id`            BIGINT          NOT NULL AUTO_INCREMENT  COMMENT '主键',\n    `user_id`       BIGINT          NOT NULL                 COMMENT '用户ID',\n    `event_type`    VARCHAR(50)     NOT NULL                 COMMENT '事件类型',\n    `event_data`    JSON            DEFAULT NULL             COMMENT '事件数据',\n    `ip`            VARCHAR(45)     DEFAULT NULL             COMMENT 'IP地址（支持IPv6）',\n    `user_agent`    VARCHAR(500)    DEFAULT NULL             COMMENT 'UA',\n    `create_time`   DATETIME(3)     NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间（毫秒精度）',\n    PRIMARY KEY (`id`, `create_time`),   -- 分区键必须在主键中\n    KEY `idx_user_event` (`user_id`, `event_type`, `create_time`)\n) ENGINE=InnoDB\n  DEFAULT CHARSET=utf8mb4\n  COMMENT='用户行为日志'\n  PARTITION BY RANGE (TO_DAYS(create_time)) (\n    PARTITION p202401 VALUES LESS THAN (TO_DAYS('2024-02-01')),\n    PARTITION p202402 VALUES LESS THAN (TO_DAYS('2024-03-01')),\n    PARTITION p_future VALUES LESS THAN MAXVALUE\n  );\n```\n\n### 配置/字典表\n```sql\nCREATE TABLE `sys_config` (\n    `id`            INT             NOT NULL AUTO_INCREMENT  COMMENT '主键',\n    `config_key`    VARCHAR(100)    NOT NULL                 COMMENT '配置键',\n    `config_value`  TEXT            NOT NULL                 COMMENT '配置值',\n    `config_type`   VARCHAR(20)     NOT NULL DEFAULT 'string' COMMENT '值类型：string/int/json/boolean',\n    `description`   VARCHAR(500)    DEFAULT NULL             COMMENT '说明',\n    `is_enabled`    TINYINT(1)      NOT NULL DEFAULT 1       COMMENT '是否启用',\n    `create_time`   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    `update_time`   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `uk_config_key` (`config_key`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统配置表';\n```\n\n---\n\n## 在线 DDL 变更（生产安全）\n\n### MySQL 在线 DDL\n```sql\n-- 查看 DDL 是否支持 INPLACE（不锁表）\n-- Algorithm=INPLACE：不重建表，不锁表（最好）\n-- Algorithm=COPY：重建表，锁表（最差）\n\n-- 加列（MySQL 8.0 支持 INSTANT，瞬间完成）\nALTER TABLE orders\n    ADD COLUMN source VARCHAR(20) DEFAULT NULL COMMENT '来源渠道'\n    AFTER status,\n    ALGORITHM=INSTANT;  -- MySQL 8.0+\n\n-- 加索引（INPLACE，不锁表）\nALTER TABLE orders\n    ADD INDEX idx_product_id (product_id),\n    ALGORITHM=INPLACE,\n    LOCK=NONE;\n\n-- 修改列类型（通常需要 COPY，会锁表，用 pt-osc 代替）\n-- 生产环境用 pt-online-schema-change 或 gh-ost\n-- pt-osc: pt-online-schema-change --alter \"MODIFY COLUMN amount DECIMAL(12,2)\" D=db,t=orders\n\n-- 删除列（INPLACE）\nALTER TABLE orders\n    DROP COLUMN old_column,\n    ALGORITHM=INPLACE,\n    LOCK=NONE;\n```\n\n### 字符集迁移（utf8 → utf8mb4）\n```sql\n-- MySQL 的 utf8 实际上是 utf8mb3（不支持 emoji）\n-- 生产迁移步骤：\n\n-- 1. 修改数据库默认字符集\nALTER DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n\n-- 2. 修改表（在线 DDL）\nALTER TABLE orders\n    CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,\n    ALGORITHM=INPLACE,\n    LOCK=NONE;\n\n-- 3. 修改连接字符集\nSET NAMES utf8mb4;\n-- 或在连接字符串中：charset=utf8mb4\n```\n\n---\n\n## 常见设计反模式\n\n```sql\n-- ❌ 反模式 1：用字符串存枚举值（无约束，难查询）\nstatus VARCHAR(20)  -- 'active', 'inactive', 'pending'...\n-- ✅ 用 TINYINT + 注释说明，或 ENUM（但 ENUM 修改成本高）\nstatus TINYINT COMMENT '1=active 2=inactive 3=pending'\n\n-- ❌ 反模式 2：用逗号分隔存多值\ntags VARCHAR(500)  -- '1,2,3,4'\n-- ✅ 单独建关联表\nCREATE TABLE user_tags (user_id BIGINT, tag_id INT, PRIMARY KEY(user_id, tag_id));\n\n-- ❌ 反模式 3：用 NULL 表示业务含义\ndiscount DECIMAL(10,2)  -- NULL 表示\"无折扣\"\n-- ✅ 用默认值 0，NULL 只表示\"未知/未填写\"\ndiscount DECIMAL(10,2) NOT NULL DEFAULT 0.00\n\n-- ❌ 反模式 4：主键用 UUID 字符串\nid VARCHAR(36) PRIMARY KEY  -- 'a1b2c3d4-...'\n-- ✅ 用 BIGINT 自增，或 BIGINT 雪花ID\nid BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY\n\n-- ❌ 反模式 5：没有 create_time / update_time\n-- ✅ 所有业务表必须有这两个字段，便于排查问题和增量同步\n```\n\nFile v1.0.1:references/dialect-guide.md\n\n# 多方言差异速查\n\n## 方言对比矩阵\n\n| 功能 | MySQL | PostgreSQL | Hive | Spark SQL | ClickHouse |\n|------|-------|-----------|------|-----------|------------|\n| 字符串拼接 | `CONCAT(a,b)` | `a \\|\\| b` | `CONCAT(a,b)` | `CONCAT(a,b)` | `concat(a,b)` |\n| 日期格式化 | `DATE_FORMAT(d,'%Y-%m')` | `TO_CHAR(d,'YYYY-MM')` | `DATE_FORMAT(d,'yyyy-MM')` | `DATE_FORMAT(d,'yyyy-MM')` | `formatDateTime(d,'%Y-%m')` |\n| 当前时间 | `NOW()` | `NOW()` | `CURRENT_TIMESTAMP` | `CURRENT_TIMESTAMP` | `now()` |\n| 日期差 | `DATEDIFF(a,b)` | `a - b` | `DATEDIFF(a,b)` | `DATEDIFF(a,b)` | `dateDiff('day',b,a)` |\n| 字符串截取 | `SUBSTRING(s,1,3)` | `SUBSTRING(s,1,3)` | `SUBSTR(s,1,3)` | `SUBSTR(s,1,3)` | `substring(s,1,3)` |\n| 条件表达式 | `IF(cond,a,b)` | `CASE WHEN` | `IF(cond,a,b)` | `IF(cond,a,b)` | `if(cond,a,b)` |\n| 行号 | `ROW_NUMBER()` | `ROW_NUMBER()` | `ROW_NUMBER()` | `ROW_NUMBER()` | `row_number()` |\n| UPSERT | `ON DUPLICATE KEY` | `ON CONFLICT DO UPDATE` | 不支持 | 不支持 | `INSERT OR REPLACE` |\n| 递归 CTE | 8.0+ 支持 | 支持 | 不支持 | 支持 | 支持 |\n| JSON 支持 | 5.7+ | 原生 JSONB | 有限 | 有限 | 有限 |\n| 窗口函数 | 8.0+ | 完整支持 | 完整支持 | 完整支持 | 完整支持 |\n\n---\n\n## MySQL 特有语法\n\n```sql\n-- LIMIT 语法\nSELECT * FROM t LIMIT 10;           -- 前10行\nSELECT * FROM t LIMIT 10, 20;       -- 跳过10行，取20行（注意：偏移量在前）\nSELECT * FROM t LIMIT 20 OFFSET 10; -- 等价写法\n\n-- GROUP_CONCAT（行转列）\nSELECT user_id, GROUP_CONCAT(tag ORDER BY tag SEPARATOR ',') AS tags\nFROM user_tags GROUP BY user_id;\n\n-- ON DUPLICATE KEY UPDATE\nINSERT INTO counters (key, cnt) VALUES ('pv', 1)\nON DUPLICATE KEY UPDATE cnt = cnt + 1;\n\n-- 日期函数\nSELECT\n    DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'),  -- 格式化\n    DATE_ADD(NOW(), INTERVAL 7 DAY),           -- 加7天\n    DATE_SUB(NOW(), INTERVAL 1 MONTH),         -- 减1月\n    LAST_DAY(NOW()),                           -- 当月最后一天\n    WEEKDAY(NOW()),                            -- 星期几（0=周一）\n    QUARTER(NOW());                            -- 季度\n\n-- 字符串函数\nSELECT\n    FIND_IN_SET('b', 'a,b,c'),    -- 返回 2（位置）\n    FIELD('b', 'a', 'b', 'c'),    -- 返回 2（位置）\n    ELT(2, 'a', 'b', 'c');        -- 返回 'b'（按位置取值）\n```\n\n---\n\n## PostgreSQL 特有语法\n\n```sql\n-- RETURNING（返回被修改的行）\nINSERT INTO users (name) VALUES ('张三') RETURNING id, name;\nUPDATE orders SET status = 'paid' WHERE id = 1 RETURNING *;\nDELETE FROM logs WHERE id = 1 RETURNING *;\n\n-- 数组操作\nSELECT ARRAY[1,2,3];\nSELECT '{1,2,3}'::INT[];\nSELECT array_agg(id ORDER BY id) FROM users;  -- 聚合为数组\nSELECT unnest(ARRAY[1,2,3]);                  -- 展开数组为行\n\n-- JSONB 操作\nSELECT data->'name' AS name FROM users;           -- 取 JSON 字段（返回 JSON）\nSELECT data->>'name' AS name FROM users;          -- 取 JSON 字段（返回文本）\nSELECT data#>>'{address,city}' AS city FROM users; -- 嵌套路径\nUPDATE users SET data = data || '{\"vip\":true}';   -- 合并 JSON\nCREATE INDEX idx_json ON users USING GIN(data);   -- JSON 索引\n\n-- 窗口函数扩展\nSELECT\n    NTILE(4) OVER (ORDER BY amount) AS quartile,  -- 四分位\n    PERCENT_RANK() OVER (ORDER BY amount),         -- 百分比排名\n    CUME_DIST() OVER (ORDER BY amount)             -- 累积分布\nFROM orders;\n\n-- 物化视图\nCREATE MATERIALIZED VIEW mv_daily_stats AS\nSELECT DATE(create_time), COUNT(*), SUM(amount)\nFROM orders GROUP BY DATE(create_time);\n\nREFRESH MATERIALIZED VIEW CONCURRENTLY mv_daily_stats;  -- 不锁表刷新\n\n-- 分区表（声明式分区）\nCREATE TABLE orders (\n    id BIGSERIAL,\n    create_time TIMESTAMPTZ NOT NULL,\n    amount NUMERIC\n) PARTITION BY RANGE (create_time);\n\nCREATE TABLE orders_2024_01 PARTITION OF orders\nFOR VALUES FROM ('2024-01-01') TO ('2024-02-01');\n```\n\n---\n\n## Hive / Spark SQL 特有语法\n\n```sql\n-- 分区操作\nSHOW PARTITIONS table_name;\nALTER TABLE t ADD PARTITION (dt='2024-01-15');\nALTER TABLE t DROP PARTITION (dt='2024-01-01');\n\n-- 动态分区插入\nSET hive.exec.dynamic.partition = true;\nSET hive.exec.dynamic.partition.mode = nonstrict;\n\nINSERT OVERWRITE TABLE dwd_orders PARTITION (dt)\nSELECT *, DATE(create_time) AS dt FROM ods_orders;\n\n-- LATERAL VIEW（展开数组/Map）\nSELECT user_id, tag\nFROM user_tags\nLATERAL VIEW EXPLODE(tags_array) tmp AS tag;\n\n-- LATERAL VIEW OUTER（保留空数组的行）\nSELECT user_id, tag\nFROM user_tags\nLATERAL VIEW OUTER EXPLODE(tags_array) tmp AS tag;\n\n-- collect_set / collect_list（聚合为数组）\nSELECT user_id,\n    collect_set(tag)  AS unique_tags,   -- 去重\n    collect_list(tag) AS all_tags       -- 不去重\nFROM user_tags GROUP BY user_id;\n\n-- 窗口函数（Hive 特有）\nSELECT *,\n    FIRST_VALUE(amount) OVER (PARTITION BY user_id ORDER BY create_time) AS first_order_amount,\n    LAST_VALUE(amount)  OVER (PARTITION BY user_id ORDER BY create_time\n                              ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS last_order_amount\nFROM orders;\n\n-- Hive 性能配置\nSET hive.vectorized.execution.enabled = true;   -- 向量化执行\nSET hive.cbo.enable = true;                     -- 开启 CBO\nSET mapreduce.job.reduces = 200;                -- 设置 Reduce 数量\nSET hive.exec.parallel = true;                  -- 并行执行无依赖 Stage\n```\n\n---\n\n## ClickHouse 特有语法\n\n```sql\n-- 引擎选择（最重要的设计决策）\n-- MergeTree：最常用，支持排序键、分区\nCREATE TABLE orders (\n    order_id    UInt64,\n    user_id     UInt32,\n    amount      Float64,\n    create_time DateTime\n) ENGINE = MergeTree()\nPARTITION BY toYYYYMM(create_time)   -- 按月分区\nORDER BY (user_id, create_time)       -- 排序键（也是稀疏索引）\nTTL create_time + INTERVAL 1 YEAR;   -- 数据过期自动删除\n\n-- ReplacingMergeTree：去重（异步，不保证实时）\nENGINE = ReplacingMergeTree(version_col)\n\n-- AggregatingMergeTree：预聚合\nENGINE = AggregatingMergeTree()\n\n-- 物化视图（实时预聚合）\nCREATE MATERIALIZED VIEW mv_daily_orders\nENGINE = SummingMergeTree()\nPARTITION BY toYYYYMM(order_date)\nORDER BY (order_date, user_id)\nAS SELECT\n    toDate(create_time) AS order_date,\n    user_id,\n    count() AS order_cnt,\n    sum(amount) AS total_amount\nFROM orders\nGROUP BY order_date, user_id;\n\n-- 数组函数\nSELECT arrayJoin([1,2,3]);                    -- 展开数组\nSELECT groupArray(amount) FROM orders;         -- 聚合为数组\nSELECT arraySum([1,2,3]);                      -- 数组求和\nSELECT arrayFilter(x -> x > 10, [5,15,20]);   -- 数组过滤\n\n-- 近似计算（大数据量下极快）\nSELECT uniq(user_id) FROM orders;              -- 近似去重计数\nSELECT quantile(0.99)(amount) FROM orders;     -- 近似分位数\nSELECT topK(10)(product_id) FROM orders;       -- 近似 Top K\n```\n\nFile v1.0.1:references/hive-skew-advanced.md\n\n# Hive 数据倾斜：大厂生产级深化补充\n\n## 目录\n1. [执行引擎底层原理（MR vs Tez vs Spark）](#1-执行引擎底层原理)\n2. [倾斜量化评估模型](#2-倾斜量化评估模型)\n3. [极端场景与边界案例](#3-极端场景与边界案例)\n4. [动态智能倾斜检测](#4-动态智能倾斜检测)\n5. [与上下游生态联动](#5-与上下游生态联动)\n\n---\n\n## 1. 执行引擎底层原理\n\n### 1.1 三种引擎的倾斜发生位置\n\n#### MapReduce 引擎\n```\nMap Task（并行）\n    ↓ Shuffle（按 key hash 分发）← 倾斜在这里产生\nReduce Task（并行）\n    ↓\n输出\n\n倾斜根因：\n  hash(key) % numReducers 决定数据去哪个 Reducer\n  user_id=1 的 400 万行全部 hash 到同一个 Reducer\n  该 Reducer 处理 400 万行，其他 Reducer 处理几万行\n  → 整个 Job 等最慢的那个 Reducer 完成\n```\n\n#### Tez 引擎（DAG 模型）\n```\nTez 把 MR Job 拆成 DAG（有向无环图），每个节点叫 Vertex\n\n典型 GROUP BY 的 DAG：\n  Map Vertex（读数据 + 局部聚合）\n      ↓ Shuffle Edge（SCATTER_GATHER，按 key 分发）← 倾斜在这里\n  Reduce Vertex（全局聚合）\n      ↓\n  Output Vertex\n\n典型 JOIN 的 DAG：\n  Map Vertex A（扫描大表）\n  Map Vertex B（扫描小表）\n      ↓ Broadcast Edge（小表广播）← MapJoin 走这条边，无倾斜\n  Map Join Vertex（在 Map 端完成 JOIN）\n\n关键差异：\n  Tez 的 SCATTER_GATHER Edge 支持 Auto Parallelism\n  → 运行时动态调整 Reduce Vertex 的并发度\n  → 比 MR 的静态 numReducers 更灵活\n\n查看 Tez DAG：\n  Tez UI（http://<rm>:8080/tez-ui）→ DAG Details → Vertex 耗时对比\n  耗时最长的 Vertex 就是倾斜所在\n```\n\n```sql\n-- 查看 Tez 执行计划（比 EXPLAIN 更详细）\nEXPLAIN FORMATTED\nSELECT user_id, COUNT(*), SUM(amount)\nFROM ods_orders\nGROUP BY user_id;\n-- 输出中找 \"Reduce Operator Tree\" 和 \"Statistics\"\n-- Statistics: Num rows: X Data size: Y → 估算各 Vertex 数据量\n```\n\n#### Spark 引擎（Hive on Spark）\n```\nSpark 把 Job 拆成 Stage，Stage 之间有 Shuffle\n\n典型 GROUP BY：\n  Stage 0：Map（读数据）\n      ↓ Shuffle Write（按 key 分区写到磁盘）← 倾斜在这里\n  Stage 1：Reduce（聚合）\n      ↓\n  Stage 2：输出\n\nSpark 特有的倾斜表现：\n  Spark UI → Stages → 某个 Stage 的 Task 列表\n  → 看 \"Duration\" 列：大多数 Task 几秒，某个 Task 几十分钟\n  → 看 \"Shuffle Read Size\"：某个 Task 读了几 GB，其他只读几 MB\n\nSpark 特有优化（Hive on Spark 可用）：\n  spark.sql.adaptive.enabled = true          -- AQE（自适应查询执行）\n  spark.sql.adaptive.skewJoin.enabled = true -- 自动倾斜 Join 处理\n  spark.sql.adaptive.skewJoin.skewedPartitionFactor = 5   -- 超过中位数5倍认为倾斜\n  spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes = 256MB\n```\n\n### 1.2 DYNAMIC_PARTITION_PRUNING 与倾斜的关系\n\n```sql\n-- DPP（动态分区裁剪）：在 JOIN 时，用小表的过滤条件裁剪大表的分区\n-- 这本身不解决倾斜，但能大幅减少参与 JOIN 的数据量，间接缓解倾斜\n\n-- 示例：查询某城市用户的订单\n-- 没有 DPP：扫描全部 orders 分区（10 亿行）\n-- 有 DPP：先扫描 dim_users 找到北京用户的 user_id，\n--          再只扫描这些 user_id 对应的 orders 分区\n\nSET hive.tez.dynamic.partition.pruning = true;\nSET hive.tez.dynamic.partition.pruning.max.data.size = 104857600; -- 100MB\n\nEXPLAIN\nSELECT o.order_id, o.amount\nFROM ods_orders o\nJOIN dim_users u ON o.user_id = u.user_id\nWHERE u.city = '北京';\n-- 执行计划中应出现：Dynamic Partitioning Event Operator\n-- 效果：大幅减少 Map 阶段读取的数据量，倾斜 key 的绝对数据量也随之减少\n```\n\n---\n\n## 2. 倾斜量化评估模型\n\n### 2.1 倾斜程度量化指标\n\n```python\n# skew_analyzer.py\n# 量化分析倾斜程度，给出方案选型建议\n# 运行：python skew_analyzer.py\n\nimport math\n\ndef analyze_skew(key_distribution: dict, total_rows: int, num_reducers: int = 200):\n    \"\"\"\n    key_distribution: {key: row_count}\n    total_rows: 总行数\n    num_reducers: Reduce 并发度\n    \"\"\"\n    counts = sorted(key_distribution.values(), reverse=True)\n    n = len(counts)\n\n    # 指标1：最大 key 占比\n    max_key_pct = counts[0] / total_rows * 100\n\n    # 指标2：基尼系数（衡量整体不均匀程度，0=完全均匀，1=极度倾斜）\n    sorted_counts = sorted(counts)\n    cumsum = 0\n    gini_sum = 0\n    for i, c in enumerate(sorted_counts):\n        cumsum += c\n        gini_sum += cumsum\n    gini = 1 - 2 * gini_sum / (n * sum(counts)) + 1/n\n\n    # 指标3：理想执行时间 vs 实际执行时间比（倾斜放大系数）\n    ideal_rows_per_reducer = total_rows / num_reducers\n    max_rows_per_reducer = counts[0]  # 最坏情况：最大 key 独占一个 Reducer\n    skew_factor = max_rows_per_reducer / ideal_rows_per_reducer\n\n    # 指标4：Top-N key 集中度\n    top1_pct  = counts[0] / total_rows * 100\n    top5_pct  = sum(counts[:5]) / total_rows * 100\n    top10_pct = sum(counts[:10]) / total_rows * 100\n\n    print(\"=\" * 60)\n    print(\"倾斜分析报告\")\n    print(\"=\" * 60)\n    print(f\"总行数：{total_rows:,}\")\n    print(f\"唯一 Key 数：{n:,}\")\n    print(f\"Reduce 并发度：{num_reducers}\")\n    print()\n    print(f\"【核心指标】\")\n    print(f\"  最大 Key 占比：{max_key_pct:.2f}%\")\n    print(f\"  基尼系数：{gini:.4f}  （0=均匀，1=极度倾斜）\")\n    print(f\"  倾斜放大系数：{skew_factor:.1f}x  （理想耗时的 {skew_factor:.1f} 倍）\")\n    print(f\"  Top-1/5/10 集中度：{top1_pct:.1f}% / {top5_pct:.1f}% / {top10_pct:.1f}%\")\n    print()\n\n    # 方案选型建议\n    print(\"【方案选型建议】\")\n    if max_key_pct > 30:\n        print(\"  ⚠️  极度倾斜（最大 Key > 30%）\")\n        print(\"  → 首选：方案三（热点 Key 分离）+ 方案一（MapJoin）\")\n        print(\"  → 备选：方案二（加盐打散，盐值建议 >= 20）\")\n    elif max_key_pct > 10:\n        print(\"  ⚠️  严重倾斜（最大 Key 10%~30%）\")\n        print(\"  → 首选：方案二（加盐打散，盐值建议 10）\")\n        print(\"  → 备选：方案四（自动优化）\")\n    elif max_key_pct > 3:\n        print(\"  ⚡ 中度倾斜（最大 Key 3%~10%）\")\n        print(\"  → 首选：方案四（自动优化，调低 skewjoin.key 阈值）\")\n        print(\"  → 备选：方案二（加盐打散，盐值 5 即可）\")\n    else:\n        print(\"  ✅ 轻度倾斜（最大 Key < 3%）\")\n        print(\"  → 方案四（自动优化）通常足够\")\n        print(\"  → 优先检查是否有 NULL 值倾斜（方案五）\")\n\n    print()\n    print(\"【盐值推荐计算】\")\n    recommended_salt = max(2, math.ceil(skew_factor / 10))\n    recommended_salt = min(recommended_salt, 100)  # 盐值过大会增加 stage2 压力\n    print(f\"  推荐盐值：{recommended_salt}\")\n    print(f\"  加盐后最大 Key 预估占比：{max_key_pct / recommended_salt:.2f}%\")\n\n    return {\n        \"max_key_pct\": max_key_pct,\n        \"gini\": gini,\n        \"skew_factor\": skew_factor,\n        \"recommended_salt\": recommended_salt\n    }\n\n\n# 模拟我们的数据分布\nif __name__ == \"__main__\":\n    import random\n    random.seed(42)\n\n    # 模拟 1000 万行的 key 分布\n    total = 10_000_000\n    dist = {\n        1: int(total * 0.40),   # user_id=1，40%\n        2: int(total * 0.20),   # user_id=2，20%\n    }\n    # user_id 3~100，各约 0.3%\n    for uid in range(3, 101):\n        dist[uid] = int(total * 0.30 / 98)\n    # user_id 101~10000，长尾均匀\n    for uid in range(101, 10001):\n        dist[uid] = int(total * 0.10 / 9900)\n\n    result = analyze_skew(dist, total, num_reducers=200)\n```\n\n```\n# 预期输出：\n# ============================================================\n# 倾斜分析报告\n# ============================================================\n# 总行数：10,000,000\n# 唯一 Key 数：10,000\n# Reduce 并发度：200\n#\n# 【核心指标】\n#   最大 Key 占比：40.00%\n#   基尼系数：0.8923  （0=均匀，1=极度倾斜）\n#   倾斜放大系数：800.0x  （理想耗时的 800.0 倍）\n#   Top-1/5/10 集中度：40.0% / 61.2% / 62.4%\n#\n# 【方案选型建议】\n#   ⚠️  极度倾斜（最大 Key > 30%）\n#   → 首选：方案三（热点 Key 分离）+ 方案一（MapJoin）\n#   → 备选：方案二（加盐打散，盐值建议 >= 20）\n#\n# 【盐值推荐计算】\n#   推荐盐值：80\n#   加盐后最大 Key 预估占比：0.50%\n```\n\n### 2.2 方案收益量化公式\n\n```\n设：\n  T_ideal  = 理想执行时间（无倾斜，数据均匀分布）\n  T_actual = 实际执行时间（有倾斜）\n  R        = Reduce 并发度\n  N_max    = 最大 key 的行数\n  N_total  = 总行数\n\n倾斜放大系数 S = N_max / (N_total / R)\n\nT_actual ≈ T_ideal × S\n\n各方案理论收益：\n  方案一（MapJoin）：消除 Reduce 阶段\n    T_mapjoin ≈ T_map（通常是 T_ideal 的 0.3~0.5 倍）\n\n  方案二（加盐，盐值=K）：\n    新的最大 key 行数 ≈ N_max / K\n    新的倾斜放大系数 S' = (N_max/K) / (N_total/R) = S/K\n    T_salted ≈ T_ideal × (S/K) + T_stage2（stage2 很小，可忽略）\n    → 盐值 K 越大，收益越大，但 K > S 后收益趋于平稳\n\n  方案三（热点分离）：\n    热点部分用 MapJoin，非热点正常跑\n    T_hot    ≈ T_map（MapJoin）\n    T_normal ≈ T_ideal（非热点数据均匀）\n    T_total  ≈ max(T_hot, T_normal) ≈ T_ideal\n```\n\n---\n\n## 3. 极端场景与边界案例\n\n### 3.1 所有 Key 都倾斜（数据本身极度不均匀）\n\n```sql\n-- 场景：电商平台，商品类目只有 10 个，每个类目数据量差异巨大\n-- 家电：50%，服装：30%，食品：10%，其他7类：各1%\n-- GROUP BY category 时，10 个 Reducer 负载极不均衡\n\n-- 分析：这种情况加盐也没用（因为每个 key 都是\"热点\"）\n-- 根本解法：重新设计聚合粒度\n\n-- ❌ 错误思路：继续在 Hive 里硬怼\nSELECT category, COUNT(*), SUM(amount) FROM orders GROUP BY category;\n\n-- ✅ 方案 A：预聚合下沉（在上游 Spark Streaming 实时预聚合）\n-- 上游每分钟写入一条聚合结果，Hive 只需 SUM 少量预聚合数据\nSELECT category, SUM(order_cnt), SUM(total_amount)\nFROM dws_category_minute_agg   -- 每分钟一条，而非原始明细\nGROUP BY category;\n\n-- ✅ 方案 B：强制指定 Reduce 数量为 key 数量\nSET mapreduce.job.reduces = 10;  -- 正好等于 category 数量\n-- 每个 Reducer 处理一个 category，虽然不均衡，但至少不会有空闲 Reducer\n\n-- ✅ 方案 C：分桶表（Bucketing）\n-- 建表时按 category 分桶，数据预先分好，JOIN 时直接 Bucket Map Join\nCREATE TABLE orders_bucketed (\n    order_id BIGINT,\n    category STRING,\n    amount   DOUBLE\n)\nCLUSTERED BY (category) INTO 10 BUCKETS\nSTORED AS ORC;\n\nSET hive.optimize.bucketmapjoin = true;\nSET hive.optimize.bucketmapjoin.sortedmerge = true;\n```\n\n### 3.2 热点 Key 数量很多（几百个）\n\n```sql\n-- 场景：热点 key 不是 1~2 个，而是 500 个\n-- 方案三（手动分离）不再适用，需要动态化\n\n-- ✅ 动态热点分离（自动化版本）\n\n-- Step 1：动态计算热点阈值（基于统计信息）\nCREATE TABLE tmp_key_stats AS\nSELECT\n    user_id,\n    cnt,\n    -- 计算该 key 是否为热点（超过平均值的 N 倍）\n    cnt > AVG(cnt) OVER() * 10  AS is_hot,\n    -- 推荐盐值（基于倾斜程度动态计算）\n    GREATEST(1, CAST(cnt / (SUM(cnt) OVER() / 200) / 10 AS INT)) AS salt_n\nFROM (\n    SELECT user_id, COUNT(*) AS cnt\n    FROM ods_orders\n    GROUP BY user_id\n) t;\n\n-- Step 2：热点 key 加动态盐（盐值因 key 而异）\nCREATE TABLE tmp_orders_salted AS\nSELECT\n    o.order_id,\n    o.user_id,\n    o.amount,\n    o.status,\n    -- 热点 key 加盐，非热点 key 盐值固定为 0\n    CASE\n        WHEN k.is_hot THEN CAST(FLOOR(RAND() * k.salt_n) AS INT)\n        ELSE 0\n    END AS salt\nFROM ods_orders o\nLEFT JOIN tmp_key_stats k ON o.user_id = k.user_id;\n\n-- Step 3：局部聚合\nCREATE TABLE tmp_stage1 AS\nSELECT user_id, salt, COUNT(*) AS cnt, SUM(amount) AS total\nFROM tmp_orders_salted\nGROUP BY user_id, salt;\n\n-- Step 4：全局聚合（去盐）\nSELECT user_id, SUM(cnt) AS order_cnt, SUM(total) AS total_amount\nFROM tmp_stage1\nGROUP BY user_id;\n```\n\n### 3.3 JOIN 两端都是大表且都倾斜\n\n```sql\n-- 场景：orders（10亿行，user_id 倾斜）JOIN user_actions（50亿行，user_id 同样倾斜）\n-- 两张表都是大表，MapJoin 不可用，两边都有热点\n\n-- ✅ 方案：双边加盐（Salted Join）\n\n-- 原理：\n--   大表 A 的 key 加随机盐 0~K-1\n--   大表 B 的 key 复制 K 份（分别加盐 0,1,2,...,K-1）\n--   JOIN 条件：(A.key, A.salt) = (B.key, B.salt)\n--   这样 A 的每行只和 B 的一份匹配，结果正确\n\nSET K = 10;  -- 盐值范围\n\n-- 大表 A：随机加盐\nCREATE TABLE tmp_orders_salted AS\nSELECT *, CAST(FLOOR(RAND() * 10) AS INT) AS salt\nFROM ods_orders;\n\n-- 大表 B：复制 K 份\nCREATE TABLE tmp_actions_replicated AS\nSELECT *, salt_val AS salt\nFROM user_actions\nLATERAL VIEW EXPLODE(ARRAY(0,1,2,3,4,5,6,7,8,9)) t AS salt_val;\n-- ⚠️ 注意：B 表数据量变为原来的 K 倍，K 不能太大（建议 5~20）\n\n-- JOIN\nSELECT a.user_id, COUNT(*) AS cnt, SUM(a.amount) AS total\nFROM tmp_orders_salted a\nJOIN tmp_actions_replicated b\n    ON a.user_id = b.user_id AND a.salt = b.salt\nGROUP BY a.user_id;\n\n-- 清理\nDROP TABLE tmp_orders_salted;\nDROP TABLE tmp_actions_replicated;\n```\n\n### 3.4 倾斜 + 数据量随时间变化（动态热点）\n\n```sql\n-- 场景：平时 user_id=1 是热点，大促期间 user_id=9999（某网红）突然变热点\n-- 静态配置的热点 key 列表会失效\n\n-- ✅ 方案：每次 ETL 前动态计算热点，写入配置表\n\n-- 每日 ETL 开始前执行（可由调度系统触发）\nINSERT OVERWRITE TABLE hot_keys_config\nSELECT\n    user_id,\n    cnt,\n    CURRENT_DATE AS stat_date\nFROM (\n    SELECT user_id, COUNT(*) AS cnt\n    FROM ods_orders\n    WHERE dt = DATE_SUB(CURRENT_DATE, 1)  -- 用昨天数据预测今天热点\n    GROUP BY user_id\n    HAVING COUNT(*) > 500000  -- 动态阈值\n) t;\n\n-- ETL 主逻辑：读取动态热点配置\n-- （后续 JOIN 逻辑同方案三，但热点 key 来自配置表而非硬编码）\n```\n\n---\n\n## 4. 动态智能倾斜检测\n\n### 4.1 自动化倾斜监控脚本\n\n```python\n# skew_monitor.py\n# 生产级倾斜监控：定期扫描关键表，发现倾斜自动告警\n# 可集成到 Airflow / DolphinScheduler\n\nimport subprocess\nimport json\nfrom datetime import datetime\n\nHIVE_CMD = \"hive -e\"\nALERT_THRESHOLD_PCT = 10.0   # 最大 key 占比超过 10% 触发告警\nALERT_THRESHOLD_GINI = 0.7   # 基尼系数超过 0.7 触发告警\n\nMONITOR_TABLES = [\n    {\"db\": \"skew_demo\", \"table\": \"ods_orders\", \"key_col\": \"user_id\",   \"dt\": \"2024-01-15\"},\n    {\"db\": \"skew_demo\", \"table\": \"ods_orders\", \"key_col\": \"product_id\", \"dt\": \"2024-01-15\"},\n]\n\ndef run_hive_query(sql):\n    result = subprocess.run(\n        f'{HIVE_CMD} \"{sql}\"',\n        shell=True, capture_output=True, text=True\n    )\n    return result.stdout.strip()\n\ndef check_skew(db, table, key_col, dt=None):\n    where = f\"WHERE dt='{dt}'\" if dt else \"\"\n    sql = f\"\"\"\n    SELECT\n        {key_col},\n        COUNT(*) AS cnt,\n        COUNT(*) * 100.0 / SUM(COUNT(*)) OVER() AS pct\n    FROM {db}.{table}\n    {where}\n    GROUP BY {key_col}\n    ORDER BY cnt DESC\n    LIMIT 20\n    \"\"\"\n    output = run_hive_query(sql)\n    rows = [line.split('\\t') for line in output.split('\\n') if line]\n\n    if not rows:\n        return None\n\n    total = sum(int(r[1]) for r in rows)\n    max_pct = float(rows[0][2])\n\n    # 简化基尼系数计算\n    counts = sorted([int(r[1]) for r in rows])\n    n = len(counts)\n    gini = sum((2*i - n - 1) * c for i, c in enumerate(counts, 1)) / (n * sum(counts))\n\n    result = {\n        \"table\": f\"{db}.{table}\",\n        \"key_col\": key_col,\n        \"dt\": dt,\n        \"max_key\": rows[0][0],\n        \"max_key_pct\": max_pct,\n        \"gini\": gini,\n        \"top5\": rows[:5],\n        \"is_skewed\": max_pct > ALERT_THRESHOLD_PCT or gini > ALERT_THRESHOLD_GINI\n    }\n    return result\n\ndef main():\n    print(f\"[{datetime.now()}] 开始倾斜检测...\")\n    alerts = []\n\n    for cfg in MONITOR_TABLES:\n        result = check_skew(**cfg)\n        if result and result[\"is_skewed\"]:\n            alerts.append(result)\n            print(f\"⚠️  倾斜告警：{result['table']}.{result['key_col']}\")\n            print(f\"   最大 Key：{result['max_key']}，占比：{result['max_key_pct']:.2f}%\")\n            print(f\"   基尼系数：{result['gini']:.4f}\")\n\n    if not alerts:\n        print(\"✅ 未发现倾斜\")\n    else:\n        # 写入告警日志（可对接钉钉/飞书/PagerDuty）\n        with open(\"/tmp/skew_alerts.json\", \"w\") as f:\n            json.dump(alerts, f, ensure_ascii=False, indent=2)\n        print(f\"\\n共发现 {len(alerts)} 个倾斜问题，详情见 /tmp/skew_alerts.json\")\n\nif __name__ == \"__main__\":\n    main()\n```\n\n---\n\n## 5. 与上下游生态联动\n\n### 5.1 上游 Spark 预聚合 → 下游 Hive 轻量消费\n\n```sql\n-- 架构思路：\n-- Spark Streaming 实时消费 Kafka → 按分钟预聚合 → 写入 Hive 预聚合表\n-- Hive 批处理只需对预聚合表做二次聚合，数据量从 10 亿行 → 几十万行\n\n-- Hive 预聚合表（接收 Spark 写入的分钟级聚合数据）\nCREATE TABLE dws_orders_minute_agg (\n    user_id         BIGINT,\n    minute_ts       STRING      COMMENT '分钟时间戳，格式 2024-01-15 10:30',\n    order_cnt       BIGINT,\n    total_amount    DOUBLE,\n    paid_cnt        BIGINT,\n    paid_amount     DOUBLE\n)\nCOMMENT '订单分钟级预聚合（由 Spark Streaming 写入）'\nPARTITIONED BY (dt STRING)\nSTORED AS ORC;\n\n-- Hive 日报只需对预聚合表做二次聚合（数据量极小，无倾斜）\nINSERT OVERWRITE TABLE ads_user_daily_report PARTITION(dt='2024-01-15')\nSELECT\n    user_id,\n    SUM(order_cnt)      AS daily_order_cnt,\n    SUM(total_amount)   AS daily_total_amount,\n    SUM(paid_cnt)       AS daily_paid_cnt,\n    SUM(paid_amount)    AS daily_paid_amount\nFROM dws_orders_minute_agg\nWHERE dt = '2024-01-15'\nGROUP BY user_id;\n-- 数据量：1440分钟 × 用户数，远小于原始明细，倾斜问题自然消失\n```\n\n### 5.2 Hive 倾斜数据传递给下游 Spark 的处理建议\n\n```python\n# 如果 Hive 输出的数据本身就是倾斜的（如按 user_id 分区），\n# 下游 Spark 读取时需要重新分区\n\nfrom pyspark.sql import SparkSession\nfrom pyspark.sql.functions import col\n\nspark = SparkSession.builder.appName(\"SkewHandling\").getOrCreate()\n\n# 开启 AQE（Spark 3.0+，自动处理倾斜）\nspark.conf.set(\"spark.sql.adaptive.enabled\", \"true\")\nspark.conf.set(\"spark.sql.adaptive.skewJoin.enabled\", \"true\")\nspark.conf.set(\"spark.sql.adaptive.skewJoin.skewedPartitionFactor\", \"5\")\nspark.conf.set(\"spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes\", \"256mb\")\n\n# 读取 Hive 倾斜表\ndf = spark.table(\"skew_demo.ods_orders\")\n\n# 手动重分区（如果 AQE 不够用）\n# 按 user_id 的 hash 重分区，让数据更均匀\ndf_repartitioned = df.repartition(200, col(\"user_id\"))\n\n# 或者用 salting（与 Hive 方案二思路相同）\nfrom pyspark.sql.functions import rand, floor, concat_ws, lit, cast\n\ndf_salted = df.withColumn(\"salt\", floor(rand() * 10).cast(\"int\"))\nresult = df_salted.groupBy(\"user_id\", \"salt\").agg({\"amount\": \"sum\", \"*\": \"count\"})\nfinal = result.groupBy(\"user_id\").agg({\"sum(amount)\": \"sum\", \"count(1)\": \"sum\"})\n```\n\nFile v1.0.1:references/index-design.md\n\n# 索引设计策略\n\n## 设计口诀\n**最左前缀、区分度高、覆盖查询、避免冗余**\n\n---\n\n## 索引类型速查\n\n| 类型 | 适用场景 | 注意事项 |\n|------|---------|---------|\n| 主键索引 | 唯一标识行 | 尽量用自增整数，避免 UUID（页分裂） |\n| 唯一索引 | 业务唯一约束 | 允许 NULL（多个 NULL 不冲突） |\n| 普通索引 | 高频查询列 | 区分度 > 20% 才值得建 |\n| 联合索引 | 多列组合查询 | 遵循最左前缀原则 |\n| 覆盖索引 | 避免回表 | 把 SELECT 列也加入索引 |\n| 前缀索引 | 长字符串列 | `INDEX(col(20))`，节省空间但不能覆盖 |\n| 函数索引 | 表达式查询 | MySQL 8.0+ / PostgreSQL 支持 |\n| 全文索引 | 文本搜索 | 替代 LIKE '%keyword%' |\n\n---\n\n## 联合索引设计原则\n\n### 最左前缀原则\n```sql\n-- 假设有联合索引 INDEX(a, b, c)\n-- ✅ 能走索引\nWHERE a = 1\nWHERE a = 1 AND b = 2\nWHERE a = 1 AND b = 2 AND c = 3\nWHERE a = 1 AND b > 2          -- a 走等值，b 走范围\nWHERE a = 1 AND c = 3          -- 只有 a 走索引，c 跳过了 b\n\n-- ❌ 不能走索引\nWHERE b = 2                    -- 跳过了 a\nWHERE b = 2 AND c = 3          -- 跳过了 a\nWHERE c = 3                    -- 跳过了 a 和 b\n```\n\n### 列顺序设计规则\n1. **等值查询列放前面**，范围查询列放后面\n2. **区分度高的列放前面**（性别不适合放第一位）\n3. **ORDER BY 列放最后**（消除 filesort）\n\n```sql\n-- 查询：WHERE status = ? AND create_time > ? ORDER BY id\n-- ✅ 好的索引设计：(status, create_time, id)\n-- status 等值在前，create_time 范围其次，id 排序在后\nCREATE INDEX idx_status_time_id ON orders(status, create_time, id);\n```\n\n### 覆盖索引设计\n```sql\n-- 查询：SELECT id, status, amount FROM orders WHERE user_id = ? AND status = 'paid'\n-- 把 SELECT 的列也加入索引，避免回表\nCREATE INDEX idx_covering ON orders(user_id, status, amount, id);\n-- Extra: Using index → 不需要回表，性能极佳\n```\n\n---\n\n## 索引失效场景（必须记住）\n\n```sql\n-- 1. 对索引列使用函数\nWHERE YEAR(create_time) = 2024          -- ❌\nWHERE create_time >= '2024-01-01'       -- ✅\n\n-- 2. 隐式类型转换（字符串 vs 数字）\nWHERE user_id = '1001'   -- user_id 是 INT  ❌\nWHERE user_id = 1001                    -- ✅\n\n-- 3. LIKE 前缀通配符\nWHERE name LIKE '%张%'                  -- ❌ 全表扫描\nWHERE name LIKE '张%'                   -- ✅ 走索引\n\n-- 4. OR 连接不同索引列\nWHERE a = 1 OR b = 2                    -- ❌（除非两列都有索引且优化器选择 index merge）\n-- 改写为 UNION ALL                     -- ✅\n\n-- 5. NOT IN / != / NOT EXISTS\nWHERE status != 'active'               -- ❌ 通常不走索引\n-- 改写为 IN 正向过滤                   -- ✅\n\n-- 6. 联合索引不满足最左前缀\n-- 见上方最左前缀原则\n\n-- 7. 索引列参与计算\nWHERE id + 1 = 100                     -- ❌\nWHERE id = 99                          -- ✅\n```\n\n---\n\n## 索引选择性分析\n\n```sql\n-- 计算列的区分度（越接近 1 越好，建议 > 0.1）\nSELECT\n    COUNT(DISTINCT status) / COUNT(*) AS status_selectivity,\n    COUNT(DISTINCT user_id) / COUNT(*) AS user_id_selectivity,\n    COUNT(DISTINCT DATE(create_time)) / COUNT(*) AS date_selectivity\nFROM orders;\n\n-- 查看索引使用情况（MySQL）\nSELECT\n    index_name,\n    stat_value AS cardinality\nFROM mysql.innodb_index_stats\nWHERE table_name = 'orders'\n  AND stat_name = 'n_diff_pfx01';\n\n-- 查看索引是否被使用（MySQL performance_schema）\nSELECT\n    object_name,\n    index_name,\n    count_read,\n    count_write\nFROM performance_schema.table_io_waits_summary_by_index_usage\nWHERE object_schema = 'your_db'\n  AND object_name = 'orders'\nORDER BY count_read DESC;\n```\n\n---\n\n## 索引维护\n\n### 查找冗余索引\n```sql\n-- MySQL：查找前缀相同的冗余索引\nSELECT\n    a.table_name,\n    a.index_name AS redundant_index,\n    a.column_name,\n    b.index_name AS dominant_index\nFROM information_schema.statistics a\nJOIN information_schema.statistics b\n    ON a.table_schema = b.table_schema\n    AND a.table_name = b.table_name\n    AND a.seq_in_index = b.seq_in_index\n    AND a.column_name = b.column_name\n    AND a.index_name != b.index_name\nWHERE a.table_schema = 'your_db';\n```\n\n### 索引碎片整理\n```sql\n-- MySQL：重建索引（会锁表，生产环境用 pt-online-schema-change）\nALTER TABLE orders ENGINE=InnoDB;  -- 重建整张表\nOPTIMIZE TABLE orders;             -- 等价\n\n-- 在线 DDL（MySQL 5.6+，不锁表）\nALTER TABLE orders ADD INDEX idx_new(col), ALGORITHM=INPLACE, LOCK=NONE;\n```\n\n---\n\n## 特殊场景索引\n\n### 时间范围查询（分区 + 索引）\n```sql\n-- 大表按时间分区，配合索引效果最佳\nCREATE TABLE logs (\n    id          BIGINT AUTO_INCREMENT,\n    user_id     INT NOT NULL,\n    action      VARCHAR(50),\n    create_time DATETIME NOT NULL,\n    PRIMARY KEY (id, create_time),  -- 分区键必须在主键中\n    INDEX idx_user_time (user_id, create_time)\n) PARTITION BY RANGE (YEAR(create_time)) (\n    PARTITION p2023 VALUES LESS THAN (2024),\n    PARTITION p2024 VALUES LESS THAN (2025),\n    PARTITION p_future VALUES LESS THAN MAXVALUE\n);\n```\n\n### JSON 列索引（MySQL 5.7+）\n```sql\n-- 对 JSON 字段的特定路径建虚拟列索引\nALTER TABLE users\n    ADD COLUMN city VARCHAR(50) GENERATED ALWAYS AS (JSON_UNQUOTE(profile->'$.city')) VIRTUAL,\n    ADD INDEX idx_city (city);\n\nSELECT * FROM users WHERE city = '北京';  -- 走虚拟列索引\n```\n\n### PostgreSQL 部分索引\n```sql\n-- 只对满足条件的行建索引（节省空间，提升效率）\nCREATE INDEX idx_active_users ON users(email)\nWHERE status = 'active';  -- 只索引活跃用户\n\n-- 查询时必须包含相同条件才能走此索引\nSELECT * FROM users WHERE email = 'x@x.com' AND status = 'active';\n```\n\nFile v1.0.1:references/query-optimization.md\n\n# 慢查询诊断 & 执行计划分析\n\n## 诊断流程\n\n```\n收到慢 SQL\n    ↓\n1. 读 EXPLAIN 输出（识别问题类型）\n    ↓\n2. 定位根因（全表扫描/索引失效/数据倾斜/...）\n    ↓\n3. 给出优化方案（改写 SQL / 加索引 / 改架构）\n    ↓\n4. 估算收益 + 提供验证方法\n```\n\n---\n\n## MySQL EXPLAIN 解读\n\n### 关键字段含义\n\n| 字段 | 含义 | 危险信号 |\n|------|------|---------|\n| `type` | 访问类型 | `ALL`（全表扫描）、`index`（全索引扫描） |\n| `key` | 实际使用的索引 | `NULL`（没用索引） |\n| `rows` | 预估扫描行数 | 远大于实际返回行数 |\n| `Extra` | 附加信息 | `Using filesort`、`Using temporary` |\n| `filtered` | 过滤比例 | 很低说明索引选择性差 |\n\n### type 访问类型（从好到差）\n```\nsystem > const > eq_ref > ref > range > index > ALL\n\nconst：主键或唯一索引等值查询，最快\neq_ref：JOIN 时被驱动表用主键/唯一索引，很快\nref：非唯一索引等值查询\nrange：索引范围扫描（BETWEEN, >, <, IN）\nindex：全索引扫描（比 ALL 好，但仍然慢）\nALL：全表扫描，必须优化\n```\n\n### Extra 字段解读\n```\nUsing index          → 覆盖索引，不需要回表，很好 ✅\nUsing where          → 在 Server 层过滤，索引不够精确\nUsing filesort       → 需要额外排序，考虑加索引 ⚠️\nUsing temporary      → 使用临时表，GROUP BY/ORDER BY 列不一致 ⚠️\nUsing index condition → 索引下推（ICP），MySQL 5.6+，较好 ✅\nSelect tables optimized away → 直接从索引返回，最优 ✅\n```\n\n### 实战示例：读懂一个 EXPLAIN\n\n```sql\nEXPLAIN SELECT u.name, COUNT(o.id) AS order_cnt\nFROM users u\nLEFT JOIN orders o ON u.id = o.user_id\nWHERE u.status = 'active'\nGROUP BY u.id\nORDER BY order_cnt DESC\nLIMIT 10;\n```\n\n```\n+----+-------------+-------+------+---------------+--------+---------+------------------+------+----------------------------------------------+\n| id | select_type | table | type | possible_keys | key    | key_len | ref              | rows | Extra                                        |\n+----+-------------+-------+------+---------------+--------+---------+------------------+------+----------------------------------------------+\n|  1 | SIMPLE      | u     | ref  | idx_status    | idx_status | 1   | const            | 5000 | Using index condition; Using temporary; Using filesort |\n|  1 | SIMPLE      | o     | ref  | idx_user_id   | idx_user_id | 4 | db.u.id          |   10 | NULL                                         |\n+----+-------------+-------+------+---------------+--------+---------+------------------+------+----------------------------------------------+\n```\n\n**诊断**：\n- `u` 表：`Using temporary; Using filesort` → GROUP BY + ORDER BY 触发了临时表和文件排序\n- `u.rows = 5000` → status='active' 过滤后还有 5000 行，选择性不够好\n- `o` 表：正常，走了 idx_user_id\n\n**优化方向**：\n1. 如果 active 用户占比很高，考虑去掉 status 过滤或换策略\n2. 加 `(status, id)` 联合索引，让 GROUP BY 利用索引顺序消除 filesort\n\n---\n\n## PostgreSQL EXPLAIN ANALYZE 解读\n\n```sql\nEXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)\nSELECT ...;\n```\n\n### 关键指标\n```\nSeq Scan        → 全表扫描，需要索引\nIndex Scan      → 索引扫描 + 回表\nIndex Only Scan → 覆盖索引，最优\nBitmap Heap Scan → 批量回表，适合低选择性查询\nHash Join       → 哈希连接，适合大表等值 JOIN\nNested Loop     → 嵌套循环，适合小表驱动大表\nMerge Join      → 归并连接，适合有序数据\n\nactual time=X..Y → X 是第一行时间，Y 是最后一行时间\nrows=N          → 实际返回行数\nloops=N         → 执行次数（Nested Loop 内层会多次执行）\nBuffers: shared hit=X read=Y → hit 是缓存命中，read 是磁盘读\n```\n\n### 实战：识别数据倾斜\n```\nHash Join  (cost=... actual time=5000..5000 rows=1000000 loops=1)\n  Hash Cond: (o.user_id = u.id)\n  ->  Seq Scan on orders  (actual time=0.1..2000 rows=10000000 loops=1)\n  ->  Hash  (actual time=100..100 rows=100 loops=1)\n        Buckets: 1024  Batches: 8  Memory Usage: 4096kB  ← Batches>1 说明内存不够，溢出磁盘\n```\n\n---\n\n## 常见慢查询模式 & 修复\n\n### 模式 1：索引失效（函数/类型转换）\n\n```sql\n-- ❌ 慢：对索引列使用函数，索引失效\nSELECT * FROM orders WHERE DATE(create_time) = '2024-01-01';\nSELECT * FROM users WHERE LOWER(email) = 'test@example.com';\nSELECT * FROM orders WHERE user_id = '1001';  -- user_id 是 INT，传了字符串\n\n-- ✅ 快：改写为范围查询，保持索引列干净\nSELECT * FROM orders\nWHERE create_time >= '2024-01-01 00:00:00'\n  AND create_time <  '2024-01-02 00:00:00';\n\n-- ✅ 快：建函数索引（MySQL 8.0+ / PostgreSQL）\nCREATE INDEX idx_email_lower ON users ((LOWER(email)));\nSELECT * FROM users WHERE LOWER(email) = 'test@example.com';\n```\n\n### 模式 2：深度分页\n\n```sql\n-- ❌ 慢：扫描 1000020 行，丢弃前 1000000 行\nSELECT * FROM logs ORDER BY id LIMIT 1000000, 20;\n\n-- ✅ 快：游标分页（业务上记录上次最大 id）\nSELECT * FROM logs WHERE id > :cursor ORDER BY id LIMIT 20;\n\n-- ✅ 快：延迟关联（必须分页时）\nSELECT l.* FROM logs l\nJOIN (SELECT id FROM logs ORDER BY id LIMIT 1000000, 20) t ON l.id = t.id;\n```\n\n### 模式 3：N+1 查询\n\n```sql\n-- ❌ 慢：查出 100 个用户，再循环查 100 次订单（N+1）\nSELECT * FROM users WHERE status = 'active';\n-- 然后对每个 user_id 执行：\nSELECT * FROM orders WHERE user_id = ?;\n\n-- ✅ 快：一次 JOIN 或 IN 查询\nSELECT u.*, o.order_id, o.amount\nFROM users u\nLEFT JOIN orders o ON u.id = o.user_id\nWHERE u.status = 'active';\n\n-- 或者（当 JOIN 结果集太大时）\nSELECT * FROM orders\nWHERE user_id IN (\n    SELECT id FROM users WHERE status = 'active'\n);\n```\n\n### 模式 4：OR 导致索引失效\n\n```sql\n-- ❌ 慢：OR 连接不同列，无法走复合索引\nSELECT * FROM orders WHERE user_id = 1001 OR product_id = 2002;\n\n-- ✅ 快：改写为 UNION ALL（各自走各自的索引）\nSELECT * FROM orders WHERE user_id = 1001\nUNION ALL\nSELECT * FROM orders WHERE product_id = 2002\n  AND user_id != 1001;  -- 避免重复\n```\n\n### 模式 5：大 IN 列表\n\n```sql\n-- ❌ 慢：IN 列表超过几百个值，优化器可能放弃索引\nSELECT * FROM products WHERE id IN (1,2,3,...,10000);\n\n-- ✅ 快：改为临时表 JOIN\nCREATE TEMPORARY TABLE tmp_ids (id INT PRIMARY KEY);\nINSERT INTO tmp_ids VALUES (1),(2),(3),...;\nSELECT p.* FROM products p JOIN tmp_ids t ON p.id = t.id;\n```\n\n### 模式 6：COUNT 优化\n\n```sql\n-- 场景：只需要知道是否存在，不需要精确数量\n-- ❌ 慢：COUNT(*) 扫描所有行\nSELECT COUNT(*) FROM orders WHERE user_id = 1001;\n\n-- ✅ 快：EXISTS 找到第一条就停止\nSELECT EXISTS(SELECT 1 FROM orders WHERE user_id = 1001);\n\n-- 场景：需要精确总数（大表）\n-- ✅ MySQL：information_schema 近似值（误差<5%）\nSELECT table_rows FROM information_schema.tables\nWHERE table_name = 'orders';\n\n-- ✅ PostgreSQL：pg_class 近似值\nSELECT reltuples::BIGINT FROM pg_class WHERE relname = 'orders';\n```\n\n---\n\n## 锁分析\n\n### MySQL 查看锁等待\n```sql\n-- 查看当前锁等待\nSELECT\n    r.trx_id                    AS waiting_trx_id,\n    r.trx_mysql_thread_id       AS waiting_thread,\n    r.trx_query                 AS waiting_query,\n    b.trx_id                    AS blocking_trx_id,\n    b.trx_mysql_thread_id       AS blocking_thread,\n    b.trx_query                 AS blocking_query\nFROM information_schema.innodb_lock_waits w\nJOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id\nJOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;\n\n-- MySQL 8.0+ 用 performance_schema\nSELECT * FROM performance_schema.data_lock_waits;\n```\n\n### 死锁分析\n```sql\n-- 查看最近一次死锁信息\nSHOW ENGINE INNODB STATUS\\G\n-- 找 LATEST DETECTED DEADLOCK 部分\n\n-- 死锁常见原因：\n-- 1. 两个事务以相反顺序锁定同一批行\n-- 2. 解决：统一加锁顺序，或缩短事务\n```\n\nFile v1.0.1:references/sql-generation.md\n\n# SQL 生成指南\n\n## 生成流程\n\n### Step 1：理解业务意图\n不要急于写 SQL，先把业务问题翻译成数据问题：\n- 要查什么实体？（用户、订单、商品...）\n- 要什么维度的聚合？（按天、按用户、按地区...）\n- 过滤条件是什么？（时间范围、状态、金额...）\n- 结果如何排序和限制？\n\n### Step 2：确认 Schema\n如果用户没有提供，主动询问：\n```\n请提供相关表的 DDL，或者告诉我：\n- 表名和主要字段\n- 哪些字段有索引\n- 大概的数据量级\n```\n\n### Step 3：生成 SQL（分难度）\n\n---\n\n## 基础查询模式\n\n### 单表聚合\n```sql\n-- 业务：统计每天的订单数和总金额（近30天）\n-- 数据库：MySQL 8.0\n-- 性能预期：orders 表千万级，date 字段有索引，<100ms\n-- 注意：create_time 为 NULL 的记录会被 WHERE 过滤掉\n\nSELECT\n    DATE(create_time)           AS order_date,\n    COUNT(*)                    AS order_cnt,\n    SUM(amount)                 AS total_amount,\n    AVG(amount)                 AS avg_amount,\n    COUNT(DISTINCT user_id)     AS uv          -- 去重用户数\nFROM orders\nWHERE create_time >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)\n  AND status != 'cancelled'                    -- 排除取消订单\nGROUP BY DATE(create_time)\nORDER BY order_date DESC;\n```\n\n### 多表 JOIN\n```sql\n-- 业务：查询用户最近一次购买的商品信息\n-- 数据库：MySQL 8.0\n-- 性能预期：需要 user_id 和 create_time 的联合索引\n-- 注意：使用子查询取最新订单，避免 GROUP BY 后 JOIN 的数据膨胀\n\nSELECT\n    u.user_id,\n    u.username,\n    o.order_id,\n    o.create_time   AS last_order_time,\n    p.product_name,\n    o.amount\nFROM users u\nINNER JOIN orders o ON u.user_id = o.user_id\nINNER JOIN (\n    -- 每个用户最新订单\n    SELECT user_id, MAX(create_time) AS max_time\n    FROM orders\n    WHERE status = 'completed'\n    GROUP BY user_id\n) latest ON o.user_id = latest.user_id\n         AND o.create_time = latest.max_time\nINNER JOIN products p ON o.product_id = p.product_id\nWHERE u.status = 'active';\n```\n\n### 窗口函数（生产必备）\n```sql\n-- 业务：计算每个用户的订单金额排名和累计金额\n-- 数据库：MySQL 8.0+ / PostgreSQL\n-- 性能预期：窗口函数在大数据量下注意分区粒度\n\nSELECT\n    user_id,\n    order_id,\n    amount,\n    -- 排名（并列不跳号用 DENSE_RANK，跳号用 RANK）\n    DENSE_RANK() OVER (\n        PARTITION BY user_id\n        ORDER BY amount DESC\n    )                                           AS amount_rank,\n    -- 累计金额\n    SUM(amount) OVER (\n        PARTITION BY user_id\n        ORDER BY create_time\n        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW\n    )                                           AS cumulative_amount,\n    -- 环比（当前行 vs 上一行）\n    amount - LAG(amount, 1, 0) OVER (\n        PARTITION BY user_id\n        ORDER BY create_time\n    )                                           AS amount_diff\nFROM orders\nWHERE status = 'completed';\n```\n\n---\n\n## 进阶查询模式\n\n### 递归 CTE（树形结构）\n```sql\n-- 业务：查询组织架构树（从某节点向下所有子节点）\n-- 数据库：MySQL 8.0+ / PostgreSQL\n-- 注意：设置 max_recursion_depth 防止死循环\n\nWITH RECURSIVE org_tree AS (\n    -- 锚点：起始节点\n    SELECT\n        id,\n        name,\n        parent_id,\n        0           AS depth,\n        CAST(name AS CHAR(1000)) AS path\n    FROM departments\n    WHERE id = 1   -- 从根节点开始\n\n    UNION ALL\n\n    -- 递归：向下展开\n    SELECT\n        d.id,\n        d.name,\n        d.parent_id,\n        ot.depth + 1,\n        CONCAT(ot.path, ' > ', d.name)\n    FROM departments d\n    INNER JOIN org_tree ot ON d.parent_id = ot.id\n    WHERE ot.depth < 10   -- 防止无限递归\n)\nSELECT * FROM org_tree ORDER BY path;\n```\n\n### 行转列（PIVOT）\n```sql\n-- 业务：将每月销售额从行格式转为列格式\n-- 数据库：MySQL（无原生 PIVOT，用条件聚合）\n\nSELECT\n    product_id,\n    SUM(CASE WHEN month = '2024-01' THEN amount ELSE 0 END) AS jan,\n    SUM(CASE WHEN month = '2024-02' THEN amount ELSE 0 END) AS feb,\n    SUM(CASE WHEN month = '2024-03' THEN amount ELSE 0 END) AS mar,\n    SUM(amount)                                              AS total\nFROM monthly_sales\nWHERE month BETWEEN '2024-01' AND '2024-03'\nGROUP BY product_id;\n\n-- PostgreSQL 版本（使用 crosstab，需要 tablefunc 扩展）\n-- SELECT * FROM crosstab(...) AS ct(product_id INT, jan NUMERIC, feb NUMERIC, mar NUMERIC);\n```\n\n### 去重保留最新记录\n```sql\n-- 业务：每个用户只保留最新的一条记录（去重）\n-- 方案 A：ROW_NUMBER（推荐，语义清晰）\nWITH ranked AS (\n    SELECT *,\n        ROW_NUMBER() OVER (\n            PARTITION BY user_id\n            ORDER BY create_time DESC\n        ) AS rn\n    FROM user_logs\n)\nSELECT * FROM ranked WHERE rn = 1;\n\n-- 方案 B：子查询（兼容性更好，但性能可能差）\nSELECT * FROM user_logs ul\nWHERE create_time = (\n    SELECT MAX(create_time)\n    FROM user_logs\n    WHERE user_id = ul.user_id\n);\n-- ⚠️ 方案 B 在大表上是相关子查询，性能极差，慎用\n```\n\n---\n\n## 大数据量专项\n\n### 分页优化（LIMIT 大偏移量）\n```sql\n-- ❌ 错误写法：LIMIT 100000, 20 会扫描 100020 行\nSELECT * FROM orders ORDER BY id LIMIT 100000, 20;\n\n-- ✅ 正确写法：延迟关联（Deferred Join）\nSELECT o.*\nFROM orders o\nINNER JOIN (\n    SELECT id FROM orders ORDER BY id LIMIT 100000, 20\n) ids ON o.id = ids.id;\n\n-- ✅ 更好的写法：游标分页（需要记录上次最大 ID）\nSELECT * FROM orders\nWHERE id > :last_max_id   -- 上次查询的最大 id\nORDER BY id\nLIMIT 20;\n```\n\n### 批量 INSERT 优化\n```sql\n-- ❌ 逐行插入（N 次网络往返）\nINSERT INTO logs VALUES (1, 'a', NOW());\nINSERT INTO logs VALUES (2, 'b', NOW());\n\n-- ✅ 批量插入（1 次网络往返）\nINSERT INTO logs (id, content, create_time) VALUES\n    (1, 'a', NOW()),\n    (2, 'b', NOW()),\n    (3, 'c', NOW());\n-- 建议每批 500-1000 行，避免单次事务过大\n\n-- ✅ UPSERT（存在则更新，不存在则插入）\n-- MySQL:\nINSERT INTO user_stats (user_id, login_cnt)\nVALUES (1001, 1)\nON DUPLICATE KEY UPDATE login_cnt = login_cnt + 1;\n\n-- PostgreSQL:\nINSERT INTO user_stats (user_id, login_cnt)\nVALUES (1001, 1)\nON CONFLICT (user_id)\nDO UPDATE SET login_cnt = user_stats.login_cnt + 1;\n```\n\n---\n\n## NULL 处理规范\n\n```sql\n-- NULL 的三值逻辑：TRUE / FALSE / UNKNOWN\n-- NULL != NULL → UNKNOWN（不是 TRUE！）\n-- 正确判断 NULL：IS NULL / IS NOT NULL\n\n-- ❌ 错误：WHERE col != 'value' 不会返回 col IS NULL 的行\nSELECT * FROM t WHERE col != 'active';\n\n-- ✅ 正确：明确处理 NULL\nSELECT * FROM t WHERE col != 'active' OR col IS NULL;\n\n-- COALESCE：返回第一个非 NULL 值\nSELECT COALESCE(nickname, username, '匿名用户') AS display_name FROM users;\n\n-- NULL 在聚合中的行为\nSELECT\n    COUNT(*)        AS total_rows,      -- 包含 NULL 行\n    COUNT(col)      AS non_null_cnt,    -- 不含 NULL 行\n    SUM(col)        AS sum_val,         -- NULL 被忽略\n    AVG(col)        AS avg_val          -- NULL 被忽略（分母也不含 NULL）\nFROM t;\n```\n\nFile v1.0.1:references/sql-internals.md\n\n# SQL 原理深度\n\n## 目录\n1. [事务 & ACID](#事务--acid)\n2. [MVCC 多版本并发控制](#mvcc-多版本并发控制)\n3. [锁机制](#锁机制)\n4. [索引原理（B+树）](#索引原理b树)\n5. [查询优化器](#查询优化器)\n6. [Join 算法](#join-算法)\n7. [WAL & 崩溃恢复](#wal--崩溃恢复)\n\n---\n\n## 事务 & ACID\n\n### 四个特性\n```\nA - Atomicity（原子性）：事务要么全成功，要么全回滚\nC - Consistency（一致性）：事务前后数据满足业务约束\nI - Isolation（隔离性）：并发事务互不干扰\nD - Durability（持久性）：提交后数据不丢失\n```\n\n### 隔离级别（从低到高）\n\n| 级别 | 脏读 | 不可重复读 | 幻读 | 说明 |\n|------|------|-----------|------|------|\n| READ UNCOMMITTED | ✅ 会 | ✅ 会 | ✅ 会 | 几乎不用 |\n| READ COMMITTED | ❌ 不会 | ✅ 会 | ✅ 会 | Oracle/PG 默认 |\n| REPEATABLE READ | ❌ 不会 | ❌ 不会 | ⚠️ 部分 | MySQL 默认 |\n| SERIALIZABLE | ❌ 不会 | ❌ 不会 | ❌ 不会 | 性能最差 |\n\n**MySQL RR 级别下的幻读**：\n- 快照读（普通 SELECT）：MVCC 解决，不会幻读\n- 当前读（SELECT FOR UPDATE / UPDATE）：需要间隙锁（Gap Lock）解决\n\n```sql\n-- 查看/设置隔离级别\nSELECT @@transaction_isolation;\nSET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;\n```\n\n---\n\n## MVCC 多版本并发控制\n\n### 核心思想\n**读不加锁，写不阻塞读** — 通过保存数据的多个历史版本，让读写操作并发执行。\n\n### InnoDB 实现原理\n\n每行数据有两个隐藏字段：\n- `trx_id`：最后修改该行的事务 ID\n- `roll_pointer`：指向 undo log 中的上一个版本\n\n```\n当前行 [trx_id=100, data='B']\n    ↓ roll_pointer\nundo log [trx_id=50, data='A']\n    ↓ roll_pointer\nundo log [trx_id=10, data='初始值']\n```\n\n### Read View（快照）\n事务开始时创建 Read View，记录：\n- `m_ids`：当前活跃（未提交）的事务 ID 列表\n- `min_trx_id`：活跃事务中最小的 ID\n- `max_trx_id`：下一个将分配的事务 ID\n\n**可见性判断**：\n```\n读取某行时，检查该行的 trx_id：\n1. trx_id < min_trx_id → 该版本在快照前已提交 → 可见 ✅\n2. trx_id >= max_trx_id → 该版本在快照后才开始 → 不可见 ❌\n3. trx_id 在 m_ids 中 → 该版本是活跃事务，未提交 → 不可见 ❌\n4. 其他 → 已提交 → 可见 ✅\n如果不可见，沿 roll_pointer 找上一个版本，重复判断\n```\n\n**RC vs RR 的区别**：\n- RC：每次 SELECT 都创建新的 Read View（所以能读到其他事务新提交的数据）\n- RR：事务开始时创建一次 Read View，之后复用（所以同一事务内多次读结果一致）\n\n---\n\n## 锁机制\n\n### InnoDB 锁类型\n\n```\n行锁：\n  S Lock（共享锁）：读锁，多个事务可同时持有\n  X Lock（排他锁）：写锁，独占\n\n表锁：\n  IS（意向共享锁）：表示事务想对某行加 S 锁\n  IX（意向排他锁）：表示事务想对某行加 X 锁\n  （意向锁是自动加的，用于快速判断表级锁冲突）\n\n间隙锁（Gap Lock）：锁定索引记录之间的间隙，防止幻读\n  例：锁定 (5, 10) 之间，阻止插入 id=7 的行\n\n临键锁（Next-Key Lock）= 行锁 + 间隙锁\n  例：锁定 (5, 10]，包含 id=10 的行和 (5,10) 的间隙\n  MySQL RR 级别默认使用临键锁\n```\n\n### 加锁规则（MySQL RR）\n```sql\n-- 等值查询，命中记录 → 行锁\nSELECT * FROM t WHERE id = 5 FOR UPDATE;  -- 锁 id=5 这一行\n\n-- 等值查询，未命中 → 间隙锁\nSELECT * FROM t WHERE id = 7 FOR UPDATE;  -- 锁 (5, 10) 间隙（假设没有 id=7）\n\n-- 范围查询 → 临键锁\nSELECT * FROM t WHERE id > 5 FOR UPDATE;  -- 锁 (5, +∞)\n\n-- 非唯一索引等值查询 → 临键锁 + 间隙锁\nSELECT * FROM t WHERE age = 25 FOR UPDATE;  -- 锁 age=25 的行 + 前后间隙\n```\n\n### 死锁预防\n```sql\n-- 原则：所有事务按相同顺序加锁\n-- ❌ 死锁场景：\n-- 事务A：UPDATE orders WHERE id=1; UPDATE orders WHERE id=2;\n-- 事务B：UPDATE orders WHERE id=2; UPDATE orders WHERE id=1;\n\n-- ✅ 解决：统一按 id 升序加锁\n-- 事务A：UPDATE orders WHERE id IN (1,2) ORDER BY id;\n-- 事务B：UPDATE orders WHERE id IN (1,2) ORDER BY id;\n\n-- 设置死锁超时\nSET innodb_lock_wait_timeout = 5;  -- 5秒超时\n```\n\n---\n\n## 索引原理（B+树）\n\n### 为什么用 B+树？\n```\n二叉树：高度太高，磁盘 IO 次数多\n哈希表：不支持范围查询\nB 树：叶子节点不连接，范围查询需要回溯\nB+树：\n  - 所有数据在叶子节点\n  - 叶子节点用链表连接 → 范围查询只需遍历链表\n  - 非叶子节点只存键值 → 单个节点能存更多键 → 树更矮\n  - 3-4 层 B+树可以存储千万级数据，只需 3-4 次 IO\n```\n\n### 聚簇索引 vs 非聚簇索引\n```\n聚簇索引（主键索引）：\n  叶子节点存储完整行数据\n  一张表只有一个聚簇索引\n  InnoDB 默认按主键建聚簇索引\n\n非聚簇索引（二级索引）：\n  叶子节点存储 [索引列值, 主键值]\n  查询时先找到主键，再回表查完整数据（回表）\n  覆盖索引可以避免回表\n```\n\n### 为什么主键推荐自增整数？\n```\nUUID 主键的问题：\n  UUID 是随机的，新插入的行可能落在 B+树中间\n  → 导致页分裂（Page Split）\n  → 大量碎片，写性能下降\n\n自增整数的优势：\n  新行总是追加到最右边的叶子节点\n  → 无页分裂，写性能好\n  → 索引紧凑，空间利用率高\n```\n\n---\n\n## 查询优化器\n\n### CBO vs RBO\n```\nRBO（Rule-Based Optimizer）：基于规则，固定套路，已淘汰\nCBO（Cost-Based Optimizer）：基于代价估算，现代数据库都用这个\n\n代价 = CPU 代价 + IO 代价\n优化器会估算不同执行计划的代价，选最小的\n```\n\n### 统计信息的重要性\n```sql\n-- 优化器依赖统计信息做决策\n-- 统计信息过期 → 优化器做出错误决策 → 慢查询\n\n-- MySQL：更新统计信息\nANALYZE TABLE orders;\n\n-- PostgreSQL：更新统计信息\nANALYZE orders;\nVACUUM ANALYZE orders;  -- 同时回收死元组\n\n-- 查看统计信息\nSHOW INDEX FROM orders;  -- MySQL，看 Cardinality（基数）\nSELECT * FROM pg_stats WHERE tablename = 'orders';  -- PostgreSQL\n```\n\n### 优化器 Hint（强制指定执行计划）\n```sql\n-- MySQL：强制使用某个索引\nSELECT * FROM orders USE INDEX (idx_user_id) WHERE user_id = 1001;\nSELECT * FROM orders FORCE INDEX (idx_user_id) WHERE user_id = 1001;\nSELECT * FROM orders IGNORE INDEX (idx_status) WHERE status = 'active';\n\n-- MySQL 8.0+：Optimizer Hints\nSELECT /*+ INDEX(orders idx_user_id) */ * FROM orders WHERE user_id = 1001;\nSELECT /*+ NO_HASH_JOIN(orders, users) */ * FROM orders JOIN users ...;\n\n-- PostgreSQL：关闭某种扫描方式（调试用）\nSET enable_seqscan = OFF;\nSET enable_hashjoin = OFF;\n```\n\n---\n\n## Join 算法\n\n### Nested Loop Join（嵌套循环）\n```\n适用：小表驱动大表，被驱动表有索引\n复杂度：O(M * log N)（M 是驱动表行数，N 是被驱动表行数）\n\nfor each row in 驱动表:\n    在被驱动表中用索引查找匹配行\n\n优化：Block Nested Loop（BNL）\n  把驱动表的一批行放入 join_buffer\n  被驱动表每行只需扫描一次 join_buffer\n  减少被驱动表的扫描次数\n```\n\n### Hash Join\n```\n适用：大表等值 JOIN，无索引\n复杂度：O(M + N)\n\nBuild 阶段：把小表（Build 表）的数据装入哈希表（内存）\nProbe 阶段：扫描大表（Probe 表），每行在哈希表中查找匹配\n\n注意：如果 Build 表太大，哈希表溢出到磁盘，性能急剧下降\nMySQL 8.0.18+ 支持 Hash Join\n```\n\n### Merge Join（归并连接）\n```\n适用：两个表在 JOIN 列上都已排序（或有索引）\n复杂度：O(M + N)（已排序的情况下）\n\n类似归并排序的合并步骤：\n  两个指针分别扫描两个有序表\n  找到相等的行就输出\n\nPostgreSQL 常用，MySQL 不支持\n```\n\n### 实战：选择 Join 策略\n```sql\n-- 场景：users(100万行) JOIN orders(1000万行)，等值 JOIN\n\n-- 方案 A：确保小表在前（Nested Loop）\nSELECT /*+ LEADING(u) USE_NL(o) */ u.name, o.amount\nFROM users u JOIN orders o ON u.id = o.user_id\nWHERE u.vip_level = 'gold';  -- 先过滤 users，减小驱动表\n\n-- 方案 B：大表 JOIN 大表，用 Hash Join（MySQL 8.0+）\nSELECT /*+ HASH_JOIN(u, o) */ u.name, o.amount\nFROM users u JOIN orders o ON u.id = o.user_id;\n\n-- 数据倾斜处理（某些 user_id 有大量订单）\n-- 方案：广播小表（Spark SQL / Hive）\nSELECT /*+ BROADCAST(u) */ u.name, o.amount\nFROM orders o JOIN users u ON o.user_id = u.id;\n```\n\n---\n\n## WAL & 崩溃恢复\n\n### WAL（Write-Ahead Logging）原理\n```\n核心思想：先写日志，再写数据\n  1. 事务提交时，先把变更写入 redo log（顺序写，极快）\n  2. 数据页的修改先在内存（Buffer Pool）中进行\n  3. 后台线程异步将脏页刷到磁盘\n\n崩溃恢复：\n  重启时，读取 redo log，重放未刷盘的变更\n  → 保证已提交事务不丢失（Durability）\n```\n\n### InnoDB 日志体系\n```\nredo log（重做日志）：\n  固定大小，循环写入\n  保证崩溃恢复（物理日志，记录页的修改）\n  innodb_log_file_size 控制大小\n\nundo log（回滚日志）：\n  记录数据修改前的值\n  用于事务回滚 + MVCC 历史版本\n\nbinlog（二进制日志）：\n  MySQL Server 层的逻辑日志\n  用于主从复制 + 数据恢复\n  记录 SQL 语句或行变更\n\n两阶段提交（2PC）：\n  保证 redo log 和 binlog 的一致性\n  1. Prepare：写 redo log，标记 prepare\n  2. Commit：写 binlog，然后 redo log 标记 commit\n```\n\nFile v1.0.1:references/sql-security.md\n\n# SQL 安全规范\n\n## 核心原则：永远不要拼接 SQL\n\n任何将用户输入直接拼接进 SQL 字符串的做法都是危险的，无论输入看起来多么\"安全\"。\n\n---\n\n## 1. 参数化查询（Parameterized Queries）\n\n### Python (psycopg2 / mysql-connector)\n```python\n# ❌ 危险：字符串拼接\nquery = f\"SELECT * FROM users WHERE name = '{user_input}'\"\n\n# ✅ 安全：参数化\ncursor.execute(\"SELECT * FROM users WHERE name = %s\", (user_input,))\n\n# ✅ 多参数\ncursor.execute(\n    \"INSERT INTO orders (user_id, amount) VALUES (%s, %s)\",\n    (user_id, amount)\n)\n```\n\n### Python (SQLite)\n```python\n# ✅ SQLite 用 ? 占位符\nconn.execute(\"SELECT * FROM users WHERE id = ?\", (user_id,))\n```\n\n### Java (JDBC PreparedStatement)\n```java\n// ✅ 安全\nPreparedStatement stmt = conn.prepareStatement(\n    \"SELECT * FROM users WHERE email = ?\"\n);\nstmt.setString(1, email);\nResultSet rs = stmt.executeQuery();\n```\n\n### Node.js (mysql2)\n```javascript\n// ✅ 安全\nconst [rows] = await conn.execute(\n  'SELECT * FROM users WHERE id = ?',\n  [userId]\n);\n```\n\n### Go (database/sql)\n```go\n// ✅ 安全\nrow := db.QueryRow(\"SELECT name FROM users WHERE id = $1\", userID)\n```\n\n---\n\n## 2. ORM 安全使用\n\nORM 本身是安全的，但原生查询（raw query）仍需参数化：\n\n```python\n# Django ORM ✅\nUser.objects.filter(name=user_input)\n\n# Django raw ✅（仍需参数化）\nUser.objects.raw(\"SELECT * FROM users WHERE name = %s\", [user_input])\n\n# SQLAlchemy ✅\nsession.query(User).filter(User.name == user_input)\n\n# SQLAlchemy text() ✅\nfrom sqlalchemy import text\nsession.execute(text(\"SELECT * FROM users WHERE name = :name\"), {\"name\": user_input})\n```\n\n---\n\n## 3. 动态表名 / 列名处理\n\n参数化查询**不能**用于表名和列名，需要白名单校验：\n\n```python\n# ❌ 危险\nquery = f\"SELECT * FROM {table_name}\"\n\n# ✅ 白名单校验\nALLOWED_TABLES = {\"users\", \"orders\", \"products\"}\nif table_name not in ALLOWED_TABLES:\n    raise ValueError(f\"Invalid table: {table_name}\")\nquery = f\"SELECT * FROM {table_name}\"  # 此时安全\n\n# ✅ 列名同理\nALLOWED_SORT_COLUMNS = {\"created_at\", \"name\", \"price\"}\nif sort_col not in ALLOWED_SORT_COLUMNS:\n    sort_col = \"created_at\"  # 默认值\n```\n\n---\n\n## 4. 常见 SQL 注入模式（识别与防御）\n\n| 攻击模式 | 示例 | 防御 |\n|---------|------|------|\n| 经典注入 | `' OR '1'='1` | 参数化查询 |\n| UNION 注入 | `' UNION SELECT password FROM users--` | 参数化 + 最小权限 |\n| 盲注 | `' AND SLEEP(5)--` | 参数化 + 超时限制 |\n| 二阶注入 | 存储后再读取执行 | 读取时也参数化 |\n| 批量注入 | `'; DROP TABLE users;--` | 参数化 + 禁止多语句 |\n\n---\n\n## 5. 最小权限原则\n\n```sql\n-- 为应用创建专用账号，只授予必要权限\n-- MySQL\nCREATE USER 'app_user'@'%' IDENTIFIED BY 'strong_password';\nGRANT SELECT, INSERT, UPDATE ON mydb.orders TO 'app_user'@'%';\n-- 不授予 DROP、CREATE、DELETE（除非必要）\n\n-- PostgreSQL\nCREATE ROLE app_user LOGIN PASSWORD 'strong_password';\nGRANT SELECT, INSERT, UPDATE ON orders TO app_user;\nREVOKE DELETE ON orders FROM app_user;\n```\n\n---\n\n## 6. 输入验证清单\n\n生成 SQL 前，对用户输入做以下检查：\n\n- [ ] 类型校验（数字就是数字，不接受字符串）\n- [ ] 长度限制（防止超长输入导致缓冲区问题）\n- [ ] 格式校验（邮箱、日期、UUID 等用正则验证）\n- [ ] 范围校验（分页 limit 不超过 1000，offset 不超过合理值）\n- [ ] 枚举校验（状态字段只接受已知值）\n\n---\n\n## 7. 风险等级标注\n\n生成涉及用户输入的 SQL 时，必须在注释中标注：\n\n```sql\n-- ⚠️ 安全级别：HIGH（含用户输入，已参数化）\n-- 参数：user_id (int, validated), status (enum, whitelisted)\nSELECT id, name FROM orders WHERE user_id = %s AND status = %s\n```\n\nArchive v1.0.0: 19 files, 72302 bytes\n\nFiles: references/cli-quickref.md (6312b), references/data-warehouse.md (8271b), references/ddl-design.md (7684b), references/dialect-guide.md (6934b), references/hive-skew-advanced.md (19618b), references/index-design.md (5900b), references/query-optimization.md (8157b), references/sql-generation.md (7250b), references/sql-internals.md (9668b), references/sql-security.md (3849b), references/visualization-guide.md (7602b), requirements.txt (1131b), scripts/__init__.py (954b), scripts/database_connector.py (15639b), scripts/file_connector.py (21761b), scripts/pipeline.py (25837b), scripts/unified_pipeline.py (28176b), SKILL.md (14391b), _meta.json (129b)\n\nFile v1.0.0:SKILL.md\n\n---\nname: sql-master\ndescription: SQL 查询、数据获取智能体。覆盖 SQL 全链路能力：自然语言转生产级 SQL、慢查询诊断与执行计划分析、索引设计与优化、数仓建模、SQL 原理深度科普、查询结果可视化。支持 MySQL / PostgreSQL / Hive / Spark SQL / ClickHouse / BigQuery 多方言。触发场景：(1) 写 SQL / 生成查询，(2) SQL 慢/优化/调优，(3) 执行计划分析 EXPLAIN，(4) 索引设计，(5) 数仓建模 / 分层设计，(6) SQL 原理问题（事务/锁/MVCC/Join算法等），(7) 表结构设计 DDL，(8) SQL 报错诊断，(9) 任何\"帮我写个查询\"、\"这个SQL为什么慢\"、\"怎么建索引\"类请求，(10) 查询结果可视化 / 出图 / 图表 / 数据展示。\n---\n\n# SQL Master — SQL 查询、数据获取智能体\n\n## ⚠️ 使用前必读\n\n本 Skill 需要 Python 依赖。**首次使用前必须安装依赖**：\n\n```bash\nskillhub_install install_skill sql-master\n```\n\n工具会自动检测 Python3 环境、pip 可用性，并安装所有依赖。\n\n### 依赖安装方式\n\n| 方式 | 命令 | 适用场景 |\n|------|------|---------|\n| **自动安装（推荐）** | `skillhub_install install_skill sql-master` | 一键安装，自动处理 |\n| **手动安装** | `pip install -r requirements.txt` | 熟悉 Python 环境的用户 |\n\n### 无依赖使用（受限模式）\n\n如果无法安装依赖，本 Skill 提供以下**降级能力**：\n\n✅ **可用功能**：\n- SQL 语句生成（纯文本输出，无需执行）\n- SQL 诊断与优化建议（基于文本分析）\n- 索引设计建议（基于规则引擎）\n- SQL 原理解释与科普\n- 执行计划分析（用户提供 EXPLAIN 结果）\n\n❌ **不可用功能**：\n- 数据库连接与 SQL 执行\n- 数据 Pipeline 处理\n- 本地文件数据获取（CSV/Excel 等）\n- 与 sql-dataviz / report-generator 联动\n\n---\n\n## 🔗 Skill 协作关系\n\n本 Skill 与 **sql-dataviz**、**report-generator** 组成完整的数据分析流水线：\n\n```\n┌─────────────┐     ┌─────────────┐     ┌─────────────┐\n│ sql-master  │ ──► │ sql-dataviz │ ──► │report-gen   │\n│  (数据层)   │     │  (可视化层)  │     │  (报告层)   │\n└─────────────┘     └─────────────┘     └─────────────┘\n      │                   │                   │\n      ▼                   ▼                   ▼\n   SQL 查询           图表生成            HTML 报告\n   数据获取           PNG/HTML            AI 洞察\n   格式转换           Dashboard           数据表格\n```\n\n### 协作模式\n\n| 模式 | 组合 | 适用场景 |\n|------|------|---------|\n| **单独使用** | sql-master | 仅需 SQL 查询/生成/优化 |\n| **可视化** | sql-master + sql-dataviz | SQL 查询 → 图表输出 |\n| **完整流程** | sql-master + sql-dataviz + report-generator | 完整数据分析报告 |\n\n### 🥇 最优使用方式：三 Skill 串联\n\n```python\nfrom scripts.unified_pipeline import UnifiedPipeline\n\nresult = (\n    UnifiedPipeline(\"销售分析\")\n    .from_file(\"sales.csv\")                                    # sql-master: 数据获取\n    .query(\"SELECT region, SUM(sales) as total FROM data GROUP BY region\")\n    .interactive_chart(\"bar\", x_col=\"region\", y_col=\"total\")   # sql-dataviz: 可视化\n    .insights(value_cols=[\"total\"])                            # AI 洞察\n    .report(title=\"销售报告\", output=\"report.html\")            # report-generator: 报告\n)\n```\n\n### 决策指南\n\n```\n你需要什么？\n├─ 仅 SQL 查询/优化 → sql-master 单独使用\n├─ SQL + 图表 → sql-master + sql-dataviz\n├─ 图表 + 报告（无 SQL）→ sql-dataviz + report-generator\n└─ 完整分析报告 → sql-master + sql-dataviz + report-generator ✅ 推荐\n```\n\n---\n\n## 新增功能：统一 Pipeline 编排（三 Skill 端到端）\n\n### `scripts/unified_pipeline.py`\n\n打通 sql-master → sql-dataviz → report-generator 的端到端自动化：\n\n```python\nfrom scripts.unified_pipeline import UnifiedPipeline, analyze_file\n\n# 完整 Pipeline\nresult = (\n    UnifiedPipeline(\"销售分析\")\n    .from_file(\"sales.csv\")                        # 数据源\n    .query(\"SELECT region, SUM(sales) as total FROM data GROUP BY region\")  # SQL\n    .interactive_chart(\"bar\", x_col=\"region\", y_col=\"total\", title=\"区域销售\")  # 交互图\n    .chart(\"line\", x_col=\"region\", y_col=\"total\")              # 静态图 (PNG)\n    .insights(value_cols=[\"total\"])                            # AI 洞察\n    .report(title=\"销售报告\", output=\"report.html\")           # 完整报告\n)\nprint(result.log())\n\n# 一键分析\nresult = analyze_file(\"sales.csv\", output=\"report.html\")\n```\n\n**支持的图表**：静态 PNG（60种）+ 交互式 HTML（12种）\n**支持的洞察**：异常检测 / 趋势 / 相关性 / TOP N / 分布 / 季节性 / 对比\n**支持的报告**：完整 HTML（图表 + 洞察 + 数据表格 + KPI 卡片）\n\n## 新增功能：数据库连接执行层 + 数据 Pipeline\n\n### 1. 数据库连接（scripts/database_connector.py）\n\n支持 SQLite / MySQL / PostgreSQL / SQL Server / ClickHouse / Oracle\n\n```python\nfrom scripts.database_connector import connect_sqlite, connect_mysql, connect_postgresql\n\n# SQLite（本地文件）\nconn = connect_sqlite(\"data/sales.db\")\nresult = conn.execute(\"SELECT region, SUM(amount) FROM sales GROUP BY region\")\nprint(result.df)           # DataFrame 访问\nprint(result.to_dict())   # dict 访问\nresult.to_csv(\"output.csv\")  # 导出 CSV\nresult.to_json(\"output.json\") # 导出 JSON\n\n# MySQL\nconn = connect_mysql(host=\"localhost\", port=3306, username=\"root\", password=\"xxx\", database=\"mydb\")\nresult = conn.execute(\"SELECT * FROM orders WHERE date >= '2024-01-01'\")\nprint(result.summary())   # 可读摘要\n\n# PostgreSQL\nconn = connect_postgresql(host=\"localhost\", database=\"mydb\", username=\"postgres\", password=\"xxx\")\ntables = conn.get_tables()  # 获取所有表名\nschema = conn.get_schema(\"orders\")  # 获取表结构\nconn.close()\n```\n\n### 2. 本地文件数据获取（scripts/file_connector.py）\n\n支持 CSV / Excel / JSON / Parquet / SQLite 等所有主流格式，自动 SQL 查询 + 格式转换\n\n```python\nfrom scripts.file_connector import load_file, load_directory\n\n# 加载本地文件\nfc = load_file(\"data/sales.csv\")        # 单个文件\nfc = load_directory(\"data/reports/\")     # 目录下所有文件\nfc = load_file(\"data/*.csv\")            # 通配符匹配\n\nprint(fc.shape)           # (10000, 12)\nprint(fc.columns)         # ['date', 'region', 'amount', ...]\nprint(fc.df.head())       # DataFrame\n\n# 用途一：SQL 查询（自动建 SQLite 内存表）\nresult = fc.query(\"SELECT region, SUM(amount) as total FROM data GROUP BY region ORDER BY total DESC\")\n\n# 用途二：格式转换\nfc.to_csv(\"output/sales_report.csv\")\nfc.to_excel(\"output/sales_report.xlsx\")\nfc.to_json(\"output/sales_report.json\")\nfc.to_parquet(\"output/sales_report.parquet\")\nfc.to_sqlite(\"output/sales.db\", table_name=\"sales\")\n\n# 用途三：传给 sql-dataviz 画图\nb64 = fc.to_dataviz(\"line\", x_col=\"month\", y_col=\"sales\", title=\"月度销售趋势\")\n```\n\n### 3. SQL Pipeline 流水线（scripts/pipeline.py）\n\n三大用途一气呵成：数据获取 → SQL 查询 → 格式转换 → 可视化 → HTML 报告\n\n```python\nfrom scripts.pipeline import SQLPipeline\n\n# 方式一：从文件开始\np = (\n    SQLPipeline()\n    .from_file(\"data/sales.csv\")\n    .query(\"SELECT region, SUM(amount) as total FROM data GROUP BY region\")\n    .to_csv(\"output/regional_sales.csv\")\n    .to_excel(\"output/regional_sales.xlsx\")\n)\n\n# 方式二：从数据库开始\np = SQLPipeline().from_db(dialect=\"sqlite\", database=\"data.db\")\np.query(\"SELECT * FROM sales WHERE amount > 1000\")\np.query(\"SELECT region, COUNT(*) FROM data GROUP BY region\")\n\n# 方式三：从 DataFrame 开始\nimport pandas as pd\ndf = pd.read_csv(\"data.csv\")\np = SQLPipeline().from_dataframe(df)\n\n# 管道操作\np.query(\"SELECT region, SUM(amount) as total FROM data GROUP BY region\")\np.transform(lambda df: df[df[\"total\"] > 1000])  # 过滤\np.to_dataviz(\"bar\", x_col=\"region\", y_col=\"total\", title=\"区域销售排行\")\np.to_report(title=\"销售分析报告\", output=\"output/report.html\")\np.log()   # 打印执行日志\n```\n\n**Pipeline 完整流程示例：**\n```python\n(\n    SQLPipeline()\n    .from_file(\"sales_2024.csv\")                        # 加载数据\n    .query(\"SELECT * FROM data WHERE region = '华东'\")  # SQL 筛选\n    .to_csv(\"output/east_sales.csv\")                   # 导出 CSV\n    .to_json(\"output/east_sales.json\")                 # 导出 JSON\n    .to_dataviz(\"line\", x_col=\"month\", y_col=\"sales\") # 生成折线图\n    .to_dataviz(\"pie\", x_col=\"product\", y_col=\"amount\") # 生成饼图\n    .to_report(title=\"华东区域销售报告\", output=\"output/report.html\")  # HTML 报告\n)\n```\n\n## 核心原则\n\n**生产级标准**：所有输出的 SQL 必须满足：\n- 注释完整（业务背景 + 性能预期 + 适用数据量级）\n- 明确标注数据库版本和方言\n- 主动提示 NULL 处理、空集合、边界条件\n- 给出多方案时说明各自 trade-off\n\n**分层回答**：同一问题，先给结论，再给原理，最后给深入扩展。自动识别用户水平（初学者/开发者/DBA），调整解释深度。\n\n**可复现**：生成的 SQL 必须附带最小可复现测试数据（DDL + INSERT），确保用户能直接验证。\n\n---\n\n## 功能模块导航\n\n| 场景 | 参考文件 |\n|------|---------|\n| 自然语言 → SQL 生成 | [references/sql-generation.md](references/sql-generation.md) |\n| 慢查询诊断 & 执行计划分析 | [references/query-optimization.md](references/query-optimization.md) |\n| 索引设计策略 | [references/index-design.md](references/index-design.md) |\n| 数仓建模 & 分层架构 | [references/data-warehouse.md](references/data-warehouse.md) |\n| Hive 数据倾斜深度（引擎原理/量化模型/极端场景） | [references/hive-skew-advanced.md](references/hive-skew-advanced.md) |\n| SQL 原理深度（事务/锁/MVCC/Join） | [references/sql-internals.md](references/sql-internals.md) |\n| 多方言差异速查 | [references/dialect-guide.md](references/dialect-guide.md) |\n| DDL 设计规范 | [references/ddl-design.md](references/ddl-design.md) |\n| SQL 安全规范（注入防护/参数化查询） | [references/sql-security.md](references/sql-security.md) |\n| CLI 实操速查（sqlite3/psql/mysql 连接与导入导出） | [references/cli-quickref.md](references/cli-quickref.md) |\n| 查询结果可视化（图表选型/Python 代码/设计原则） | [references/visualization-guide.md](references/visualization-guide.md) |\n\n---\n\n## 工作流程\n\n### 1. 意图识别\n收到请求后，先判断属于哪个场景：\n- **生成类**：用户描述业务需求，需要输出 SQL\n- **优化类**：用户提供现有 SQL 或 EXPLAIN，需要诊断和改写\n- **设计类**：表结构、索引、数仓架构设计\n- **科普类**：原理解释、概念问答\n- **诊断类**：报错信息分析\n- **可视化类**：将查询结果转化为图表 → 加载 [references/visualization-guide.md](references/visualization-guide.md)\n\n### 2. 上下文收集\n生成或优化 SQL 前，主动确认（如未提供）：\n- 数据库类型和版本\n- 关键表的 schema（列名、类型、索引）\n- 数据量级（行数、数据大小）\n- 查询频率和性能目标（P99 < Xms？）\n\n### 3. 输出规范\n\n**SQL 输出模板**：\n```sql\n-- ============================================================\n-- 业务说明：[描述这段 SQL 解决什么业务问题]\n-- 数据库：MySQL 8.0 / PostgreSQL 15 / ...\n-- 性能预期：[预计执行时间，适用数据量级]\n-- 注意事项：[NULL 处理、边界条件、已知限制]\n-- ============================================================\n\nSELECT ...\nFROM ...\nWHERE ...\n```\n\n**优化报告模板**：\n```\n## 问题诊断\n[执行计划中发现的问题，按严重程度排序]\n\n## 优化方案\n### 方案 A（推荐）\n[改写后的 SQL + 原因]\n\n### 方案 B（备选）\n[另一种思路 + 适用场景]\n\n## 预期收益\n[优化前 vs 优化后的性能对比估算]\n\n## 可复现测试\n[最小 DDL + 数据 + 验证步骤]\n```\n\n### 4. 加载参考文件\n根据意图识别结果，读取对应的 references/ 文件获取详细指导。\n\n---\n\n## 快速参考\n\n### 常见性能陷阱（立即识别）\n- `SELECT *` → 明确列名，避免回表\n- `WHERE` 列上有函数 → 索引失效\n- `OR` 连接不同列 → 考虑 UNION ALL\n- `!=` / `NOT IN` → 无法走索引\n- 隐式类型转换 → 索引失效\n- `LIMIT` 大偏移量 → 延迟关联优化\n- `COUNT(*)` vs `COUNT(col)` → NULL 语义差异\n\n### Join 算法选择直觉\n- 小表 JOIN 大表 → Nested Loop（小表驱动）\n- 两个大表等值 JOIN → Hash Join\n- 有序数据等值 JOIN → Merge Join\n- 数据倾斜 → 广播小表 / 加盐打散\n\n### 索引设计口诀\n**最左前缀、区分度高、覆盖查询、避免冗余**\n\n---\n\n## 强制规范（MUST DO / MUST NOT）\n\n借鉴 sql-pro 的约束清单，以下规则在任何情况下都必须遵守：\n\n### ✅ MUST DO\n- 优化前**必须先分析执行计划**（EXPLAIN / EXPLAIN ANALYZE）\n- 优先使用**集合操作**，避免逐行处理（游标/循环）\n- **尽早过滤**：WHERE 条件尽量前置，减少中间结果集\n- 存在性检查用 `EXISTS`，不用 `COUNT(*) > 0`\n- **显式处理 NULL**：IS NULL / IS NOT NULL / COALESCE / NULLIF\n- 为高频查询创建**覆盖索引**\n- 涉及安全场景时，必须使用**参数化查询**，详见 [references/sql-security.md](references/sql-security.md)\n- 跨数据库迁移时，必须标注**方言差异**，详见 [references/dialect-guide.md](references/dialect-guide.md)\n\n### ❌ MUST NOT\n- 不在 WHERE / JOIN 条件列上使用函数（导致索引失效）\n- 不用 `SELECT *`（回表开销 + 隐式依赖）\n- 不用字符串拼接构造 SQL（SQL 注入风险）\n- 不在大表上做无索引的全表扫描\n- 不用 `OFFSET` 大偏移量分页（改用游标/keyset 分页）\n- 不忽略隐式类型转换（导致索引失效 + 数据截断）\n- 不在生产环境直接运行未经 EXPLAIN 验证的复杂查询\n\nFile v1.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn76k6338wpqydkxgdztg6vb9x83hz20\",\n  \"slug\": \"sql-master\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1774588810029\n}\n\nFile v1.0.0:references/cli-quickref.md\n\n# CLI 实操速查\n\n数据库命令行工具的常用操作速查，适合直接上手。\n\n---\n\n## SQLite\n\nSQLite 内置于 Python，零配置，适合本地开发和原型验证。\n\n### 连接与基本操作\n```bash\n# 打开/创建数据库\nsqlite3 mydb.sqlite\n\n# 单行查询（不进入交互模式）\nsqlite3 mydb.sqlite \"SELECT COUNT(*) FROM users;\"\n\n# 交互模式开启表头和列对齐\nsqlite3 -header -column mydb.sqlite\n```\n\n### 数据导入导出\n```bash\n# 导入 CSV\nsqlite3 mydb.sqlite \".mode csv\" \".import data.csv mytable\" \"SELECT COUNT(*) FROM mytable;\"\n\n# 导出为 CSV\nsqlite3 -header -csv mydb.sqlite \"SELECT * FROM orders;\" > orders.csv\n\n# 导出整个数据库为 SQL\nsqlite3 mydb.sqlite .dump > backup.sql\n\n# 从 SQL 文件恢复\nsqlite3 mydb.sqlite < backup.sql\n```\n\n### 常用 Meta 命令\n```\n.tables              -- 列出所有表\n.schema users        -- 查看表结构\n.indexes users       -- 查看索引\n.mode column         -- 列对齐显示\n.headers on          -- 显示列名\n.quit                -- 退出\n```\n\n### 关键 PRAGMA\n```sql\nPRAGMA journal_mode = WAL;        -- 提升并发写入性能\nPRAGMA synchronous = NORMAL;      -- 平衡安全与性能\nPRAGMA foreign_keys = ON;         -- 启用外键约束（默认关闭！）\nPRAGMA cache_size = -64000;       -- 设置缓存 64MB\nPRAGMA temp_store = MEMORY;       -- 临时表放内存\nPRAGMA integrity_check;           -- 数据库完整性检查\n```\n\n---\n\n## PostgreSQL\n\n### 连接\n```bash\n# 基本连接\npsql -h localhost -U myuser -d mydb\n\n# 连接字符串\npsql \"postgresql://user:pass@localhost:5432/mydb?sslmode=require\"\n\n# 单行查询\npsql -h localhost -U myuser -d mydb -c \"SELECT NOW();\"\n\n# 执行 SQL 文件\npsql -h localhost -U myuser -d mydb -f migration.sql\n\n# 列出所有数据库\npsql -l\n```\n\n### 数据导入导出\n```bash\n# 导出整个数据库\npg_dump -h localhost -U myuser mydb > backup.sql\n\n# 导出为自定义格式（推荐，支持并行恢复）\npg_dump -h localhost -U myuser -Fc mydb > backup.dump\n\n# 恢复\npsql -h localhost -U myuser mydb < backup.sql\npg_restore -h localhost -U myuser -d mydb backup.dump\n\n# 导出单表为 CSV\npsql -h localhost -U myuser -d mydb -c \"\\COPY orders TO 'orders.csv' CSV HEADER\"\n\n# 导入 CSV\npsql -h localhost -U myuser -d mydb -c \"\\COPY orders FROM 'orders.csv' CSV HEADER\"\n```\n\n### 常用 Meta 命令\n```\n\\l                   -- 列出数据库\n\\c mydb              -- 切换数据库\n\\dt                  -- 列出表\n\\d users             -- 查看表结构（含索引）\n\\di                  -- 列出索引\n\\df                  -- 列出函数\n\\timing              -- 显示查询耗时\n\\x                   -- 切换扩展显示模式（宽表友好）\n\\e                   -- 用编辑器编辑查询\n\\q                   -- 退出\n```\n\n### 性能诊断\n```sql\n-- 查看慢查询（需开启 pg_stat_statements）\nSELECT query, calls, mean_exec_time, total_exec_time\nFROM pg_stat_statements\nORDER BY mean_exec_time DESC\nLIMIT 10;\n\n-- 查看表大小\nSELECT relname, pg_size_pretty(pg_total_relation_size(relid))\nFROM pg_stat_user_tables\nORDER BY pg_total_relation_size(relid) DESC;\n\n-- 查看锁等待\nSELECT pid, wait_event_type, wait_event, query\nFROM pg_stat_activity\nWHERE wait_event IS NOT NULL;\n\n-- 终止慢查询\nSELECT pg_terminate_backend(pid)\nFROM pg_stat_activity\nWHERE query_start < NOW() - INTERVAL '5 minutes'\n  AND state = 'active';\n```\n\n---\n\n## MySQL / MariaDB\n\n### 连接\n```bash\n# 基本连接\nmysql -h localhost -u myuser -p mydb\n\n# 单行查询\nmysql -h localhost -u myuser -p mydb -e \"SELECT NOW();\"\n\n# 执行 SQL 文件\nmysql -h localhost -u myuser -p mydb < migration.sql\n\n# 不显示密码警告（脚本用）\nmysql --defaults-extra-file=~/.my.cnf mydb -e \"SELECT 1;\"\n```\n\n### ~/.my.cnf 配置（避免明文密码）\n```ini\n[client]\nhost=localhost\nuser=myuser\npassword=mypassword\n```\n\n### 数据导入导出\n```bash\n# 导出整个数据库\nmysqldump -h localhost -u myuser -p mydb > backup.sql\n\n# 导出单表\nmysqldump -h localhost -u myuser -p mydb orders > orders.sql\n\n# 恢复\nmysql -h localhost -u myuser -p mydb < backup.sql\n\n# 导出为 CSV（需 FILE 权限）\nmysql -h localhost -u myuser -p mydb -e \\\n  \"SELECT * FROM orders INTO OUTFILE '/tmp/orders.csv' FIELDS TERMINATED BY ',' ENCLOSED BY '\\\"' LINES TERMINATED BY '\\n';\"\n```\n\n### 常用 Meta 命令\n```sql\nSHOW DATABASES;\nUSE mydb;\nSHOW TABLES;\nDESCRIBE users;           -- 查看表结构\nSHOW CREATE TABLE users;  -- 查看建表语句（含索引）\nSHOW INDEX FROM users;    -- 查看索引详情\nSHOW PROCESSLIST;         -- 查看当前连接和查询\nSHOW VARIABLES LIKE 'innodb%';  -- 查看 InnoDB 配置\n```\n\n### 性能诊断\n```sql\n-- 查看慢查询日志状态\nSHOW VARIABLES LIKE 'slow_query%';\nSHOW VARIABLES LIKE 'long_query_time';\n\n-- 开启慢查询（临时）\nSET GLOBAL slow_query_log = 'ON';\nSET GLOBAL long_query_time = 1;  -- 超过 1 秒记录\n\n-- 查看 InnoDB 状态（锁、事务）\nSHOW ENGINE INNODB STATUS\\G\n\n-- 查看当前锁等待\nSELECT * FROM information_schema.INNODB_LOCK_WAITS;\n\n-- 终止慢查询\nKILL QUERY <pid>;\n```\n\n---\n\n## 通用技巧\n\n### EXPLAIN 快速解读\n```sql\n-- MySQL\nEXPLAIN SELECT ...;\nEXPLAIN FORMAT=JSON SELECT ...;   -- 更详细\n\n-- PostgreSQL\nEXPLAIN SELECT ...;\nEXPLAIN (ANALYZE, BUFFERS) SELECT ...;  -- 实际执行 + 缓存命中\n\n-- 关注指标\n-- type: ALL（全表扫描，危险）→ ref/eq_ref/const（索引，好）\n-- rows: 预估扫描行数，越小越好\n-- Extra: Using filesort / Using temporary（需优化）\n```\n\n### 快速生成测试数据\n```sql\n-- MySQL：生成 10000 行测试数据\nINSERT INTO test_table (name, value, created_at)\nSELECT\n  CONCAT('user_', seq),\n  FLOOR(RAND() * 1000),\n  DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 365) DAY)\nFROM (\n  SELECT @row := @row + 1 AS seq\n  FROM information_schema.columns, (SELECT @row := 0) r\n  LIMIT 10000\n) t;\n\n-- PostgreSQL：generate_series\nINSERT INTO test_table (name, value, created_at)\nSELECT\n  'user_' || i,\n  (RANDOM() * 1000)::INT,\n  NOW() - (RANDOM() * 365 || ' days')::INTERVAL\nFROM generate_series(1, 10000) AS i;\n\n-- SQLite\nWITH RECURSIVE cnt(x) AS (\n  SELECT 1 UNION ALL SELECT x+1 FROM cnt WHERE x < 10000\n)\nINSERT INTO test_table (name, value)\nSELECT 'user_' || x, ABS(RANDOM() % 1000) FROM cnt;\n```\n\nFile v1.0.0:references/data-warehouse.md\n\n# 数仓建模 & 分层架构\n\n## 数仓分层架构（标准）\n\n```\n原始数据\n    ↓\nODS（Operational Data Store）操作数据层\n    ↓\nDWD（Data Warehouse Detail）明细数据层\n    ↓\nDWS（Data Warehouse Summary）汇总数据层\n    ↓\nADS（Application Data Store）应用数据层\n    ↓\n报表 / BI / 应用\n```\n\n### 各层职责\n\n| 层 | 职责 | 特点 |\n|----|------|------|\n| ODS | 原始数据落地，不做业务加工 | 保留原始字段，全量或增量同步 |\n| DWD | 数据清洗、标准化、维度关联 | 1:1 对应业务事实，最细粒度 |\n| DWS | 按主题聚合，轻度汇总 | 按天/周/月聚合，宽表 |\n| ADS | 面向具体应用的指标 | 直接支撑报表，高度聚合 |\n\n---\n\n## 维度建模\n\n### 星型模型 vs 雪花模型\n\n```\n星型模型：\n  事实表 ← 直接关联 → 维度表（维度表不再关联其他维度表）\n  优点：查询简单，JOIN 少，性能好\n  缺点：维度表可能有冗余\n\n雪花模型：\n  事实表 ← 维度表 ← 子维度表（维度表继续规范化）\n  优点：存储空间小，无冗余\n  缺点：JOIN 多，查询复杂，性能差\n\n实践建议：OLAP 场景优先用星型模型\n```\n\n### 事实表设计\n```sql\n-- 事实表：记录业务事件，包含度量值和外键\nCREATE TABLE dwd_order_detail (\n    order_id        BIGINT          COMMENT '订单ID',\n    user_id         BIGINT          COMMENT '用户ID（关联用户维度）',\n    product_id      BIGINT          COMMENT '商品ID（关联商品维度）',\n    date_id         INT             COMMENT '日期ID（关联日期维度，格式 20240101）',\n    -- 度量值\n    quantity        INT             COMMENT '购买数量',\n    unit_price      DECIMAL(10,2)   COMMENT '单价',\n    discount_amount DECIMAL(10,2)   COMMENT '优惠金额',\n    actual_amount   DECIMAL(10,2)   COMMENT '实付金额',\n    -- 分区字段\n    dt              STRING          COMMENT '数据日期分区 yyyy-MM-dd'\n)\nCOMMENT '订单明细事实表'\nPARTITIONED BY (dt STRING)\nSTORED AS ORC;\n```\n\n### 维度表设计\n```sql\n-- 维度表：描述业务实体的属性\nCREATE TABLE dim_user (\n    user_id         BIGINT          COMMENT '用户ID',\n    username        STRING          COMMENT '用户名',\n    register_date   STRING          COMMENT '注册日期',\n    city            STRING          COMMENT '城市',\n    age_group       STRING          COMMENT '年龄段（18-24/25-34/...）',\n    user_level      STRING          COMMENT '用户等级（普通/银牌/金牌/钻石）',\n    -- SCD（缓慢变化维度）字段\n    start_date      STRING          COMMENT '该版本生效日期',\n    end_date        STRING          COMMENT '该版本失效日期（9999-12-31 表示当前有效）',\n    is_current      TINYINT         COMMENT '是否当前版本（1=是）'\n)\nCOMMENT '用户维度表'\nSTORED AS ORC;\n```\n\n---\n\n## 缓慢变化维度（SCD）\n\n### SCD Type 1：直接覆盖\n```sql\n-- 适用：不需要历史，只关心当前值\nUPDATE dim_user SET city = '上海' WHERE user_id = 1001;\n-- 缺点：历史数据丢失\n```\n\n### SCD Type 2：新增版本（最常用）\n```sql\n-- 适用：需要保留历史，分析不同时期的属性\n-- 用户从北京迁到上海时，新增一行，旧行标记失效\n\n-- 失效旧版本\nUPDATE dim_user\nSET end_date = '2024-01-14', is_current = 0\nWHERE user_id = 1001 AND is_current = 1;\n\n-- 插入新版本\nINSERT INTO dim_user VALUES\n(1001, '张三', '2020-01-01', '上海', '25-34', '金牌', '2024-01-15', '9999-12-31', 1);\n\n-- 查询某时间点的用户属性（点查）\nSELECT * FROM dim_user\nWHERE user_id = 1001\n  AND start_date <= '2024-01-10'\n  AND end_date > '2024-01-10';\n```\n\n---\n\n## 数仓常用 SQL 模式\n\n### 增量数据处理（每日 ETL）\n```sql\n-- 场景：每天增量同步订单数据到 DWD\n-- 策略：按分区覆盖写（INSERT OVERWRITE）\n\nINSERT OVERWRITE TABLE dwd_order_detail PARTITION (dt = '2024-01-15')\nSELECT\n    o.order_id,\n    o.user_id,\n    o.product_id,\n    DATE_FORMAT(o.create_time, '%Y%m%d')    AS date_id,\n    o.quantity,\n    o.unit_price,\n    COALESCE(o.discount_amount, 0)          AS discount_amount,\n    o.actual_amount,\n    '2024-01-15'                            AS dt\nFROM ods_orders o\nWHERE DATE(o.create_time) = '2024-01-15'\n  AND o.status != 'cancelled';\n```\n\n### 拉链表（全量历史快照）\n```sql\n-- 场景：记录每天的用户状态快照，支持任意时间点查询\n-- 比 SCD Type 2 更简单，适合数仓\n\n-- 每天生成当天快照\nINSERT INTO dws_user_snapshot PARTITION (dt = '2024-01-15')\nSELECT\n    user_id,\n    username,\n    status,\n    vip_level,\n    total_orders,\n    total_amount,\n    '2024-01-15' AS dt\nFROM (\n    -- 昨天快照 + 今天变更 = 今天快照\n    SELECT * FROM dws_user_snapshot WHERE dt = '2024-01-14'\n    UNION ALL\n    SELECT user_id, username, status, vip_level, ...\n    FROM ods_user_changes WHERE dt = '2024-01-15'\n) t\n-- 如果同一用户有多条，取最新的\nQUALIFY ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY dt DESC) = 1;\n```\n\n### 漏斗分析\n```sql\n-- 场景：分析用户从浏览 → 加购 → 下单 → 支付的转化漏斗\nWITH funnel AS (\n    SELECT\n        user_id,\n        MAX(CASE WHEN event = 'view'    THEN 1 ELSE 0 END) AS step1_view,\n        MAX(CASE WHEN event = 'add_cart' THEN 1 ELSE 0 END) AS step2_cart,\n        MAX(CASE WHEN event = 'order'   THEN 1 ELSE 0 END) AS step3_order,\n        MAX(CASE WHEN event = 'pay'     THEN 1 ELSE 0 END) AS step4_pay\n    FROM user_events\n    WHERE dt = '2024-01-15'\n    GROUP BY user_id\n)\nSELECT\n    SUM(step1_view)                                     AS view_cnt,\n    SUM(step2_cart)                                     AS cart_cnt,\n    SUM(step3_order)                                    AS order_cnt,\n    SUM(step4_pay)                                      AS pay_cnt,\n    ROUND(SUM(step2_cart) / SUM(step1_view) * 100, 2)  AS view_to_cart_rate,\n    ROUND(SUM(step3_order) / SUM(step2_cart) * 100, 2) AS cart_to_order_rate,\n    ROUND(SUM(step4_pay) / SUM(step3_order) * 100, 2)  AS order_to_pay_rate\nFROM funnel;\n```\n\n### 留存分析\n```sql\n-- 场景：计算用户次日留存、7日留存、30日留存\nWITH new_users AS (\n    -- 每天新注册用户\n    SELECT user_id, DATE(register_time) AS register_date\n    FROM users\n    WHERE register_time >= '2024-01-01'\n),\nactive_users AS (\n    -- 每天活跃用户\n    SELECT DISTINCT user_id, DATE(login_time) AS active_date\n    FROM user_logins\n)\nSELECT\n    n.register_date,\n    COUNT(DISTINCT n.user_id)                                           AS new_user_cnt,\n    COUNT(DISTINCT CASE WHEN DATEDIFF(a1.active_date, n.register_date) = 1  THEN n.user_id END) AS day1_retain,\n    COUNT(DISTINCT CASE WHEN DATEDIFF(a7.active_date, n.register_date) = 7  THEN n.user_id END) AS day7_retain,\n    COUNT(DISTINCT CASE WHEN DATEDIFF(a30.active_date, n.register_date) = 30 THEN n.user_id END) AS day30_retain\nFROM new_users n\nLEFT JOIN active_users a1  ON n.user_id = a1.user_id AND DATEDIFF(a1.active_date, n.register_date) = 1\nLEFT JOIN active_users a7  ON n.user_id = a7.user_id AND DATEDIFF(a7.active_date, n.register_date) = 7\nLEFT JOIN active_users a30 ON n.user_id = a30.user_id AND DATEDIFF(a30.active_date, n.register_date) = 30\nGROUP BY n.register_date\nORDER BY n.register_date;\n```\n\n---\n\n## 数据倾斜处理（Hive/Spark）\n\n```sql\n-- 症状：某些 Reduce 任务跑很久，其他早就完成了\n-- 原因：JOIN 或 GROUP BY 的 key 分布不均\n\n-- 方案 1：广播小表（Map Join）\nSELECT /*+ MAPJOIN(dim_user) */ o.*, u.username\nFROM dwd_orders o\nJOIN dim_user u ON o.user_id = u.user_id;\n\n-- 方案 2：加盐打散（处理 GROUP BY 倾斜）\n-- 第一步：加随机盐，局部聚合\nSELECT\n    CONCAT(user_id, '_', FLOOR(RAND() * 10)) AS salted_key,\n    SUM(amount) AS partial_sum\nFROM orders\nGROUP BY CONCAT(user_id, '_', FLOOR(RAND() * 10));\n\n-- 第二步：去盐，全局聚合\nSELECT\n    SPLIT(salted_key, '_')[0] AS user_id,\n    SUM(partial_sum) AS total_amount\nFROM (上面的结果)\nGROUP BY SPLIT(salted_key, '_')[0];\n\n-- 方案 3：Hive 配置（自动处理倾斜）\nSET hive.optimize.skewjoin = true;\nSET hive.skewjoin.key = 100000;  -- 超过此行数认为倾斜\n```\n\nFile v1.0.0:references/ddl-design.md\n\n# DDL 设计规范\n\n## 表设计原则\n\n### 命名规范\n```\n表名：小写 + 下划线，加业务前缀\n  ods_orders          原始订单表\n  dwd_order_detail    订单明细事实表\n  dim_user            用户维度表\n  dws_user_daily      用户日汇总表\n  ads_funnel_report   漏斗报表\n\n列名：小写 + 下划线，语义清晰\n  user_id（不用 uid）\n  create_time（不用 ctime）\n  is_deleted（布尔用 is_ 前缀）\n  order_status（不用 status，加业务前缀）\n```\n\n### 字段类型选择\n\n```sql\n-- 整数：按范围选最小类型（节省存储，提升缓存命中）\nTINYINT     -- 1字节，-128~127，适合状态码、等级\nSMALLINT    -- 2字节，适合年份、小范围数值\nINT         -- 4字节，适合普通 ID（<21亿）\nBIGINT      -- 8字节，适合雪花ID、大流水号\n\n-- 字符串\nCHAR(n)     -- 定长，适合固定长度（手机号、身份证）\nVARCHAR(n)  -- 变长，适合普通文本，n 不要设太大（影响内存分配）\nTEXT        -- 大文本，不能建普通索引，不能作为主键\n\n-- 金额：绝对不用 FLOAT/DOUBLE（精度丢失）\nDECIMAL(10,2)   -- 精确小数，10位总长，2位小数\n-- 或者存分（整数），避免小数运算\nBIGINT          -- 单位：分，1元 = 100\n\n-- 时间\nDATETIME        -- MySQL，不含时区，'2024-01-15 10:30:00'\nTIMESTAMP       -- MySQL，含时区转换，范围到2038年（慎用）\nTIMESTAMPTZ     -- PostgreSQL，推荐，含时区\n\n-- 布尔\nTINYINT(1)      -- MySQL（没有原生 BOOLEAN）\nBOOLEAN         -- PostgreSQL\n```\n\n---\n\n## 生产级建表模板\n\n### 业务表（MySQL）\n```sql\nCREATE TABLE `orders` (\n    `id`            BIGINT          NOT NULL AUTO_INCREMENT  COMMENT '主键ID',\n    `order_no`      VARCHAR(32)     NOT NULL                 COMMENT '订单号（业务唯一标识）',\n    `user_id`       BIGINT          NOT NULL                 COMMENT '用户ID',\n    `product_id`    BIGINT          NOT NULL                 COMMENT '商品ID',\n    `quantity`      INT             NOT NULL DEFAULT 1       COMMENT '购买数量',\n    `unit_price`    DECIMAL(10,2)   NOT NULL                 COMMENT '单价（元）',\n    `discount`      DECIMAL(10,2)   NOT NULL DEFAULT 0.00    COMMENT '优惠金额（元）',\n    `actual_amount` DECIMAL(10,2)   NOT NULL                 COMMENT '实付金额（元）',\n    `status`        TINYINT         NOT NULL DEFAULT 0       COMMENT '订单状态：0待支付 1已支付 2已发货 3已完成 4已取消',\n    `remark`        VARCHAR(500)    DEFAULT NULL             COMMENT '备注',\n    `is_deleted`    TINYINT(1)      NOT NULL DEFAULT 0       COMMENT '软删除：0正常 1已删除',\n    `create_time`   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP  COMMENT '创建时间',\n    `update_time`   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `uk_order_no` (`order_no`),\n    KEY `idx_user_id_status` (`user_id`, `status`),\n    KEY `idx_create_time` (`create_time`)\n) ENGINE=InnoDB\n  DEFAULT CHARSET=utf8mb4\n  COLLATE=utf8mb4_unicode_ci\n  COMMENT='订单表';\n```\n\n### 日志/流水表（大数据量）\n```sql\n-- 大流水表：分区 + 不建过多索引\nCREATE TABLE `user_behavior_logs` (\n    `id`            BIGINT          NOT NULL AUTO_INCREMENT  COMMENT '主键',\n    `user_id`       BIGINT          NOT NULL                 COMMENT '用户ID',\n    `event_type`    VARCHAR(50)     NOT NULL                 COMMENT '事件类型',\n    `event_data`    JSON            DEFAULT NULL             COMMENT '事件数据',\n    `ip`            VARCHAR(45)     DEFAULT NULL             COMMENT 'IP地址（支持IPv6）',\n    `user_agent`    VARCHAR(500)    DEFAULT NULL             COMMENT 'UA',\n    `create_time`   DATETIME(3)     NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间（毫秒精度）',\n    PRIMARY KEY (`id`, `create_time`),   -- 分区键必须在主键中\n    KEY `idx_user_event` (`user_id`, `event_type`, `create_time`)\n) ENGINE=InnoDB\n  DEFAULT CHARSET=utf8mb4\n  COMMENT='用户行为日志'\n  PARTITION BY RANGE (TO_DAYS(create_time)) (\n    PARTITION p202401 VALUES LESS THAN (TO_DAYS('2024-02-01')),\n    PARTITION p202402 VALUES LESS THAN (TO_DAYS('2024-03-01')),\n    PARTITION p_future VALUES LESS THAN MAXVALUE\n  );\n```\n\n### 配置/字典表\n```sql\nCREATE TABLE `sys_config` (\n    `id`            INT             NOT NULL AUTO_INCREMENT  COMMENT '主键',\n    `config_key`    VARCHAR(100)    NOT NULL                 COMMENT '配置键',\n    `config_value`  TEXT            NOT NULL                 COMMENT '配置值',\n    `config_type`   VARCHAR(20)     NOT NULL DEFAULT 'string' COMMENT '值类型：string/int/json/boolean',\n    `description`   VARCHAR(500)    DEFAULT NULL             COMMENT '说明',\n    `is_enabled`    TINYINT(1)      NOT NULL DEFAULT 1       COMMENT '是否启用',\n    `create_time`   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    `update_time`   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `uk_config_key` (`config_key`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统配置表';\n```\n\n---\n\n## 在线 DDL 变更（生产安全）\n\n### MySQL 在线 DDL\n```sql\n-- 查看 DDL 是否支持 INPLACE（不锁表）\n-- Algorithm=INPLACE：不重建表，不锁表（最好）\n-- Algorithm=COPY：重建表，锁表（最差）\n\n-- 加列（MySQL 8.0 支持 INSTANT，瞬间完成）\nALTER TABLE orders\n    ADD COLUMN source VARCHAR(20) DEFAULT NULL COMMENT '来源渠道'\n    AFTER status,\n    ALGORITHM=INSTANT;  -- MySQL 8.0+\n\n-- 加索引（INPLACE，不锁表）\nALTER TABLE orders\n    ADD INDEX idx_product_id (product_id),\n    ALGORITHM=INPLACE,\n    LOCK=NONE;\n\n-- 修改列类型（通常需要 COPY，会锁表，用 pt-osc 代替）\n-- 生产环境用 pt-online-schema-change 或 gh-ost\n-- pt-osc: pt-online-schema-change --alter \"MODIFY COLUMN amount DECIMAL(12,2)\" D=db,t=orders\n\n-- 删除列（INPLACE）\nALTER TABLE orders\n    DROP COLUMN old_column,\n    ALGORITHM=INPLACE,\n    LOCK=NONE;\n```\n\n### 字符集迁移（utf8 → utf8mb4）\n```sql\n-- MySQL 的 utf8 实际上是 utf8mb3（不支持 emoji）\n-- 生产迁移步骤：\n\n-- 1. 修改数据库默认字符集\nALTER DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n\n-- 2. 修改表（在线 DDL）\nALTER TABLE orders\n    CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,\n    ALGORITHM=INPLACE,\n    LOCK=NONE;\n\n-- 3. 修改连接字符集\nSET NAMES utf8mb4;\n-- 或在连接字符串中：charset=utf8mb4\n```\n\n---\n\n## 常见设计反模式\n\n```sql\n-- ❌ 反模式 1：用字符串存枚举值（无约束，难查询）\nstatus VARCHAR(20)  -- 'active', 'inactive', 'pending'...\n-- ✅ 用 TINYINT + 注释说明，或 ENUM（但 ENUM 修改成本高）\nstatus TINYINT COMMENT '1=active 2=inactive 3=pending'\n\n-- ❌ 反模式 2：用逗号分隔存多值\ntags VARCHAR(500)  -- '1,2,3,4'\n-- ✅ 单独建关联表\nCREATE TABLE user_tags (user_id BIGINT, tag_id INT, PRIMARY KEY(user_id, tag_id));\n\n-- ❌ 反模式 3：用 NULL 表示业务含义\ndiscount DECIMAL(10,2)  -- NULL 表示\"无折扣\"\n-- ✅ 用默认值 0，NULL 只表示\"未知/未填写\"\ndiscount DECIMAL(10,2) NOT NULL DEFAULT 0.00\n\n-- ❌ 反模式 4：主键用 UUID 字符串\nid VARCHAR(36) PRIMARY KEY  -- 'a1b2c3d4-...'\n-- ✅ 用 BIGINT 自增，或 BIGINT 雪花ID\nid BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY\n\n-- ❌ 反模式 5：没有 create_time / update_time\n-- ✅ 所有业务表必须有这两个字段，便于排查问题和增量同步\n```\n\nFile v1.0.0:references/dialect-guide.md\n\n# 多方言差异速查\n\n## 方言对比矩阵\n\n| 功能 | MySQL | PostgreSQL | Hive | Spark SQL | ClickHouse |\n|------|-------|-----------|------|-----------|------------|\n| 字符串拼接 | `CONCAT(a,b)` | `a \\|\\| b` | `CONCAT(a,b)` | `CONCAT(a,b)` | `concat(a,b)` |\n| 日期格式化 | `DATE_FORMAT(d,'%Y-%m')` | `TO_CHAR(d,'YYYY-MM')` | `DATE_FORMAT(d,'yyyy-MM')` | `DATE_FORMAT(d,'yyyy-MM')` | `formatDateTime(d,'%Y-%m')` |\n| 当前时间 | `NOW()` | `NOW()` | `CURRENT_TIMESTAMP` | `CURRENT_TIMESTAMP` | `now()` |\n| 日期差 | `DATEDIFF(a,b)` | `a - b` | `DATEDIFF(a,b)` | `DATEDIFF(a,b)` | `dateDiff('day',b,a)` |\n| 字符串截取 | `SUBSTRING(s,1,3)` | `SUBSTRING(s,1,3)` | `SUBSTR(s,1,3)` | `SUBSTR(s,1,3)` | `substring(s,1,3)` |\n| 条件表达式 | `IF(cond,a,b)` | `CASE WHEN` | `IF(cond,a,b)` | `IF(cond,a,b)` | `if(cond,a,b)` |\n| 行号 | `ROW_NUMBER()` | `ROW_NUMBER()` | `ROW_NUMBER()` | `ROW_NUMBER()` | `row_number()` |\n| UPSERT | `ON DUPLICATE KEY` | `ON CONFLICT DO UPDATE` | 不支持 | 不支持 | `INSERT OR REPLACE` |\n| 递归 CTE | 8.0+ 支持 | 支持 | 不支持 | 支持 | 支持 |\n| JSON 支持 | 5.7+ | 原生 JSONB | 有限 | 有限 | 有限 |\n| 窗口函数 | 8.0+ | 完整支持 | 完整支持 | 完整支持 | 完整支持 |\n\n---\n\n## MySQL 特有语法\n\n```sql\n-- LIMIT 语法\nSELECT * FROM t LIMIT 10;           -- 前10行\nSELECT * FROM t LIMIT 10, 20;       -- 跳过10行，取20行（注意：偏移量在前）\nSELECT * FROM t LIMIT 20 OFFSET 10; -- 等价写法\n\n-- GROUP_CONCAT（行转列）\nSELECT user_id, GROUP_CONCAT(tag ORDER BY tag SEPARATOR ',') AS tags\nFROM user_tags GROUP BY user_id;\n\n-- ON DUPLICATE KEY UPDATE\nINSERT INTO counters (key, cnt) VALUES ('pv', 1)\nON DUPLICATE KEY UPDATE cnt = cnt + 1;\n\n-- 日期函数\nSELECT\n    DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'),  -- 格式化\n    DATE_ADD(NOW(), INTERVAL 7 DAY),           -- 加7天\n    DATE_SUB(NOW(), INTERVAL 1 MONTH),         -- 减1月\n    LAST_DAY(NOW()),                           -- 当月最后一天\n    WEEKDAY(NOW()),                            -- 星期几（0=周一）\n    QUARTER(NOW());                            -- 季度\n\n-- 字符串函数\nSELECT\n    FIND_IN_SET('b', 'a,b,c'),    -- 返回 2（位置）\n    FIELD('b', 'a', 'b', 'c'),    -- 返回 2（位置）\n    ELT(2, 'a', 'b', 'c');        -- 返回 'b'（按位置取值）\n```\n\n---\n\n## PostgreSQL 特有语法\n\n```sql\n-- RETURNING（返回被修改的行）\nINSERT INTO users (name) VALUES ('张三') RETURNING id, name;\nUPDATE orders SET status = 'paid' WHERE id = 1 RETURNING *;\nDELETE FROM logs WHERE id = 1 RETURNING *;\n\n-- 数组操作\nSELECT ARRAY[1,2,3];\nSELECT '{1,2,3}'::INT[];\nSELECT array_agg(id ORDER BY id) FROM users;  -- 聚合为数组\nSELECT unnest(ARRAY[1,2,3]);                  -- 展开数组为行\n\n-- JSONB 操作\nSELECT data->'name' AS name FROM users;           -- 取 JSON 字段（返回 JSON）\nSELECT data->>'name' AS name FROM users;          -- 取 JSON 字段（返回文本）\nSELECT data#>>'{address,city}' AS city FROM users; -- 嵌套路径\nUPDATE users SET data = data || '{\"vip\":true}';   -- 合并 JSON\nCREATE INDEX idx_json ON users USING GIN(data);   -- JSON 索引\n\n-- 窗口函数扩展\nSELECT\n    NTILE(4) OVER (ORDER BY amount) AS quartile,  -- 四分位\n    PERCENT_RANK() OVER (ORDER BY amount),         -- 百分比排名\n    CUME_DIST() OVER (ORDER BY amount)             -- 累积分布\nFROM orders;\n\n-- 物化视图\nCREATE MATERIALIZED VIEW mv_daily_stats AS\nSELECT DATE(create_time), COUNT(*), SUM(amount)\nFROM orders GROUP BY DATE(create_time);\n\nREFRESH MATERIALIZED VIEW CONCURRENTLY mv_daily_stats;  -- 不锁表刷新\n\n-- 分区表（声明式分区）\nCREATE TABLE orders (\n    id BIGSERIAL,\n    create_time TIMESTAMPTZ NOT NULL,\n    amount NUMERIC\n) PARTITION BY RANGE (create_time);\n\nCREATE TABLE orders_2024_01 PARTITION OF orders\nFOR VALUES FROM ('2024-01-01') TO ('2024-02-01');\n```\n\n---\n\n## Hive / Spark SQL 特有语法\n\n```sql\n-- 分区操作\nSHOW PARTITIONS table_name;\nALTER TABLE t ADD PARTITION (dt='2024-01-15');\nALTER TABLE t DROP PARTITION (dt='2024-01-01');\n\n-- 动态分区插入\nSET hive.exec.dynamic.partition = true;\nSET hive.exec.dynamic.partition.mode = nonstrict;\n\nINSERT OVERWRITE TABLE dwd_orders PARTITION (dt)\nSELECT *, DATE(create_time) AS dt FROM ods_orders;\n\n-- LATERAL VIEW（展开数组/Map）\nSELECT user_id, tag\nFROM user_tags\nLATERAL VIEW EXPLODE(tags_array) tmp AS tag;\n\n-- LATERAL VIEW OUTER（保留空数组的行）\nSELECT user_id, tag\nFROM user_tags\nLATERAL VIEW OUTER EXPLODE(tags_array) tmp AS tag;\n\n-- collect_set / collect_list（聚合为数组）\nSELECT user_id,\n    collect_set(tag)  AS unique_tags,   -- 去重\n    collect_list(tag) AS all_tags       -- 不去重\nFROM user_tags GROUP BY user_id;\n\n-- 窗口函数（Hive 特有）\nSELECT *,\n    FIRST_VALUE(amount) OVER (PARTITION BY user_id ORDER BY create_time) AS first_order_amount,\n    LAST_VALUE(amount)  OVER (PARTITION BY user_id ORDER BY create_time\n                              ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS last_order_amount\nFROM orders;\n\n-- Hive 性能配置\nSET hive.vectorized.execution.enabled = true;   -- 向量化执行\nSET hive.cbo.enable = true;                     -- 开启 CBO\nSET mapreduce.job.reduces = 200;                -- 设置 Reduce 数量\nSET hive.exec.parallel = true;                  -- 并行执行无依赖 Stage\n```\n\n---\n\n## ClickHouse 特有语法\n\n```sql\n-- 引擎选择（最重要的设计决策）\n-- MergeTree：最常用，支持排序键、分区\nCREATE TABLE orders (\n    order_id    UInt64,\n    user_id     UInt32,\n    amount      Float64,\n    create_time DateTime\n) ENGINE = MergeTree()\nPARTITION BY toYYYYMM(create_time)   -- 按月分区\nORDER BY (user_id, create_time)       -- 排序键（也是稀疏索引）\nTTL create_time + INTERVAL 1 YEAR;   -- 数据过期自动删除\n\n-- ReplacingMergeTree：去重（异步，不保证实时）\nENGINE = ReplacingMergeTree(version_col)\n\n-- AggregatingMergeTree：预聚合\nENGINE = AggregatingMergeTree()\n\n-- 物化视图（实时预聚合）\nCREATE MATERIALIZED VIEW mv_daily_orders\nENGINE = SummingMergeTree()\nPARTITION BY toYYYYMM(order_date)\nORDER BY (order_date, user_id)\nAS SELECT\n    toDate(create_time) AS order_date,\n    user_id,\n    count() AS order_cnt,\n    sum(amount) AS total_amount\nFROM orders\nGROUP BY order_date, user_id;\n\n-- 数组函数\nSELECT arrayJoin([1,2,3]);                    -- 展开数组\nSELECT groupArray(amount) FROM orders;         -- 聚合为数组\nSELECT arraySum([1,2,3]);                      -- 数组求和\nSELECT arrayFilter(x -> x > 10, [5,15,20]);   -- 数组过滤\n\n-- 近似计算（大数据量下极快）\nSELECT uniq(user_id) FROM orders;              -- 近似去重计数\nSELECT quantile(0.99)(amount) FROM orders;     -- 近似分位数\nSELECT topK(10)(product_id) FROM orders;       -- 近似 Top K\n```\n\nFile v1.0.0:references/hive-skew-advanced.md\n\n# Hive 数据倾斜：大厂生产级深化补充\n\n## 目录\n1. [执行引擎底层原理（MR vs Tez vs Spark）](#1-执行引擎底层原理)\n2. [倾斜量化评估模型](#2-倾斜量化评估模型)\n3. [极端场景与边界案例](#3-极端场景与边界案例)\n4. [动态智能倾斜检测](#4-动态智能倾斜检测)\n5. [与上下游生态联动](#5-与上下游生态联动)\n\n---\n\n## 1. 执行引擎底层原理\n\n### 1.1 三种引擎的倾斜发生位置\n\n#### MapReduce 引擎\n```\nMap Task（并行）\n    ↓ Shuffle（按 key hash 分发）← 倾斜在这里产生\nReduce Task（并行）\n    ↓\n输出\n\n倾斜根因：\n  hash(key) % numReducers 决定数据去哪个 Reducer\n  user_id=1 的 400 万行全部 hash 到同一个 Reducer\n  该 Reducer 处理 400 万行，其他 Reducer 处理几万行\n  → 整个 Job 等最慢的那个 Reducer 完成\n```\n\n#### Tez 引擎（DAG 模型）\n```\nTez 把 MR Job 拆成 DAG（有向无环图），每个节点叫 Vertex\n\n典型 GROUP BY 的 DAG：\n  Map Vertex（读数据 + 局部聚合）\n      ↓ Shuffle Edge（SCATTER_GATHER，按 key 分发）← 倾斜在这里\n  Reduce Vertex（全局聚合）\n      ↓\n  Output Vertex\n\n典型 JOIN 的 DAG：\n  Map Vertex A（扫描大表）\n  Map Vertex B（扫描小表）\n      ↓ Broadcast Edge（小表广播）← MapJoin 走这条边，无倾斜\n  Map Join Vertex（在 Map 端完成 JOIN）\n\n关键差异：\n  Tez 的 SCATTER_GATHER Edge 支持 Auto Parallelism\n  → 运行时动态调整 Reduce Vertex 的并发度\n  → 比 MR 的静态 numReducers 更灵活\n\n查看 Tez DAG：\n  Tez UI（http://<rm>:8080/tez-ui）→ DAG Details → Vertex 耗时对比\n  耗时最长的 Vertex 就是倾斜所在\n```\n\n```sql\n-- 查看 Tez 执行计划（比 EXPLAIN 更详细）\nEXPLAIN FORMATTED\nSELECT user_id, COUNT(*), SUM(amount)\nFROM ods_orders\nGROUP BY user_id;\n-- 输出中找 \"Reduce Operator Tree\" 和 \"Statistics\"\n-- Statistics: Num rows: X Data size: Y → 估算各 Vertex 数据量\n```\n\n#### Spark 引擎（Hive on Spark）\n```\nSpark 把 Job 拆成 Stage，Stage 之间有 Shuffle\n\n典型 GROUP BY：\n  Stage 0：Map（读数据）\n      ↓ Shuffle Write（按 key 分区写到磁盘）← 倾斜在这里\n  Stage 1：Reduce（聚合）\n      ↓\n  Stage 2：输出\n\nSpark 特有的倾斜表现：\n  Spark UI → Stages → 某个 Stage 的 Task 列表\n  → 看 \"Duration\" 列：大多数 Task 几秒，某个 Task 几十分钟\n  → 看 \"Shuffle Read Size\"：某个 Task 读了几 GB，其他只读几 MB\n\nSpark 特有优化（Hive on Spark 可用）：\n  spark.sql.adaptive.enabled = true          -- AQE（自适应查询执行）\n  spark.sql.adaptive.skewJoin.enabled = true -- 自动倾斜 Join 处理\n  spark.sql.adaptive.skewJoin.skewedPartitionFactor = 5   -- 超过中位数5倍认为倾斜\n  spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes = 256MB\n```\n\n### 1.2 DYNAMIC_PARTITION_PRUNING 与倾斜的关系\n\n```sql\n-- DPP（动态分区裁剪）：在 JOIN 时，用小表的过滤条件裁剪大表的分区\n-- 这本身不解决倾斜，但能大幅减少参与 JOIN 的数据量，间接缓解倾斜\n\n-- 示例：查询某城市用户的订单\n-- 没有 DPP：扫描全部 orders 分区（10 亿行）\n-- 有 DPP：先扫描 dim_users 找到北京用户的 user_id，\n--          再只扫描这些 user_id 对应的 orders 分区\n\nSET hive.tez.dynamic.partition.pruning = true;\nSET hive.tez.dynamic.partition.pruning.max.data.size = 104857600; -- 100MB\n\nEXPLAIN\nSELECT o.order_id, o.amount\nFROM ods_orders o\nJOIN dim_users u ON o.user_id = u.user_id\nWHERE u.city = '北京';\n-- 执行计划中应出现：Dynamic Partitioning Event Operator\n-- 效果：大幅减少 Map 阶段读取的数据量，倾斜 key 的绝对数据量也随之减少\n```\n\n---\n\n## 2. 倾斜量化评估模型\n\n### 2.1 倾斜程度量化指标\n\n```python\n# skew_analyzer.py\n# 量化分析倾斜程度，给出方案选型建议\n# 运行：python skew_analyzer.py\n\nimport math\n\ndef analyze_skew(key_distribution: dict, total_rows: int, num_reducers: int = 200):\n    \"\"\"\n    key_distribution: {key: row_count}\n    total_rows: 总行数\n    num_reducers: Reduce 并发度\n    \"\"\"\n    counts = sorted(key_distribution.values(), reverse=True)\n    n = len(counts)\n\n    # 指标1：最大 key 占比\n    max_key_pct = counts[0] / total_rows * 100\n\n    # 指标2：基尼系数（衡量整体不均匀程度，0=完全均匀，1=极度倾斜）\n    sorted_counts = sorted(counts)\n    cumsum = 0\n    gini_sum = 0\n    for i, c in enumerate(sorted_counts):\n        cumsum += c\n        gini_sum += cumsum\n    gini = 1 - 2 * gini_sum / (n * sum(counts)) + 1/n\n\n    # 指标3：理想执行时间 vs 实际执行时间比（倾斜放大系数）\n    ideal_rows_per_reducer = total_rows / num_reducers\n    max_rows_per_reducer = counts[0]  # 最坏情况：最大 key 独占一个 Reducer\n    skew_factor = max_rows_per_reducer / ideal_rows_per_reducer\n\n    # 指标4：Top-N key 集中度\n    top1_pct  = counts[0] / total_rows * 100\n    top5_pct  = sum(counts[:5]) / total_rows * 100\n    top10_pct = sum(counts[:10]) / total_rows * 100\n\n    print(\"=\" * 60)\n    print(\"倾斜分析报告\")\n    print(\"=\" * 60)\n    print(f\"总行数：{total_rows:,}\")\n    print(f\"唯一 Key 数：{n:,}\")\n    print(f\"Reduce 并发度：{num_reducers}\")\n    print()\n    print(f\"【核心指标】\")\n    print(f\"  最大 Key 占比：{max_key_pct:.2f}%\")\n    print(f\"  基尼系数：{gini:.4f}  （0=均匀，1=极度倾斜）\")\n    print(f\"  倾斜放大系数：{skew_factor:.1f}x  （理想耗时的 {skew_factor:.1f} 倍）\")\n    print(f\"  Top-1/5/10 集中度：{top1_pct:.1f}% / {top5_pct:.1f}% / {top10_pct:.1f}%\")\n    print()\n\n    # 方案选型建议\n    print(\"【方案选型建议】\")\n    if max_key_pct > 30:\n        print(\"  ⚠️  极度倾斜（最大 Key > 30%）\")\n        print(\"  → 首选：方案三（热点 Key 分离）+ 方案一（MapJoin）\")\n        print(\"  → 备选：方案二（加盐打散，盐值建议 >= 20）\")\n    elif max_key_pct > 10:\n        print(\"  ⚠️  严重倾斜（最大 Key 10%~30%）\")\n        print(\"  → 首选：方案二（加盐打散，盐值建议 10）\")\n        print(\"  → 备选：方案四（自动优化）\")\n    elif max_key_pct > 3:\n        print(\"  ⚡ 中度倾斜（最大 Key 3%~10%）\")\n        print(\"  → 首选：方案四（自动优化，调低 skewjoin.key 阈值）\")\n        print(\"  → 备选：方案二（加盐打散，盐值 5 即可）\")\n    else:\n        print(\"  ✅ 轻度倾斜（最大 Key < 3%）\")\n        print(\"  → 方案四（自动优化）通常足够\")\n        print(\"  → 优先检查是否有 NULL 值倾斜（方案五）\")\n\n    print()\n    print(\"【盐值推荐计算】\")\n    recommended_salt = max(2, math.ceil(skew_factor / 10))\n    recommended_salt = min(recommended_salt, 100)  # 盐值过大会增加 stage2 压力\n    print(f\"  推荐盐值：{recommended_salt}\")\n    print(f\"  加盐后最大 Key 预估占比：{max_key_pct / recommended_salt:.2f}%\")\n\n    return {\n        \"max_key_pct\": max_key_pct,\n        \"gini\": gini,\n        \"skew_factor\": skew_factor,\n        \"recommended_salt\": recommended_salt\n    }\n\n\n# 模拟我们的数据分布\nif __name__ == \"__main__\":\n    import random\n    random.seed(42)\n\n    # 模拟 1000 万行的 key 分布\n    total = 10_000_000\n    dist = {\n        1: int(total * 0.40),   # user_id=1，40%\n        2: int(total * 0.20),   # user_id=2，20%\n    }\n    # user_id 3~100，各约 0.3%\n    for uid in range(3, 101):\n        dist[uid] = int(total * 0.30 / 98)\n    # user_id 101~10000，长尾均匀\n    for uid in range(101, 10001):\n        dist[uid] = int(total * 0.10 / 9900)\n\n    result = analyze_skew(dist, total, num_reducers=200)\n```\n\n```\n# 预期输出：\n# ============================================================\n# 倾斜分析报告\n# ============================================================\n# 总行数：10,000,000\n# 唯一 Key 数：10,000\n# Reduce 并发度：200\n#\n# 【核心指标】\n#   最大 Key 占比：40.00%\n#   基尼系数：0.8923  （0=均匀，1=极度倾斜）\n#   倾斜放大系数：800.0x  （理想耗时的 800.0 倍）\n#   Top-1/5/10 集中度：40.0% / 61.2% / 62.4%\n#\n# 【方案选型建议】\n#   ⚠️  极度倾斜（最大 Key > 30%）\n#   → 首选：方案三（热点 Key 分离）+ 方案一（MapJoin）\n#   → 备选：方案二（加盐打散，盐值建议 >= 20）\n#\n# 【盐值推荐计算】\n#   推荐盐值：80\n#   加盐后最大 Key 预估占比：0.50%\n```\n\n### 2.2 方案收益量化公式\n\n```\n设：\n  T_ideal  = 理想执行时间（无倾斜，数据均匀分布）\n  T_actual = 实际执行时间（有倾斜）\n  R        = Reduce 并发度\n  N_max    = 最大 key 的行数\n  N_total  = 总行数\n\n倾斜放大系数 S = N_max / (N_total / R)\n\nT_actual ≈ T_ideal × S\n\n各方案理论收益：\n  方案一（MapJoin）：消除 Reduce 阶段\n    T_mapjoin ≈ T_map（通常是 T_ideal 的 0.3~0.5 倍）\n\n  方案二（加盐，盐值=K）：\n    新的最大 key 行数 ≈ N_max / K\n    新的倾斜放大系数 S' = (N_max/K) / (N_total/R) = S/K\n    T_salted ≈ T_ideal × (S/K) + T_stage2（stage2 很小，可忽略）\n    → 盐值 K 越大，收益越大，但 K > S 后收益趋于平稳\n\n  方案三（热点分离）：\n    热点部分用 MapJoin，非热点正常跑\n    T_hot    ≈ T_map（MapJoin）\n    T_normal ≈ T_ideal（非热点数据均匀）\n    T_total  ≈ max(T_hot, T_normal) ≈ T_ideal\n```\n\n---\n\n## 3. 极端场景与边界案例\n\n### 3.1 所有 Key 都倾斜（数据本身极度不均匀）\n\n```sql\n-- 场景：电商平台，商品类目只有 10 个，每个类目数据量差异巨大\n-- 家电：50%，服装：30%，食品：10%，其他7类：各1%\n-- GROUP BY category 时，10 个 Reducer 负载极不均衡\n\n-- 分析：这种情况加盐也没用（因为每个 key 都是\"热点\"）\n-- 根本解法：重新设计聚合粒度\n\n-- ❌ 错误思路：继续在 Hive 里硬怼\nSELECT category, COUNT(*), SUM(amount) FROM orders GROUP BY category;\n\n-- ✅ 方案 A：预聚合下沉（在上游 Spark Streaming 实时预聚合）\n-- 上游每分钟写入一条聚合结果，Hive 只需 SUM 少量预聚合数据\nSELECT category, SUM(order_cnt), SUM(total_amount)\nFROM dws_category_minute_agg   -- 每分钟一条，而非原始明细\nGROUP BY category;\n\n-- ✅ 方案 B：强制指定 Reduce 数量为 key 数量\nSET mapreduce.job.reduces = 10;  -- 正好等于 category 数量\n-- 每个 Reducer 处理一个 category，虽然不均衡，但至少不会有空闲 Reducer\n\n-- ✅ 方案 C：分桶表（Bucketing）\n-- 建表时按 category 分桶，数据预先分好，JOIN 时直接 Bucket Map Join\nCREATE TABLE orders_bucketed (\n    order_id BIGINT,\n    category STRING,\n    amount   DOUBLE\n)\nCLUSTERED BY (category) INTO 10 BUCKETS\nSTORED AS ORC;\n\nSET hive.optimize.bucketmapjoin = true;\nSET hive.optimize.bucketmapjoin.sortedmerge = true;\n```\n\n### 3.2 热点 Key 数量很多（几百个）\n\n```sql\n-- 场景：热点 key 不是 1~2 个，而是 500 个\n-- 方案三（手动分离）不再适用，需要动态化\n\n-- ✅ 动态热点分离（自动化版本）\n\n-- Step 1：动态计算热点阈值（基于统计信息）\nCREATE TABLE tmp_key_stats AS\nSELECT\n    user_id,\n    cnt,\n    -- 计算该 key 是否为热点（超过平均值的 N 倍）\n    cnt > AVG(cnt) OVER() * 10  AS is_hot,\n    -- 推荐盐值（基于倾斜程度动态计算）\n    GREATEST(1, CAST(cnt / (SUM(cnt) OVER() / 200) / 10 AS INT)) AS salt_n\nFROM (\n    SELECT user_id, COUNT(*) AS cnt\n    FROM ods_orders\n    GROUP BY user_id\n) t;\n\n-- Step 2：热点 key 加动态盐（盐值因 key 而异）\nCREATE TABLE tmp_orders_salted AS\nSELECT\n    o.order_id,\n    o.user_id,\n    o.amount,\n    o.status,\n    -- 热点 key 加盐，非热点 key 盐值固定为 0\n    CASE\n        WHEN k.is_hot THEN CAST(FLOOR(RAND() * k.salt_n) AS INT)\n        ELSE 0\n    END AS salt\nFROM ods_orders o\nLEFT JOIN tmp_key_stats k ON o.user_id = k.user_id;\n\n-- Step 3：局部聚合\nCREATE TABLE tmp_stage1 AS\nSELECT user_id, salt, COUNT(*) AS cnt, SUM(amount) AS total\nFROM tmp_orders_salted\nGROUP BY user_id, salt;\n\n-- Step 4：全局聚合（去盐）\nSELECT user_id, SUM(cnt) AS order_cnt, SUM(total) AS total_amount\nFROM tmp_stage1\nGROUP BY user_id;\n```\n\n### 3.3 JOIN 两端都是大表且都倾斜\n\n```sql\n-- 场景：orders（10亿行，user_id 倾斜）JOIN user_actions（50亿行，user_id 同样倾斜）\n-- 两张表都是大表，MapJoin 不可用，两边都有热点\n\n-- ✅ 方案：双边加盐（Salted Join）\n\n-- 原理：\n--   大表 A 的 key 加随机盐 0~K-1\n--   大表 B 的 key 复制 K 份（分别加盐 0,1,2,...,K-1）\n--   JOIN 条件：(A.key, A.salt) = (B.key, B.salt)\n--   这样 A 的每行只和 B 的一份匹配，结果正确\n\nSET K = 10;  -- 盐值范围\n\n-- 大表 A：随机加盐\nCREATE TABLE tmp_orders_salted AS\nSELECT *, CAST(FLOOR(RAND() * 10) AS INT) AS salt\nFROM ods_orders;\n\n-- 大表 B：复制 K 份\nCREATE TABLE tmp_actions_replicated AS\nSELECT *, salt_val AS salt\nFROM user_actions\nLATERAL VIEW EXPLODE(ARRAY(0,1,2,3,4,5,6,7,8,9)) t AS salt_val;\n-- ⚠️ 注意：B 表数据量变为原来的 K 倍，K 不能太大（建议 5~20）\n\n-- JOIN\nSELECT a.user_id, COUNT(*) AS cnt, SUM(a.amount) AS total\nFROM tmp_orders_salted a\nJOIN tmp_actions_replicated b\n    ON a.user_id = b.user_id AND a.salt = b.salt\nGROUP BY a.user_id;\n\n-- 清理\nDROP TABLE tmp_orders_salted;\nDROP TABLE tmp_actions_replicated;\n```\n\n### 3.4 倾斜 + 数据量随时间变化（动态热点）\n\n```sql\n-- 场景：平时 user_id=1 是热点，大促期间 user_id=9999（某网红）突然变热点\n-- 静态配置的热点 key 列表会失效\n\n-- ✅ 方案：每次 ETL 前动态计算热点，写入配置表\n\n-- 每日 ETL 开始前执行（可由调度系统触发）\nINSERT OVERWRITE TABLE hot_keys_config\nSELECT\n    user_id,\n    cnt,\n    CURRENT_DATE AS stat_date\nFROM (\n    SELECT user_id, COUNT(*) AS cnt\n    FROM ods_orders\n    WHERE dt = DATE_SUB(CURRENT_DATE, 1)  -- 用昨天数据预测今天热点\n    GROUP BY user_id\n    HAVING COUNT(*) > 500000  -- 动态阈值\n) t;\n\n-- ETL 主逻辑：读取动态热点配置\n-- （后续 JOIN 逻辑同方案三，但热点 key 来自配置表而非硬编码）\n```\n\n---\n\n## 4. 动态智能倾斜检测\n\n### 4.1 自动化倾斜监控脚本\n\n```python\n# skew_monitor.py\n# 生产级倾斜监控：定期扫描关键表，发现倾斜自动告警\n# 可集成到 Airflow / DolphinScheduler\n\nimport subprocess\nimport json\nfrom datetime import datetime\n\nHIVE_CMD = \"hive -e\"\nALERT_THRESHOLD_PCT = 10.0   # 最大 key 占比超过 10% 触发告警\nALERT_THRESHOLD_GINI = 0.7   # 基尼系数超过 0.7 触发告警\n\nMONITOR_TABLES = [\n    {\"db\": \"skew_demo\", \"table\": \"ods_orders\", \"key_col\": \"user_id\",   \"dt\": \"2024-01-15\"},\n    {\"db\": \"skew_demo\", \"table\": \"ods_orders\", \"key_col\": \"product_id\", \"dt\": \"2024-01-15\"},\n]\n\ndef run_hive_query(sql):\n    result = subprocess.run(\n        f'{HIVE_CMD} \"{sql}\"',\n        shell=True, capture_output=True, text=True\n    )\n    return result.stdout.strip()\n\ndef check_skew(db, table, key_col, dt=None):\n    where = f\"WHERE dt='{dt}'\" if dt else \"\"\n    sql = f\"\"\"\n    SELECT\n        {key_col},\n        COUNT(*) AS cnt,\n        COUNT(*) * 100.0 / SUM(COUNT(*)) OVER() AS pct\n    FROM {db}.{table}\n    {where}\n    GROUP BY {key_col}\n    ORDER BY cnt DESC\n    LIMIT 20\n    \"\"\"\n    output = run_hive_query(sql)\n    rows = [line.split('\\t') for line in output.split('\\n') if line]\n\n    if not rows:\n        return None\n\n    total = sum(int(r[1]) for r in rows)\n    max_pct = float(rows[0][2])\n\n    # 简化基尼系数计算\n    counts = sorted([int(r[1]) for r in rows])\n    n = len(counts)\n    gini = sum((2*i - n - 1) * c for i, c in enumerate(counts, 1)) / (n * sum(counts))\n\n    result = {\n        \"table\": f\"{db}.{table}\",\n        \"key_col\": key_col,\n        \"dt\": dt,\n        \"max_key\": rows[0][0],\n        \"max_key_pct\": max_pct,\n        \"gini\": gini,\n        \"top5\": rows[:5],\n        \"is_skewed\": max_pct > ALERT_THRESHOLD_PCT or gini > ALERT_THRESHOLD_GINI\n    }\n    return result\n\ndef main():\n    print(f\"[{datetime.now()}] 开始倾斜检测...\")\n    alerts = []\n\n    for cfg in MONITOR_TABLES:\n        result = check_skew(**cfg)\n        if result and result[\"is_skewed\"]:\n            alerts.append(result)\n            print(f\"⚠️  倾斜告警：{result['table']}.{result['key_col']}\")\n            print(f\"   最大 Key：{result['max_key']}，占比：{result['max_key_pct']:.2f}%\")\n            print(f\"   基尼系数：{result['gini']:.4f}\")\n\n    if not alerts:\n        print(\"✅ 未发现倾斜\")\n    else:\n        # 写入告警日志（可对接钉钉/飞书/PagerDuty）\n        with open(\"/tmp/skew_alerts.json\", \"w\") as f:\n            json.dump(alerts, f, ensure_ascii=False, indent=2)\n        print(f\"\\n共发现 {len(alerts)} 个倾斜问题，详情见 /tmp/skew_alerts.json\")\n\nif __name__ == \"__main__\":\n    main()\n```\n\n---\n\n## 5. 与上下游生态联动\n\n### 5.1 上游 Spark 预聚合 → 下游 Hive 轻量消费\n\n```sql\n-- 架构思路：\n-- Spark Streaming 实时消费 Kafka → 按分钟预聚合 → 写入 Hive 预聚合表\n-- Hive 批处理只需对预聚合表做二次聚合，数据量从 10 亿行 → 几十万行\n\n-- Hive 预聚合表（接收 Spark 写入的分钟级聚合数据）\nCREATE TABLE dws_orders_minute_agg (\n    user_id         BIGINT,\n    minute_ts       STRING      COMMENT '分钟时间戳，格式 2024-01-15 10:30',\n    order_cnt       BIGINT,\n    total_amount    DOUBLE,\n    paid_cnt        BIGINT,\n    paid_amount     DOUBLE\n)\nCOMMENT '订单分钟级预聚合（由 Spark Streaming 写入）'\nPARTITIONED BY (dt STRING)\nSTORED AS ORC;\n\n-- Hive 日报只需对预聚合表做二次聚合（数据量极小，无倾斜）\nINSERT OVERWRITE TABLE ads_user_daily_report PARTITION(dt='2024-01-15')\nSELECT\n    user_id,\n    SUM(order_cnt)      AS daily_order_cnt,\n    SUM(total_amount)   AS daily_total_amount,\n    SUM(paid_cnt)       AS daily_paid_cnt,\n    SUM(paid_amount)    AS daily_paid_amount\nFROM dws_orders_minute_agg\nWHERE dt = '2024-01-15'\nGROUP BY user_id;\n-- 数据量：1440分钟 × 用户数，远小于原始明细，倾斜问题自然消失\n```\n\n### 5.2 Hive 倾斜数据传递给下游 Spark 的处理建议\n\n```python\n# 如果 Hive 输出的数据本身就是倾斜的（如按 user_id 分区），\n# 下游 Spark 读取时需要重新分区\n\nfrom pyspark.sql import SparkSession\nfrom pyspark.sql.functions import col\n\nspark = SparkSession.builder.appName(\"SkewHandling\").getOrCreate()\n\n# 开启 AQE（Spark 3.0+，自动处理倾斜）\nspark.conf.set(\"spark.sql.adaptive.enabled\", \"true\")\nspark.conf.set(\"spark.sql.adaptive.skewJoin.enabled\", \"true\")\nspark.conf.set(\"spark.sql.adaptive.skewJoin.skewedPartitionFactor\", \"5\")\nspark.conf.set(\"spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes\", \"256mb\")\n\n# 读取 Hive 倾斜表\ndf = spark.table(\"skew_demo.ods_orders\")\n\n# 手动重分区（如果 AQE 不够用）\n# 按 user_id 的 hash 重分区，让数据更均匀\ndf_repartitioned = df.repartition(200, col(\"user_id\"))\n\n# 或者用 salting（与 Hive 方案二思路相同）\nfrom pyspark.sql.functions import rand, floor, concat_ws, lit, cast\n\ndf_salted = df.withColumn(\"salt\", floor(rand() * 10).cast(\"int\"))\nresult = df_salted.groupBy(\"user_id\", \"salt\").agg({\"amount\": \"sum\", \"*\": \"count\"})\nfinal = result.groupBy(\"user_id\").agg({\"sum(amount)\": \"sum\", \"count(1)\": \"sum\"})\n```\n\nFile v1.0.0:references/index-design.md\n\n# 索引设计策略\n\n## 设计口诀\n**最左前缀、区分度高、覆盖查询、避免冗余**\n\n---\n\n## 索引类型速查\n\n| 类型 | 适用场景 | 注意事项 |\n|------|---------|---------|\n| 主键索引 | 唯一标识行 | 尽量用自增整数，避免 UUID（页分裂） |\n| 唯一索引 | 业务唯一约束 | 允许 NULL（多个 NULL 不冲突） |\n| 普通索引 | 高频查询列 | 区分度 > 20% 才值得建 |\n| 联合索引 | 多列组合查询 | 遵循最左前缀原则 |\n| 覆盖索引 | 避免回表 | 把 SELECT 列也加入索引 |\n| 前缀索引 | 长字符串列 | `INDEX(col(20))`，节省空间但不能覆盖 |\n| 函数索引 | 表达式查询 | MySQL 8.0+ / PostgreSQL 支持 |\n| 全文索引 | 文本搜索 | 替代 LIKE '%keyword%' |\n\n---\n\n## 联合索引设计原则\n\n### 最左前缀原则\n```sql\n-- 假设有联合索引 INDEX(a, b, c)\n-- ✅ 能走索引\nWHERE a = 1\nWHERE a = 1 AND b = 2\nWHERE a = 1 AND b = 2 AND c = 3\nWHERE a = 1 AND b > 2          -- a 走等值，b 走范围\nWHERE a = 1 AND c = 3          -- 只有 a 走索引，c 跳过了 b\n\n-- ❌ 不能走索引\nWHERE b = 2                    -- 跳过了 a\nWHERE b = 2 AND c = 3          -- 跳过了 a\nWHERE c = 3                    -- 跳过了 a 和 b\n```\n\n### 列顺序设计规则\n1. **等值查询列放前面**，范围查询列放后面\n2. **区分度高的列放前面**（性别不适合放第一位）\n3. **ORDER BY 列放最后**（消除 filesort）\n\n```sql\n-- 查询：WHERE status = ? AND create_time > ? ORDER BY id\n-- ✅ 好的索引设计：(status, create_time, id)\n-- status 等值在前，create_time 范围其次，id 排序在后\nCREATE INDEX idx_status_time_id ON orders(status, create_time, id);\n```\n\n### 覆盖索引设计\n```sql\n-- 查询：SELECT id, status, amount FROM orders WHERE user_id = ? AND status = 'paid'\n-- 把 SELECT 的列也加入索引，避免回表\nCREATE INDEX idx_covering ON orders(user_id, status, amount, id);\n-- Extra: Using index → 不需要回表，性能极佳\n```\n\n---\n\n## 索引失效场景（必须记住）\n\n```sql\n-- 1. 对索引列使用函数\nWHERE YEAR(create_time) = 2024          -- ❌\nWHERE create_time >= '2024-01-01'       -- ✅\n\n-- 2. 隐式类型转换（字符串 vs 数字）\nWHERE user_id = '1001'   -- user_id 是 INT  ❌\nWHERE user_id = 1001                    -- ✅\n\n-- 3. LIKE 前缀通配符\nWHERE name LIKE '%张%'                  -- ❌ 全表扫描\nWHERE name LIKE '张%'                   -- ✅ 走索引\n\n-- 4. OR 连接不同索引列\nWHERE a = 1 OR b = 2                    -- ❌（除非两列都有索引且优化器选择 index merge）\n-- 改写为 UNION ALL                     -- ✅\n\n-- 5. NOT IN / != / NOT EXISTS\nWHERE status != 'active'               -- ❌ 通常不走索引\n-- 改写为 IN 正向过滤                   -- ✅\n\n-- 6. 联合索引不满足最左前缀\n-- 见上方最左前缀原则\n\n-- 7. 索引列参与计算\nWHERE id + 1 = 100                     -- ❌\nWHERE id = 99                          -- ✅\n```\n\n---\n\n## 索引选择性分析\n\n```sql\n-- 计算列的区分度（越接近 1 越好，建议 > 0.1）\nSELECT\n    COUNT(DISTINCT status) / COUNT(*) AS status_selectivity,\n    COUNT(DISTINCT user_id) / COUNT(*) AS user_id_selectivity,\n    COUNT(DISTINCT DATE(create_time)) / COUNT(*) AS date_selectivity\nFROM orders;\n\n-- 查看索引使用情况（MySQL）\nSELECT\n    index_name,\n    stat_value AS cardinality\nFROM mysql.innodb_index_stats\nWHERE table_name = 'orders'\n  AND stat_name = 'n_diff_pfx01';\n\n-- 查看索引是否被使用（MySQL performance_schema）\nSELECT\n    object_name,\n    index_name,\n    count_read,\n    count_write\nFROM performance_schema.table_io_waits_summary_by_index_usage\nWHERE object_schema = 'your_db'\n  AND object_name = 'orders'\nORDER BY count_read DESC;\n```\n\n---\n\n## 索引维护\n\n### 查找冗余索引\n```sql\n-- MySQL：查找前缀相同的冗余索引\nSELECT\n    a.table_name,\n    a.index_name AS redundant_index,\n    a.column_name,\n    b.index_name AS dominant_index\nFROM information_schema.statistics a\nJOIN information_schema.statistics b\n    ON a.table_schema = b.table_schema\n    AND a.table_name = b.table_name\n    AND a.seq_in_index = b.seq_in_index\n    AND a.column_name = b.column_name\n    AND a.index_name != b.index_name\nWHERE a.table_schema = 'your_db';\n```\n\n### 索引碎片整理\n```sql\n-- MySQL：重建索引（会锁表，生产环境用 pt-online-schema-change）\nALTER TABLE orders ENGINE=InnoDB;  -- 重建整张表\nOPTIMIZE TABLE orders;             -- 等价\n\n-- 在线 DDL（MySQL 5.6+，不锁表）\nALTER TABLE orders ADD INDEX idx_new(col), ALGORITHM=INPLACE, LOCK=NONE;\n```\n\n---\n\n## 特殊场景索引\n\n### 时间范围查询（分区 + 索引）\n```sql\n-- 大表按时间分区，配合索引效果最佳\nCREATE TABLE logs (\n    id          BIGINT AUTO_INCREMENT,\n    user_id     INT NOT NULL,\n    action      VARCHAR(50),\n    create_time DATETIME NOT NULL,\n    PRIMARY KEY (id, create_time),  -- 分区键必须在主键中\n    INDEX idx_user_time (user_id, create_time)\n) PARTITION BY RANGE (YEAR(create_time)) (\n    PARTITION p2023 VALUES LESS THAN (2024),\n    PARTITION p2024 VALUES LESS THAN (2025),\n    PARTITION p_future VALUES LESS THAN MAXVALUE\n);\n```\n\n### JSON 列索引（MySQL 5.7+）\n```sql\n-- 对 JSON 字段的特定路径建虚拟列索引\nALTER TABLE users\n    ADD COLUMN city VARCHAR(50) GENERATED ALWAYS AS (JSON_UNQUOTE(profile->'$.city')) VIRTUAL,\n    ADD INDEX idx_city (city);\n\nSELECT * FROM users WHERE city = '北京';  -- 走虚拟列索引\n```\n\n### PostgreSQL 部分索引\n```sql\n-- 只对满足条件的行建索引（节省空间，提升效率）\nCREATE INDEX idx_active_users ON users(email)\nWHERE status = 'active';  -- 只索引活跃用户\n\n-- 查询时必须包含相同条件才能走此索引\nSELECT * FROM users WHERE email = 'x@x.com' AND status = 'active';\n```\n\nFile v1.0.0:references/query-optimization.md\n\n# 慢查询诊断 & 执行计划分析\n\n## 诊断流程\n\n```\n收到慢 SQL\n    ↓\n1. 读 EXPLAIN 输出（识别问题类型）\n    ↓\n2. 定位根因（全表扫描/索引失效/数据倾斜/...）\n    ↓\n3. 给出优化方案（改写 SQL / 加索引 / 改架构）\n    ↓\n4. 估算收益 + 提供验证方法\n```\n\n---\n\n## MySQL EXPLAIN 解读\n\n### 关键字段含义\n\n| 字段 | 含义 | 危险信号 |\n|------|------|---------|\n| `type` | 访问类型 | `ALL`（全表扫描）、`index`（全索引扫描） |\n| `key` | 实际使用的索引 | `NULL`（没用索引） |\n| `rows` | 预估扫描行数 | 远大于实际返回行数 |\n| `Extra` | 附加信息 | `Using filesort`、`Using temporary` |\n| `filtered` | 过滤比例 | 很低说明索引选择性差 |\n\n### type 访问类型（从好到差）\n```\nsystem > const > eq_ref > ref > range > index > ALL\n\nconst：主键或唯一索引等值查询，最快\neq_ref：JOIN 时被驱动表用主键/唯一索引，很快\nref：非唯一索引等值查询\nrange：索引范围扫描（BETWEEN, >, <, IN）\nindex：全索引扫描（比 ALL 好，但仍然慢）\nALL：全表扫描，必须优化\n```\n\n### Extra 字段解读\n```\nUsing index          → 覆盖索引，不需要回表，很好 ✅\nUsing where          → 在 Server 层过滤，索引不够精确\nUsing filesort       → 需要额外排序，考虑加索引 ⚠️\nUsing temporary      → 使用临时表，GROUP BY/ORDER BY 列不一致 ⚠️\nUsing index condition → 索引下推（ICP），MySQL 5.6+，较好 ✅\nSelect tables optimized away → 直接从索引返回，最优 ✅\n```\n\n### 实战示例：读懂一个 EXPLAIN\n\n```sql\nEXPLAIN SELECT u.name, COUNT(o.id) AS order_cnt\nFROM users u\nLEFT JOIN orders o ON u.id = o.user_id\nWHERE u.status = 'active'\nGROUP BY u.id\nORDER BY order_cnt DESC\nLIMIT 10;\n```\n\n```\n+----+-------------+-------+------+---------------+--------+---------+------------------+------+----------------------------------------------+\n| id | select_type | table | type | possible_keys | key    | key_len | ref              | rows | Extra                                        |\n+----+-------------+-------+------+---------------+--------+---------+------------------+------+----------------------------------------------+\n|  1 | SIMPLE      | u     | ref  | idx_status    | idx_status | 1   | const            | 5000 | Using index condition; Using temporary; Using filesort |\n|  1 | SIMPLE      | o     | ref  | idx_user_id   | idx_user_id | 4 | db.u.id          |   10 | NULL                                         |\n+----+-------------+-------+------+---------------+--------+---------+------------------+------+----------------------------------------------+\n```\n\n**诊断**：\n- `u` 表：`Using temporary; Using filesort` → GROUP BY + ORDER BY 触发了临时表和文件排序\n- `u.rows = 5000` → status='active' 过滤后还有 5000 行，选择性不够好\n- `o` 表：正常，走了 idx_user_id\n\n**优化方向**：\n1. 如果 active 用户占比很高，考虑去掉 status 过滤或换策略\n2. 加 `(status, id)` 联合索引，让 GROUP BY 利用索引顺序消除 filesort\n\n---\n\n## PostgreSQL EXPLAIN ANALYZE 解读\n\n```sql\nEXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)\nSELECT ...;\n```\n\n### 关键指标\n```\nSeq Scan        → 全表扫描，需要索引\nIndex Scan      → 索引扫描 + 回表\nIndex Only Scan → 覆盖索引，最优\nBitmap Heap Scan → 批量回表，适合低选择性查询\nHash Join       → 哈希连接，适合大表等值 JOIN\nNested Loop     → 嵌套循环，适合小表驱动大表\nMerge Join      → 归并连接，适合有序数据\n\nactual time=X..Y → X 是第一行时间，Y 是最后一行时间\nrows=N          → 实际返回行数\nloops=N         → 执行次数（Nested Loop 内层会多次执行）\nBuffers: shared hit=X read=Y → hit 是缓存命中，read 是磁盘读\n```\n\n### 实战：识别数据倾斜\n```\nHash Join  (cost=... actual time=5000..5000 rows=1000000 loops=1)\n  Hash Cond: (o.user_id = u.id)\n  ->  Seq Scan on orders  (actual time=0.1..2000 rows=10000000 loops=1)\n  ->  Hash  (actual time=100..100 rows=100 loops=1)\n        Buckets: 1024  Batches: 8  Memory Usage: 4096kB  ← Batches>1 说明内存不够，溢出磁盘\n```\n\n---\n\n## 常见慢查询模式 & 修复\n\n### 模式 1：索引失效（函数/类型转换）\n\n```sql\n-- ❌ 慢：对索引列使用函数，索引失效\nSELECT * FROM orders WHERE DATE(create_time) = '2024-01-01';\nSELECT * FROM users WHERE LOWER(email) = 'test@example.com';\nSELECT * FROM orders WHERE user_id = '1001';  -- user_id 是 INT，传了字符串\n\n-- ✅ 快：改写为范围查询，保持索引列干净\nSELECT * FROM orders\nWHERE create_time >= '2024-01-01 00:00:00'\n  AND create_time <  '2024-01-02 00:00:00';\n\n-- ✅ 快：建函数索引（MySQL 8.0+ / PostgreSQL）\nCREATE INDEX idx_email_lower ON users ((LOWER(email)));\nSELECT * FROM users WHERE LOWER(email) = 'test@example.com';\n```\n\n### 模式 2：深度分页\n\n```sql\n-- ❌ 慢：扫描 1000020 行，丢弃前 1000000 行\nSELECT * FROM logs ORDER BY id LIMIT 1000000, 20;\n\n-- ✅ 快：游标分页（业务上记录上次最大 id）\nSELECT * FROM logs WHERE id > :cursor ORDER BY id LIMIT 20;\n\n-- ✅ 快：延迟关联（必须分页时）\nSELECT l.* FROM logs l\nJOIN (SELECT id FROM logs ORDER BY id LIMIT 1000000, 20) t ON l.id = t.id;\n```\n\n### 模式 3：N+1 查询\n\n```sql\n-- ❌ 慢：查出 100 个用户，再循环查 100 次订单（N+1）\nSELECT * FROM users WHERE status = 'active';\n-- 然后对每个 user_id 执行：\nSELECT * FROM orders WHERE user_id = ?;\n\n-- ✅ 快：一次 JOIN 或 IN 查询\nSELECT u.*, o.order_id, o.amount\nFROM users u\nLEFT JOIN orders o ON u.id = o.user_id\nWHERE u.status = 'active';\n\n-- 或者（当 JOIN 结果集太大时）\nSELECT * FROM orders\nWHERE user_id IN (\n    SELECT id FROM users WHERE status = 'active'\n);\n```\n\n### 模式 4：OR 导致索引失效\n\n```sql\n-- ❌ 慢：OR 连接不同列，无法走复合索引\nSELECT * FROM orders WHERE user_id = 1001 OR product_id = 2002;\n\n-- ✅ 快：改写为 UNION ALL（各自走各自的索引）\nSELECT * FROM orders WHERE user_id = 1001\nUNION ALL\nSELECT * FROM orders WHERE product_id = 2002\n  AND user_id != 1001;  -- 避免重复\n```\n\n### 模式 5：大 IN 列表\n\n```sql\n-- ❌ 慢：IN 列表超过几百个值，优化器可能放弃索引\nSELECT * FROM products WHERE id IN (1,2,3,...,10000);\n\n-- ✅ 快：改为临时表 JOIN\nCREATE TEMPORARY TABLE tmp_ids (id INT PRIMARY KEY);\nINSERT INTO tmp_ids VALUES (1),(2),(3),...;\nSELECT p.* FROM products p JOIN tmp_ids t ON p.id = t.id;\n```\n\n### 模式 6：COUNT 优化\n\n```sql\n-- 场景：只需要知道是否存在，不需要精确数量\n-- ❌ 慢：COUNT(*) 扫描所有行\nSELECT COUNT(*) FROM orders WHERE user_id = 1001;\n\n-- ✅ 快：EXISTS 找到第一条就停止\nSELECT EXISTS(SELECT 1 FROM orders WHERE user_id = 1001);\n\n-- 场景：需要精确总数（大表）\n-- ✅ MySQL：information_schema 近似值（误差<5%）\nSELECT table_rows FROM information_schema.tables\nWHERE table_name = 'orders';\n\n-- ✅ PostgreSQL：pg_class 近似值\nSELECT reltuples::BIGINT FROM pg_class WHERE relname = 'orders';\n```\n\n---\n\n## 锁分析\n\n### MySQL 查看锁等待\n```sql\n-- 查看当前锁等待\nSELECT\n    r.trx_id                    AS waiting_trx_id,\n    r.trx_mysql_thread_id       AS waiting_thread,\n    r.trx_query                 AS waiting_query,\n    b.trx_id                    AS blocking_trx_id,\n    b.trx_mysql_thread_id       AS blocking_thread,\n    b.trx_query                 AS blocking_query\nFROM information_schema.innodb_lock_waits w\nJOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id\nJOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;\n\n-- MySQL 8.0+ 用 performance_schema\nSELECT * FROM performance_schema.data_lock_waits;\n```\n\n### 死锁分析\n```sql\n-- 查看最近一次死锁信息\nSHOW ENGINE INNODB STATUS\\G\n-- 找 LATEST DETECTED DEADLOCK 部分\n\n-- 死锁常见原因：\n-- 1. 两个事务以相反顺序锁定同一批行\n-- 2. 解决：统一加锁顺序，或缩短事务\n```\n\nFile v1.0.0:references/sql-generation.md\n\n# SQL 生成指南\n\n## 生成流程\n\n### Step 1：理解业务意图\n不要急于写 SQL，先把业务问题翻译成数据问题：\n- 要查什么实体？（用户、订单、商品...）\n- 要什么维度的聚合？（按天、按用户、按地区...）\n- 过滤条件是什么？（时间范围、状态、金额...）\n- 结果如何排序和限制？\n\n### Step 2：确认 Schema\n如果用户没有提供，主动询问：\n```\n请提供相关表的 DDL，或者告诉我：\n- 表名和主要字段\n- 哪些字段有索引\n- 大概的数据量级\n```\n\n### Step 3：生成 SQL（分难度）\n\n---\n\n## 基础查询模式\n\n### 单表聚合\n```sql\n-- 业务：统计每天的订单数和总金额（近30天）\n-- 数据库：MySQL 8.0\n-- 性能预期：orders 表千万级，date 字段有索引，<100ms\n-- 注意：create_time 为 NULL 的记录会被 WHERE 过滤掉\n\nSELECT\n    DATE(create_time)           AS order_date,\n    COUNT(*)                    AS order_cnt,\n    SUM(amount)                 AS total_amount,\n    AVG(amount)                 AS avg_amount,\n    COUNT(DISTINCT user_id)     AS uv          -- 去重用户数\nFROM orders\nWHERE create_time >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)\n  AND status != 'cancelled'                    -- 排除取消订单\nGROUP BY DATE(create_time)\nORDER BY order_date DESC;\n```\n\n### 多表 JOIN\n```sql\n-- 业务：查询用户最近一次购买的商品信息\n-- 数据库：MySQL 8.0\n-- 性能预期：需要 user_id 和 create_time 的联合索引\n-- 注意：使用子查询取最新订单，避免 GROUP BY 后 JOIN 的数据膨胀\n\nSELECT\n    u.user_id,\n    u.username,\n    o.order_id,\n    o.create_time   AS last_order_time,\n    p.product_name,\n    o.amount\nFROM users u\nINNER JOIN orders o ON u.user_id = o.user_id\nINNER JOIN (\n    -- 每个用户最新订单\n    SELECT user_id, MAX(create_time) AS max_time\n    FROM orders\n    WHERE status = 'completed'\n    GROUP BY user_id\n) latest ON o.user_id = latest.user_id\n         AND o.create_time = latest.max_time\nINNER JOIN products p ON o.product_id = p.product_id\nWHERE u.status = 'active';\n```\n\n### 窗口函数（生产必备）\n```sql\n-- 业务：计算每个用户的订单金额排名和累计金额\n-- 数据库：MySQL 8.0+ / PostgreSQL\n-- 性能预期：窗口函数在大数据量下注意分区粒度\n\nSELECT\n    user_id,\n    order_id,\n    amount,\n    -- 排名（并列不跳号用 DENSE_RANK，跳号用 RANK）\n    DENSE_RANK() OVER (\n        PARTITION BY user_id\n        ORDER BY amount DESC\n    )                                           AS amount_rank,\n    -- 累计金额\n    SUM(amount) OVER (\n        PARTITION BY user_id\n        ORDER BY create_time\n        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW\n    )                                           AS cumulative_amount,\n    -- 环比（当前行 vs 上一行）\n    amount - LAG(amount, 1, 0) OVER (\n        PARTITION BY user_id\n        ORDER BY create_time\n    )                                           AS amount_diff\nFROM orders\nWHERE status = 'completed';\n```\n\n---\n\n## 进阶查询模式\n\n### 递归 CTE（树形结构）\n```sql\n-- 业务：查询组织架构树（从某节点向下所有子节点）\n-- 数据库：MySQL 8.0+ / PostgreSQL\n-- 注意：设置 max_recursion_depth 防止死循环\n\nWITH RECURSIVE org_tree AS (\n    -- 锚点：起始节点\n    SELECT\n        id,\n        name,\n        parent_id,\n        0           AS depth,\n        CAST(name AS CHAR(1000)) AS path\n    FROM departments\n    WHERE id = 1   -- 从根节点开始\n\n    UNION ALL\n\n    -- 递归：向下展开\n    SELECT\n        d.id,\n        d.name,\n        d.parent_id,\n        ot.depth + 1,\n        CONCAT(ot.path, ' > ', d.name)\n    FROM departments d\n    INNER JOIN org_tree ot ON d.parent_id = ot.id\n    WHERE ot.depth < 10   -- 防止无限递归\n)\nSELECT * FROM org_tree ORDER BY path;\n```\n\n### 行转列（PIVOT）\n```sql\n-- 业务：将每月销售额从行格式转为列格式\n-- 数据库：MySQL（无原生 PIVOT，用条件聚合）\n\nSELECT\n    product_id,\n    SUM(CASE WHEN month = '2024-01' THEN amount ELSE 0 END) AS jan,\n    SUM(CASE WHEN month = '2024-02' THEN amount ELSE 0 END) AS feb,\n    SUM(CASE WHEN month = '2024-03' THEN amount ELSE 0 END) AS mar,\n    SUM(amount)                                              AS total\nFROM monthly_sales\nWHERE month BETWEEN '2024-01' AND '2024-03'\nGROUP BY product_id;\n\n-- PostgreSQL 版本（使用 crosstab，需要 tablefunc 扩展）\n-- SELECT * FROM crosstab(...) AS ct(product_id INT, jan NUMERIC, feb NUMERIC, mar NUMERIC);\n```\n\n### 去重保留最新记录\n```sql\n-- 业务：每个用户只保留最新的一条记录（去重）\n-- 方案 A：ROW_NUMBER（推荐，语义清晰）\nWITH ranked AS (\n    SELECT *,\n        ROW_NUMBER() OVER (\n            PARTITION BY user_id\n            ORDER BY create_time DESC\n        ) AS rn\n    FROM user_logs\n)\nSELECT * FROM ranked WHERE rn = 1;\n\n-- 方案 B：子查询（兼容性更好，但性能可能差）\nSELECT * FROM user_logs ul\nWHERE create_time = (\n    SELECT MAX(create_time)\n    FROM user_logs\n    WHERE user_id = ul.user_id\n);\n-- ⚠️ 方案 B 在大表上是相关子查询，性能极差，慎用\n```\n\n---\n\n## 大数据量专项\n\n### 分页优化（LIMIT 大偏移量）\n```sql\n-- ❌ 错误写法：LIMIT 100000, 20 会扫描 100020 行\nSELECT * FROM orders ORDER BY id LIMIT 100000, 20;\n\n-- ✅ 正确写法：延迟关联（Deferred Join）\nSELECT o.*\nFROM orders o\nINNER JOIN (\n    SELECT id FROM orders ORDER BY id LIMIT 100000, 20\n) ids ON o.id = ids.id;\n\n-- ✅ 更好的写法：游标分页（需要记录上次最大 ID）\nSELECT * FROM orders\nWHERE id > :last_max_id   -- 上次查询的最大 id\nORDER BY id\nLIMIT 20;\n```\n\n### 批量 INSERT 优化\n```sql\n-- ❌ 逐行插入（N 次网络往返）\nINSERT INTO logs VALUES (1, 'a', NOW());\nINSERT INTO logs VALUES (2, 'b', NOW());\n\n-- ✅ 批量插入（1 次网络往返）\nINSERT INTO logs (id, content, create_time) VALUES\n    (1, 'a', NOW()),\n    (2, 'b', NOW()),\n    (3, 'c', NOW());\n-- 建议每批 500-1000 行，避免单次事务过大\n\n-- ✅ UPSERT（存在则更新，不存在则插入）\n-- MySQL:\nINSERT INTO user_stats (user_id, login_cnt)\nVALUES (1001, 1)\nON DUPLICATE KEY UPDATE login_cnt = login_cnt + 1;\n\n-- PostgreSQL:\nINSERT INTO user_stats (user_id, login_cnt)\nVALUES (1001, 1)\nON CONFLICT (user_id)\nDO UPDATE SET login_cnt = user_stats.login_cnt + 1;\n```\n\n---\n\n## NULL 处理规范\n\n```sql\n-- NULL 的三值逻辑：TRUE / FALSE / UNKNOWN\n-- NULL != NULL → UNKNOWN（不是 TRUE！）\n-- 正确判断 NULL：IS NULL / IS NOT NULL\n\n-- ❌ 错误：WHERE col != 'value' 不会返回 col IS NULL 的行\nSELECT * FROM t WHERE col != 'active';\n\n-- ✅ 正确：明确处理 NULL\nSELECT * FROM t WHERE col != 'active' OR col IS NULL;\n\n-- COALESCE：返回第一个非 NULL 值\nSELECT COALESCE(nickname, username, '匿名用户') AS display_name FROM users;\n\n-- NULL 在聚合中的行为\nSELECT\n    COUNT(*)        AS total_rows,      -- 包含 NULL 行\n    COUNT(col)      AS non_null_cnt,    -- 不含 NULL 行\n    SUM(col)        AS sum_val,         -- NULL 被忽略\n    AVG(col)        AS avg_val          -- NULL 被忽略（分母也不含 NULL）\nFROM t;\n```\n\nFile v1.0.0:references/sql-internals.md\n\n# SQL 原理深度\n\n## 目录\n1. [事务 & ACID](#事务--acid)\n2. [MVCC 多版本并发控制](#mvcc-多版本并发控制)\n3. [锁机制](#锁机制)\n4. [索引原理（B+树）](#索引原理b树)\n5. [查询优化器](#查询优化器)\n6. [Join 算法](#join-算法)\n7. [WAL & 崩溃恢复](#wal--崩溃恢复)\n\n---\n\n## 事务 & ACID\n\n### 四个特性\n```\nA - Atomicity（原子性）：事务要么全成功，要么全回滚\nC - Consistency（一致性）：事务前后数据满足业务约束\nI - Isolation（隔离性）：并发事务互不干扰\nD - Durability（持久性）：提交后数据不丢失\n```\n\n### 隔离级别（从低到高）\n\n| 级别 | 脏读 | 不可重复读 | 幻读 | 说明 |\n|------|------|-----------|------|------|\n| READ UNCOMMITTED | ✅ 会 | ✅ 会 | ✅ 会 | 几乎不用 |\n| READ COMMITTED | ❌ 不会 | ✅ 会 | ✅ 会 | Oracle/PG 默认 |\n| REPEATABLE READ | ❌ 不会 | ❌ 不会 | ⚠️ 部分 | MySQL 默认 |\n| SERIALIZABLE | ❌ 不会 | ❌ 不会 | ❌ 不会 | 性能最差 |\n\n**MySQL RR 级别下的幻读**：\n- 快照读（普通 SELECT）：MVCC 解决，不会幻读\n- 当前读（SELECT FOR UPDATE / UPDATE）：需要间隙锁（Gap Lock）解决\n\n```sql\n-- 查看/设置隔离级别\nSELECT @@transaction_isolation;\nSET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;\n```\n\n---\n\n## MVCC 多版本并发控制\n\n### 核心思想\n**读不加锁，写不阻塞读** — 通过保存数据的多个历史版本，让读写操作并发执行。\n\n### InnoDB 实现原理\n\n每行数据有两个隐藏字段：\n- `trx_id`：最后修改该行的事务 ID\n- `roll_pointer`：指向 undo log 中的上一个版本\n\n```\n当前行","readmeExcerpt":"Skill: SQL Master Owner: moniq888 Summary: SQL 查询、数据获取智能体。覆盖 SQL 全链路能力：自然语言转生产级 SQL、慢查询诊断与执行计划分析、索引设计与优化、数仓建模、SQL 原理深度科普、查询结果可视化。支持 MySQL / PostgreSQL / Hive / Spark SQL / ClickHouse / BigQuery 多方言。触发... Tags: latest:1.0.1 Version history: v1.0.1 | 2026-03-27T10:31:31.453Z | user Version 1.0.1 - 更新 Skill 协作工具名称，“report-generator” 统一更名为 “sql-report-generator” - 所有协作流程、范例代码和说明同步修改，保持命名一致性 - 其他功能、依赖及接口保持不变 v1.0.0 | 2026","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"skillhub_install install_skill sql-master"},{"language":"text","snippet":"┌─────────────┐     ┌──────────────┐     ┌────────────────────────┐\n│ sql-master  │ ──► │ sql-dataviz  │ ──► │ sql-report-generator   │\n│  (数据层)   │     │  (可视化层)  │     │  (报告层)              │\n└─────────────┘     └──────────────┘     └────────────────────────┘\n      │                   │                   │\n      ▼                   ▼                   ▼\n   SQL 查询           图表生成            HTML 报告\n   数据获取           PNG/HTML            AI 洞察\n   格式转换           Dashboard           数据表格"},{"language":"python","snippet":"from scripts.unified_pipeline import UnifiedPipeline\n\nresult = (\n    UnifiedPipeline(\"销售分析\")\n    .from_file(\"sales.csv\")                                    # sql-master: 数据获取\n    .query(\"SELECT region, SUM(sales) as total FROM data GROUP BY region\")\n    .interactive_chart(\"bar\", x_col=\"region\", y_col=\"total\")   # sql-dataviz: 可视化\n    .insights(value_cols=[\"total\"])                            # AI 洞察\n    .report(title=\"销售报告\", output=\"report.html\")            # sql-report-generator: 报告\n)"},{"language":"text","snippet":"你需要什么？\n├─ 仅 SQL 查询/优化 → sql-master 单独使用\n├─ SQL + 图表 → sql-master + sql-dataviz\n├─ 图表 + 报告（无 SQL）→ sql-dataviz + sql-report-generator\n└─ 完整分析报告 → sql-master + sql-dataviz + sql-report-generator ✅ 推荐"},{"language":"python","snippet":"from scripts.unified_pipeline import UnifiedPipeline, analyze_file\n\n# 完整 Pipeline\nresult = (\n    UnifiedPipeline(\"销售分析\")\n    .from_file(\"sales.csv\")                        # 数据源\n    .query(\"SELECT region, SUM(sales) as total FROM data GROUP BY region\")  # SQL\n    .interactive_chart(\"bar\", x_col=\"region\", y_col=\"total\", title=\"区域销售\")  # 交互图\n    .chart(\"line\", x_col=\"region\", y_col=\"total\")              # 静态图 (PNG)\n    .insights(value_cols=[\"total\"])                            # AI 洞察\n    .report(title=\"销售报告\", output=\"report.html\")           # 完整报告\n)\nprint(result.log())\n\n# 一键分析\nresult = analyze_file(\"sales.csv\", output=\"report.html\")"},{"language":"python","snippet":"from scripts.database_connector import connect_sqlite, connect_mysql, connect_postgresql\n\n# SQLite（本地文件）\nconn = connect_sqlite(\"data/sales.db\")\nresult = conn.execute(\"SELECT region, SUM(amount) FROM sales GROUP BY region\")\nprint(result.df)           # DataFrame 访问\nprint(result.to_dict())   # dict 访问\nresult.to_csv(\"output.csv\")  # 导出 CSV\nresult.to_json(\"output.json\") # 导出 JSON\n\n# MySQL\nconn = connect_mysql(host=\"localhost\", port=3306, username=\"root\", password=\"xxx\", database=\"mydb\")\nresult = conn.execute(\"SELECT * FROM orders WHERE date >= '2024-01-01'\")\nprint(result.summary())   # 可读摘要\n\n# PostgreSQL\nconn = connect_postgresql(host=\"localhost\", database=\"mydb\", username=\"postgres\", password=\"xxx\")\ntables = conn.get_tables()  # 获取所有表名\nschema = conn.get_schema(\"orders\")  # 获取表结构\nconn.close()"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: sql-master\ndescription: SQL 查询、数据获取智能体。覆盖 SQL 全链路能力：自然语言转生产级 SQL、慢查询诊断与执行计划分析、索引设计与优化、数仓建模、SQL 原理深度科普、查询结果可视化。支持 MySQL / PostgreSQL / Hive / Spark SQL / ClickHouse / BigQuery 多方言。触发场景：(1) 写 SQL / 生成查询，(2) SQL 慢/优化/调优，(3) 执行计划分析 EXPLAIN，(4) 索引设计，(5) 数仓建模 / 分层设计，(6) SQL 原理问题（事务/锁/MVCC/Join算法等），(7) 表结构设计 DDL，(8) SQL 报错诊断，(9) 任何\"帮我写个查询\"、\"这个SQL为什么慢\"、\"怎么建索引\"类请求，(10) 查询结果可视化 / 出图 / 图表 / 数据展示。\n---\n\n# SQL Master — SQL 查询、数据获取智能体\n\n## ⚠️ 使用前必读\n\n本 Skill 需要 Python 依赖。**首次使用前必须安装依赖**：\n\n```bash\nskillhub_install install_skill sql-master\n```\n\n工具会自动检测 Python3 环境、pip 可用性，并安装所有依赖。\n\n### 依赖安装方式\n\n| 方式 | 命令 | 适用场景 |\n|------|------|---------|\n| **自动安装（推荐）** | `skillhub_install install_skill sql-master` | 一键安装，自动处理 |\n| **手动安装** | `pip install -r requirements.txt` | 熟悉 Python 环境的用户 |\n\n### 无依赖使用（受限模式）\n\n如果无法安装依赖，本 Skill 提供以下**降级能力**：\n\n✅ **可用功能**：\n- SQL 语句生成（纯文本输出，无需执行）\n- SQL 诊断与优化建议（基于文本分析）\n- 索引设计建议（基于规则引擎）\n- SQL 原理解释与科普\n- 执行计划分析（用户提供 EXPLAIN 结果）\n\n❌ **不可用功能**：\n- 数据库连接与 SQL 执行\n- 数据 Pipeline 处理\n- 本地文件数据获取（CSV/Excel 等）\n- 与 sql-dataviz / sql-report-generator 联动\n\n---\n\n## 🔗 Skill 协作关系\n\n本 Skill 与 **sql-dataviz**、**sql-report-generator** 组成完整的数据分析流水线：\n\n```\n┌─────────────┐     ┌──────────────┐     ┌────────────────────────┐\n│ sql-master  │ ──► │ sql-dataviz  │ ──► │ sql-report-generator   │\n│  (数据层)   │     │  (可视化层)  │     │  (报告层)              │\n└─────────────┘     └──────────────┘     └────────────────────────┘\n      │                   │                   │\n      ▼                   ▼                   ▼\n   SQL 查询           图表生成            HTML 报告\n   数据获取           PNG/HTML            AI 洞察\n   格式转换           Dashboard           数据表格\n```\n\n### 协作模式\n\n| 模式 | 组合 | 适用场景 |\n|------|------|---------|\n| **单独使用** | sql-master | 仅需 SQL 查询/生成/优化 |\n| **可视化** | sql-master + sql-dataviz | SQL 查询 → 图表输出 |\n| **完整流程** | sql-master + sql-dataviz + sql-report-generator | 完整数据分析报告 |\n\n### 🥇 最优使用方式：三 Skill 串联\n\n```python\nfrom scripts.unified_pipeline import UnifiedPipeline\n\nresult = (\n    UnifiedPipeline(\"销售分析\")\n    .from_file(\"sales.csv\")                                    # sql-master: 数据获取\n    .query(\"SELECT region, SUM(sales) as total FROM data GROUP BY region\")\n    .interactive_chart(\"bar\", x_col=\"region\", y_col=\"total\")   # sql-dataviz: 可视化\n    .insights(value_cols=[\"total\"])                            # AI 洞察\n    .report(title=\"销售报告\", output=\"report.html\")            # sql-report-generator: 报告\n)\n```\n\n### 决策指南\n\n```\n你需要什么？\n├─ 仅 SQL 查询/优化 → sql-master 单独使用\n├─ SQL + 图表 → sql-master + sql-dataviz\n├─ 图表 + 报告（无 SQL）→ sql-dataviz + sql-report-generator\n└─ 完整分析报告 → sql-master + sql-dataviz + sql-report-generator ✅ 推荐\n```\n\n---\n\n## 新增功能：统一 Pipeline 编排（三 Skill 端到端）\n\n### `scripts/unified_pipeline.py`\n\n打通 sql-master → sql-dataviz → sql-report-generator 的端到端自动化：\n\n```python\nfrom scripts.unified_pipeline import UnifiedPipeline, analyze_file\n\n# 完整 Pipeline\nresult = (\n    UnifiedPipeline(\"销售分析\")\n    .from_file(\"sales.csv\")                        # 数据源\n    .query(\"SELECT region, SUM(sales) as total FROM d"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn76k6338wpqydkxgdztg6vb9x83hz20\",\n  \"slug\": \"sql-master\",\n  \"version\": \"1.0.1\",\n  \"publishedAt\": 1774607491453\n}"},{"path":"references/cli-quickref.md","content":"# CLI 实操速查\n\n数据库命令行工具的常用操作速查，适合直接上手。\n\n---\n\n## SQLite\n\nSQLite 内置于 Python，零配置，适合本地开发和原型验证。\n\n### 连接与基本操作\n```bash\n# 打开/创建数据库\nsqlite3 mydb.sqlite\n\n# 单行查询（不进入交互模式）\nsqlite3 mydb.sqlite \"SELECT COUNT(*) FROM users;\"\n\n# 交互模式开启表头和列对齐\nsqlite3 -header -column mydb.sqlite\n```\n\n### 数据导入导出\n```bash\n# 导入 CSV\nsqlite3 mydb.sqlite \".mode csv\" \".import data.csv mytable\" \"SELECT COUNT(*) FROM mytable;\"\n\n# 导出为 CSV\nsqlite3 -header -csv mydb.sqlite \"SELECT * FROM orders;\" > orders.csv\n\n# 导出整个数据库为 SQL\nsqlite3 mydb.sqlite .dump > backup.sql\n\n# 从 SQL 文件恢复\nsqlite3 mydb.sqlite < backup.sql\n```\n\n### 常用 Meta 命令\n```\n.tables              -- 列出所有表\n.schema users        -- 查看表结构\n.indexes users       -- 查看索引\n.mode column         -- 列对齐显示\n.headers on          -- 显示列名\n.quit                -- 退出\n```\n\n### 关键 PRAGMA\n```sql\nPRAGMA journal_mode = WAL;        -- 提升并发写入性能\nPRAGMA synchronous = NORMAL;      -- 平衡安全与性能\nPRAGMA foreign_keys = ON;         -- 启用外键约束（默认关闭！）\nPRAGMA cache_size = -64000;       -- 设置缓存 64MB\nPRAGMA temp_store = MEMORY;       -- 临时表放内存\nPRAGMA integrity_check;           -- 数据库完整性检查\n```\n\n---\n\n## PostgreSQL\n\n### 连接\n```bash\n# 基本连接\npsql -h localhost -U myuser -d mydb\n\n# 连接字符串\npsql \"postgresql://user:pass@localhost:5432/mydb?sslmode=require\"\n\n# 单行查询\npsql -h localhost -U myuser -d mydb -c \"SELECT NOW();\"\n\n# 执行 SQL 文件\npsql -h localhost -U myuser -d mydb -f migration.sql\n\n# 列出所有数据库\npsql -l\n```\n\n### 数据导入导出\n```bash\n# 导出整个数据库\npg_dump -h localhost -U myuser mydb > backup.sql\n\n# 导出为自定义格式（推荐，支持并行恢复）\npg_dump -h localhost -U myuser -Fc mydb > backup.dump\n\n# 恢复\npsql -h localhost -U myuser mydb < backup.sql\npg_restore -h localhost -U myuser -d mydb backup.dump\n\n# 导出单表为 CSV\npsql -h localhost -U myuser -d mydb -c \"\\COPY orders TO 'orders.csv' CSV HEADER\"\n\n# 导入 CSV\npsql -h localhost -U myuser -d mydb -c \"\\COPY orders FROM 'orders.csv' CSV HEADER\"\n```\n\n### 常用 Meta 命令\n```\n\\l                   -- 列出数据库\n\\c mydb              -- 切换数据库\n\\dt                  -- 列出表\n\\d users             -- 查看表结构（含索引）\n\\di                  -- 列出索引\n\\df                  -- 列出函数\n\\timing              -- 显示查询耗时\n\\x                   -- 切换扩展显示模式（宽表友好）\n\\e                   -- 用编辑器编辑查询\n\\q                   -- 退出\n```\n\n### 性能诊断\n```sql\n-- 查看慢查询（需开启 pg_stat_statements）\nSELECT query, calls, mean_exec_time, total_exec_time\nFROM pg_stat_statements\nORDER BY mean_exec_time DESC\nLIMIT 10;\n\n-- 查看表大小\nSELECT relname, pg_size_pretty(pg_total_relation_size(relid))\nFROM pg_stat_user_tables\nORDER BY pg_total_relation_size(relid) DESC;\n\n-- 查看锁等待\nSELECT pid, wait_event_type, wait_event, query\nFROM pg_stat_activity\nWHERE wait_event IS NOT NULL;\n\n-- 终止慢查询\nSELECT pg_terminate_backend(pid)\nFROM pg_stat_activity\nWHERE query_start < NOW() - INTERVAL '5 minutes'\n  AND state = 'active';\n```\n\n---\n\n## MySQL / MariaDB\n\n### 连接\n```bash\n# 基本连接\nmysql -h localhost -u myuser -p mydb\n\n# 单行查询\nmysql -h localhost -u myuser -p mydb -e \"SELECT NOW();\"\n\n# 执行 SQL 文件\nmysql -h localhost -u myuser -p mydb < migration.sql\n\n# 不显示密码警告（脚本用）\nmysql --defaults-extra-file=~/.my.cnf mydb"},{"path":"references/data-warehouse.md","content":"# 数仓建模 & 分层架构\n\n## 数仓分层架构（标准）\n\n```\n原始数据\n    ↓\nODS（Operational Data Store）操作数据层\n    ↓\nDWD（Data Warehouse Detail）明细数据层\n    ↓\nDWS（Data Warehouse Summary）汇总数据层\n    ↓\nADS（Application Data Store）应用数据层\n    ↓\n报表 / BI / 应用\n```\n\n### 各层职责\n\n| 层 | 职责 | 特点 |\n|----|------|------|\n| ODS | 原始数据落地，不做业务加工 | 保留原始字段，全量或增量同步 |\n| DWD | 数据清洗、标准化、维度关联 | 1:1 对应业务事实，最细粒度 |\n| DWS | 按主题聚合，轻度汇总 | 按天/周/月聚合，宽表 |\n| ADS | 面向具体应用的指标 | 直接支撑报表，高度聚合 |\n\n---\n\n## 维度建模\n\n### 星型模型 vs 雪花模型\n\n```\n星型模型：\n  事实表 ← 直接关联 → 维度表（维度表不再关联其他维度表）\n  优点：查询简单，JOIN 少，性能好\n  缺点：维度表可能有冗余\n\n雪花模型：\n  事实表 ← 维度表 ← 子维度表（维度表继续规范化）\n  优点：存储空间小，无冗余\n  缺点：JOIN 多，查询复杂，性能差\n\n实践建议：OLAP 场景优先用星型模型\n```\n\n### 事实表设计\n```sql\n-- 事实表：记录业务事件，包含度量值和外键\nCREATE TABLE dwd_order_detail (\n    order_id        BIGINT          COMMENT '订单ID',\n    user_id         BIGINT          COMMENT '用户ID（关联用户维度）',\n    product_id      BIGINT          COMMENT '商品ID（关联商品维度）',\n    date_id         INT             COMMENT '日期ID（关联日期维度，格式 20240101）',\n    -- 度量值\n    quantity        INT             COMMENT '购买数量',\n    unit_price      DECIMAL(10,2)   COMMENT '单价',\n    discount_amount DECIMAL(10,2)   COMMENT '优惠金额',\n    actual_amount   DECIMAL(10,2)   COMMENT '实付金额',\n    -- 分区字段\n    dt              STRING          COMMENT '数据日期分区 yyyy-MM-dd'\n)\nCOMMENT '订单明细事实表'\nPARTITIONED BY (dt STRING)\nSTORED AS ORC;\n```\n\n### 维度表设计\n```sql\n-- 维度表：描述业务实体的属性\nCREATE TABLE dim_user (\n    user_id         BIGINT          COMMENT '用户ID',\n    username        STRING          COMMENT '用户名',\n    register_date   STRING          COMMENT '注册日期',\n    city            STRING          COMMENT '城市',\n    age_group       STRING          COMMENT '年龄段（18-24/25-34/...）',\n    user_level      STRING          COMMENT '用户等级（普通/银牌/金牌/钻石）',\n    -- SCD（缓慢变化维度）字段\n    start_date      STRING          COMMENT '该版本生效日期',\n    end_date        STRING          COMMENT '该版本失效日期（9999-12-31 表示当前有效）',\n    is_current      TINYINT         COMMENT '是否当前版本（1=是）'\n)\nCOMMENT '用户维度表'\nSTORED AS ORC;\n```\n\n---\n\n## 缓慢变化维度（SCD）\n\n### SCD Type 1：直接覆盖\n```sql\n-- 适用：不需要历史，只关心当前值\nUPDATE dim_user SET city = '上海' WHERE user_id = 1001;\n-- 缺点：历史数据丢失\n```\n\n### SCD Type 2：新增版本（最常用）\n```sql\n-- 适用：需要保留历史，分析不同时期的属性\n-- 用户从北京迁到上海时，新增一行，旧行标记失效\n\n-- 失效旧版本\nUPDATE dim_user\nSET end_date = '2024-01-14', is_current = 0\nWHERE user_id = 1001 AND is_current = 1;\n\n-- 插入新版本\nINSERT INTO dim_user VALUES\n(1001, '张三', '2020-01-01', '上海', '25-34', '金牌', '2024-01-15', '9999-12-31', 1);\n\n-- 查询某时间点的用户属性（点查）\nSELECT * FROM dim_user\nWHERE user_id = 1001\n  AND start_date <= '2024-01-10'\n  AND end_date > '2024-01-10';\n```\n\n---\n\n## 数仓常用 SQL 模式\n\n### 增量数据处理（每日 ETL）\n```sql\n-- 场景：每天增量同步订单数据到 DWD\n-- 策略：按分区覆盖写（INSERT OVERWRITE）\n\nINSERT OVERWRITE TABLE dwd_order_detail PARTITION (dt = '2024-01-15')\nSELECT\n    o.order_id,\n    o.user_id,\n    o.product_id,\n    DATE_FORMAT(o.create_time, '%Y%m%d')    AS date_id,\n    o.quantity,\n    o.unit_price,\n    COALESCE(o.discount_amount, 0)          AS discount_amount,\n    o.actual_amount,\n    '2024-01-15'                            AS dt\nFROM ods_orders o\nWHER"},{"path":"references/ddl-design.md","content":"# DDL 设计规范\n\n## 表设计原则\n\n### 命名规范\n```\n表名：小写 + 下划线，加业务前缀\n  ods_orders          原始订单表\n  dwd_order_detail    订单明细事实表\n  dim_user            用户维度表\n  dws_user_daily      用户日汇总表\n  ads_funnel_report   漏斗报表\n\n列名：小写 + 下划线，语义清晰\n  user_id（不用 uid）\n  create_time（不用 ctime）\n  is_deleted（布尔用 is_ 前缀）\n  order_status（不用 status，加业务前缀）\n```\n\n### 字段类型选择\n\n```sql\n-- 整数：按范围选最小类型（节省存储，提升缓存命中）\nTINYINT     -- 1字节，-128~127，适合状态码、等级\nSMALLINT    -- 2字节，适合年份、小范围数值\nINT         -- 4字节，适合普通 ID（<21亿）\nBIGINT      -- 8字节，适合雪花ID、大流水号\n\n-- 字符串\nCHAR(n)     -- 定长，适合固定长度（手机号、身份证）\nVARCHAR(n)  -- 变长，适合普通文本，n 不要设太大（影响内存分配）\nTEXT        -- 大文本，不能建普通索引，不能作为主键\n\n-- 金额：绝对不用 FLOAT/DOUBLE（精度丢失）\nDECIMAL(10,2)   -- 精确小数，10位总长，2位小数\n-- 或者存分（整数），避免小数运算\nBIGINT          -- 单位：分，1元 = 100\n\n-- 时间\nDATETIME        -- MySQL，不含时区，'2024-01-15 10:30:00'\nTIMESTAMP       -- MySQL，含时区转换，范围到2038年（慎用）\nTIMESTAMPTZ     -- PostgreSQL，推荐，含时区\n\n-- 布尔\nTINYINT(1)      -- MySQL（没有原生 BOOLEAN）\nBOOLEAN         -- PostgreSQL\n```\n\n---\n\n## 生产级建表模板\n\n### 业务表（MySQL）\n```sql\nCREATE TABLE `orders` (\n    `id`            BIGINT          NOT NULL AUTO_INCREMENT  COMMENT '主键ID',\n    `order_no`      VARCHAR(32)     NOT NULL                 COMMENT '订单号（业务唯一标识）',\n    `user_id`       BIGINT          NOT NULL                 COMMENT '用户ID',\n    `product_id`    BIGINT          NOT NULL                 COMMENT '商品ID',\n    `quantity`      INT             NOT NULL DEFAULT 1       COMMENT '购买数量',\n    `unit_price`    DECIMAL(10,2)   NOT NULL                 COMMENT '单价（元）',\n    `discount`      DECIMAL(10,2)   NOT NULL DEFAULT 0.00    COMMENT '优惠金额（元）',\n    `actual_amount` DECIMAL(10,2)   NOT NULL                 COMMENT '实付金额（元）',\n    `status`        TINYINT         NOT NULL DEFAULT 0       COMMENT '订单状态：0待支付 1已支付 2已发货 3已完成 4已取消',\n    `remark`        VARCHAR(500)    DEFAULT NULL             COMMENT '备注',\n    `is_deleted`    TINYINT(1)      NOT NULL DEFAULT 0       COMMENT '软删除：0正常 1已删除',\n    `create_time`   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP  COMMENT '创建时间',\n    `update_time`   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `uk_order_no` (`order_no`),\n    KEY `idx_user_id_status` (`user_id`, `status`),\n    KEY `idx_create_time` (`create_time`)\n) ENGINE=InnoDB\n  DEFAULT CHARSET=utf8mb4\n  COLLATE=utf8mb4_unicode_ci\n  COMMENT='订单表';\n```\n\n### 日志/流水表（大数据量）\n```sql\n-- 大流水表：分区 + 不建过多索引\nCREATE TABLE `user_behavior_logs` (\n    `id`            BIGINT          NOT NULL AUTO_INCREMENT  COMMENT '主键',\n    `user_id`       BIGINT          NOT NULL                 COMMENT '用户ID',\n    `event_type`    VARCHAR(50)     NOT NULL                 COMMENT '事件类型',\n    `event_data`    JSON            DEFAULT NULL             COMMENT '事件数据',\n    `ip`            VARCHAR(45)     DEFAULT NULL             COMMENT 'IP地址（支持IPv6）',\n    `user_agent`    VARCHAR(500)    DEFAULT NULL             COMMENT 'UA',\n    `create_time`   DATETIME(3)     NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间（毫秒精"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":1036,"uniquenessScore":44,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-11T05:16:30.801Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-11T05:16:30.801Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-11T07:39:19.318Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}