Skill Tracer
Skills › Knowledge Work Plugins › sql-queries

sql-queries

skill · 428 lines · Apache-2.0 · data/skills/sql-queries/SKILL.md

The facts

Its description

Write correct, performant SQL across all major data warehouse dialects (Snowflake, BigQuery, Databricks, PostgreSQL, etc.). Use when writing queries, optimizing slow SQL, translating between dialects, or building complex analytical queries with CTEs, window functions, or aggregations.

The description the agent reads to decide when to use it.

From its settingsline 3

When it starts

This skill is marked not user-invocable, so it is plainly loaded by another skill or agent rather than by something a person says; it is pulled in when SQL needs to be written, optimized, or translated between warehouse dialects.

A model's summary of when it is used.

A model's reading

Who can start it

Only the agent

Only the agent starts it. It's hidden from your commands.

From its settingsline 4

What leads to it

Other skills that hand off to this one, call it, require it or offer it instead, as a model read them.

A model's reading

Licence

Apache-2.0, from data/LICENSE

The terms its author shares it under, which let it be shown here.

From the repositorylicence text

The text

Open on GitHub
1---
2name: sql-queries
3description: Write correct, performant SQL across all major data warehouse dialects (Snowflake, BigQuery, Databricks, PostgreSQL, etc.). Use when writing queries, optimizing slow SQL, translating between dialects, or building complex analytical queries with CTEs, window functions, or aggregations.
4user-invocable: false
5---
6
7# SQL Queries Skill
8
9Write correct, performant, readable SQL across all major data warehouse dialects.

SQL Queries Skill

Write correct, performant, readable SQL across all major data warehouse dialects.

Raw lines 1 to 10
11## Dialect-Specific Reference

Dialect-Specific Reference

Raw lines 11 to 12
13### PostgreSQL (including Aurora, RDS, Supabase, Neon)
15**Date/time:**
16```sql
17-- Current date/time
18CURRENT_DATE, CURRENT_TIMESTAMP, NOW()
20-- Date arithmetic
21date_column + INTERVAL '7 days'
22date_column - INTERVAL '1 month'
24-- Truncate to period
25DATE_TRUNC('month', created_at)
27-- Extract parts
28EXTRACT(YEAR FROM created_at)
29EXTRACT(DOW FROM created_at) -- 0=Sunday
31-- Format
32TO_CHAR(created_at, 'YYYY-MM-DD')
33```
35**String functions:**
36```sql
37-- Concatenation
38first_name || ' ' || last_name
39CONCAT(first_name, ' ', last_name)
41-- Pattern matching
42column ILIKE '%pattern%' -- case-insensitive
43column ~ '^regex_pattern$' -- regex
45-- String manipulation
46LEFT(str, n), RIGHT(str, n)
47SPLIT_PART(str, delimiter, position)
48REGEXP_REPLACE(str, pattern, replacement)
49```
51**Arrays and JSON:**
52```sql
53-- JSON access
54data->>'key' -- text
55data->'nested'->'key' -- json
56data#>>'{path,to,key}' -- nested text
58-- Array operations
59ARRAY_AGG(column)
60ANY(array_column)
61array_column @> ARRAY['value']
62```
64**Performance tips:**
65- Use `EXPLAIN ANALYZE` to profile queries
66- Create indexes on frequently filtered/joined columns
67- Use `EXISTS` over `IN` for correlated subqueries
68- Partial indexes for common filter conditions
69- Use connection pooling for concurrent access
71---

PostgreSQL (including Aurora, RDS, Supabase, Neon)

Date/time:

-- Current date/time
CURRENT_DATE, CURRENT_TIMESTAMP, NOW()

-- Date arithmetic
date_column + INTERVAL '7 days'
date_column - INTERVAL '1 month'

-- Truncate to period
DATE_TRUNC('month', created_at)

-- Extract parts
EXTRACT(YEAR FROM created_at)
EXTRACT(DOW FROM created_at)  -- 0=Sunday

-- Format
TO_CHAR(created_at, 'YYYY-MM-DD')

String functions:

-- Concatenation
first_name || ' ' || last_name
CONCAT(first_name, ' ', last_name)

-- Pattern matching
column ILIKE '%pattern%'  -- case-insensitive
column ~ '^regex_pattern$'  -- regex

