Cortex Skills
  • Skills Directory
  • Categories
  • Docs
Add SkillBrowse Skills
  1. Home
  2. Skills Directory
  3. Document AI
  4. dynamic-tables
IntermediateDocument AIfeatured

dynamic-tables

[REQUIRED] Use for ALL Snowflake Dynamic Table operations: creating, optimizing, monitoring, and troubleshooting.
Jump to Installation

Compatible with

Cortex

About this skill

Dynamic Tables

Expert guidance for Snowflake Dynamic Tables: creating pipelines, configuring refreshes, monitoring health, troubleshooting issues, and optimizing performance.

When to Use

Use this skill when users ask about:

  • Creating dynamic tables with appropriate refresh modes
  • Setting up dynamic table pipelines with proper target lag configuration
  • Monitoring dynamic table health and refresh history
  • Troubleshooting refresh failures or performance issues
  • Optimizing refresh modes and query patterns
  • Breaking large dynamic tables into smaller, more efficient ones

Dynamic Tables vs Streams+Tasks

Dynamic Tables are the default choice for Snowflake data pipelines. For multi-step transformations, chain multiple DTs together—each DT handles one transformation step, and Snowflake manages the dependency graph and refresh order automatically.

raw_table → dt_bronze → dt_silver → dt_gold
            (DOWNSTREAM)  (DOWNSTREAM)  (5 min lag)

Only use Streams+Tasks when a specific blocker prevents DT usage:

BlockerWhy DTs Can't Handle It
Append-only stream semanticsDTs track all changes (insert/update/delete), can't isolate inserts only. First check: can we add IMMUTABLE WHERE on the DT? Consult optimize/SKILL.md.
External/directory table sourcesNot supported as DT sources
Sub-minute latency requirementDT minimum TARGET_LAG is 1 minute
DT → View → DT dependencyNot supported (view cannot sit between two DTs)
Stream with static dimension joinDTs recompute full join on any base table change; can't isolate stream-side changes
Procedural logic (IF/ELSE, loops)DTs are declarative SELECT statements only
Side effects (API calls, notifications)DTs cannot call external functions with side effects
Write to multiple targets from one sourceOne DT = one target table

Decision tree:

Is there a blocker from the table above?
    │
    ├─ NO  → Use Dynamic Tables (chain multiple for complexity)
    │
    └─ YES → Use Streams+Tasks (or hybrid pattern)

For migrating existing Streams+Tasks pipelines to DTs, see task-to-dt/SKILL.md.

⚠️ MANDATORY INITIALIZATION

Before any workflow, you MUST:

Step 1: Load Core References

Load the following reference documents:

  1. Load: references/sql-syntax.md - SQL command syntax
  2. Load: references/monitoring-functions.md - monitoring function router (database context rules + links to state, refresh analysis, and graph references)

⚠️ MANDATORY STOPPING POINT: Do NOT proceed until you have loaded these references.

Step 2: Establish Connection Context

Goal: Identify the Snowflake connection to namespace diary entries.

  1. Ask user for their Snowflake CLI connection name:

    • "Which Snowflake connection are you using? (e.g., the connection name from snow CLI)"
  2. Store connection name for this session: <connection>

  3. Setup diary directory:

    mkdir -p ~/.snowflake/cortex/memory/dynamic_tables/<connection>
    
  4. Check/update connection diary at ~/.snowflake/cortex/memory/dynamic_tables/<connection>/_connection_diary.md:

    • If exists: Read to understand account context, known DTs, warehouses
    • If new connection: Create with initial discovery (see Connection Diary section)

⚠️ MANDATORY STOPPING POINT: Do NOT proceed until connection context is established.

Step 3: Check DT-Specific Diary (conditional — only when user references a specific DT)

Skip this step for CREATE intent (no DT exists yet). For MONITOR, TROUBLESHOOT, OPTIMIZE, or PERMISSIONS intent on a named DT:

  1. Check if DT diary entry exists: ~/.snowflake/cortex/memory/dynamic_tables/<connection>/<database>.<schema>.<dt_name>.md
  2. If exists: Read most recent entry to compare current vs historical metrics
  3. If not exists: Note "First analysis of this DT - no historical baseline available"

Intent Detection

When a user makes a request, detect their intent and route to the appropriate sub-skill:

CREATE Intent

Trigger phrases: "create dynamic table", "set up DT", "new dynamic table", "define pipeline", "build DT"

→ Load: create/SKILL.md

