[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6G2DM20WC8EMW86JBGE5VPM":3,"skill-same:01M6G2DM20WC8EMW86JBGE5VPM":68},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":40,"linkOnly":41,"markdownContent":42,"markdownBody":43,"frontMatter":44,"refName":45,"commitSha":46,"skillDirectory":47,"forksCount":48,"repositoryHtmlUrl":49,"repositoryHomepageUrl":6,"ownerHtmlUrl":50,"ownerBlogUrl":51,"ownerDescription":52,"updatedAt":53,"install":54,"resources":67},"01M6G2DM20WC8EMW86JBGE5VPM","clickhouse-io",null,"ClickHouse database patterns, query optimization, analytics, and data engineering best practices for high-performance analytical workloads.","oabdelmaksoud\u002FAgention","oabdelmaksoud","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F50699149?v=4","01M6D5CVVN778ZCDP1WHS816SP","Omar Abdelmaksoud","01M6D8A4TE76V3DCSH3D94VG1V",2,"MIT","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fdocs\u002Fzh-TW\u002Fskills\u002Fclickhouse-io\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39],"clickhouse","clickhouse io","io","performance","analytics","best practices","database","data engineering","engineering","Optimization","data","query optimization","patterns","query","analytical","high-performance","2026-11-10 10:12:30",false,"---\nname: clickhouse-io\ndescription: ClickHouse database patterns, query optimization, analytics, and data engineering best practices for high-performance analytical workloads.\n---\n\n# ClickHouse 分析模式\n\n用於高效能分析和資料工程的 ClickHouse 特定模式。\n\n## 概述\n\nClickHouse 是一個列式資料庫管理系統（DBMS），用於線上分析處理（OLAP）。它針對大型資料集的快速分析查詢進行了優化。\n\n**關鍵特性：**\n- 列式儲存\n- 資料壓縮\n- 平行查詢執行\n- 分散式查詢\n- 即時分析\n\n## 表格設計模式\n\n### MergeTree 引擎（最常見）\n\n```sql\nCREATE TABLE markets_analytics (\n    date Date,\n    market_id String,\n    market_name String,\n    volume UInt64,\n    trades UInt32,\n    unique_traders UInt32,\n    avg_trade_size Float64,\n    created_at DateTime\n) ENGINE = MergeTree()\nPARTITION BY toYYYYMM(date)\nORDER BY (date, market_id)\nSETTINGS index_granularity = 8192;\n```\n\n### ReplacingMergeTree（去重）\n\n```sql\n-- 用於可能有重複的資料（例如來自多個來源）\nCREATE TABLE user_events (\n    event_id String,\n    user_id String,\n    event_type String,\n    timestamp DateTime,\n    properties String\n) ENGINE = ReplacingMergeTree()\nPARTITION BY toYYYYMM(timestamp)\nORDER BY (user_id, event_id, timestamp)\nPRIMARY KEY (user_id, event_id);\n```\n\n### AggregatingMergeTree（預聚合）\n\n```sql\n-- 用於維護聚合指標\nCREATE TABLE market_stats_hourly (\n    hour DateTime,\n    market_id String,\n    total_volume AggregateFunction(sum, UInt64),\n    total_trades AggregateFunction(count, UInt32),\n    unique_users AggregateFunction(uniq, String)\n) ENGINE = AggregatingMergeTree()\nPARTITION BY toYYYYMM(hour)\nORDER BY (hour, market_id);\n\n-- 查詢聚合資料\nSELECT\n    hour,\n    market_id,\n    sumMerge(total_volume) AS volume,\n    countMerge(total_trades) AS trades,\n    uniqMerge(unique_users) AS users\nFROM market_stats_hourly\nWHERE hour >= toStartOfHour(now() - INTERVAL 24 HOUR)\nGROUP BY hour, market_id\nORDER BY hour DESC;\n```\n\n## 查詢優化模式\n\n### 高效過濾\n\n```sql\n-- ✅ 良好：先使用索引欄位\nSELECT *\nFROM markets_analytics\nWHERE date >= '2025-01-01'\n  AND market_id = 'market-123'\n  AND volume > 1000\nORDER BY date DESC\nLIMIT 100;\n\n-- ❌ 不良：先過濾非索引欄位\nSELECT *\nFROM markets_analytics\nWHERE volume > 1000\n  AND market_name LIKE '%election%'\n  AND date >= '2025-01-01';\n```\n\n### 聚合\n\n```sql\n-- ✅ 良好：使用 ClickHouse 特定聚合函式\nSELECT\n    toStartOfDay(created_at) AS day,\n    market_id,\n    sum(volume) AS total_volume,\n    count() AS total_trades,\n    uniq(trader_id) AS unique_traders,\n    avg(trade_size) AS avg_size\nFROM trades\nWHERE created_at >= today() - INTERVAL 7 DAY\nGROUP BY day, market_id\nORDER BY day DESC, total_volume DESC;\n\n-- ✅ 使用 quantile 計算百分位數（比 percentile 更高效）\nSELECT\n    quantile(0.50)(trade_size) AS median,\n    quantile(0.95)(trade_size) AS p95,\n    quantile(0.99)(trade_size) AS p99\nFROM trades\nWHERE created_at >= now() - INTERVAL 1 HOUR;\n```\n\n### 視窗函式\n\n```sql\n-- 計算累計總和\nSELECT\n    date,\n    market_id,\n    volume,\n    sum(volume) OVER (\n        PARTITION BY market_id\n        ORDER BY date\n        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW\n    ) AS cumulative_volume\nFROM markets_analytics\nWHERE date >= today() - INTERVAL 30 DAY\nORDER BY market_id, date;\n```\n\n## 資料插入模式\n\n### 批量插入（推薦）\n\n```typescript\nimport { ClickHouse } from 'clickhouse'\n\nconst clickhouse = new ClickHouse({\n  url: process.env.CLICKHOUSE_URL,\n  port: 8123,\n  basicAuth: {\n    username: process.env.CLICKHOUSE_USER,\n    password: process.env.CLICKHOUSE_PASSWORD\n  }\n})\n\n\u002F\u002F ✅ 批量插入（高效）\nasync function bulkInsertTrades(trades: Trade[]) {\n  const values = trades.map(trade => `(\n    '${trade.id}',\n    '${trade.market_id}',\n    '${trade.user_id}',\n    ${trade.amount},\n    '${trade.timestamp.toISOString()}'\n  )`).join(',')\n\n  await clickhouse.query(`\n    INSERT INTO trades (id, market_id, user_id, amount, timestamp)\n    VALUES ${values}\n  `).toPromise()\n}\n\n\u002F\u002F ❌ 個別插入（慢）\nasync function insertTrade(trade: Trade) {\n  \u002F\u002F 不要在迴圈中這樣做！\n  await clickhouse.query(`\n    INSERT INTO trades VALUES ('${trade.id}', ...)\n  `).toPromise()\n}\n```\n\n### 串流插入\n\n```typescript\n\u002F\u002F 用於持續資料攝取\nimport { createWriteStream } from 'fs'\nimport { pipeline } from 'stream\u002Fpromises'\n\nasync function streamInserts() {\n  const stream = clickhouse.insert('trades').stream()\n\n  for await (const batch of dataSource) {\n    stream.write(batch)\n  }\n\n  await stream.end()\n}\n```\n\n## 物化視圖\n\n### 即時聚合\n\n```sql\n-- 建立每小時統計的物化視圖\nCREATE MATERIALIZED VIEW market_stats_hourly_mv\nTO market_stats_hourly\nAS SELECT\n    toStartOfHour(timestamp) AS hour,\n    market_id,\n    sumState(amount) AS total_volume,\n    countState() AS total_trades,\n    uniqState(user_id) AS unique_users\nFROM trades\nGROUP BY hour, market_id;\n\n-- 查詢物化視圖\nSELECT\n    hour,\n    market_id,\n    sumMerge(total_volume) AS volume,\n    countMerge(total_trades) AS trades,\n    uniqMerge(unique_users) AS users\nFROM market_stats_hourly\nWHERE hour >= now() - INTERVAL 24 HOUR\nGROUP BY hour, market_id;\n```\n\n## 效能監控\n\n### 查詢效能\n\n```sql\n-- 檢查慢查詢\nSELECT\n    query_id,\n    user,\n    query,\n    query_duration_ms,\n    read_rows,\n    read_bytes,\n    memory_usage\nFROM system.query_log\nWHERE type = 'QueryFinish'\n  AND query_duration_ms > 1000\n  AND event_time >= now() - INTERVAL 1 HOUR\nORDER BY query_duration_ms DESC\nLIMIT 10;\n```\n\n### 表格統計\n\n```sql\n-- 檢查表格大小\nSELECT\n    database,\n    table,\n    formatReadableSize(sum(bytes)) AS size,\n    sum(rows) AS rows,\n    max(modification_time) AS latest_modification\nFROM system.parts\nWHERE active\nGROUP BY database, table\nORDER BY sum(bytes) DESC;\n```\n\n## 常見分析查詢\n\n### 時間序列分析\n\n```sql\n-- 每日活躍使用者\nSELECT\n    toDate(timestamp) AS date,\n    uniq(user_id) AS daily_active_users\nFROM events\nWHERE timestamp >= today() - INTERVAL 30 DAY\nGROUP BY date\nORDER BY date;\n\n-- 留存分析\nSELECT\n    signup_date,\n    countIf(days_since_signup = 0) AS day_0,\n    countIf(days_since_signup = 1) AS day_1,\n    countIf(days_since_signup = 7) AS day_7,\n    countIf(days_since_signup = 30) AS day_30\nFROM (\n    SELECT\n        user_id,\n        min(toDate(timestamp)) AS signup_date,\n        toDate(timestamp) AS activity_date,\n        dateDiff('day', signup_date, activity_date) AS days_since_signup\n    FROM events\n    GROUP BY user_id, activity_date\n)\nGROUP BY signup_date\nORDER BY signup_date DESC;\n```\n\n### 漏斗分析\n\n```sql\n-- 轉換漏斗\nSELECT\n    countIf(step = 'viewed_market') AS viewed,\n    countIf(step = 'clicked_trade') AS clicked,\n    countIf(step = 'completed_trade') AS completed,\n    round(clicked \u002F viewed * 100, 2) AS view_to_click_rate,\n    round(completed \u002F clicked * 100, 2) AS click_to_completion_rate\nFROM (\n    SELECT\n        user_id,\n        session_id,\n        event_type AS step\n    FROM events\n    WHERE event_date = today()\n)\nGROUP BY session_id;\n```\n\n### 世代分析\n\n```sql\n-- 按註冊月份的使用者世代\nSELECT\n    toStartOfMonth(signup_date) AS cohort,\n    toStartOfMonth(activity_date) AS month,\n    dateDiff('month', cohort, month) AS months_since_signup,\n    count(DISTINCT user_id) AS active_users\nFROM (\n    SELECT\n        user_id,\n        min(toDate(timestamp)) OVER (PARTITION BY user_id) AS signup_date,\n        toDate(timestamp) AS activity_date\n    FROM events\n)\nGROUP BY cohort, month, months_since_signup\nORDER BY cohort, months_since_signup;\n```\n\n## 資料管線模式\n\n### ETL 模式\n\n```typescript\n\u002F\u002F 提取、轉換、載入\nasync function etlPipeline() {\n  \u002F\u002F 1. 從來源提取\n  const rawData = await extractFromPostgres()\n\n  \u002F\u002F 2. 轉換\n  const transformed = rawData.map(row => ({\n    date: new Date(row.created_at).toISOString().split('T')[0],\n    market_id: row.market_slug,\n    volume: parseFloat(row.total_volume),\n    trades: parseInt(row.trade_count)\n  }))\n\n  \u002F\u002F 3. 載入到 ClickHouse\n  await bulkInsertToClickHouse(transformed)\n}\n\n\u002F\u002F 定期執行\nsetInterval(etlPipeline, 60 * 60 * 1000)  \u002F\u002F 每小時\n```\n\n### 變更資料捕獲（CDC）\n\n```typescript\n\u002F\u002F 監聽 PostgreSQL 變更並同步到 ClickHouse\nimport { Client } from 'pg'\n\nconst pgClient = new Client({ connectionString: process.env.DATABASE_URL })\n\npgClient.query('LISTEN market_updates')\n\npgClient.on('notification', async (msg) => {\n  const update = JSON.parse(msg.payload)\n\n  await clickhouse.insert('market_updates', [\n    {\n      market_id: update.id,\n      event_type: update.operation,  \u002F\u002F INSERT, UPDATE, DELETE\n      timestamp: new Date(),\n      data: JSON.stringify(update.new_data)\n    }\n  ])\n})\n```\n\n## 最佳實務\n\n### 1. 分區策略\n- 按時間分區（通常按月或日）\n- 避免太多分區（效能影響）\n- 分區鍵使用 DATE 類型\n\n### 2. 排序鍵\n- 最常過濾的欄位放在最前面\n- 考慮基數（高基數優先）\n- 排序影響壓縮\n\n### 3. 資料類型\n- 使用最小的適當類型（UInt32 vs UInt64）\n- 重複字串使用 LowCardinality\n- 分類資料使用 Enum\n\n### 4. 避免\n- SELECT *（指定欄位）\n- FINAL（改為在查詢前合併資料）\n- 太多 JOINs（為分析反正規化）\n- 小量頻繁插入（改用批量）\n\n### 5. 監控\n- 追蹤查詢效能\n- 監控磁碟使用\n- 檢查合併操作\n- 審查慢查詢日誌\n\n**記住**：ClickHouse 擅長分析工作負載。為你的查詢模式設計表格，批量插入，並利用物化視圖進行即時聚合。\n","# ClickHouse 分析模式\n\n用於高效能分析和資料工程的 ClickHouse 特定模式。\n\n## 概述\n\nClickHouse 是一個列式資料庫管理系統（DBMS），用於線上分析處理（OLAP）。它針對大型資料集的快速分析查詢進行了優化。\n\n**關鍵特性：**\n- 列式儲存\n- 資料壓縮\n- 平行查詢執行\n- 分散式查詢\n- 即時分析\n\n## 表格設計模式\n\n### MergeTree 引擎（最常見）\n\n```sql\nCREATE TABLE markets_analytics (\n    date Date,\n    market_id String,\n    market_name String,\n    volume UInt64,\n    trades UInt32,\n    unique_traders UInt32,\n    avg_trade_size Float64,\n    created_at DateTime\n) ENGINE = MergeTree()\nPARTITION BY toYYYYMM(date)\nORDER BY (date, market_id)\nSETTINGS index_granularity = 8192;\n```\n\n### ReplacingMergeTree（去重）\n\n```sql\n-- 用於可能有重複的資料（例如來自多個來源）\nCREATE TABLE user_events (\n    event_id String,\n    user_id String,\n    event_type String,\n    timestamp DateTime,\n    properties String\n) ENGINE = ReplacingMergeTree()\nPARTITION BY toYYYYMM(timestamp)\nORDER BY (user_id, event_id, timestamp)\nPRIMARY KEY (user_id, event_id);\n```\n\n### AggregatingMergeTree（預聚合）\n\n```sql\n-- 用於維護聚合指標\nCREATE TABLE market_stats_hourly (\n    hour DateTime,\n    market_id String,\n    total_volume AggregateFunction(sum, UInt64),\n    total_trades AggregateFunction(count, UInt32),\n    unique_users AggregateFunction(uniq, String)\n) ENGINE = AggregatingMergeTree()\nPARTITION BY toYYYYMM(hour)\nORDER BY (hour, market_id);\n\n-- 查詢聚合資料\nSELECT\n    hour,\n    market_id,\n    sumMerge(total_volume) AS volume,\n    countMerge(total_trades) AS trades,\n    uniqMerge(unique_users) AS users\nFROM market_stats_hourly\nWHERE hour >= toStartOfHour(now() - INTERVAL 24 HOUR)\nGROUP BY hour, market_id\nORDER BY hour DESC;\n```\n\n## 查詢優化模式\n\n### 高效過濾\n\n```sql\n-- ✅ 良好：先使用索引欄位\nSELECT *\nFROM markets_analytics\nWHERE date >= '2025-01-01'\n  AND market_id = 'market-123'\n  AND volume > 1000\nORDER BY date DESC\nLIMIT 100;\n\n-- ❌ 不良：先過濾非索引欄位\nSELECT *\nFROM markets_analytics\nWHERE volume > 1000\n  AND market_name LIKE '%election%'\n  AND date >= '2025-01-01';\n```\n\n### 聚合\n\n```sql\n-- ✅ 良好：使用 ClickHouse 特定聚合函式\nSELECT\n    toStartOfDay(created_at) AS day,\n    market_id,\n    sum(volume) AS total_volume,\n    count() AS total_trades,\n    uniq(trader_id) AS unique_traders,\n    avg(trade_size) AS avg_size\nFROM trades\nWHERE created_at >= today() - INTERVAL 7 DAY\nGROUP BY day, market_id\nORDER BY day DESC, total_volume DESC;\n\n-- ✅ 使用 quantile 計算百分位數（比 percentile 更高效）\nSELECT\n    quantile(0.50)(trade_size) AS median,\n    quantile(0.95)(trade_size) AS p95,\n    quantile(0.99)(trade_size) AS p99\nFROM trades\nWHERE created_at >= now() - INTERVAL 1 HOUR;\n```\n\n### 視窗函式\n\n```sql\n-- 計算累計總和\nSELECT\n    date,\n    market_id,\n    volume,\n    sum(volume) OVER (\n        PARTITION BY market_id\n        ORDER BY date\n        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW\n    ) AS cumulative_volume\nFROM markets_analytics\nWHERE date >= today() - INTERVAL 30 DAY\nORDER BY market_id, date;\n```\n\n## 資料插入模式\n\n### 批量插入（推薦）\n\n```typescript\nimport { ClickHouse } from 'clickhouse'\n\nconst clickhouse = new ClickHouse({\n  url: process.env.CLICKHOUSE_URL,\n  port: 8123,\n  basicAuth: {\n    username: process.env.CLICKHOUSE_USER,\n    password: process.env.CLICKHOUSE_PASSWORD\n  }\n})\n\n\u002F\u002F ✅ 批量插入（高效）\nasync function bulkInsertTrades(trades: Trade[]) {\n  const values = trades.map(trade => `(\n    '${trade.id}',\n    '${trade.market_id}',\n    '${trade.user_id}',\n    ${trade.amount},\n    '${trade.timestamp.toISOString()}'\n  )`).join(',')\n\n  await clickhouse.query(`\n    INSERT INTO trades (id, market_id, user_id, amount, timestamp)\n    VALUES ${values}\n  `).toPromise()\n}\n\n\u002F\u002F ❌ 個別插入（慢）\nasync function insertTrade(trade: Trade) {\n  \u002F\u002F 不要在迴圈中這樣做！\n  await clickhouse.query(`\n    INSERT INTO trades VALUES ('${trade.id}', ...)\n  `).toPromise()\n}\n```\n\n### 串流插入\n\n```typescript\n\u002F\u002F 用於持續資料攝取\nimport { createWriteStream } from 'fs'\nimport { pipeline } from 'stream\u002Fpromises'\n\nasync function streamInserts() {\n  const stream = clickhouse.insert('trades').stream()\n\n  for await (const batch of dataSource) {\n    stream.write(batch)\n  }\n\n  await stream.end()\n}\n```\n\n## 物化視圖\n\n### 即時聚合\n\n```sql\n-- 建立每小時統計的物化視圖\nCREATE MATERIALIZED VIEW market_stats_hourly_mv\nTO market_stats_hourly\nAS SELECT\n    toStartOfHour(timestamp) AS hour,\n    market_id,\n    sumState(amount) AS total_volume,\n    countState() AS total_trades,\n    uniqState(user_id) AS unique_users\nFROM trades\nGROUP BY hour, market_id;\n\n-- 查詢物化視圖\nSELECT\n    hour,\n    market_id,\n    sumMerge(total_volume) AS volume,\n    countMerge(total_trades) AS trades,\n    uniqMerge(unique_users) AS users\nFROM market_stats_hourly\nWHERE hour >= now() - INTERVAL 24 HOUR\nGROUP BY hour, market_id;\n```\n\n## 效能監控\n\n### 查詢效能\n\n```sql\n-- 檢查慢查詢\nSELECT\n    query_id,\n    user,\n    query,\n    query_duration_ms,\n    read_rows,\n    read_bytes,\n    memory_usage\nFROM system.query_log\nWHERE type = 'QueryFinish'\n  AND query_duration_ms > 1000\n  AND event_time >= now() - INTERVAL 1 HOUR\nORDER BY query_duration_ms DESC\nLIMIT 10;\n```\n\n### 表格統計\n\n```sql\n-- 檢查表格大小\nSELECT\n    database,\n    table,\n    formatReadableSize(sum(bytes)) AS size,\n    sum(rows) AS rows,\n    max(modification_time) AS latest_modification\nFROM system.parts\nWHERE active\nGROUP BY database, table\nORDER BY sum(bytes) DESC;\n```\n\n## 常見分析查詢\n\n### 時間序列分析\n\n```sql\n-- 每日活躍使用者\nSELECT\n    toDate(timestamp) AS date,\n    uniq(user_id) AS daily_active_users\nFROM events\nWHERE timestamp >= today() - INTERVAL 30 DAY\nGROUP BY date\nORDER BY date;\n\n-- 留存分析\nSELECT\n    signup_date,\n    countIf(days_since_signup = 0) AS day_0,\n    countIf(days_since_signup = 1) AS day_1,\n    countIf(days_since_signup = 7) AS day_7,\n    countIf(days_since_signup = 30) AS day_30\nFROM (\n    SELECT\n        user_id,\n        min(toDate(timestamp)) AS signup_date,\n        toDate(timestamp) AS activity_date,\n        dateDiff('day', signup_date, activity_date) AS days_since_signup\n    FROM events\n    GROUP BY user_id, activity_date\n)\nGROUP BY signup_date\nORDER BY signup_date DESC;\n```\n\n### 漏斗分析\n\n```sql\n-- 轉換漏斗\nSELECT\n    countIf(step = 'viewed_market') AS viewed,\n    countIf(step = 'clicked_trade') AS clicked,\n    countIf(step = 'completed_trade') AS completed,\n    round(clicked \u002F viewed * 100, 2) AS view_to_click_rate,\n    round(completed \u002F clicked * 100, 2) AS click_to_completion_rate\nFROM (\n    SELECT\n        user_id,\n        session_id,\n        event_type AS step\n    FROM events\n    WHERE event_date = today()\n)\nGROUP BY session_id;\n```\n\n### 世代分析\n\n```sql\n-- 按註冊月份的使用者世代\nSELECT\n    toStartOfMonth(signup_date) AS cohort,\n    toStartOfMonth(activity_date) AS month,\n    dateDiff('month', cohort, month) AS months_since_signup,\n    count(DISTINCT user_id) AS active_users\nFROM (\n    SELECT\n        user_id,\n        min(toDate(timestamp)) OVER (PARTITION BY user_id) AS signup_date,\n        toDate(timestamp) AS activity_date\n    FROM events\n)\nGROUP BY cohort, month, months_since_signup\nORDER BY cohort, months_since_signup;\n```\n\n## 資料管線模式\n\n### ETL 模式\n\n```typescript\n\u002F\u002F 提取、轉換、載入\nasync function etlPipeline() {\n  \u002F\u002F 1. 從來源提取\n  const rawData = await extractFromPostgres()\n\n  \u002F\u002F 2. 轉換\n  const transformed = rawData.map(row => ({\n    date: new Date(row.created_at).toISOString().split('T')[0],\n    market_id: row.market_slug,\n    volume: parseFloat(row.total_volume),\n    trades: parseInt(row.trade_count)\n  }))\n\n  \u002F\u002F 3. 載入到 ClickHouse\n  await bulkInsertToClickHouse(transformed)\n}\n\n\u002F\u002F 定期執行\nsetInterval(etlPipeline, 60 * 60 * 1000)  \u002F\u002F 每小時\n```\n\n### 變更資料捕獲（CDC）\n\n```typescript\n\u002F\u002F 監聽 PostgreSQL 變更並同步到 ClickHouse\nimport { Client } from 'pg'\n\nconst pgClient = new Client({ connectionString: process.env.DATABASE_URL })\n\npgClient.query('LISTEN market_updates')\n\npgClient.on('notification', async (msg) => {\n  const update = JSON.parse(msg.payload)\n\n  await clickhouse.insert('market_updates', [\n    {\n      market_id: update.id,\n      event_type: update.operation,  \u002F\u002F INSERT, UPDATE, DELETE\n      timestamp: new Date(),\n      data: JSON.stringify(update.new_data)\n    }\n  ])\n})\n```\n\n## 最佳實務\n\n### 1. 分區策略\n- 按時間分區（通常按月或日）\n- 避免太多分區（效能影響）\n- 分區鍵使用 DATE 類型\n\n### 2. 排序鍵\n- 最常過濾的欄位放在最前面\n- 考慮基數（高基數優先）\n- 排序影響壓縮\n\n### 3. 資料類型\n- 使用最小的適當類型（UInt32 vs UInt64）\n- 重複字串使用 LowCardinality\n- 分類資料使用 Enum\n\n### 4. 避免\n- SELECT *（指定欄位）\n- FINAL（改為在查詢前合併資料）\n- 太多 JOINs（為分析反正規化）\n- 小量頻繁插入（改用批量）\n\n### 5. 監控\n- 追蹤查詢效能\n- 監控磁碟使用\n- 檢查合併操作\n- 審查慢查詢日誌\n\n**記住**：ClickHouse 擅長分析工作負載。為你的查詢模式設計表格，批量插入，並利用物化視圖進行即時聚合。\n",{"name":5,"description":7},"main","a318f48de3c5718e8ea2e9f6fae7790e8a759e85","upstream-sync\u002Fwork\u002Fecc\u002Fdocs\u002Fzh-TW\u002Fskills\u002Fclickhouse-io",0,"https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fo-abdelmaksoud\u002F","Full-Stack Engineer | AI Systems Architect | Building autonomous multi-agent systems","2026-03-08 21:23:03",{"source":8,"skillName":5,"commands":55,"prompt":65,"zipAvailable":66},[56,59,62],{"tool":57,"command":58},"npx","npx skills add oabdelmaksoud\u002FAgention --skill clickhouse-io",{"tool":60,"command":61},"bunx","bunx skills add oabdelmaksoud\u002FAgention --skill clickhouse-io",{"tool":63,"command":64},"pnpm","pnpm dlx skills add oabdelmaksoud\u002FAgention --skill clickhouse-io","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fdocs\u002Fzh-TW\u002Fskills\u002Fclickhouse-io\u002FSKILL.md",true,[],[69,93,107,138,160,180],{"skillNo":70,"name":71,"nameZh":6,"description":72,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":73,"categories":74,"keywords":80,"publishedAt":40},"01M6G2QFCBNBYAJF2KTK85G9A6","visa-doc-translate","Translate visa application documents (images) to English and create a bilingual PDF with original and translation","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Fvisa-doc-translate\u002FSKILL.md",[75],{"categoryNo":76,"categoryName":77,"taxonomyType":78,"taxonomyTypeDesc":79},"01JBSEEDCAT000000000000002","文档处理","SCENARIO","使用场景",[81,82,83,84,85,86,87,88,89,90,91,92],"translate","doc","visa doc translate","bilingual","with","english","application","PDF","translation","documents","create","images",{"skillNo":94,"name":95,"nameZh":6,"description":96,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":97,"categories":98,"keywords":99,"publishedAt":40},"01M6G2QB09TSK6BY4ME5X20EVQ","verification-loop","A comprehensive verification system for Claude Code sessions.","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Fverification-loop\u002FSKILL.md",[],[100,101,102,103,104,105,106],"verification","loop","verification loop","sessions","code","claude code sessions","system",{"skillNo":108,"name":109,"nameZh":6,"description":110,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":111,"categories":112,"keywords":122,"publishedAt":40},"01M6G2Q6V2F7G03VAAF27FXPJR","tdd-workflow","Use this skill when writing new features, fixing bugs, or refactoring code. Enforces test-driven development with 80%+ coverage including unit, integration, and E2E tests.","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Ftdd-workflow\u002FSKILL.md",[113,116,119],{"categoryNo":114,"categoryName":115,"taxonomyType":78,"taxonomyTypeDesc":79},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":117,"categoryName":118,"taxonomyType":78,"taxonomyTypeDesc":79},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":120,"categoryName":121,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000030","开发工程师",[123,124,125,126,127,128,129,130,131,104,132,133,134,135,85,136,137],"workflow","tdd","tdd workflow","use","unit","coverage","refactoring","e2e","integration","development","e2e tests","tests","driven","test","writing",{"skillNo":139,"name":140,"nameZh":6,"description":141,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":142,"categories":143,"keywords":144,"publishedAt":40},"01M6G2Q359YJRWAR2PFCEQAXF3","swiftui-patterns","SwiftUI architecture patterns, state management with @Observable, view composition, navigation, performance optimization, and modern iOS\u002FmacOS UI best practices.","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Fswiftui-patterns\u002FSKILL.md",[],[36,145,146,147,148,27,149,29,150,151,33,152,153,154,155,156,157,158,159,85],"swiftui","swiftui patterns","architecture","macos","state management","state","ui","ios","architecture patterns","Composition","performance optimization","view","management","swiftui architecture","navigation",{"skillNo":161,"name":162,"nameZh":6,"description":163,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":164,"categories":165,"keywords":167,"publishedAt":40},"01M6G2PY8B2FWZTWPZ84H83Y7C","swift-protocol-di-testing","Protocol-based dependency injection for testable Swift code — mock file system, network, and external APIs using focused protocols and Swift Testing.","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Fswift-protocol-di-testing\u002FSKILL.md",[166],{"categoryNo":114,"categoryName":115,"taxonomyType":78,"taxonomyTypeDesc":79},[168,169,170,171,104,172,173,174,175,106,176,177,178,179],"swift protocol di testing","protocol","swift","testing","protocols","file","network","injection","dependency","swift testing","dependency injection","mock",{"skillNo":181,"name":182,"nameZh":6,"description":183,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":184,"categories":185,"keywords":186,"publishedAt":40},"01M6G2PSCN2GXV9FFZDRRRWVB6","swift-concurrency-6-2","Swift 6.2 Approachable Concurrency — single-threaded by default, @concurrent for explicit background offloading, isolated conformances for main actor types.","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Fswift-concurrency-6-2\u002FSKILL.md",[],[187,188,189,170,190,45,191],"concurrency","swift concurrency","swift concurrency 6 2","default","types"]