-- String manipulation
LEFT(str, n), RIGHT(str, n)
SPLIT_PART(str, delimiter, position)
REGEXP_REPLACE(str, pattern, replacement)

Arrays and JSON:

-- JSON access
data->>'key'  -- text
data->'nested'->'key'  -- json
data#>>'{path,to,key}'  -- nested text

-- Array operations
ARRAY_AGG(column)
ANY(array_column)
array_column @> ARRAY['value']

Performance tips:

  • Use EXPLAIN ANALYZE to profile queries
  • Create indexes on frequently filtered/joined columns
  • Use EXISTS over IN for correlated subqueries
  • Partial indexes for common filter conditions
  • Use connection pooling for concurrent access

Raw lines 13 to 72
73### Snowflake
75**Date/time:**
76```sql
77-- Current date/time
78CURRENT_DATE(), CURRENT_TIMESTAMP(), SYSDATE()
80-- Date arithmetic
81DATEADD(day, 7, date_column)
82DATEDIFF(day, start_date, end_date)
84-- Truncate to period
85DATE_TRUNC('month', created_at)
87-- Extract parts
88YEAR(created_at), MONTH(created_at), DAY(created_at)
89DAYOFWEEK(created_at)
91-- Format
92TO_CHAR(created_at, 'YYYY-MM-DD')
93```
95**String functions:**
96```sql
97-- Case-insensitive by default (depends on collation)
98column ILIKE '%pattern%'
99REGEXP_LIKE(column, 'pattern')
101-- Parse JSON
102column:key::string -- dot notation for VARIANT
103PARSE_JSON('{"key": "value"}')
104GET_PATH(variant_col, 'path.to.key')
106-- Flatten arrays/objects
107SELECT f.value FROM table, LATERAL FLATTEN(input => array_col) f
108```
110**Semi-structured data:**
111```sql
112-- VARIANT type access
113data:customer:name::STRING
114data:items[0]:price::NUMBER
116-- Flatten nested structures
117SELECT
118 t.id,
119 item.value:name::STRING as item_name,
120 item.value:qty::NUMBER as quantity
121FROM my_table t,
122LATERAL FLATTEN(input => t.data:items) item
123```
125**Performance tips:**
126- Use clustering keys on large tables (not traditional indexes)
127- Filter on clustering key columns for partition pruning
128- Set appropriate warehouse size for query complexity
129- Use `RESULT_SCAN(LAST_QUERY_ID())` to avoid re-running expensive queries
130- Use transient tables for staging/temp data
132---

Snowflake

Date/time:

-- Current date/time
CURRENT_DATE(), CURRENT_TIMESTAMP(), SYSDATE()

-- Date arithmetic
DATEADD(day, 7, date_column)
DATEDIFF(day, start_date, end_date)

-- Truncate to period
DATE_TRUNC('month', created_at)

-- Extract parts
YEAR(created_at), MONTH(created_at), DAY(created_at)
DAYOFWEEK(created_at)

-- Format
TO_CHAR(created_at, 'YYYY-MM-DD')

String functions:

-- Case-insensitive by default (depends on collation)
column ILIKE '%pattern%'
REGEXP_LIKE(column, 'pattern')

-- Parse JSON
column:key::string  -- dot notation for VARIANT
PARSE_JSON('{"key": "value"}')
GET_PATH(variant_col, 'path.to.key')

-- Flatten arrays/objects
SELECT f.value FROM table, LATERAL FLATTEN(input => array_col) f

Semi-structured data:

-- VARIANT type access
data:customer:name::STRING
data:items[0]:price::NUMBER

-- Flatten nested structures
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

Performance tips:

  • Use clustering keys on large tables (not traditional indexes)
  • Filter on clustering key columns for partition pruning
  • Set appropriate warehouse size for query complexity
  • Use RESULT_SCAN(LAST_QUERY_ID()) to avoid re-running expensive queries
  • Use transient tables for staging/temp data