MONITOR Intent

Trigger phrases: "check status", "refresh history", "is it healthy", "target lag", "how is my DT", "DT state"

→ Load: monitor/SKILL.md

TROUBLESHOOT Intent

Trigger phrases: "failing refresh", "not refreshing", "UPSTREAM_FAILED", "suspended", "full refresh instead of incremental", "refresh_mode_reason", "why is it failing", "DT broken", "errors"

→ Load: troubleshoot/SKILL.md

OPTIMIZE Intent

Trigger phrases: "slow refresh", "make incremental", "improve performance", "immutability", "break into smaller", "decompose", "speed up", "reduce cost"

→ Load: optimize/SKILL.md

ALERTING Intent

Trigger phrases: "set up alerts", "alert on failure", "notify on refresh failure", "DT alerting", "event table alerts", "monitor failures", "email when DT fails"

→ Load: dt-alerting/SKILL.md

PERMISSIONS Intent

Trigger phrases: "insufficient privileges", "permission denied", "privilege error", "DT permissions", "ownership transfer", "can't refresh", "access denied", "masking policy error"

→ Load: permissions/SKILL.md

TASK-TO-DT Intent

Trigger phrases: "convert tasks", "migrate from tasks", "replace stream and task", "task to DT", "streams and tasks to dynamic table", "modernize pipeline"

→ Load: task-to-dt/SKILL.md


Workflow Decision Tree

Start Session
    ↓
MANDATORY: Load reference documents (sql-syntax.md, monitoring-functions.md router)
    ↓
MANDATORY: Establish connection context (get connection name, setup diary)
    ↓
Check/update connection diary (discovered DTs, warehouses, account info)
    ↓
Check DT-specific diary for historical context (if specific DT mentioned)
    ↓
Detect User Intent
    ↓
    ├─→ CREATE → Load create/SKILL.md
    │   (Triggers: "create dynamic table", "new DT", "set up pipeline")
    │
    ├─→ MONITOR → Load monitor/SKILL.md
    │   (Triggers: "check status", "is it healthy", "refresh history")
    │
    ├─→ TROUBLESHOOT → Load troubleshoot/SKILL.md
    │   (Triggers: "failing", "not refreshing", "UPSTREAM_FAILED", "suspended")
    │
    ├─→ OPTIMIZE → Load optimize/SKILL.md
    │   (Triggers: "slow", "improve", "decompose", "make incremental")
    │
    ├─→ ALERTING → Load dt-alerting/SKILL.md
    │   (Triggers: "set up alerts", "notify on failure", "event table alerts")
    │
    ├─→ PERMISSIONS → Load permissions/SKILL.md
    │   (Triggers: "insufficient privileges", "permission denied", "ownership")
    │
    └─→ TASK-TO-DT → Load task-to-dt/SKILL.md
        (Triggers: "convert tasks", "migrate tasks", "replace stream and task")

Sub-Skills

Sub-SkillPurposeWhen to Load
create/SKILL.mdCreate new dynamic tablesCREATE intent
monitor/SKILL.mdHealth checks and status monitoringMONITOR intent
troubleshoot/SKILL.mdDiagnose and fix issuesTROUBLESHOOT intent
optimize/SKILL.mdPerformance improvementsOPTIMIZE intent
dt-alerting/SKILL.mdSet up alerts for refresh failuresALERTING intent
permissions/SKILL.mdTroubleshoot privilege/permission issuesPERMISSIONS intent
task-to-dt/SKILL.mdConvert streams+tasks pipelines to DTsTASK-TO-DT intent

Quick Diagnostic Queries

For immediate assessment before routing:

-- Quick health check for all DTs in schema
SELECT name, scheduling_state, last_completed_refresh_state, 
       refresh_mode, time_within_target_lag_ratio
FROM TABLE(INFORMATION_SCHEMA.DYNAMIC_TABLES())
ORDER BY name;

-- Check for any errors
SELECT name, state, state_message, refresh_action
FROM TABLE(INFORMATION_SCHEMA.DYNAMIC_TABLE_REFRESH_HISTORY(
  NAME_PREFIX => '<database>.<schema>', ERROR_ONLY => TRUE
))
ORDER BY refresh_start_time DESC
LIMIT 5;

Important Constraints

1. Incremental DTs cannot depend on Full refresh DTs

Dynamic tables in incremental mode cannot depend on full refresh mode dynamic tables.

