{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "bd04-title",
   "metadata": {},
   "source": [
    "# 🏬 04 · Hive 与数仓建模：四层分层 + SQL 实践\n",
    "\n",
    "> 数仓岗的重头戏：**分层架构（ODS/DWD/DWS/ADS）** 与 **建模方法（星型/雪花/维度建模）**。\n",
    "> 本节用 **sqlite3** 构建迷你数仓，手写 SQL 完成**清洗→明细→汇总→应用**四层。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd04-q",
   "metadata": {},
   "source": [
    "## 📋 本节要解决的问题\n",
    "\n",
    "| # | 问题 | 方法 |\n",
    "|---|---|---|\n",
    "| 1 | 数仓为什么分层？ | 解耦/复用/成本/质量 |\n",
    "| 2 | 四层各做什么？ | ODS/DWD/DWS/ADS |\n",
    "| 3 | 怎么建模？ | 星型/雪花/宽表 |\n",
    "| 4 | 怎么用 SQL 落地？ | sqlite3 四层 ETL |"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "bd04-setup",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T09:49:08.619679Z",
     "iopub.status.busy": "2026-10-08T09:49:08.619043Z",
     "iopub.status.idle": "2026-10-08T09:49:23.011604Z",
     "shell.execute_reply": "2026-10-08T09:49:23.006671Z"
    }
   },
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import matplotlib\n",
    "import matplotlib.pyplot as plt\n",
    "matplotlib.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'PingFang SC', 'Noto Sans CJK SC', 'Arial Unicode MS']\n",
    "matplotlib.rcParams['axes.unicode_minus'] = False\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import sqlite3\n",
    "import os\n",
    "rng = np.random.default_rng(42)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd04-1",
   "metadata": {},
   "source": [
    "## 1️⃣ 数仓分层（必背框架）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b386e0ae",
   "metadata": {},
   "source": [
    "### 前置概念：数据库 vs 数据仓库（OLTP vs OLAP）\n",
    "\n",
    "| 维度 | 数据库（OLTP） | 数据仓库（OLAP） |\n",
    "|---|---|---|\n",
    "| 目标 | 高频事务：增删改单条记录 | 复杂分析：聚合扫亿级行 |\n",
    "| 典型操作 | 查订单详情、扣库存 | 算 GMV、留存、转化漏斗 |\n",
    "| 读写字节 | 读/写几行 | 扫全表/大范围分区 |\n",
    "| 设计 | 三范式（防冗余） | 维度建模（允许冗余换速度） |\n",
    "| 代表 | MySQL、PostgreSQL | Hive、Doris、ClickHouse |\n",
    "\n",
    "事务库不适合直接跑分析（锁冲突 + 全表扫描拖垮线上业务），\n",
    "所以要把数据 **T+1 同步到数仓**里做分析——这就是\"分层\"的起点。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "bd04-layers",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T09:49:23.016620Z",
     "iopub.status.busy": "2026-10-08T09:49:23.015803Z",
     "iopub.status.idle": "2026-10-08T09:49:23.048856Z",
     "shell.execute_reply": "2026-10-08T09:49:23.041953Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "数仓四层：\n",
      "  ODS（原始层）  ：贴源存储，原样保留（保留全量/增量，可回溯）；\n",
      "  DWD（明细层）  ：清洗+标准化+维度退化，明细粒度（最细粒度事实）；\n",
      "  DWS（汇总层）  ：按主题汇总（日/周维度宽表，供报表/应用）;\n",
      "  ADS（应用层）  ：面向业务应用/报表/算法特征（指标结果）。\n",
      "\n",
      "分层的价值：解耦（上游变化不传导）、复用（一致口径）、\n",
      "            成本（去重存储）、质量（逐层校验）。\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print('''数仓四层：\n",
    "  ODS（原始层）  ：贴源存储，原样保留（保留全量/增量，可回溯）；\n",
    "  DWD（明细层）  ：清洗+标准化+维度退化，明细粒度（最细粒度事实）；\n",
    "  DWS（汇总层）  ：按主题汇总（日/周维度宽表，供报表/应用）;\n",
    "  ADS（应用层）  ：面向业务应用/报表/算法特征（指标结果）。\n",
    "\n",
    "分层的价值：解耦（上游变化不传导）、复用（一致口径）、\n",
    "            成本（去重存储）、质量（逐层校验）。\n",
    "''')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd04-2",
   "metadata": {},
   "source": [
    "## 2️⃣ 建一张\"订单事实表\"进 ODS（sqlite3）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "bd04-ods",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T09:49:23.053773Z",
     "iopub.status.busy": "2026-10-08T09:49:23.053076Z",
     "iopub.status.idle": "2026-10-08T09:49:26.142234Z",
     "shell.execute_reply": "2026-10-08T09:49:26.137481Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ODS 层订单数: 6\n",
      "➡️ ODS 几乎原样存，仅做分区/压缩；可以看到脏数据（city=None）等后续清洗\n"
     ]
    }
   ],
   "source": [
    "if os.path.exists('mini_wh.db'):\n",
    "    os.remove('mini_wh.db')\n",
    "conn = sqlite3.connect('mini_wh.db')\n",
    "cur = conn.cursor()\n",
    "cur.execute('''CREATE TABLE ods_orders (\n",
    "    order_id TEXT, user_id TEXT, item_id TEXT, city TEXT,\n",
    "    amount REAL, status TEXT, ts TEXT\n",
    ")''')\n",
    "orders = [\n",
    "    ('o1', 'u1', 'i_phone', '北京', 5999.0, 'paid', '2024-05-01 10:00:00'),\n",
    "    ('o2', 'u2', 'i_phone', '上海', 5999.0, 'paid', '2024-05-01 10:05:00'),\n",
    "    ('o3', 'u1', 'i_laptop', '北京', 8999.0, 'refunded', '2024-05-02 09:00:00'),\n",
    "    ('o4', 'u3', 'i_tablet', '广州', 3999.0, 'paid', '2024-05-02 11:00:00'),\n",
    "    ('o5', 'u2', 'i_bag', '上海', 299.0, 'paid', '2024-05-03 14:30:00'),\n",
    "    ('o6', 'u4', 'i_keyboard', None, 199.0, 'paid', '2024-05-03 15:00:00'),\n",
    "]\n",
    "cur.executemany('INSERT INTO ods_orders VALUES (?,?,?,?,?,?,?)', orders)\n",
    "conn.commit()\n",
    "n = cur.execute('SELECT COUNT(*) FROM ods_orders').fetchone()[0]\n",
    "print(f'ODS 层订单数: {n}')\n",
    "print('➡️ ODS 几乎原样存，仅做分区/压缩；可以看到脏数据（city=None）等后续清洗')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd04-3",
   "metadata": {},
   "source": [
    "## 3️⃣ DWD：清洗 + 标准化（脏数据处理）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "bd04-dwd",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T09:49:26.147392Z",
     "iopub.status.busy": "2026-10-08T09:49:26.146469Z",
     "iopub.status.idle": "2026-10-08T09:49:27.311696Z",
     "shell.execute_reply": "2026-10-08T09:49:27.305421Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('o1', 'u1', 'i_phone', '北京', 5999.0, 1, '2024-05-01')\n",
      "('o2', 'u2', 'i_phone', '上海', 5999.0, 1, '2024-05-01')\n",
      "('o3', 'u1', 'i_laptop', '北京', 8999.0, 0, '2024-05-02')\n",
      "('o4', 'u3', 'i_tablet', '广州', 3999.0, 1, '2024-05-02')\n",
      "('o5', 'u2', 'i_bag', '上海', 299.0, 1, '2024-05-03')\n",
      "('o6', 'u4', 'i_keyboard', '未知', 199.0, 1, '2024-05-03')\n",
      "\n",
      "➡️ DWD：补空值、状态转成可聚合标志、抽日期分区字段\n"
     ]
    }
   ],
   "source": [
    "cur.execute('''CREATE TABLE dwd_orders AS\n",
    "SELECT order_id, user_id, item_id,\n",
    "       COALESCE(city, '未知') AS city,\n",
    "       amount,\n",
    "       CASE WHEN status = 'refunded' THEN 0 ELSE 1 END AS is_valid,\n",
    "       substr(ts, 1, 10) AS dt\n",
    "FROM ods_orders''')\n",
    "conn.commit()\n",
    "rows = cur.execute('SELECT * FROM dwd_orders').fetchall()\n",
    "for r in rows:\n",
    "    print(r)\n",
    "print('\\n➡️ DWD：补空值、状态转成可聚合标志、抽日期分区字段')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd04-4",
   "metadata": {},
   "source": [
    "## 4️⃣ DWS：按主题日汇总（宽表雏形）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "bd04-dws",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T09:49:27.317570Z",
     "iopub.status.busy": "2026-10-08T09:49:27.315992Z",
     "iopub.status.idle": "2026-10-08T09:49:28.352283Z",
     "shell.execute_reply": "2026-10-08T09:49:28.333376Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "DWS 日汇总（dt, city, 订单数, 用户数, GMV, 有效单）:\n",
      "  ('2024-05-01', '上海', 1, 1, 5999.0, 1)\n",
      "  ('2024-05-01', '北京', 1, 1, 5999.0, 1)\n",
      "  ('2024-05-02', '北京', 1, 1, 8999.0, 0)\n",
      "  ('2024-05-02', '广州', 1, 1, 3999.0, 1)\n",
      "  ('2024-05-03', '上海', 1, 1, 299.0, 1)\n",
      "  ('2024-05-03', '未知', 1, 1, 199.0, 1)\n",
      "➡️ DWS 就是\"主题宽表\"：一次计算多指标，供多处复用\n"
     ]
    }
   ],
   "source": [
    "cur.execute('''CREATE TABLE dws_daily_orders AS\n",
    "SELECT dt, city,\n",
    "       COUNT(*) AS order_cnt,\n",
    "       COUNT(DISTINCT user_id) AS user_cnt,\n",
    "       SUM(amount) AS gmv,\n",
    "       SUM(is_valid) AS valid_cnt\n",
    "FROM dwd_orders\n",
    "GROUP BY dt, city''')\n",
    "conn.commit()\n",
    "rows = cur.execute('SELECT * FROM dws_daily_orders ORDER BY dt, city').fetchall()\n",
    "print('DWS 日汇总（dt, city, 订单数, 用户数, GMV, 有效单）:')\n",
    "for r in rows:\n",
    "    print(' ', r)\n",
    "print('➡️ DWS 就是\"主题宽表\"：一次计算多指标，供多处复用')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd04-5",
   "metadata": {},
   "source": [
    "## 5️⃣ ADS：应用层指标（Top 城市 / 全量汇总）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "bd04-ads",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T09:49:28.357148Z",
     "iopub.status.busy": "2026-10-08T09:49:28.356155Z",
     "iopub.status.idle": "2026-10-08T09:49:28.392652Z",
     "shell.execute_reply": "2026-10-08T09:49:28.390906Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GMV Top3 城市:\n",
      "  北京: 14998 元\n",
      "  上海: 6298 元\n",
      "  广州: 3999 元\n",
      "全量: 订单 6, 用户(SUM) 6, 用户(去重) 4, GMV 25494 元, 有效单 5\n",
      "➡️ 教学点：DWS 按(天,城市)分组 COUNT(DISTINCT user_id) 后 SUM 会重复计数，跨组去重需单独统计（如按天去重）\n",
      "✅ ADS 指标正确（去重用户数=4：u1,u2,u3,u4）\n"
     ]
    }
   ],
   "source": [
    "top_city = cur.execute('''\n",
    "    SELECT city, SUM(gmv) AS gmv FROM dws_daily_orders\n",
    "    GROUP BY city ORDER BY gmv DESC LIMIT 3''').fetchall()\n",
    "print('GMV Top3 城市:')\n",
    "for c, g in top_city:\n",
    "    print(f'  {c}: {g:.0f} 元')\n",
    "total = cur.execute('''\n",
    "    SELECT SUM(order_cnt), SUM(user_cnt), SUM(gmv), SUM(valid_cnt)\n",
    "    FROM dws_daily_orders''').fetchone()\n",
    "distinct_users = cur.execute('SELECT COUNT(DISTINCT user_id) FROM dwd_orders').fetchone()[0]\n",
    "print(f'全量: 订单 {total[0]}, 用户(SUM) {total[1]}, 用户(去重) {distinct_users}, '\n",
    "      f'GMV {total[2]:.0f} 元, 有效单 {total[3]}')\n",
    "print('➡️ 教学点：DWS 按(天,城市)分组 COUNT(DISTINCT user_id) 后 SUM 会重复计数，'\n",
    "      '跨组去重需单独统计（如按天去重）')\n",
    "assert total[1] >= 4 and distinct_users == 4\n",
    "print('✅ ADS 指标正确（去重用户数=4：u1,u2,u3,u4）')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "9efb0dae",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T09:49:28.396952Z",
     "iopub.status.busy": "2026-10-08T09:49:28.396120Z",
     "iopub.status.idle": "2026-10-08T09:49:29.706633Z",
     "shell.execute_reply": "2026-10-08T09:49:29.686127Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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npT+CHR19OE87jAD/VfIiAlH7LpeAPdl9dPRh8mkYwz0RP8BtYb51NRUFCpUCHv37oWGhgZ+++03vHr1Chs3blQa5u3t7TFt2jTxeVxcHCwsLP7LIsuJiUtGcmo6Jg1sgdLF2WZfXd5ExmHdfh/ExCUzzH+F+KQYpKYlY1CH8SheuJS6i/NLi/z4FvvcNiI+KYZh/isISTFAWjIKtB0PTe7LapP58S0+uW/M+nswzBN9d2oN80WLFkWJEiXk7jhap04dHDhwQOn8enp60NPT+6+Kp7LSxY1R3ryouotB9E2KFy4F82KW6i4G0TfTLFwKWmbcl4no16DWDrCNGjXCgwcP5Ka9evUKlpY8CBMRERER5UatYX7UqFF49uwZHBwckJiYCC8vL2zZsgXjxo1TZ7GIiIiIiCRBrWG+QoUKOHr0KNatW4eCBQuiY8eOGDNmDEaPHq3OYhERERERSYJa28wDQOfOnREWFobIyEgULlwY+vr66i4SEREREZEkqD3MA4CmpiZKlmSPdyIiIiKivFBrMxsiIiIiIvp6DPNERERERBLFME9EREREJFEM80REREREEsUwT0REREQkUQzzREREREQSxTBPRERERCRRDPNERERERBLFME9EREREJFEM80REREREEsUwT0REREQkUQzzREREREQSxTBPRERERCRRDPNERERERBLFME9EREREJFEM80REREREEsUwT0REREQkUQzzREREREQSxTBPRERERCRRDPNERERERBLFME9EREREJFEM80REREREEsUwT0REREQkUQzzREREREQSxTBPRERERCRRDPNERERERBLFME9EREREJFEM80REREREEsUwT0REREQkUQzzREREREQSxTBPRERERCRRDPNERERERBLFME9EREREJFEM80REREREEsUwT0REREQkUQzzREREREQSxTBPRERERCRRDPNERERERBKlre4CLFiwAImJiXLTli9fDm1ttReNiIiIiOiHpvbEvGLFCrRv3x5FixYVpwmCoMYSERERERFJg1rD/KdPn5CSkoLly5ejRo0a6iwKEREREZHkqLXNfExMDADAxMREncUgIiIiIpKkHyLMGxsbq7MYRERERESSpNZmNrIw7+DgACMjI9SpUwe///47NDQ0lM6fkpKClJQU8XlcXNx/UUwiIiIioh+SWmvm9fX10adP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",
      "text/plain": [
       "<Figure size 900x420 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 可视化：四层各层的行数与粒度（自下而上加工）\n",
    "import matplotlib.pyplot as plt\n",
    "fig, ax = plt.subplots(figsize=(9, 4.2))\n",
    "layers = ['ODS(原始订单)', 'DWD(清洗明细)', 'DWS(日×城市汇总)', 'ADS(Top城市)']\n",
    "# 实际行数来自上面建的表\n",
    "cnt = [\n",
    "    cur.execute('SELECT COUNT(*) FROM ods_orders').fetchone()[0],\n",
    "    cur.execute('SELECT COUNT(*) FROM dwd_orders').fetchone()[0],\n",
    "    cur.execute('SELECT COUNT(*) FROM dws_daily_orders').fetchone()[0],\n",
    "    len(top_city),\n",
    "]\n",
    "bars = ax.bar(layers, cnt, color=['#D9E2F3', '#E2EFDA', '#FBE5D6', '#F8CBAD'],\n",
    "              ec=['#2F5597', '#548235', '#ED7D31', '#C00000'], width=0.55)\n",
    "for b, v in zip(bars, cnt):\n",
    "    ax.text(b.get_x() + b.get_width() / 2, v + 0.15, str(v), ha='center', fontsize=12, weight='bold')\n",
    "ax.set_ylabel('行数'); ax.set_title('四层粒度变化：明细行 → 汇总行 → 指标（数据越上层越小、越直接可用）')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd04-6",
   "metadata": {},
   "source": [
    "## 6️⃣ 建模方法：星型 / 雪花 / 宽表"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "bd04-model",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T09:49:29.711865Z",
     "iopub.status.busy": "2026-10-08T09:49:29.710956Z",
     "iopub.status.idle": "2026-10-08T09:49:29.779302Z",
     "shell.execute_reply": "2026-10-08T09:49:29.744959Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "事实表 + 维度表：\n",
      "  星型模型：事实表居中，维度表直接连（冗余维度，查询快）；\n",
      "  雪花模型：维度表再规范化拆分（省空间，查询多 join）；\n",
      "  宽表（维度退化）：把常用维度字段直接冗余进事实表（DWS 常用，最快）。\n",
      "\n",
      "选型口诀：查询性能优先 -> 星型/宽表；存储与一致性优先 -> 雪花。\n",
      "数仓工具：Hive（离线）、Presto/StarRocks/Doris（即席/实时）、Iceberg/Hudi（湖仓）。\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print('''事实表 + 维度表：\n",
    "  星型模型：事实表居中，维度表直接连（冗余维度，查询快）；\n",
    "  雪花模型：维度表再规范化拆分（省空间，查询多 join）；\n",
    "  宽表（维度退化）：把常用维度字段直接冗余进事实表（DWS 常用，最快）。\n",
    "\n",
    "选型口诀：查询性能优先 -> 星型/宽表；存储与一致性优先 -> 雪花。\n",
    "数仓工具：Hive（离线）、Presto/StarRocks/Doris（即席/实时）、Iceberg/Hudi（湖仓）。\n",
    "''')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd04-7",
   "metadata": {},
   "source": [
    "## 7️⃣ 面试高频：数仓治理"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2401d71e",
   "metadata": {},
   "source": [
    "### 缓慢变化维（SCD）与拉链表\n",
    "\n",
    "维度属性会变化（用户搬家了城市从北京→上海）。两种处理：\n",
    "\n",
    "- **Type 1（覆盖）**：直接 UPDATE 新值。简单，但**丢失历史**；\n",
    "- **Type 2（版本）**：旧行改 `end_date` 封版，插新行 `start_date` 生效，`end_date=9999-12-31` 表示当前有效。\n",
    "  可回答\"3 月份这个用户在哪个城市\"——历史可溯。\n",
    "\n",
    "**拉链表**就是 Type 2 的落地形式：`user_id, 属性, start_date, end_date` 四列，\n",
    "每天用\"昨天的闭链 + 今天的新开链\"两条 SQL 完成更新。适合**变化缓慢、量大**的维表（用户/商品）。\n",
    "\n",
    "下面用 sqlite3 演示一次完整的拉链更新。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "944f7505",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T09:49:29.785806Z",
     "iopub.status.busy": "2026-10-08T09:49:29.785106Z",
     "iopub.status.idle": "2026-10-08T09:49:33.271915Z",
     "shell.execute_reply": "2026-10-08T09:49:33.197996Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "拉链表（user, city, 起始日, 截止日）:\n",
      "   ('u1', '北京', '2024-01-01', '2024-04-01')\n",
      "   ('u1', '上海', '2024-04-01', '9999-12-31')\n",
      "   ('u2', '上海', '2024-01-01', '9999-12-31')\n",
      "\n",
      "✅ 按时间点查询历史正确：3月15日在北京，5月1日在上海\n"
     ]
    }
   ],
   "source": [
    "# 拉链表演示：u1 的 city 从 北京 -> 上海（2024-04-01 生效）\n",
    "cur.execute('CREATE TABLE IF NOT EXISTS d_user_zip (user_id TEXT, city TEXT, start_date TEXT, end_date TEXT)')\n",
    "cur.execute('DELETE FROM d_user_zip')\n",
    "cur.executemany('INSERT INTO d_user_zip VALUES (?,?,?,?)', [\n",
    "    ('u1', '北京', '2024-01-01', '9999-12-31'),\n",
    "    ('u2', '上海', '2024-01-01', '9999-12-31'),\n",
    "])\n",
    "conn.commit()\n",
    "\n",
    "def zipper_update(user, new_city, eff_date):\n",
    "    # 1) 闭链：当前有效记录的 end_date 改为 生效日前一天\n",
    "    cur.execute(\"UPDATE d_user_zip SET end_date = ? WHERE user_id=? AND end_date='9999-12-31'\",\n",
    "                (eff_date, user))\n",
    "    # 2) 开链：插入新版本\n",
    "    cur.execute('INSERT INTO d_user_zip VALUES (?,?,?,?)', (user, new_city, eff_date, '9999-12-31'))\n",
    "    conn.commit()\n",
    "\n",
    "zipper_update('u1', '上海', '2024-04-01')\n",
    "rows = cur.execute('SELECT * FROM d_user_zip ORDER BY user_id, start_date').fetchall()\n",
    "print('拉链表（user, city, 起始日, 截止日）:')\n",
    "for r in rows:\n",
    "    print('  ', r)\n",
    "# 验证：2024-03-15 查 u1 应在北京，2024-05-01 查 u1 应在上海\n",
    "q = lambda d: cur.execute(\n",
    "    \"SELECT city FROM d_user_zip WHERE user_id='u1' AND ? >= start_date AND ? <= end_date\", (d, d)\n",
    ").fetchone()[0]\n",
    "assert q('2024-03-15') == '北京' and q('2024-05-01') == '上海'\n",
    "print('\\n✅ 按时间点查询历史正确：3月15日在北京，5月1日在上海')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e599c585",
   "metadata": {},
   "source": [
    "### Hive 本身是什么（面试常混）\n",
    "\n",
    "Hive 不是数据库，而是**把 SQL 翻译成分布式任务的引擎**：\n",
    "- 表结构（schema）存在 **Metastore**（元数据服务，常配 MySQL）；\n",
    "- 数据存在 HDFS/对象存储上（就是普通文件）；\n",
    "- 查询时 HiveSQL → 解析器 → 优化器 → 执行计划（早期 MR，现 Tez/Spark 引擎）；\n",
    "- **分区（partition）**＝按目录切分（如 `dt=2024-05-01/`），查询带分区条件可跳过无关目录（裁剪）；\n",
    "- **分桶（bucket）**＝按 key 哈希切文件，利于采样与 map-side join。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "bd04-gov",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T09:49:33.276301Z",
     "iopub.status.busy": "2026-10-08T09:49:33.275653Z",
     "iopub.status.idle": "2026-10-08T09:49:33.392058Z",
     "shell.execute_reply": "2026-10-08T09:49:33.362227Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1) 指标口径：同一指标全局唯一（命名/计算/来源统一）；\n",
      "2) 数据质量：完整性/准确性/一致性/及时性 四大维度 + 质量监控；\n",
      "3) 元数据管理：表血缘、字段字典、变更管理；\n",
      "4) 权限与安全：行列级权限、脱敏、审计；\n",
      "5) 数据倾斜：大 key 打散/两阶段聚合/倾斜检测；\n",
      "6) 缓慢变化维（SCD）：维度属性变化如何处理（Type1 覆盖/Type2 加版本）。\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "print('''1) 指标口径：同一指标全局唯一（命名/计算/来源统一）；\n",
    "2) 数据质量：完整性/准确性/一致性/及时性 四大维度 + 质量监控；\n",
    "3) 元数据管理：表血缘、字段字典、变更管理；\n",
    "4) 权限与安全：行列级权限、脱敏、审计；\n",
    "5) 数据倾斜：大 key 打散/两阶段聚合/倾斜检测；\n",
    "6) 缓慢变化维（SCD）：维度属性变化如何处理（Type1 覆盖/Type2 加版本）。\n",
    "''')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd04-8",
   "metadata": {},
   "source": [
    "## 📝 自测清单\n",
    "\n",
    "- [ ] 能画出 ODS/DWD/DWS/ADS 四层与职责\n",
    "- [ ] 能用 SQL 完成一层 ETL（清洗/聚合）\n",
    "- [ ] 能对比星型/雪花/宽表\n",
    "- [ ] 能答 6 个数仓治理高频点\n",
    "- [ ] 能解释指标口径统一与数据质量四维度"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