Raw lines 73 to 133
134### BigQuery (Google Cloud)
136**Date/time:**
137```sql
138-- Current date/time
139CURRENT_DATE(), CURRENT_TIMESTAMP()
141-- Date arithmetic
142DATE_ADD(date_column, INTERVAL 7 DAY)
143DATE_SUB(date_column, INTERVAL 1 MONTH)
144DATE_DIFF(end_date, start_date, DAY)
145TIMESTAMP_DIFF(end_ts, start_ts, HOUR)
147-- Truncate to period
148DATE_TRUNC(created_at, MONTH)
149TIMESTAMP_TRUNC(created_at, HOUR)
151-- Extract parts
152EXTRACT(YEAR FROM created_at)
153EXTRACT(DAYOFWEEK FROM created_at) -- 1=Sunday
155-- Format
156FORMAT_DATE('%Y-%m-%d', date_column)
157FORMAT_TIMESTAMP('%Y-%m-%d %H:%M:%S', ts_column)
158```
160**String functions:**
161```sql
162-- No ILIKE, use LOWER()
163LOWER(column) LIKE '%pattern%'
164REGEXP_CONTAINS(column, r'pattern')
165REGEXP_EXTRACT(column, r'pattern')
167-- String manipulation
168SPLIT(str, delimiter) -- returns ARRAY
169ARRAY_TO_STRING(array, delimiter)
170```
172**Arrays and structs:**
173```sql
174-- Array operations
175ARRAY_AGG(column)
176UNNEST(array_column)
177ARRAY_LENGTH(array_column)
178value IN UNNEST(array_column)
180-- Struct access
181struct_column.field_name
182```
184**Performance tips:**
185- Always filter on partition columns (usually date) to reduce bytes scanned
186- Use clustering for frequently filtered columns within partitions
187- Use `APPROX_COUNT_DISTINCT()` for large-scale cardinality estimates
188- Avoid `SELECT *` -- billing is per-byte scanned
189- Use `DECLARE` and `SET` for parameterized scripts
190- Preview query cost with dry run before executing large queries
192---

BigQuery (Google Cloud)

Date/time:

-- Current date/time
CURRENT_DATE(), CURRENT_TIMESTAMP()

-- Date arithmetic
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)

-- Truncate to period
DATE_TRUNC(created_at, MONTH)
TIMESTAMP_TRUNC(created_at, HOUR)

-- Extract parts
EXTRACT(YEAR FROM created_at)
EXTRACT(DAYOFWEEK FROM created_at)  -- 1=Sunday

-- Format
FORMAT_DATE('%Y-%m-%d', date_column)
FORMAT_TIMESTAMP('%Y-%m-%d %H:%M:%S', ts_column)

String functions:

-- No ILIKE, use LOWER()
LOWER(column) LIKE '%pattern%'
REGEXP_CONTAINS(column, r'pattern')
REGEXP_EXTRACT(column, r'pattern')

-- String manipulation
SPLIT(str, delimiter)  -- returns ARRAY
ARRAY_TO_STRING(array, delimiter)

Arrays and structs:

-- Array operations
ARRAY_AGG(column)
UNNEST(array_column)
ARRAY_LENGTH(array_column)
value IN UNNEST(array_column)

-- Struct access
struct_column.field_name

Performance tips:

  • Always filter on partition columns (usually date) to reduce bytes scanned
  • Use clustering for frequently filtered columns within partitions
  • Use APPROX_COUNT_DISTINCT() for large-scale cardinality estimates
  • Avoid SELECT * -- billing is per-byte scanned
  • Use DECLARE and SET for parameterized scripts
  • Preview query cost with dry run before executing large queries

Raw lines 134 to 193
194### Redshift (Amazon)
196**Date/time:**
197```sql
198-- Current date/time
199CURRENT_DATE, GETDATE(), SYSDATE
201-- Date arithmetic
202DATEADD(day, 7, date_column)
203DATEDIFF(day, start_date, end_date)
205-- Truncate to period
206DATE_TRUNC('month', created_at)
208-- Extract parts
209EXTRACT(YEAR FROM created_at)
210DATE_PART('dow', created_at)
211```
213**String functions:**
214```sql
215-- Case-insensitive
216column ILIKE '%pattern%'
217REGEXP_INSTR(column, 'pattern') > 0
219-- String manipulation
220SPLIT_PART(str, delimiter, position)
221LISTAGG(column, ', ') WITHIN GROUP (ORDER BY column)
222```
224**Performance tips:**
225- Design distribution keys for collocated joins (DISTKEY)
226- Use sort keys for frequently filtered columns (SORTKEY)
227- Use `EXPLAIN` to check query plan
228- Avoid cross-node data movement (watch for DS_BCAST and DS_DIST)
229- `ANALYZE` and `VACUUM` regularly
230- Use late-binding views for schema flexibility
232---