-- ❌ BREAKS: dt_final is INCREMENTAL but depends on dt_upstream which is FULL
CREATE DYNAMIC TABLE dt_upstream
  TARGET_LAG = DOWNSTREAM
  REFRESH_MODE = FULL  -- This DT uses FULL refresh
  AS SELECT * FROM source_table;

CREATE DYNAMIC TABLE dt_final
  TARGET_LAG = '5 minutes'
  REFRESH_MODE = INCREMENTAL  -- ERROR: Cannot be INCREMENTAL if upstream is FULL
  AS SELECT * FROM dt_upstream;

2. Target lag cannot be shorter than upstream's lag

-- ❌ BREAKS: dt_final has 1 minute lag but dt_upstream has 10 minutes
CREATE DYNAMIC TABLE dt_upstream
  TARGET_LAG = '10 minutes'  -- 10 minute lag
  AS SELECT * FROM source_table;

CREATE DYNAMIC TABLE dt_final
  TARGET_LAG = '1 minute'  -- ERROR: Cannot be fresher than upstream (10 min)
  AS SELECT * FROM dt_upstream;

3. Change tracking must remain enabled on base objects

-- ❌ BREAKS: Disabling change tracking after DT creation
CREATE DYNAMIC TABLE my_dt AS SELECT * FROM base_table;

-- Later...
ALTER TABLE base_table SET CHANGE_TRACKING = FALSE;  -- ERROR: DT refreshes will fail

4. SELECT * fails on schema changes

-- ❌ BREAKS: Using SELECT * then adding a column to source
CREATE DYNAMIC TABLE my_dt AS SELECT * FROM source_table;

-- Later...
ALTER TABLE source_table ADD COLUMN new_col VARCHAR;  -- DT refresh will FAIL

-- ✅ FIX: Use explicit columns
CREATE DYNAMIC TABLE my_dt AS SELECT id, name, amount FROM source_table;

5. IMMUTABLE WHERE restrictions

-- ❌ BREAKS: Subquery in IMMUTABLE WHERE
CREATE DYNAMIC TABLE my_dt
  IMMUTABLE WHERE (id IN (SELECT id FROM archived_ids))  -- ERROR: No subqueries
  AS SELECT * FROM source_table;

-- ❌ BREAKS: UDF in IMMUTABLE WHERE
CREATE DYNAMIC TABLE my_dt
  IMMUTABLE WHERE (my_custom_udf(status) = TRUE)  -- ERROR: No UDFs
  AS SELECT * FROM source_table;

-- ❌ BREAKS: Non-deterministic function (except timestamps)
CREATE DYNAMIC TABLE my_dt
  IMMUTABLE WHERE (RANDOM() < 0.5)  -- ERROR: Non-deterministic
  AS SELECT * FROM source_table;

-- ✅ OK: Timestamp functions are allowed
CREATE DYNAMIC TABLE my_dt
  IMMUTABLE WHERE (created_at < CURRENT_TIMESTAMP() - INTERVAL '7 days')
  AS SELECT * FROM source_table;

Stopping Points Summary

All sub-skills follow this philosophy: NO changes without explicit user approval.

  • READ-ONLY queries: Can run freely (diagnostics, monitoring)
  • ANY mutation: Requires stopping point and user approval

See individual sub-skills for specific stopping points.


Diary System

This skill maintains a two-level diary system, namespaced by Snowflake connection:

Directory Structure

~/.snowflake/cortex/memory/dynamic_tables/
├── <connection_name>/
│   ├── _connection_diary.md              # Connection-level diary
│   ├── <DATABASE>.<SCHEMA>.<DT_NAME>.md  # Per-DT diary
│   └── ...
├── <another_connection>/
│   └── ...

Connection Diary (_connection_diary.md)

Location: ~/.snowflake/cortex/memory/dynamic_tables/<connection>/_connection_diary.md

Purpose: Track account-level context, discovered resources, session history.

Template:

# Connection: <connection_name>

## Account Metadata
- **Account**: <account_identifier>
- **Region**: <cloud_region>
- **User**: <username>
- **Default Role**: <role>
- **First Connected**: <timestamp>
- **Last Session**: <timestamp>

---

## Discovered Dynamic Tables

| Database.Schema.Name | Refresh Mode | Target Lag | Status | Last Checked |
|---------------------|--------------|------------|--------|--------------|
| DB.SCHEMA.DT_1 | INCREMENTAL | 5 min | ACTIVE | 2026-01-08 |
| DB.SCHEMA.DT_2 | FULL | 1 hour | SUSPENDED | 2026-01-07 |

