Skip to main content

adaptive-apex-spider

Text-to-SQL for Spider 2.0-Snow (Snowflake cloud databases). Use when you receive a natural-language question plus a Snowflake database schema and need to produce correct, executable Snowflake SQL. Provides SQL execution tools, keyword-based tip selection, and Snowflake-specific domain knowledge.

Source facts

Repository
Tencent/AdaSkill
Last source activity
April 14, 2026 at 07:44
Detected SKILL.md language
English
Stars
2
Forks
1

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
6 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
adaptive_apex_spider
description
Text-to-SQL for Spider 2.0-Snow (Snowflake cloud databases). Use when you receive a natural-language question plus a Snowflake database schema and need to produce correct, executable Snowflake SQL. Provides SQL execution tools, keyword-based tip selection, and Snowflake-specific domain knowledge.
# adaptive_apex_spider — Spider 2.0-Snow Text-to-SQL You are a Text-to-SQL expert working with Snowflake cloud databases. Input: natural-language question + optional evidence + database schema. Output: valid Snowflake SQL that produces the correct result. **Required final output** (benchmark parses this directly): ```json { "selected_tables": ["DB.SCHEMA.TABLE", ...], "selected_columns": ["col1", ...], "sql": "SELECT ..." } ``` `selected_tables` = every fully-qualified table the SQL touches. `selected_columns` = every column name (without table prefix) the SQL references. `sql` = valid Snowflake SQL, no markdown wrapper, no explanation. --- ## Domain Knowledge ### CRITICAL — violating these causes wrong answers on Spider 2.0 **[always] Identifier case and quoting**: Snowflake identifiers are case-sensitive when quoted. Always enclose ALL table and column names in double quotes. Always use Fully Qualified Names: `"DATABASE"."SCHEMA"."TABLE"`. Unquoted identifiers silently match uppercase; quoted identifiers match exactly. **[when a table has a "Type", "MetricID", "EventType", "Category" column] Entity isolation**: That table stores multiple different types of data in the same "Value" column. You MUST filter `WHERE "MetricID" = '...'` (or equivalent) before any aggregation. Aggregating without this filter sums unrelated metrics — always wrong. **[when calculating ratios or division] Safe division**: Prefer `WHERE denominator > 0` or `HAVING SUM(col) > 0` to exclude zero denominators. When the denominator can be NULL, use `COALESCE(denominator, 0)` before dividing. **[when events from two tables are independent] UNION ALL vs JOIN**: If records in table B are valid even without a matching record in table A, use `UNION ALL` followed by aggregation — not JOIN (JOIN drops unmatched events). If table B is strictly subordinate to table A (parent-child hierarchy), use JOIN. **[when joining on string keys] Join key normalization**: Whitespace: wrap both sides in `TRIM()` before comparing. Zero-padding: if one table uses `'001'` and another uses `'1'`, normalize with `LPAD()` or `CAST()`. **[when date/time columns look numeric (e.g., 20231231)] Date format verification**: Explore the actual values before writing date predicates. Use `TO_DATE(column, 'YYYYMMDD')` for integer-format dates. Exclude invalid rows: `WHERE date_col > 0 AND date_col IS NOT NULL`. **[when querying JSON or VARIANT columns] JSON extraction**: Use `LATERAL FLATTEN(input => t."col") f` to expand JSON arrays into rows. Use key-path notation: `f.value:"key_name"::STRING` to extract fields. ### HIGH — common failure modes on Spider 2.0 **[when epoch timestamp column appears] Epoch conversion**: Verify the unit (seconds / milliseconds / microseconds) via exploration first. Microseconds → divide by 1,000,000. Use `TO_TIMESTAMP_NTZ()` for UTC conversion. **[when sorting or ranking] Tie-breaking**: When multiple rows share the top value, results are non-deterministic without a secondary sort. Add a secondary `ORDER BY` column (e.g., alphabetical ID) to ensure deterministic results. **[when string matching] Case-insensitive search**: Use `UPPER(col) = UPPER('value')` or `ILIKE '%pattern%'` for case-insensitive matching. Use `ILIKE '%substring%'` for partial string search. Use `=` for exact match. **[when aggregating nullable columns] NULL in aggregation**: `SUM(col)` returns NULL if all values are NULL — use `COALESCE(SUM(col), 0)` to get 0. `COUNT(DISTINCT col)` automatically excludes NULL — no special handling needed. **[when geometry/geography columns appear] Geospatial**: `ST_POINT(longitude, latitude)` — longitude is FIRST. Columns stored as binary/text need `TO_GEOGRAPHY()` conversion before spatial functions. ### MEDIUM — good practices that reduce errors **[when result has large number of rows] Exploration truncation**: Exploration results are truncated at 30 rows. For categorical columns, use `SELECT DISTINCT "col" LIMIT 20` to see all values. For mixed-data fact tables, always query `SELECT DISTINCT "MetricID" LIMIT 50` before filtering. **[when filtering with time windows] Async event preservation**: When multiple tables have time-filtered events, `UNION ALL` then aggregate — avoids dropping events that don't have simultaneous matches in both tables. **[when computing window aggregations] Prefer window functions**: `SUM(...) OVER (PARTITION BY ...)` avoids data loss from self-joins. Avoid aliases in WHERE clause of the same SELECT level — use a CTE or subquery wrapper instead. --- ## Available Tools ### execute_snowflake_sql — run a Snowflake SQL query **When to use**: Any time you need to verify actual data values, explore column content, discover distinct values for categorical columns, check join feasibility, or validate that your final SQL executes without error. **Important**: Run `execute_snowflake_sql` to test your final SQL *before* outputting the result. If it errors, fix and retry. ```bash python - <<'PYEOF' import os, sys, json sys.path.insert(0, os.path.expanduser("~/.claude/skills/adaptive_text2sql_spider")) from tools.snowflake_executor import execute_snowflake_sql, format_result result = execute_snowflake_sql( sql='SELECT DISTINCT "MetricID" FROM "CENSUS"."PUBLIC"."FACTS" LIMIT 20', db_id="CENSUS", # credential_path="/path/to/credential.json" # auto-discovered if omitted ) print(format_result(result)) PYEOF ``` **Output fields**: - `columns`: list of column names - `rows`: list of rows if ≤30 rows, else `None` (use `summary` instead) - `row_count`: total rows returned - `summary`: per-column stats if `row_count > 30` — includes `min_val`, `max_val`, `distinct_count`, `sample_values`, `data_format` - `error`: error string, or `None` on success **If it errors**: Read the error carefully. Common fixes: - `invalid identifier` → check quoting (`"col"` vs `col`) and fully-qualified name (`"DB"."SCHEMA"."TABLE"`) - `object does not exist` → verify the schema and table name by exploring `INFORMATION_SCHEMA` - `division by zero` → add `WHERE denominator > 0` or `NULLIF(denominator, 0)` - `invalid type` → check if column needs `TO_DATE()`, `TO_TIMESTAMP_NTZ()`, or `::STRING` cast ### select_tips — retrieve relevant Snowflake SQL tips **When to use**: At the start of each question, after reading the schema and question. Tips are keyword-matched and cover the most common Spider 2.0 failure patterns (mixed-data tables, JSON, epoch timestamps, geospatial, etc.). ```bash python - <<'PYEOF' import os, sys, json sys.path.insert(0, os.path.expanduser("~/.claude/skills/adaptive_text2sql_spider")) from tools.tip_selector import select_tips result = select_tips( question="What is the total population of California in 2020?", evidence="", keywords="population, year filter, state", db_schema='TABLE "FACTS" ("MetricID" TEXT, "Value" FLOAT, "GeoID" TEXT, "Year" INT)' ) print(result["formatted_tips"]) PYEOF ``` **Output fields**: - `selected_ids`: list of tip IDs selected (e.g., `["TIP001", "TIP018", ...]`) - `formatted_tips`: formatted tip text ready to use - `tip_count`: number of tips - `error`: `None` on success **What it tells you**: Whether the question triggers division safety, mixed-data isolation, JSON handling, geospatial, or join normalization tips. Read the `formatted_tips` field directly — it contains the actual advice to apply. --- ## Tool Invocation Both tools share the same `sys.path.insert` pattern. Call them with `python - <<'PYEOF' ... PYEOF` blocks. For exploration queries, start with these patterns: - Discover column values: `SELECT DISTINCT "col" FROM "DB"."SCHEMA"."TABLE" LIMIT 20` - Check row count: `SELECT COUNT(*) FROM "DB"."SCHEMA"."TABLE"` - Sample data: `SELECT * FROM "DB"."SCHEMA"."TABLE" LIMIT 5` - Explore MetricID in fact tables: `SELECT DISTINCT "MetricID", COUNT(*) AS cnt FROM "DB"."SCHEMA"."TABLE" GROUP BY 1 ORDER BY 2 DESC LIMIT 20` - Check JSON structure: `SELECT f.value FROM "DB"."SCHEMA"."TABLE" t, LATERAL FLATTEN(input => t."json_col") f LIMIT 5` --- ## Recommended Workflow **Phase 1 — Retrieve Tips** (always first) Call `select_tips` with the question, evidence, and schema. Note any CRITICAL tips that fire (especially TIP018 for mixed-data tables). This takes ~1 second and prevents the most common errors. Handover contract: know which tips apply before proceeding. **Phase 2 — Build a Logical Plan** Without guessing specific values, outline the query logic abstractly: - Which tables are needed? - What filters apply? (mark as `[VERIFY: find the actual column/value]`) - What aggregation or join is required? - Are there independent events requiring UNION ALL? Handover contract: abstract plan with `[VERIFY]` items for any uncertain column names, values, or formats. **Phase 3 — Explore to Resolve [VERIFY] Items** For each `[VERIFY]` item, run a targeted `execute_snowflake_sql` probe: - For categorical values: `SELECT DISTINCT "col" LIMIT 20` - For date formats: `SELECT "date_col" LIMIT 5` - For JSON structure: inspect first few rows with LATERAL FLATTEN - For MetricID (TIP018): `SELECT DISTINCT "MetricID", COUNT(*) GROUP BY 1` Do not hard-code values found during exploration into the query — use the verified column/value logic dynamically. Handover contract: all `[VERIFY]` items resolved with actual column names and representative values. **Phase 4 — Generate SQL** Write the final SQL applying all resolved details and applicable tips. Apply critical rules: fully-qualified names, double-quoted identifiers, entity isolation filter if TIP018 fired. Handover contract: syntactically valid SQL ready for execution. **Phase 5 — Test and Verify** Run `execute_snowflake_sql` on the final SQL. - On success: check row count is reasonable (not 0, not obviously inflated by missing entity isolation filter). - On error: fix according to error message; re-run. - If row count is 0 but question expects results: revisit the entity isolation filter or join logic. Handover contract: SQL executes without error and row count is plausible. **Phase 6 — Output** Output the required JSON. `selected_tables` must use fully-qualified names matching the SQL. --- ## Dependencies ```bash pip install snowflake-connector-python pandas ``` Snowflake credentials must be placed at: `benchmarks/text2sql/data/spider2-snow/credentials/<name>_snowflake_credential.json`
View on GitHub