Redshift (Amazon)

Date/time:

-- Current date/time
CURRENT_DATE, GETDATE(), SYSDATE

-- Date arithmetic
DATEADD(day, 7, date_column)
DATEDIFF(day, start_date, end_date)

-- Truncate to period
DATE_TRUNC('month', created_at)

-- Extract parts
EXTRACT(YEAR FROM created_at)
DATE_PART('dow', created_at)

String functions:

-- Case-insensitive
column ILIKE '%pattern%'
REGEXP_INSTR(column, 'pattern') > 0

-- String manipulation
SPLIT_PART(str, delimiter, position)
LISTAGG(column, ', ') WITHIN GROUP (ORDER BY column)

Performance tips:

  • Design distribution keys for collocated joins (DISTKEY)
  • Use sort keys for frequently filtered columns (SORTKEY)
  • Use EXPLAIN to check query plan
  • Avoid cross-node data movement (watch for DS_BCAST and DS_DIST)
  • ANALYZE and VACUUM regularly
  • Use late-binding views for schema flexibility

Raw lines 194 to 233
234### Databricks SQL
236**Date/time:**
237```sql
238-- Current date/time
239CURRENT_DATE(), CURRENT_TIMESTAMP()
241-- Date arithmetic
242DATE_ADD(date_column, 7)
243DATEDIFF(end_date, start_date)
244ADD_MONTHS(date_column, 1)
246-- Truncate to period
247DATE_TRUNC('MONTH', created_at)
248TRUNC(date_column, 'MM')
250-- Extract parts
251YEAR(created_at), MONTH(created_at)
252DAYOFWEEK(created_at)
253```
255**Delta Lake features:**
256```sql
257-- Time travel
258SELECT * FROM my_table TIMESTAMP AS OF '2024-01-15'
259SELECT * FROM my_table VERSION AS OF 42
261-- Describe history
262DESCRIBE HISTORY my_table
264-- Merge (upsert)
265MERGE INTO target USING source
266ON target.id = source.id
267WHEN MATCHED THEN UPDATE SET *
268WHEN NOT MATCHED THEN INSERT *
269```
271**Performance tips:**
272- Use Delta Lake's `OPTIMIZE` and `ZORDER` for query performance
273- Leverage Photon engine for compute-intensive queries
274- Use `CACHE TABLE` for frequently accessed datasets
275- Partition by low-cardinality date columns
277---

Databricks SQL

Date/time:

-- Current date/time
CURRENT_DATE(), CURRENT_TIMESTAMP()

-- Date arithmetic
DATE_ADD(date_column, 7)
DATEDIFF(end_date, start_date)
ADD_MONTHS(date_column, 1)

-- Truncate to period
DATE_TRUNC('MONTH', created_at)
TRUNC(date_column, 'MM')

-- Extract parts
YEAR(created_at), MONTH(created_at)
DAYOFWEEK(created_at)

Delta Lake features:

-- Time travel
SELECT * FROM my_table TIMESTAMP AS OF '2024-01-15'
SELECT * FROM my_table VERSION AS OF 42

-- Describe history
DESCRIBE HISTORY my_table

-- Merge (upsert)
MERGE INTO target USING source
ON target.id = source.id
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *

Performance tips:

  • Use Delta Lake's OPTIMIZE and ZORDER for query performance
  • Leverage Photon engine for compute-intensive queries
  • Use CACHE TABLE for frequently accessed datasets
  • Partition by low-cardinality date columns

Raw lines 234 to 278
279## Common SQL Patterns

Common SQL Patterns