---

## Warehouses

| Warehouse | Size | Used by DTs | Notes |
|-----------|------|-------------|-------|
| COMPUTE_WH | X-SMALL | DT_1, DT_2 | Shared warehouse |
| DT_DEDICATED_WH | MEDIUM | DT_3 | Dedicated for large DT |

---

## Session History

### Session: 2026-01-08T14:30:00Z
- **Intent**: Monitor DT health
- **DTs Analyzed**: DB.SCHEMA.DT_1, DB.SCHEMA.DT_2
- **Actions Taken**: None (read-only)
- **Findings**: DT_2 suspended due to upstream failure
- **Follow-up**: Troubleshoot DT_2 upstream

### Session: 2026-01-07T10:00:00Z
- **Intent**: Create new pipeline
- **DTs Created**: DB.SCHEMA.DT_3
- **Notes**: Set up 3-stage pipeline with DOWNSTREAM intermediates

---

## Cross-DT Recommendations

- Consider consolidating DT_1 and DT_2 into single pipeline
- COMPUTE_WH may be undersized for current DT workload
- Enable change tracking on RAW.EVENTS table for future DTs

---

## Notes

- <general observations about this account's DT usage>

Update connection diary:

  • On first session: Run discovery queries, populate metadata
  • Each session: Add session history entry
  • When discovering new DTs: Add to inventory table
  • When finding cross-DT patterns: Add to recommendations

Discovery queries for new connections:

-- Get account info
SELECT CURRENT_ACCOUNT(), CURRENT_REGION(), CURRENT_USER(), CURRENT_ROLE();

-- Discover all dynamic tables
SELECT database_name, schema_name, name, refresh_mode, 
       target_lag_sec, scheduling_state
FROM TABLE(INFORMATION_SCHEMA.DYNAMIC_TABLES())
ORDER BY database_name, schema_name, name;

-- List warehouses
SHOW WAREHOUSES;

Per-DT Diary (<DATABASE>.<SCHEMA>.<DT_NAME>.md)

Location: ~/.snowflake/cortex/memory/dynamic_tables/<connection>/<DATABASE>.<SCHEMA>.<DT_NAME>.md

Purpose: Track individual DT metrics, troubleshooting history, optimizations.

Usage: Each sub-skill reads/writes entries as appropriate. Compare current vs historical state.

When revisiting a DT, compare current state to historical baseline and highlight significant changes.

Installation

Install using the Cortex CLI:

bash
$cortex skill install dynamic-tables

Requires Cortex CLI v2.0+. Install guide →

Repository Sources

Loading repository files...

SKILL.md Preview

SKILL.md
---
name: dynamic-tables
description: "**[REQUIRED]** Use for **ALL** Snowflake Dynamic Table operations: creating, optimizing, monitoring, and troubleshooting. This is the required entry point for any dynamic table related tasks (DT is an acronym for dynamic table). Triggers: dynamic table, data pipeline, incremental pipeline, DT pipeline, incremental refresh, target lag, UPSTREAM_FAILED, refresh failing, full refresh instead of incremental, DT health, create DT, debug DT."
---

# Dynamic Tables

Expert guidance for Snowflake Dynamic Tables: creating pipelines, configuring refreshes, monitoring health, troubleshooting issues, and optimizing performance.

## When to Use

Use this skill when users ask about:
- Creating dynamic tables with appropriate refresh modes
- Setting up dynamic table pipelines with proper target lag configuration
- Monitoring dynamic table health and refresh history
- Troubleshooting refresh failures or performance issues
- Optimizing refresh modes and query patterns
- Breaking large dynamic tables into smaller, more efficient ones

## Dynamic Tables vs Streams+Tasks

**Dynamic Tables are the default choice for Snowflake data pipelines.** For multi-step transformations, chain multiple DTs together—each DT handles one transformation step, and Snowflake manages the dependency graph and refresh order automatically.

```
raw_table → dt_bronze → dt_silver → dt_gold
            (DOWNSTREAM)  (DOWNSTREAM)  (5 min lag)
```

**Only use Streams+Tasks when a specific blocker prevents DT usage:**

