| name | tool.sql_queries.aff09c2fbf49d9f8 |
| description | 在所有主流数据仓库方言(Snowflake、BigQuery、Databricks、PostgreSQL 等)中编写正确、高效的 SQL。用于编写查询、优化慢查询、在方言间转换,或构建含 CTE、窗口函数和聚合的复杂分析查询时触发。触发词:SQL查询、写SQL |
Use this skill when the OpenART registry selects tool.sql_queries.aff09c2fbf49d9f8 for the current task.
SQL 查询技能
在所有主流数据仓库方言中,编写正确、高效、可读的 SQL。
方言专项参考
PostgreSQL(含 Aurora、RDS、Supabase、Neon)
日期/时间:
CURRENT_DATE, CURRENT_TIMESTAMP, NOW()
date_column + INTERVAL '7 days'
date_column - INTERVAL '1 month'
DATE_TRUNC('month', created_at)
EXTRACT(YEAR FROM created_at)
EXTRACT(DOW FROM created_at)
TO_CHAR(created_at, 'YYYY-MM-DD')
字符串函数:
first_name || ' ' || last_name
CONCAT(first_name, ' ', last_name)
column ILIKE '%pattern%'
column ~ '^regex_pattern$'
LEFT(str, n), RIGHT(str, n)
SPLIT_PART(str, delimiter, position)
REGEXP_REPLACE(str, pattern, replacement)
数组与 JSON:
data->>'key'
data->'nested'->'key'
data#>>'{path,to,key}'
ARRAY_AGG(column)
ANY(array_column)
array_column @> ARRAY['value']
性能技巧:
- 使用
EXPLAIN ANALYZE 分析查询
- 为频繁过滤/关联的字段创建索引
- 关联子查询优先使用
EXISTS 而非 IN
- 常用过滤条件使用部分索引
- 并发访问使用连接池
Snowflake
日期/时间:
CURRENT_DATE(), CURRENT_TIMESTAMP(), SYSDATE()
DATEADD(day, 7, date_column)
DATEDIFF(day, start_date, end_date)
DATE_TRUNC('month', created_at)
YEAR(created_at), MONTH(created_at), DAY(created_at)
DAYOFWEEK(created_at)
TO_CHAR(created_at, 'YYYY-MM-DD')
字符串函数:
column ILIKE '%pattern%'
REGEXP_LIKE(column, 'pattern')
column:key::string
PARSE_JSON('{"key": "value"}')
GET_PATH(variant_col, 'path.to.key')
SELECT f.value FROM table, LATERAL FLATTEN(input => array_col) f
半结构化数据:
data:customer:name::STRING
data:items[0]:price::NUMBER
SELECT
t.id,
item.value:name::STRING as item_name,
item.value:qty::NUMBER as quantity
FROM my_table t,
LATERAL FLATTEN(input => t.data:items) item
性能技巧:
- 大表使用聚簇键(而非传统索引)
- 在聚簇键字段上过滤以进行分区裁剪
- 根据查询复杂度设置合适的仓库大小
- 使用
RESULT_SCAN(LAST_QUERY_ID()) 避免重复运行昂贵查询
- 暂存/临时数据使用临时表
BigQuery(Google Cloud)
日期/时间:
CURRENT_DATE(), CURRENT_TIMESTAMP()
DATE_ADD(date_column, INTERVAL 7 DAY)
DATE_SUB(date_column, INTERVAL 1 MONTH)
DATE_DIFF(end_date, start_date, DAY)
TIMESTAMP_DIFF(end_ts, start_ts, HOUR)
DATE_TRUNC(created_at, MONTH)
TIMESTAMP_TRUNC(created_at, HOUR)
EXTRACT(YEAR FROM created_at)
EXTRACT(DAYOFWEEK FROM created_at)
FORMAT_DATE('%Y-%m-%d', date_column)
FORMAT_TIMESTAMP('%Y-%m-%d %H:%M:%S', ts_column)
字符串函数:
LOWER(column) LIKE '%pattern%'
REGEXP_CONTAINS(column, r'pattern')
REGEXP_EXTRACT(column, r'pattern')
SPLIT(str, delimiter)
ARRAY_TO_STRING(array, delimiter)
数组与结构体:
ARRAY_AGG(column)
UNNEST(array_column)
ARRAY_LENGTH(array_column)
value IN UNNEST(array_column)
struct_column.field_name
性能技巧:
- 始终在分区字段(通常是日期)上过滤,以减少扫描字节数
- 对分区内频繁过滤的字段使用聚簇
- 大规模基数估算使用
APPROX_COUNT_DISTINCT()
- 避免
SELECT *——按扫描字节计费
- 使用
DECLARE 和 SET 编写参数化脚本
- 执行大查询前通过 dry run 预览查询成本
Redshift(Amazon)
日期/时间:
CURRENT_DATE, GETDATE(), SYSDATE
DATEADD(day, 7, date_column)
DATEDIFF(day, start_date, end_date)
DATE_TRUNC('month', created_at)
EXTRACT(YEAR FROM created_at)
DATE_PART('dow', created_at)
字符串函数:
column ILIKE '%pattern%'
REGEXP_INSTR(column, 'pattern') > 0
SPLIT_PART(str, delimiter, position)
LISTAGG(column, ', ') WITHIN GROUP (ORDER BY column)
性能技巧:
- 为并置关联设计分布键(DISTKEY)
- 频繁过滤字段使用排序键(SORTKEY)
- 使用
EXPLAIN 查看查询计划
- 避免跨节点数据移动(注意 DS_BCAST 和 DS_DIST)
- 定期执行
ANALYZE 和 VACUUM
- 使用延迟绑定视图提高 Schema 灵活性
Databricks SQL
日期/时间:
CURRENT_DATE(), CURRENT_TIMESTAMP()
DATE_ADD(date_column, 7)
DATEDIFF(end_date, start_date)
ADD_MONTHS(date_column, 1)
DATE_TRUNC('MONTH', created_at)
TRUNC(date_column, 'MM')
YEAR(created_at), MONTH(created_at)
DAYOFWEEK(created_at)
Delta Lake 特性:
SELECT * FROM my_table TIMESTAMP AS OF '2024-01-15'
SELECT * FROM my_table VERSION AS OF 42
DESCRIBE HISTORY my_table
MERGE INTO target USING source
ON target.id = source.id
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *
性能技巧:
- 使用 Delta Lake 的
OPTIMIZE 和 ZORDER 提升查询性能
- 利用 Photon 引擎处理计算密集型查询
- 频繁访问的数据集使用
CACHE TABLE
- 按低基数日期字段分区
常用 SQL 模式
窗口函数
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC)
RANK() OVER (PARTITION BY category ORDER BY revenue DESC)
DENSE_RANK() OVER (ORDER BY score DESC)
SUM(revenue) OVER (ORDER BY date_col ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as running_total
AVG(revenue) OVER (ORDER BY date_col ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) as moving_avg_7d
LAG(value, 1) OVER (PARTITION BY entity ORDER BY date_col) as prev_value
LEAD(value, 1) OVER ( entity date_col) next_value
(status) ( user_id created_at UNBOUNDED PRECEDING UNBOUNDED FOLLOWING)
(status) ( user_id created_at UNBOUNDED PRECEDING UNBOUNDED FOLLOWING)
revenue (revenue) () pct_of_total
revenue (revenue) ( category) pct_of_category
用 CTE 提升可读性
WITH
base_users AS (
SELECT user_id, created_at, plan_type
FROM users
WHERE created_at >= DATE '2024-01-01'
AND status = 'active'
),
user_metrics AS (
SELECT
u.user_id,
u.plan_type,
COUNT(DISTINCT e.session_id) as session_count,
SUM(e.revenue) as total_revenue
FROM base_users u
LEFT JOIN events e ON u.user_id = e.user_id
GROUP BY u.user_id, u.plan_type
),
summary AS (
SELECT
plan_type,
COUNT(*) as user_count,
AVG(session_count) as avg_sessions,
SUM(total_revenue) as total_revenue
FROM user_metrics
GROUP BY plan_type
)
SELECT * FROM summary ORDER BY total_revenue DESC;
同期群留存
WITH cohorts AS (
SELECT
user_id,
DATE_TRUNC('month', first_activity_date) as cohort_month
FROM users
),
activity AS (
SELECT
user_id,
DATE_TRUNC('month', activity_date) as activity_month
FROM user_activity
)
SELECT
c.cohort_month,
COUNT(DISTINCT c.user_id) as cohort_size,
COUNT(DISTINCT CASE
WHEN a.activity_month = c.cohort_month THEN a.user_id
END) as month_0,
COUNT(DISTINCT CASE
WHEN a.activity_month = c.cohort_month + INTERVAL '1 month' THEN a.user_id
END) as month_1,
COUNT(DISTINCT CASE
WHEN a.activity_month = c.cohort_month + INTERVAL '3 months' THEN a.user_id
END) as month_3
FROM cohorts c
LEFT JOIN activity a ON c.user_id = a.user_id
c.cohort_month
c.cohort_month;
漏斗分析
WITH funnel AS (
SELECT
user_id,
MAX(CASE WHEN event = 'page_view' THEN 1 ELSE 0 END) as step_1_view,
MAX(CASE WHEN event = 'signup_start' THEN 1 ELSE 0 END) as step_2_start,
MAX(CASE WHEN event = 'signup_complete' THEN 1 ELSE 0 END) as step_3_complete,
MAX(CASE WHEN event = 'first_purchase' THEN 1 ELSE 0 END) as step_4_purchase
FROM events
WHERE event_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY user_id
)
() total_users,
(step_1_view) viewed,
(step_2_start) started_signup,
(step_3_complete) completed_signup,
(step_4_purchase) purchased,
ROUND( (step_2_start) ((step_1_view), ), ) view_to_start_pct,
ROUND( (step_3_complete) ((step_2_start), ), ) start_to_complete_pct,
ROUND( (step_4_purchase) ((step_3_complete), ), ) complete_to_purchase_pct
funnel;
去重
WITH ranked AS (
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY entity_id
ORDER BY updated_at DESC
) as rn
FROM source_table
)
SELECT * FROM ranked WHERE rn = 1;
错误处理与调试
查询失败时:
- 语法错误:检查方言专属语法(例如
ILIKE 在 BigQuery 中不可用,SAFE_DIVIDE 仅在 BigQuery 中可用)
- 字段未找到:对照 Schema 验证字段名——检查拼写错误、大小写敏感性(PostgreSQL 对带引号的标识符区分大小写)
- 类型不匹配:比较不同类型时显式转换(
CAST(col AS DATE)、col::DATE)
- 除零:使用
NULLIF(denominator, 0) 或方言专属的安全除法
- 字段歧义:在 JOIN 中始终用表别名限定字段名
- GROUP BY 错误:所有非聚合字段必须在 GROUP BY 中(BigQuery 允许按别名分组除外)