Raw lines 279 to 280
281### Window Functions
283```sql
284-- Ranking
285ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC)
286RANK() OVER (PARTITION BY category ORDER BY revenue DESC)
287DENSE_RANK() OVER (ORDER BY score DESC)
289-- Running totals / moving averages
290SUM(revenue) OVER (ORDER BY date_col ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as running_total
291AVG(revenue) OVER (ORDER BY date_col ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) as moving_avg_7d
293-- Lag / Lead
294LAG(value, 1) OVER (PARTITION BY entity ORDER BY date_col) as prev_value
295LEAD(value, 1) OVER (PARTITION BY entity ORDER BY date_col) as next_value
297-- First / Last value
298FIRST_VALUE(status) OVER (PARTITION BY user_id ORDER BY created_at ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
299LAST_VALUE(status) OVER (PARTITION BY user_id ORDER BY created_at ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
301-- Percent of total
302revenue / SUM(revenue) OVER () as pct_of_total
303revenue / SUM(revenue) OVER (PARTITION BY category) as pct_of_category
304```

Window Functions

-- Ranking
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)

-- Running totals / moving averages
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 / Lead
LAG(value, 1) OVER (PARTITION BY entity ORDER BY date_col) as prev_value
LEAD(value, 1) OVER (PARTITION BY entity ORDER BY date_col) as next_value

-- First / Last value
FIRST_VALUE(status) OVER (PARTITION BY user_id ORDER BY created_at ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
LAST_VALUE(status) OVER (PARTITION BY user_id ORDER BY created_at ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)

-- Percent of total
revenue / SUM(revenue) OVER () as pct_of_total
revenue / SUM(revenue) OVER (PARTITION BY category) as pct_of_category
Raw lines 281 to 305
306### CTEs for Readability
308```sql
309WITH
310-- Step 1: Define the base population
311base_users AS (
312 SELECT user_id, created_at, plan_type
313 FROM users
314 WHERE created_at >= DATE '2024-01-01'
315 AND status = 'active'
316),
318-- Step 2: Calculate user-level metrics
319user_metrics AS (
320 SELECT
321 u.user_id,
322 u.plan_type,
323 COUNT(DISTINCT e.session_id) as session_count,
324 SUM(e.revenue) as total_revenue
325 FROM base_users u
326 LEFT JOIN events e ON u.user_id = e.user_id
327 GROUP BY u.user_id, u.plan_type
328),
330-- Step 3: Aggregate to summary level
331summary AS (
332 SELECT
333 plan_type,
334 COUNT(*) as user_count,
335 AVG(session_count) as avg_sessions,
336 SUM(total_revenue) as total_revenue
337 FROM user_metrics
338 GROUP BY plan_type
341SELECT * FROM summary ORDER BY total_revenue DESC;
342```

CTEs for Readability

WITH
-- Step 1: Define the base population
base_users AS (
    SELECT user_id, created_at, plan_type
    FROM users
    WHERE created_at >= DATE '2024-01-01'
      AND status = 'active'
),

-- Step 2: Calculate user-level metrics
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
),

-- Step 3: Aggregate to summary level
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;
Raw lines 306 to 343
344### Cohort Retention
346```sql
347WITH cohorts AS (
348 SELECT
349 user_id,
350 DATE_TRUNC('month', first_activity_date) as cohort_month
351 FROM users
352),
353activity AS (
354 SELECT
355 user_id,
356 DATE_TRUNC('month', activity_date) as activity_month
357 FROM user_activity
359SELECT
360 c.cohort_month,
361 COUNT(DISTINCT c.user_id) as cohort_size,
362 COUNT(DISTINCT CASE
363 WHEN a.activity_month = c.cohort_month THEN a.user_id
364 END) as month_0,
365 COUNT(DISTINCT CASE
366 WHEN a.activity_month = c.cohort_month + INTERVAL '1 month' THEN a.user_id
367 END) as month_1,
368 COUNT(DISTINCT CASE
369 WHEN a.activity_month = c.cohort_month + INTERVAL '3 months' THEN a.user_id
370 END) as month_3
371FROM cohorts c
372LEFT JOIN activity a ON c.user_id = a.user_id
373GROUP BY c.cohort_month
374ORDER BY c.cohort_month;
375```