| Blocker | Why DTs Can't Handle It |
|---------|------------------------|
| Append-only stream semantics | DTs track all changes (insert/update/delete), can't isolate inserts only. **First check:** can we add IMMUTABLE WHERE on the DT? Consult [optimize/SKILL.md](optimize/SKILL.md). |
| External/directory table sources | Not supported as DT sources |
| Sub-minute latency requirement | DT minimum TARGET_LAG is 1 minute |
| DT → View → DT dependency | Not supported (view cannot sit between two DTs) |
| Stream with static dimension join | DTs recompute full join on any base table change; can't isolate stream-side changes |
| Procedural logic (IF/ELSE, loops) | DTs are declarative SELECT statements only |
| Side effects (API calls, notifications) | DTs cannot call external functions with side effects |
| Write to multiple targets from one source | One DT = one target table |

**Decision tree:**
```
Is there a blocker from the table above?
    │
    ├─ NO  → Use Dynamic Tables (chain multiple for complexity)
    │
    └─ YES → Use Streams+Tasks (or hybrid pattern)
```

For migrating existing Streams+Tasks pipelines to DTs, see [task-to-dt/SKILL.md](task-to-dt/SKILL.md).

## ⚠️ MANDATORY INITIALIZATION

Before any workflow, you MUST:

### Step 1: Load Core References

**Load** the following reference documents:

1. **Load**: [references/sql-syntax.md](references/sql-syntax.md) - SQL command syntax
2. **Load**: [references/monitoring-functions.md](references/monitoring-functions.md) - monitoring function router (database context rules + links to state, refresh analysis, and graph references)

**⚠️ MANDATORY STOPPING POINT**: Do NOT proceed until you have loaded these references.

### Step 2: Establish Connection Context

**Goal:** Identify the Snowflake connection to namespace diary entries.

1. **Ask user** for their Snowflake CLI connection name:
   - "Which Snowflake connection are you using? (e.g., the connection name from `snow` CLI)"
   
2. **Store connection name** for this session: `<connection>`

3. **Setup diary directory**:
   ```bash
   mkdir -p ~/.snowflake/cortex/memory/dynamic_tables/<connection>
   ```

4. **Check/update connection diary** at `~/.snowflake/cortex/memory/dynamic_tables/<connection>/_connection_diary.md`:
   - If exists: Read to understand account context, known DTs, warehouses
   - If new connection: Create with initial discovery (see Connection Diary section)

**⚠️ MANDATORY STOPPING POINT**: Do NOT proceed until connection context is established.

### Step 3: Check DT-Specific Diary *(conditional — only when user references a specific DT)*

Skip this step for CREATE intent (no DT exists yet). For MONITOR, TROUBLESHOOT, OPTIMIZE, or PERMISSIONS intent on a named DT:

1. **Check** if DT diary entry exists: `~/.snowflake/cortex/memory/dynamic_tables/<connection>/<database>.<schema>.<dt_name>.md`
2. **If exists**: Read most recent entry to compare current vs historical metrics
3. **If not exists**: Note "First analysis of this DT - no historical baseline available"

---

## Intent Detection

When a user makes a request, detect their intent and route to the appropriate sub-skill:

### CREATE Intent

**Trigger phrases**: "create dynamic table", "set up DT", "new dynamic table", "define pipeline", "build DT"

**→ Load**: [create/SKILL.md](create/SKILL.md)

### MONITOR Intent

**Trigger phrases**: "check status", "refresh history", "is it healthy", "target lag", "how is my DT", "DT state"

**→ Load**: [monitor/SKILL.md](monitor/SKILL.md)

### TROUBLESHOOT Intent

**Trigger phrases**: "failing refresh", "not refreshing", "UPSTREAM_FAILED", "suspended", "full refresh instead of incremental", "refresh_mode_reason", "why is it failing", "DT broken", "errors"

**→ Load**: [troubleshoot/SKILL.md](troubleshoot/SKILL.md)

### OPTIMIZE Intent

**Trigger phrases**: "slow refresh", "make incremental", "improve performance", "immutability", "break into smaller", "decompose", "speed up", "reduce cost"

**→ Load**: [optimize/SKILL.md](optimize/SKILL.md)

### ALERTING Intent

**Trigger phrases**: "set up alerts", "alert on failure", "notify on refresh failure", "DT alerting", "event table alerts", "monitor failures", "email when DT fails"

**→ Load**: [dt-alerting/SKILL.md](dt-alerting/SKILL.md)

### PERMISSIONS Intent

**Trigger phrases**: "insufficient privileges", "permission denied", "privilege error", "DT permissions", "ownership transfer", "can't refresh", "access denied", "masking policy error"

**→ Load**: [permissions/SKILL.md](permissions/SKILL.md)

