关于 sql-cookbook
面向分析师和数据从业者的 SQL 参考 Skill,涵盖 PostgreSQL、Snowflake、BigQuery、Redshift 和 Databricks SQL 的方言语法、分析模式、调优与故障排查。
主要功能
- 01五种 SQL 方言语法参考
- 02窗口函数与 CTE 模式
- 03留存和转化分析示例
- 04查询调优建议
- 05常见查询错误检查
适用场景
- 01按方言调整日期或 JSON 查询
- 02构建留存与转化分析
- 03排查查询错误
- 04提升查询执行效率
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ okou-ai/ okou-skills --skill sql-cookbook安装后在 Claude Code 中输入/sql-cookbook确认已加载。
详细介绍
平台特定语法指南
PostgreSQL(Aurora、RDS、Supabase、Neon)
处理日期和时间:
-- Today and now
CURRENT_DATE, CURRENT_TIMESTAMP, NOW()
-- Adding and subtracting intervals
some_date + INTERVAL '7 days'
some_date - INTERVAL '1 month'
-- Round down to a time boundary
DATE_TRUNC('month', event_ts)
-- Pull out individual components
EXTRACT(YEAR FROM event_ts)
EXTRACT(DOW FROM event_ts) -- Sunday = 0
-- Render as formatted text
TO_CHAR(event_ts, 'YYYY-MM-DD')
文本操作:
-- Joining strings together
first_name || ' ' || last_name
CONCAT(first_name, ' ', last_name)
-- Flexible matching
col ILIKE '%term%' -- ignores case
col ~ '^regex$' -- POSIX regex
-- Splitting and extracting
LEFT(str, n), RIGHT(str, n)
SPLIT_PART(str, delimiter, part_num)
REGEXP_REPLACE(str, pattern, replacement)
JSON 和数组处理:
-- Accessing JSON fields
payload->>'key' -- returns text
payload->'nested'->'field' -- returns json object
payload#>>'{a,b,c}' -- deep path as text
-- Array utilities
ARRAY_AGG(col)
ANY(arr_col)
arr_col @> ARRAY['val']
调优指南:
- 运行
EXPLAIN ANALYZE检查实际执行计划 - 为 WHERE 和 JOIN 条件中使用的列添加索引
- 对相关查找,优先使用
EXISTS而不是IN - 对常用的筛选谓词,考虑使用部分索引
- 处理并发工作负载时部署连接池
Snowflake
日期和时间函数:
-- Getting current moments
CURRENT_DATE(), CURRENT_TIMESTAMP(), SYSDATE()
-- Shifting dates
DATEADD(day, 7, some_date)
DATEDIFF(day, start_dt, end_dt)
-- Truncation
DATE_TRUNC('month', event_ts)
-- Component extraction
YEAR(event_ts), MONTH(event_ts), DAY(event_ts)
DAYOFWEEK(event_ts)
-- Formatting
TO_CHAR(event_ts, 'YYYY-MM-DD')
文本和模式匹配:
-- Case-insensitive matching (default collation dependent)
col ILIKE '%term%'
REGEXP_LIKE(col, 'pattern')
-- Working with VARIANT / semi-structured data
col:key::string
PARSE_JSON('{"a": 1}')
GET_PATH(variant_col, 'path.to.field')
-- Expanding nested arrays
SELECT f.value FROM tbl, LATERAL FLATTEN(input => arr_col) f
访问半结构化列:
-- Dot-path access on VARIANT
payload:customer:name::STRING
payload:items[0]:price::NUMBER
-- Exploding nested arrays into rows
SELECT
t.id,
elem.value:name::STRING AS item_name,
elem.value:qty::NUMBER AS item_qty
FROM my_table t,
LATERAL FLATTEN(input => t.payload:items) elem
调优指南:
- 对大表使用聚簇键,而不是传统索引
- 在筛选条件中使用聚簇键列,以最大限度地进行分区裁剪
- 根据查询复杂度适当设置虚拟仓库的大小
- 使用
RESULT_SCAN(LAST_QUERY_ID())重用结果,避免重新执行 - 对临时暂存数据使用瞬态表
BigQuery
日期和时间函数:
-- Current moments
CURRENT_DATE(), CURRENT_TIMESTAMP()
-- Shifting dates forward and backward
DATE_ADD(some_date, INTERVAL 7 DAY)
DATE_SUB(some_date, INTERVAL 1 MONTH)
DATE_DIFF(end_dt, start_dt, DAY)
TIMESTAMP_DIFF(end_ts, start_ts, HOUR)
-- Truncation
DATE_TRUNC(event_ts, MONTH)
TIMESTAMP_TRUNC(event_ts, HOUR)
-- Component extraction
EXTRACT(YEAR FROM event_ts)
EXTRACT(DAYOFWEEK FROM event_ts) -- Sunday = 1
-- Display formatting
FORMAT_DATE('%Y-%m-%d', date_col)
FORMAT_TIMESTAMP('%Y-%m-%d %H:%M:%S', ts_col)
文本操作:
-- No native ILIKE; lowercase first
LOWER(col) LIKE '%term%'
REGEXP_CONTAINS(col, r'pattern')
REGEXP_EXTRACT(col, r'pattern')
-- Splitting and reassembling
SPLIT(str, delimiter) -- produces an ARRAY
ARRAY_TO_STRING(arr, delimiter)
数组和结构体:
-- Array utilities
ARRAY_AGG(col)
UNNEST(arr_col)
ARRAY_LENGTH(arr_col)
val IN UNNEST(arr_col)
-- Struct field access
struct_col.field_name
调优指南:
- 始终对分区列(通常是日期)应用筛选条件,以尽量减少扫描的字节数
- 对经常出现在 WHERE 子句中的列进行聚簇
- 对高基数数据,使用
APPROX_COUNT_DISTINCT()替代COUNT(DISTINCT ...) - 避免使用
SELECT *,因为费用会随读取的字节数增加 - 使用
DECLARE/SET实现参数化脚本逻辑 - 执行开销较大的查询前,先进行试运行估算
Redshift
日期和时间函数:
-- Current moments
CURRENT_DATE, GETDATE(), SYSDATE
-- Shifting dates
DATEADD(day, 7, some_date)
DATEDIFF(day, start_dt, end_dt)
-- Truncation
DATE_TRUNC('month', event_ts)
-- Component extraction
EXTRACT(YEAR FROM event_ts)
DATE_PART('dow', event_ts)
文本操作:
-- Case-insensitive matching
col ILIKE '%term%'
REGEXP_INSTR(col, 'pattern') > 0
-- Splitting and concatenation
SPLIT_PART(str, delimiter, part_num)
LISTAGG(col, ', ') WITHIN GROUP (ORDER BY col)
调优指南:
- 分配分布键(DISTKEY),使联接表位于相同节点上
- 为常用于筛选的列定义排序键(SORTKEY)
- 使用
EXPLAIN检查查询计划 - 留意计划中的 DS_BCAST 和 DS_DIST(它们表明存在代价高昂的跨节点数据重分布)
- 定期运行
ANALYZE和VACUUM - 随着架构演变,使用后期绑定视图以保持灵活性
Databricks SQL
日期和时间函数:
-- Current moments
CURRENT_DATE(), CURRENT_TIMESTAMP()
-- Shifting dates
DATE_ADD(some_date, 7)
DATEDIFF(end_dt, start_dt)
ADD_MONTHS(some_date, 1)
-- Truncation
DATE_TRUNC('MONTH', event_ts)
TRUNC(date_col, 'MM')
-- Component extraction
YEAR(event_ts), MONTH(event_ts)
DAYOFWEEK(event_ts)
Delta Lake 功能:
-- Query historical snapshots
SELECT * FROM tbl TIMESTAMP AS OF '2024-01-15'
SELECT * FROM tbl VERSION AS OF 42
-- Inspect table history
DESCRIBE HISTORY tbl
-- Upsert with MERGE
MERGE INTO target USING source
ON target.id = source.id
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *
调优指南:
- 对经常查询的列运行带有
ZORDER的OPTIMIZE - 对计算密集型工作负载启用 Photon 引擎
- 对反复读取的数据集应用
CACHE TABLE - 按低基数日期列分区,以便高效裁剪
可复用的分析模式
窗口函数示例
-- Assign sequential position within groups
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_ts DESC)
RANK() OVER (PARTITION BY category ORDER BY revenue DESC)
DENSE_RANK() OVER (ORDER BY score DESC)
-- Cumulative sums and rolling averages
SUM(amount) OVER (ORDER BY dt ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cumulative_total
AVG(amount) OVER (ORDER BY dt ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS rolling_7d_avg
-- Accessing adjacent rows
LAG(val, 1) OVER (PARTITION BY entity ORDER BY dt) AS prior_val
LEAD(val, 1) OVER (PARTITION BY entity ORDER BY dt) AS next_val
-- Boundary values within a partition
FIRST_VALUE(status) OVER (PARTITION BY user_id ORDER BY event_ts ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
LAST_VALUE(status) OVER (PARTITION BY user_id ORDER BY event_ts ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
-- Proportional share
revenue / SUM(revenue) OVER () AS share_of_total
revenue / SUM(revenue) OVER (PARTITION BY category) AS share_within_category
使用 CTE 的结构化查询
WITH
-- Stage 1: Narrow down to the target population
eligible_users AS (
SELECT user_id, signup_date, tier
FROM users
WHERE signup_date >= DATE '2024-01-01'
AND account_status = 'active'
),
-- Stage 2: Derive per-user measures
per_user AS (
SELECT
u.user_id,
u.tier,
COUNT(DISTINCT e.session_id) AS sessions,
SUM(e.revenue) AS revenue
FROM eligible_users u
LEFT JOIN events e ON u.user_id = e.user_id
GROUP BY u.user_id, u.tier
),
-- Stage 3: Roll up to tier-level summary
tier_summary AS (
SELECT
tier,
COUNT(*) AS users,
AVG(sessions) AS avg_sessions,
SUM(revenue) AS total_revenue
FROM per_user
GROUP BY tier
)
SELECT * FROM tier_summary ORDER BY total_revenue DESC;
群组留存跟踪
WITH signup_cohorts AS (
SELECT
user_id,
DATE_TRUNC('month', first_seen) AS cohort
FROM users
),
monthly_activity AS (
SELECT
user_id,
DATE_TRUNC('month', activity_date) AS active_month
FROM user_events
)
SELECT
s.cohort,
COUNT(DISTINCT s.user_id) AS cohort_size,
COUNT(DISTINCT CASE
WHEN a.active_month = s.cohort THEN a.user_id
END) AS m0,
COUNT(DISTINCT CASE
WHEN a.active_month = s.cohort + INTERVAL '1 month' THEN a.user_id
END) AS m1,
COUNT(DISTINCT CASE
WHEN a.active_month = s.cohort + INTERVAL '3 months' THEN a.user_id
END) AS m3
FROM signup_cohorts s
LEFT JOIN monthly_activity a ON s.user_id = a.user_id
GROUP BY s.cohort
ORDER BY s.cohort;
转化漏斗衡量
WITH step_flags AS (
SELECT
user_id,
MAX(CASE WHEN event_name = 'page_view' THEN 1 ELSE 0 END) AS saw_page,
MAX(CASE WHEN event_name = 'signup_start' THEN 1 ELSE 0 END) AS began_signup,
MAX(CASE WHEN event_name = 'signup_complete' THEN 1 ELSE 0 END) AS finished_signup,
MAX(CASE WHEN event_name = 'first_purchase' THEN 1 ELSE 0 END) AS made_purchase
FROM events
WHERE event_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY user_id
)
SELECT
COUNT(*) AS total_users,
SUM(saw_page) AS viewers,
SUM(began_signup) AS signup_starts,
SUM(finished_signup) AS signup_completions,
SUM(made_purchase) AS purchasers,
ROUND(100.0 * SUM(began_signup) / NULLIF(SUM(saw_page), 0), 1) AS view_to_start_rate,
ROUND(100.0 * SUM(finished_signup) / NULLIF(SUM(began_signup), 0), 1) AS start_to_finish_rate,
ROUND(100.0 * SUM(made_purchase) / NULLIF(SUM(finished_signup), 0), 1) AS finish_to_purchase_rate
FROM step_flags;
删除重复记录
-- Retain only the latest version of each entity
WITH ordered AS (
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY entity_id
ORDER BY modified_at DESC
) AS seq
FROM raw_table
)
SELECT * FROM ordered WHERE seq = 1;
查询失败故障排除
查询出现错误时,请逐项检查:
- 语法问题:确认方言兼容性(例如,BigQuery 不支持
ILIKE;只有 BigQuery 支持SAFE_DIVIDE) - 缺少列:根据实际架构确认列名;在 PostgreSQL 中,注意带引号标识符的大小写敏感性
- 类型冲突:比较类型不匹配的值时,添加显式类型转换(
CAST(col AS DATE)或col::DATE) - 除以零:使用
NULLIF(denominator, 0)包装分母,或使用平台特定的安全除法函数 - 列名歧义:为 JOIN 中的每张表设置别名,并始终为列引用添加前缀
- GROUP BY 违规:每个不在聚合函数内的选定列都必须出现在 GROUP BY 中(BigQuery 允许引用别名,但这是例外)
评论
支持 Markdown