Cohort Retention

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
GROUP BY c.cohort_month
ORDER BY c.cohort_month;
Raw lines 344 to 376
377### Funnel Analysis
379```sql
380WITH funnel AS (
381 SELECT
382 user_id,
383 MAX(CASE WHEN event = 'page_view' THEN 1 ELSE 0 END) as step_1_view,
384 MAX(CASE WHEN event = 'signup_start' THEN 1 ELSE 0 END) as step_2_start,
385 MAX(CASE WHEN event = 'signup_complete' THEN 1 ELSE 0 END) as step_3_complete,
386 MAX(CASE WHEN event = 'first_purchase' THEN 1 ELSE 0 END) as step_4_purchase
387 FROM events
388 WHERE event_date >= CURRENT_DATE - INTERVAL '30 days'
389 GROUP BY user_id
391SELECT
392 COUNT(*) as total_users,
393 SUM(step_1_view) as viewed,
394 SUM(step_2_start) as started_signup,
395 SUM(step_3_complete) as completed_signup,
396 SUM(step_4_purchase) as purchased,
397 ROUND(100.0 * SUM(step_2_start) / NULLIF(SUM(step_1_view), 0), 1) as view_to_start_pct,
398 ROUND(100.0 * SUM(step_3_complete) / NULLIF(SUM(step_2_start), 0), 1) as start_to_complete_pct,
399 ROUND(100.0 * SUM(step_4_purchase) / NULLIF(SUM(step_3_complete), 0), 1) as complete_to_purchase_pct
400FROM funnel;
401```

Funnel Analysis

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
)
SELECT
    COUNT(*) as total_users,
    SUM(step_1_view) as viewed,
    SUM(step_2_start) as started_signup,
    SUM(step_3_complete) as completed_signup,
    SUM(step_4_purchase) as purchased,
    ROUND(100.0 * SUM(step_2_start) / NULLIF(SUM(step_1_view), 0), 1) as view_to_start_pct,
    ROUND(100.0 * SUM(step_3_complete) / NULLIF(SUM(step_2_start), 0), 1) as start_to_complete_pct,
    ROUND(100.0 * SUM(step_4_purchase) / NULLIF(SUM(step_3_complete), 0), 1) as complete_to_purchase_pct
FROM funnel;
Raw lines 377 to 402
403### Deduplication
405```sql
406-- Keep the most recent record per key
407WITH ranked AS (
408 SELECT
409 *,
410 ROW_NUMBER() OVER (
411 PARTITION BY entity_id
412 ORDER BY updated_at DESC
413 ) as rn
414 FROM source_table
416SELECT * FROM ranked WHERE rn = 1;
417```

Deduplication

-- Keep the most recent record per key
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;
Raw lines 403 to 418
419## Error Handling and Debugging
421When a query fails:
4231. **Syntax errors**: Check for dialect-specific syntax (e.g., `ILIKE` not available in BigQuery, `SAFE_DIVIDE` only in BigQuery)
4242. **Column not found**: Verify column names against schema -- check for typos, case sensitivity (PostgreSQL is case-sensitive for quoted identifiers)
4253. **Type mismatches**: Cast explicitly when comparing different types (`CAST(col AS DATE)`, `col::DATE`)
4264. **Division by zero**: Use `NULLIF(denominator, 0)` or dialect-specific safe division
4275. **Ambiguous columns**: Always qualify column names with table alias in JOINs
4286. **Group by errors**: All non-aggregated columns must be in GROUP BY (except in BigQuery which allows grouping by alias)

Error Handling and Debugging

When a query fails:

  1. Syntax errors: Check for dialect-specific syntax (e.g., ILIKE not available in BigQuery, SAFE_DIVIDE only in BigQuery)
  2. Column not found: Verify column names against schema -- check for typos, case sensitivity (PostgreSQL is case-sensitive for quoted identifiers)
  3. Type mismatches: Cast explicitly when comparing different types (CAST(col AS DATE), col::DATE)
  4. Division by zero: Use NULLIF(denominator, 0) or dialect-specific safe division
  5. Ambiguous columns: Always qualify column names with table alias in JOINs
  6. Group by errors: All non-aggregated columns must be in GROUP BY (except in BigQuery which allows grouping by alias)
Raw lines 419 to 428

From anthropics/knowledge-work-plugins at 2574e29, 2026-10-27. Skills read by DeepSeek V4.1 Flash on 2026-11-04. Summary and techniques by Claude Opus 5.5 on 2026-11-06.