### TASK-TO-DT Intent

**Trigger phrases**: "convert tasks", "migrate from tasks", "replace stream and task", "task to DT", "streams and tasks to dynamic table", "modernize pipeline"

**→ Load**: [task-to-dt/SKILL.md](task-to-dt/SKILL.md)

---

## Workflow Decision Tree

```
Start Session
    ↓
MANDATORY: Load reference documents (sql-syntax.md, monitoring-functions.md router)
    ↓
MANDATORY: Establish connection context (get connection name, setup diary)
    ↓
Check/update connection diary (discovered DTs, warehouses, account info)
    ↓
Check DT-specific diary for historical context (if specific DT mentioned)
    ↓
Detect User Intent
    ↓
    ├─→ CREATE → Load create/SKILL.md
    │   (Triggers: "create dynamic table", "new DT", "set up pipeline")
    │
    ├─→ MONITOR → Load monitor/SKILL.md
    │   (Triggers: "check status", "is it healthy", "refresh history")
    │
    ├─→ TROUBLESHOOT → Load troubleshoot/SKILL.md
    │   (Triggers: "failing", "not refreshing", "UPSTREAM_FAILED", "suspended")
    │
    ├─→ OPTIMIZE → Load optimize/SKILL.md
    │   (Triggers: "slow", "improve", "decompose", "make incremental")
    │
    ├─→ ALERTING → Load dt-alerting/SKILL.md
    │   (Triggers: "set up alerts", "notify on failure", "event table alerts")
    │
    ├─→ PERMISSIONS → Load permissions/SKILL.md
    │   (Triggers: "insufficient privileges", "permission denied", "ownership")
    │
    └─→ TASK-TO-DT → Load task-to-dt/SKILL.md
        (Triggers: "convert tasks", "migrate tasks", "replace stream and task")
```

---

## Sub-Skills

| Sub-Skill | Purpose | When to Load |
|-----------|---------|--------------|
| [create/SKILL.md](create/SKILL.md) | Create new dynamic tables | CREATE intent |
| [monitor/SKILL.md](monitor/SKILL.md) | Health checks and status monitoring | MONITOR intent |
| [troubleshoot/SKILL.md](troubleshoot/SKILL.md) | Diagnose and fix issues | TROUBLESHOOT intent |
| [optimize/SKILL.md](optimize/SKILL.md) | Performance improvements | OPTIMIZE intent |
| [dt-alerting/SKILL.md](dt-alerting/SKILL.md) | Set up alerts for refresh failures | ALERTING intent |
| [permissions/SKILL.md](permissions/SKILL.md) | Troubleshoot privilege/permission issues | PERMISSIONS intent |
| [task-to-dt/SKILL.md](task-to-dt/SKILL.md) | Convert streams+tasks pipelines to DTs | TASK-TO-DT intent |

---

## Quick Diagnostic Queries

For immediate assessment before routing:

```sql
-- Quick health check for all DTs in schema
SELECT name, scheduling_state, last_completed_refresh_state, 
       refresh_mode, time_within_target_lag_ratio
FROM TABLE(INFORMATION_SCHEMA.DYNAMIC_TABLES())
ORDER BY name;

-- Check for any errors
SELECT name, state, state_message, refresh_action
FROM TABLE(INFORMATION_SCHEMA.DYNAMIC_TABLE_REFRESH_HISTORY(
  NAME_PREFIX => '<database>.<schema>', ERROR_ONLY => TRUE
))
ORDER BY refresh_start_time DESC
LIMIT 5;
```

---

## Important Constraints

### 1. Incremental DTs cannot depend on Full refresh DTs

Dynamic tables in incremental mode cannot depend on full refresh mode dynamic tables.

```sql
-- ❌ BREAKS: dt_final is INCREMENTAL but depends on dt_upstream which is FULL
CREATE DYNAMIC TABLE dt_upstream
  TARGET_LAG = DOWNSTREAM
  REFRESH_MODE = FULL  -- This DT uses FULL refresh
  AS SELECT * FROM source_table;

CREATE DYNAMIC TABLE dt_final
  TARGET_LAG = '5 minutes'
  REFRESH_MODE = INCREMENTAL  -- ERROR: Cannot be INCREMENTAL if upstream is FULL
  AS SELECT * FROM dt_upstream;
```

### 2. Target lag cannot be shorter than upstream's lag

```sql
-- ❌ BREAKS: dt_final has 1 minute lag but dt_upstream has 10 minutes
CREATE DYNAMIC TABLE dt_upstream
  TARGET_LAG = '10 minutes'  -- 10 minute lag
  AS SELECT * FROM source_table;

CREATE DYNAMIC TABLE dt_final
  TARGET_LAG = '1 minute'  -- ERROR: Cannot be fresher than upstream (10 min)
  AS SELECT * FROM dt_upstream;
```

### 3. Change tracking must remain enabled on base objects

```sql
-- ❌ BREAKS: Disabling change tracking after DT creation
CREATE DYNAMIC TABLE my_dt AS SELECT * FROM base_table;

-- Later...
ALTER TABLE base_table SET CHANGE_TRACKING = FALSE;  -- ERROR: DT refreshes will fail
```

### 4. `SELECT *` fails on schema changes

```sql
-- ❌ BREAKS: Using SELECT * then adding a column to source
CREATE DYNAMIC TABLE my_dt AS SELECT * FROM source_table;

-- Later...
ALTER TABLE source_table ADD COLUMN new_col VARCHAR;  -- DT refresh will FAIL

-- ✅ FIX: Use explicit columns
CREATE DYNAMIC TABLE my_dt AS SELECT id, name, amount FROM source_table;
```

### 5. IMMUTABLE WHERE restrictions

```sql
-- ❌ BREAKS: Subquery in IMMUTABLE WHERE
CREATE DYNAMIC TABLE my_dt
  IMMUTABLE WHERE (id IN (SELECT id FROM archived_ids))  -- ERROR: No subqueries
  AS SELECT * FROM source_table;

-- ❌ BREAKS: UDF in IMMUTABLE WHERE
CREATE DYNAMIC TABLE my_dt
  IMMUTABLE WHERE (my_custom_udf(status) = TRUE)  -- ERROR: No UDFs
  AS SELECT * FROM source_table;

-- ❌ BREAKS: Non-deterministic function (except timestamps)
CREATE DYNAMIC TABLE my_dt
  IMMUTABLE WHERE (RANDOM() < 0.5)  -- ERROR: Non-deterministic
  AS SELECT * FROM source_table;

-- ✅ OK: Timestamp functions are allowed
CREATE DYNAMIC TABLE my_dt
  IMMUTABLE WHERE (created_at < CURRENT_TIMESTAMP() - INTERVAL '7 days')
  AS SELECT * FROM source_table;
```

---

## Stopping Points Summary

All sub-skills follow this philosophy: **NO changes without explicit user approval.**

- **READ-ONLY queries**: Can run freely (diagnostics, monitoring)
- **ANY mutation**: Requires stopping point and user approval

See individual sub-skills for specific stopping points.

---

## Diary System

This skill maintains a two-level diary system, namespaced by Snowflake connection:

### Directory Structure

```
~/.snowflake/cortex/memory/dynamic_tables/
├── <connection_name>/
│   ├── _connection_diary.md              # Connection-level diary
│   ├── <DATABASE>.<SCHEMA>.<DT_NAME>.md  # Per-DT diary
│   └── ...
├── <another_connection>/
│   └── ...
```

### Connection Diary (`_connection_diary.md`)

**Location**: `~/.snowflake/cortex/memory/dynamic_tables/<connection>/_connection_diary.md`

**Purpose**: Track account-level context, discovered resources, session history.

**Template**:
```markdown
# Connection: <connection_name>

## Account Metadata
- **Account**: <account_identifier>
- **Region**: <cloud_region>
- **User**: <username>
- **Default Role**: <role>
- **First Connected**: <timestamp>
- **Last Session**: <timestamp>

---

## Discovered Dynamic Tables

| Database.Schema.Name | Refresh Mode | Target Lag | Status | Last Checked |
|---------------------|--------------|------------|--------|--------------|
| DB.SCHEMA.DT_1 | INCREMENTAL | 5 min | ACTIVE | 2026-01-08 |
| DB.SCHEMA.DT_2 | FULL | 1 hour | SUSPENDED | 2026-01-07 |

---

## Warehouses

| Warehouse | Size | Used by DTs | Notes |
|-----------|------|-------------|-------|
| COMPUTE_WH | X-SMALL | DT_1, DT_2 | Shared warehouse |
| DT_DEDICATED_WH | MEDIUM | DT_3 | Dedicated for large DT |

---

## Session History

### Session: 2026-01-08T14:30:00Z
- **Intent**: Monitor DT health
- **DTs Analyzed**: DB.SCHEMA.DT_1, DB.SCHEMA.DT_2
- **Actions Taken**: None (read-only)
- **Findings**: DT_2 suspended due to upstream failure
- **Follow-up**: Troubleshoot DT_2 upstream

### Session: 2026-01-07T10:00:00Z
- **Intent**: Create new pipeline
- **DTs Created**: DB.SCHEMA.DT_3
- **Notes**: Set up 3-stage pipeline with DOWNSTREAM intermediates

---

## Cross-DT Recommendations

- Consider consolidating DT_1 and DT_2 into single pipeline
- COMPUTE_WH may be undersized for current DT workload
- Enable change tracking on RAW.EVENTS table for future DTs

---

## Notes

- <general observations about this account's DT usage>
```

**Update connection diary**:
- On first session: Run discovery queries, populate metadata
- Each session: Add session history entry
- When discovering new DTs: Add to inventory table
- When finding cross-DT patterns: Add to recommendations

**Discovery queries for new connections**:
```sql
-- Get account info
SELECT CURRENT_ACCOUNT(), CURRENT_REGION(), CURRENT_USER(), CURRENT_ROLE();

-- Discover all dynamic tables
SELECT database_name, schema_name, name, refresh_mode, 
       target_lag_sec, scheduling_state
FROM TABLE(INFORMATION_SCHEMA.DYNAMIC_TABLES())
ORDER BY database_name, schema_name, name;

-- List warehouses
SHOW WAREHOUSES;
```

### Per-DT Diary (`<DATABASE>.<SCHEMA>.<DT_NAME>.md`)

**Location**: `~/.snowflake/cortex/memory/dynamic_tables/<connection>/<DATABASE>.<SCHEMA>.<DT_NAME>.md`

**Purpose**: Track individual DT metrics, troubleshooting history, optimizations.

**Usage**: Each sub-skill reads/writes entries as appropriate. Compare current vs historical state.

When revisiting a DT, compare current state to historical baseline and highlight significant changes.

Tags

#dynamic-tables

Related Skills

Browse all
v1.0.0

snowflake-notebooks

cortex-community
Create and edit Workspace notebooks (.ipynb files) for Snowflake. Use when: creating workspace notebooks, editing notebooks, debugging notebook issues, converting code to notebooks, working with Snowpark, SQL cells, or data analysis in Snowflake. Triggers: notebook, .ipynb, snowflake notebook, workspace notebook, create notebook, edit notebook, jupyter, ipynb file, notebook cell, SQL cell, snowpark session.
#snowflake-notebooks
Cortex
4822.0k
2 hours ago
v0.1.0

snowconvert-assessment

cortex-community
Analyzes workloads to be migrated to Snowflake using SnowConvert assessment reports. Routes to specialized sub-skills for high-quality assessments. Use this skill when user wants to do an assessment of their code or ETL workload, waves generation, object exclusion, sql dynamic and/or ETL analysis (SSIS)
#snowconvert-assessment
Cortex
2615.1k
2 hours ago
v1.0.0

iceberg

cortex-community
Use for ALL Iceberg table requests in Snowflake. This is the REQUIRED entry point for catalog integrations, catalog-linked databases, external volumes, auto-refresh issues, and Snowflake Intelligence. DO NOT work with Iceberg manually - invoke this skill first. Triggers: iceberg, iceberg table, apache iceberg, catalog integration, REST catalog, ICEBERG_REST, glue, AWS glue, glue IRC, lake formation, unity catalog, databricks, polaris, opencatalog, open catalog, CLD, catalog-linked database, linked catalog, auto-discover tables, sync tables, LINKED_CATALOG, external volume, storage access, S3, Azure blob, GCS, IAM role, trust policy, Access Denied, 403 error, ALLOW_WRITES, storage permissions, auto-refresh, autorefresh, stale data, refresh stuck, delta direct, snowflake intelligence, text-to-SQL iceberg, query iceberg natural language.
#iceberg
Cortex
2091.7k
2 hours ago
v1.0.0

deploy-to-spcs

cortex-community
Deploy containerized apps to Snowpark Container Services. Use when: deploying Docker apps, creating SPCS services, pushing images to Snowflake registry.
#deploy-to-spcs
Cortex
854.9k
2 hours ago

Details

Stars379
Installs1.2k
Authorcortex-community
Versionv1.0.0
Updated2 hours ago
LicenseMIT
View Repository
quick install
$ cortex skill install dynamic-tables