Modern data architectures are evolving rapidly, and Snowflake Cortex AISQL is at the forefront of this change. It lets you query unstructured data—files, images, and text—directly using SQL enhanced with AI capabilities. But here’s the catch: these powerful AI features come with significant computational overhead. If you’re not careful about optimization, you’ll face slow queries and skyrocketing costs.
This guide walks you through practical strategies to get the most out of Cortex AISQL while keeping your warehouse credits in check.
Why Snowflake Cortex AISQL Query Optimization Matters in 2025
The amount of unstructured data in cloud warehouses has exploded. Cortex AISQL makes it easier for developers to work with this data without needing deep data science expertise. That’s great for democratizing AI, but it also puts serious strain on your computational resources.
Here’s what happens when you neglect optimization:
Costs spiral out of control – Poorly optimized queries can unexpectedly spike your cloud computing bills
Slow results hurt decision-making – Business users need timely insights, not queries that take minutes to complete
Limited concurrency – Inefficient queries hog resources, preventing other users from accessing AI insights
The good news? With proper optimization, you can protect your budget, improve performance, and enable more users to leverage AI across your organization.
Understanding How Cortex AISQL Works
Cortex AISQL translates your SQL statements into complex workflows that involve AI models. When you run a query, Snowflake:
Parses your request and identifies which AI functions to call (like CORTEX_ANALYST or embedding generation)
Determines the optimal execution plan, balancing data retrieval with external model calls
Executes the query across both storage and compute layers
The key to optimization is minimizing data movement and reducing the amount of data sent to the AI processing layer. Think of it like this: every row you can filter out before calling an AI function is money and time saved.
Getting Started: Profile Your Queries First
Before you start optimizing, you need to understand where your bottlenecks are. Use Snowflake’s Query Profile feature to identify:
Steps that consume the most time
External function calls that are slowing things down
Massive table scans that could be avoided
Here’s a real example of what NOT to do:
-- ❌ BAD: Passing all documents to the AI function
SELECT
document_id,
CORTEX_ANALYST(document_text, 'Summarize key themes') AS summary
FROM
large_documents;
This query sends every single document through the AI function. If you have millions of documents, you’re looking at a very expensive (and slow) operation.
The Single Most Effective Optimization: Filter Early, Filter Hard
The best way to optimize AISQL queries is brutally simple: reduce your data before calling AI functions. Use standard SQL filtering to narrow down your dataset first.
Here’s the improved version:
-- ✅ GOOD: Filter aggressively before using AI functions
SELECT
d.document_id,
d.document_name,
CORTEX_ANALYST(d.document_text, 'Summarize key themes') AS summary
FROM
large_documents d
INNER JOIN
document_metadata m ON d.document_id = m.document_id
WHERE
m.created_date >= DATEADD(month, -1, CURRENT_DATE())
AND m.category = 'Financial Reports'
AND m.status = 'Published'
AND d.document_text IS NOT NULL
LIMIT 500;
This query only processes recent financial reports that are published and have actual text content. We’ve potentially reduced the dataset from millions to hundreds of rows before the expensive AI operation runs.
Smart Join Strategies
Joins can make or break your AISQL performance. Here’s what works:
Prioritize inner joins over outer joins – They reduce your result set immediately:
-- ✅ GOOD: Inner join reduces data early
SELECT
c.customer_id,
c.feedback_text,
CORTEX_SENTIMENT(c.feedback_text) AS sentiment_score
FROM
customer_feedback c
INNER JOIN
active_customers a ON c.customer_id = a.customer_id
WHERE
c.feedback_date >= '2025-01-01'
AND a.subscription_status = 'Active';
Filter out test data explicitly – Don’t let test accounts pollute your AI analysis:
-- ✅ GOOD: Exclude test accounts
SELECT
email,
message_content,
CORTEX_ANALYST(message_content, 'Extract action items') AS actions
FROM
support_messages
WHERE
email NOT LIKE '%@test.com'
AND email NOT LIKE '%test%@%'
AND user_type = 'Production'
AND created_date >= DATEADD(week, -2, CURRENT_DATE());
Pre-Calculate and Store Embeddings
If you’re doing semantic search or similarity matching, generating embeddings on the fly is expensive. Instead, calculate them once and store them:
-- Step 1: Create a table with pre-calculated embeddings
CREATE TABLE product_descriptions_with_embeddings AS
SELECT
product_id,
description,
CORTEX_EMBED_TEXT('e5-base-v2', description) AS description_embedding
FROM
products
WHERE
description IS NOT NULL;
-- Step 2: Use the pre-calculated embeddings for fast similarity search
SELECT
product_id,
description,
VECTOR_COSINE_SIMILARITY(
description_embedding,
CORTEX_EMBED_TEXT('e5-base-v2', 'wireless headphones')
) AS similarity_score
FROM
product_descriptions_with_embeddings
ORDER BY
similarity_score DESC
LIMIT 20;
This approach transforms an expensive embedding calculation into a fast lookup. The difference can be dramatic—queries that took minutes might now run in seconds.
Optimize Your Table Structure
Set up clustering keys that align with your most common query patterns:
-- Cluster by fields you frequently filter on
ALTER TABLE customer_documents
CLUSTER BY (document_type, created_month);
-- Now queries filtering by these fields run much faster
SELECT
document_id,
CORTEX_ANALYST(document_content, 'Extract key dates') AS key_dates
FROM
customer_documents
WHERE
document_type = 'Contract'
AND created_month >= '2025-01-01';
Size Your Warehouse Appropriately
AI workloads need more compute power than traditional SQL queries. Don’t be afraid to scale up:
-- Configure a dedicated warehouse for AI workloads
CREATE WAREHOUSE AI_ANALYSIS_WH WITH
WAREHOUSE_SIZE = 'LARGE'
AUTO_SUSPEND = 120
AUTO_RESUME = TRUE
INITIALLY_SUSPENDED = TRUE
STATEMENT_TIMEOUT_IN_SECONDS = 7200;
-- Use it for your Cortex queries
USE WAREHOUSE AI_ANALYSIS_WH;
Start with a LARGE warehouse for AI tasks. You can always scale down if it’s overkill, but starting too small will frustrate users and mask optimization opportunities.
Common Mistakes to Avoid
#1: Using AI functions inside loops or repeated operations
-- ❌ BAD: Calling AI function for each row unnecessarily
SELECT
product_id,
(SELECT CORTEX_ANALYST(description, 'Extract features')
FROM products p2
WHERE p2.product_id = p1.product_id) AS features
FROM
products p1;
Mistake #2: Not checking for NULL values
-- ❌ BAD: Wasting AI calls on empty data
SELECT
CORTEX_ANALYST(user_comment, 'Analyze sentiment')
FROM
feedback;
-- ✅ GOOD: Filter out NULLs first
SELECT
CORTEX_ANALYST(user_comment, 'Analyze sentiment')
FROM
feedback
WHERE
user_comment IS NOT NULL
AND LENGTH(user_comment) > 10;
Mistake #3: Ignoring warehouse resource monitors
Set up resource monitors to prevent runaway queries from draining your credits:
CREATE RESOURCE MONITOR ai_workload_monitor WITH
CREDIT_QUOTA = 1000
TRIGGERS
ON 75 PERCENT DO NOTIFY
ON 90 PERCENT DO SUSPEND
ON 100 PERCENT DO SUSPEND_IMMEDIATE;
ALTER WAREHOUSE AI_ANALYSIS_WH
SET RESOURCE_MONITOR = ai_workload_monitor;
Monitoring and Maintaining Performance
Don’t set it and forget it. Regularly review:
Query execution times – Are they trending up?
Credit consumption – Any unexpected spikes?
Warehouse queuing – Are queries waiting too long to start?
Use Snowflake’s Query History to track these metrics:
-- Find your most expensive AISQL queries
SELECT
query_text,
execution_time,
credits_used_cloud_services,
warehouse_name
FROM
SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
WHERE
query_text ILIKE '%CORTEX%'
AND start_time >= DATEADD(day, -7, CURRENT_DATE())
ORDER BY
execution_time DESC
LIMIT 20;
Putting It All Together: A Real-World Example
Let’s say you need to analyze customer support tickets to identify trends. Here’s how to do it efficiently:
-- Create a materialized view for frequently accessed metadata
CREATE MATERIALIZED VIEW support_ticket_summary AS
SELECT
ticket_id,
customer_id,
category,
priority,
created_date,
status
FROM
support_tickets
WHERE
created_date >= DATEADD(year, -1, CURRENT_DATE());
-- Now run your AI analysis efficiently
SELECT
s.ticket_id,
s.category,
s.priority,
CORTEX_ANALYST(t.ticket_description,
'Extract: 1) main issue, 2) customer sentiment, 3) urgency level'
) AS ai_analysis
FROM
support_ticket_summary s
INNER JOIN
support_ticket_text t ON s.ticket_id = t.ticket_id
WHERE
s.created_date >= DATEADD(week, -1, CURRENT_DATE())
AND s.category = 'Technical'
AND s.priority IN ('High', 'Critical')
AND s.status = 'Open'
AND t.ticket_description IS NOT NULL
LIMIT 1000;
This query:
Uses a materialized view for fast metadata access
Filters early on date, category, priority, and status
Checks for NULL values before calling the AI function
Limits results to a reasonable number
Key Takeaways
Optimizing Cortex AISQL queries isn’t rocket science, but it does require discipline:
Filter aggressively before calling AI functions
Pre-calculate embeddings for repeated use
Use appropriate warehouse sizes for AI workloads
Set up clustering keys aligned with your query patterns
Monitor performance regularly and adjust as needed
Exclude test data explicitly from production queries
The combination of traditional Snowflake optimization techniques with AI-specific strategies will give you fast queries and manageable costs. Start with these fundamentals, measure the impact, and iterate from there.
I’ve spent the last few days working with Snowflake Intelligence, and I want to share what actually works—not just the marketing pitch. If you’re tired of being the bottleneck for every data request in your organization, this might be exactly what you need.
Why This Actually Matters
Here’s the thing: most companies still treat data like it’s 2010. Your sales team wants to know last quarter’s performance by region? They file a ticket. Marketing needs customer segmentation data? Another ticket. By the time your data team gets through the backlog, the insights are already stale.
I was skeptical at first. Natural language querying isn’t new—we’ve all seen chatbots that completely miss the point. But the difference here is the architecture. The system uses AI agents that understand your specific business context, not generic SQL generation.
The Three Building Blocks
Understanding how this works helps you use it better. There are three key pieces:
Natural Language Processing (NLP) translates what you’re asking into something the system can work with. It’s not just keyword matching—it understands context. When someone asks about “Q4 performance,” it knows whether they mean fiscal or calendar year based on your company’s setup.
AI Agents are where the magic happens. Think of them as specialized assistants. Your finance agent knows the difference between GAAP revenue and recognized revenue. Your supply chain agent understands lead times and reorder points. You configure these agents to match how your business actually works.
Semantic Views sit between the agents and your raw data. They’re essentially curated views of your data that make sense to humans and AI alike. Instead of exposing 47 columns from your sales table, you create a semantic view with the 12 that actually matter for reporting.
Setting This Up (The Real Way)
Let me walk you through a realistic implementation. I’m using Snowflake’s sample data so you can follow along.
Step 1: Create Your Semantic View
Start simple. Here’s a semantic view built on Snowflake’s TPCH sample dataset:
-- First, get access to the sample data
USE DATABASE SNOWFLAKE_SAMPLE_DATA;
USE SCHEMA TPCH_SF1;
-- Create your own database for semantic views
CREATE DATABASE IF NOT EXISTS MY_INTELLIGENCE_DB;
CREATE SCHEMA IF NOT EXISTS MY_INTELLIGENCE_DB.SEMANTIC_LAYER;
-- Build a semantic view for customer orders
CREATE OR REPLACE VIEW MY_INTELLIGENCE_DB.SEMANTIC_LAYER.CUSTOMER_ORDERS AS
SELECT
c.C_CUSTKEY as customer_id,
c.C_NAME as customer_name,
c.C_MKTSEGMENT as market_segment,
n.N_NAME as country,
o.O_ORDERKEY as order_id,
o.O_ORDERDATE as order_date,
o.O_TOTALPRICE as order_total,
o.O_ORDERSTATUS as order_status
FROM SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.CUSTOMER c
JOIN SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.ORDERS o
ON c.C_CUSTKEY = o.O_CUSTKEY
JOIN SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.NATION n
ON c.C_NATIONKEY = n.N_NATIONKEY
WHERE o.O_ORDERDATE >= '1995-01-01';
This view hides the complexity of joins and uses clear, business-friendly column names. Your AI agent will query this, not the raw tables.
For views that get hit frequently,regular view makes a huge difference:
CREATE OR REPLACE VIEW MY_INTELLIGENCE_DB.SEMANTIC_LAYER.DAILY_SALES_SUMMARY AS
SELECT
DATE_TRUNC('day', order_date) AS sale_date,
market_segment,
country,
COUNT(DISTINCT order_id) AS order_count,
SUM(order_total) AS total_revenue,
AVG(order_total) AS avg_order_value
FROM MY_INTELLIGENCE_DB.SEMANTIC_LAYER.CUSTOMER_ORDERS
GROUP BY 1, 2, 3;
Run this and check the results:
SELECT *
FROM MY_INTELLIGENCE_DB.SEMANTIC_LAYER.DAILY_SALES_SUMMARY
WHERE sale_date >= '1998-01-01'
ORDER BY total_revenue DESC
LIMIT 20;
When you set up an AI agent in Snowflake Intelligence, you give it specific instructions. Here’s what mine looks like for a sales agent:
Agent Name: Sales Analytics Agent
Instructions:
You have access to customer order data through the SEMANTIC_LAYER.CUSTOMER_ORDERS view.
When users ask about:
- "Revenue" or "sales" - use the order_total column
- "Customers" - always include customer_name and market_segment
- Time periods - default to the last 90 days unless specified
- "Top customers" - rank by total order_total, limit to 10 unless specified
Always format currency as USD with 2 decimal places.
If a query would scan more than 1 million rows, ask the user to narrow the date range.
What Actually Breaks (And How to Fix It)
I’ve seen these issues kill projects:
Vague Questions = Expensive Queries When someone asks “show me everything about customers,” the system might scan your entire data warehouse. Train your users to be specific: “Show me customers in the AUTOMOBILE segment who ordered more than $100k in 1998.”
Semantic Views That Drift Your source tables change. Columns get renamed. New status codes appear. Your semantic views break, and suddenly the AI returns garbage. Set up a weekly validation job:
-- Quick health check for your semantic views
SELECT
TABLE_SCHEMA,
TABLE_NAME,
LAST_ALTERED,
ROW_COUNT
FROM MY_INTELLIGENCE_DB.INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'SEMANTIC_LAYER'
AND TABLE_TYPE = 'VIEW'
ORDER BY LAST_ALTERED DESC;
Runaway Costs One enthusiastic user can rack up hundreds in compute charges with poorly scoped questions. Use resource monitors:
-- Create a resource monitor for your Intelligence workload
CREATE RESOURCE MONITOR INTELLIGENCE_BUDGET
WITH CREDIT_QUOTA = 100
FREQUENCY = MONTHLY
START_TIMESTAMP = IMMEDIATELY
TRIGGERS
ON 75 PERCENT DO NOTIFY
ON 100 PERCENT DO SUSPEND;
-- Assign it to your warehouse
ALTER WAREHOUSE INTELLIGENCE_WH SET RESOURCE_MONITOR = INTELLIGENCE_BUDGET;
Performance Tips That Actually Work
Clustering Keys If your semantic views filter by date constantly, cluster on that date:
-- Add clustering to improve query performance
ALTER TABLE MY_INTELLIGENCE_DB.SEMANTIC_LAYER.DAILY_SALES_SUMMARY
CLUSTER BY (sale_date);
Query Tagging for Cost Tracking Tag queries so you can see exactly what each agent costs:
-- At the start of an agent session
ALTER SESSION SET QUERY_TAG = 'sales_agent_q4_analysis';
-- Your queries here
-- View tagged query costs later
SELECT
QUERY_TAG,
COUNT(*) as query_count,
SUM(TOTAL_ELAPSED_TIME)/1000 as total_seconds,
SUM(CREDITS_USED_CLOUD_SERVICES) as credits_used
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
WHERE QUERY_TAG IS NOT NULL
AND START_TIME >= DATEADD(day, -7, CURRENT_TIMESTAMP())
GROUP BY 1
ORDER BY credits_used DESC;
Test Queries to Validate Your Setup
Run these to make sure everything works:
-- Test 1: Basic aggregation
SELECT
market_segment,
COUNT(DISTINCT customer_id) as customer_count,
SUM(order_total) as total_revenue
FROM MY_INTELLIGENCE_DB.SEMANTIC_LAYER.CUSTOMER_ORDERS
WHERE order_date BETWEEN '1998-01-01' AND '1998-12-31'
GROUP BY 1
ORDER BY 2 DESC;
-- Test 2: Top customers
SELECT
customer_name,
country,
COUNT(order_id) as order_count,
SUM(order_total) as lifetime_value
FROM MY_INTELLIGENCE_DB.SEMANTIC_LAYER.CUSTOMER_ORDERS
GROUP BY 1, 2
HAVING SUM(order_total) > 500000
ORDER BY 4 DESC
LIMIT 10;
-- Test 3: Time series
SELECT
DATE_TRUNC('month', order_date) as month,
market_segment,
SUM(order_total) as monthly_revenue
FROM MY_INTELLIGENCE_DB.SEMANTIC_LAYER.CUSTOMER_ORDERS
WHERE order_date >= '1997-01-01'
GROUP BY 1, 2
ORDER BY 1, 3 DESC;
The Bottom Line
Snowflake Intelligence isn’t magic, but it does work when you set it up right. Focus on clean semantic views, specific agent instructions, and cost controls from day one.
Start with one use case—maybe sales reporting or customer analytics. Get that working well before expanding. And involve your actual end users in testing. They’ll phrase questions in ways you never anticipated, and that feedback is gold.
The goal isn’t to eliminate your data team. It’s to free them from repetitive requests so they can focus on complex analysis and building better data products.
Run dbt Core Directly in Snowflake Without Infrastructure
Snowflake native dbt integration announced at Summit 2025 eliminates the need for separate containers or VMs to run dbt Core. Data teams can now execute dbt transformations directly within Snowflake, with built-in lineage tracking, logging, and job scheduling through Snowsight. This breakthrough simplifies data pipeline architecture and reduces operational overhead significantly.
For years, running dbt meant managing separate infrastructure—deploying containers, configuring CI/CD pipelines, and maintaining compute resources outside your data warehouse. The Snowflake native dbt integration changes everything by bringing dbt Core execution inside Snowflake’s secure environment.
What Is Snowflake Native dbt Integration?
Snowflake native dbt integration allows data teams to run dbt Core transformations directly within Snowflake without external orchestration tools. The integration provides a managed environment where dbt projects execute using Snowflake’s compute resources, with full visibility through Snowsight.
Key Benefits
The native integration delivers:
Zero infrastructure management – No containers, VMs, or separate compute
Built-in lineage tracking – Automatic data flow visualization
Native job scheduling – Schedule dbt runs using Snowflake Tasks
Integrated logging – Debug pipelines directly in Snowsight
No licensing costs – dbt Core runs free within Snowflake
Organizations using Snowflake Dynamic Tables can now complement those automated refreshes with sophisticated dbt transformations, creating comprehensive data pipeline solutions entirely within the Snowflake ecosystem.
How Native dbt Integration Works
Execution Architecture
When you deploy a dbt project to Snowflake native dbt integration, the platform:
Stores project files in Snowflake’s internal stage
Compiles dbt models using Snowflake’s compute
Executes SQL transformations against your data
Captures lineage automatically for all dependencies
Logs results to Snowsight for debugging
Similar to how real-time data pipeline architectures require proper orchestration, dbt projects benefit from Snowflake’s native task scheduling and dependency management.
-- Create a dbt job in Snowflake
CREATE OR REPLACE TASK run_dbt_models
WAREHOUSE = transform_wh
SCHEDULE = 'USING CRON 0 2 * * * America/Los_Angeles'
AS
CALL DBT.RUN_DBT_PROJECT('my_analytics_project');
-- Enable the task
ALTER TASK run_dbt_models RESUME;
Setting Up Native dbt Integration
Prerequisites
Before deploying dbt projects natively:
Snowflake account with ACCOUNTADMIN or appropriate role
Existing dbt project with proper structure
Git repository containing dbt code (optional but recommended)
Step-by-Step Implementation
1: Prepare Your dbt Project
Ensure your project follows standard dbt structure:
Improved security (execution stays within Snowflake perimeter)
Better integration with Snowflake features
Cost Considerations
Compute Consumption
Snowflake native dbt integration uses standard warehouse compute:
Charged per second of active execution
Auto-suspend reduces idle costs
Share warehouses across multiple jobs for efficiency
Comparison with External Solutions
Aspect
External dbt
Native dbt Integration
Infrastructure
EC2/VM costs
Only Snowflake compute
Maintenance
Manual updates
Managed by Snowflake
Licensing
dbt Cloud fees
Free (dbt Core)
Integration
External APIs
Native Snowflake
Organizations using automation strategies across their data stack can consolidate tools and reduce total cost of ownership.
Real-World Use Cases
Use Case 1: Financial Services Reporting
A fintech company moved 200+ dbt models from AWS containers to Snowflake native dbt integration, achieving:
60% reduction in infrastructure costs
40% faster transformation execution
Zero downtime migrations using blue-green deployment
Use Case 2: E-commerce Analytics
An online retailer consolidated their data pipeline by combining native dbt with Dynamic Tables:
dbt handles complex business logic transformations
Dynamic Tables maintain real-time aggregations
Both execute entirely within Snowflake
Use Case 3: Healthcare Data Warehousing
A healthcare provider simplified compliance by keeping all transformations inside Snowflake’s secure perimeter:
HIPAA compliance maintained without data egress
Audit logs automatically captured
PHI never leaves Snowflake environment
Advanced Features
Git Integration
Connect dbt projects directly to repositories:
CREATE GIT REPOSITORY dbt_repo
ORIGIN = 'https://github.com/myorg/dbt-project.git'
API_INTEGRATION = github_integration;
-- Run dbt from specific branch
CALL run_dbt_from_git('dbt_repo', 'production');
Testing and Validation
Native integration supports full dbt testing:
Schema tests validate data structure
Data tests check business rules
Custom tests enforce specific requirements
Multi-Environment Support
Manage dev, staging, and production through Snowflake databases:
sql
-- Development environment
USE DATABASE dev_analytics;
CALL run_dbt('dev_project');
-- Production environment
USE DATABASE prod_analytics;
CALL run_dbt('prod_project');
Troubleshooting Common Issues
Issue 1: Slow Model Compilation
Solution: Pre-compile dbt projects and cache results:
sql
-- Cache compiled SQL for faster execution
ALTER TASK dbt_refresh SET
SUSPEND_TASK_AFTER_NUM_FAILURES = 3;
Issue 2: Dependency Conflicts
Solution: Use Snowflake’s Python environment isolation:
Snowflake plans to enhance native dbt integration with:
Visual dbt model builder for low-code transformations
Automatic optimization suggestions using AI
Enhanced collaboration features for team workflows
Deeper integration with Snowflake’s AI capabilities
Organizations exploring autonomous AI agents in other platforms will find similar intelligence coming to dbt optimization.
Conclusion: Simplified Data Transformation
Snowflake native dbt integration represents a significant evolution in data transformation architecture. By eliminating external infrastructure and bringing dbt Core inside Snowflake, data teams achieve simplified operations, reduced costs, and enhanced security.
The integration is production-ready today, with thousands of organizations already migrating their dbt workloads. Teams should evaluate their current dbt architecture and plan migrations to take advantage of this native capability.
Start with non-critical projects, validate performance, and progressively move production workloads. The combination of zero infrastructure overhead, built-in observability, and seamless Snowflake integration makes native dbt integration the future of transformation pipelines.
The era of AI in CRM is here, and its name is Salesforce Copilot. It’s more than just a chatbot that answers questions; in fact,it’s an intelligent assistant designed to take action. But its true power is unlocked when you teach it to perform custom tasks specific to your business.
Ultimately, this guide will walk you through the entire process of building your very first custom Salesforce Copilot action. We’ll create a practical tool that allows a user to summarize a complex support Case and post that summary to Chatter with a single command.
Understanding the Core Concepts of Salesforce Copilot
First, What is a Copilot Action?
A Copilot Action is, in essence, a custom skill you give to your Salesforce Copilot. It connects a user’s natural language request to a specific automation built on the Salesforce platform, usually using a Salesforce Flow.
To see how this works, think of the following sequence:
1. To begin, a user gives a command like, “Summarize this case for me and share an update.”
2. Salesforce Copilot then immediately recognizes the user’s intent.
3. This recognition subsequently triggers the specific Copilot Action you built.
4. Finally, the Flow connected to that action runs all the necessary logic, such as calling Apex, getting the summary, and posting the result to Chatter.
Our Project Goal: The Automated Case Summary Action
Our goal is to build a Salesforce Copilot action that can be triggered from a Case record page. To achieve this, our action will perform three key steps:
1. It will read the details of the current Case.
2. Next, the action will use AI to generate a concise summary.
3. Lastly, it will post that summary to the Case’s Chatter feed for team visibility.
Although you can do a lot in Flow, complex logic is often best handled in Apex. Therefore, we’ll start by creating a simple Apex method that takes a Case ID and returns its Subject and Description, which the Flow can then call.
The CaseSummarizer Apex Class
// Apex Class: CaseSummarizer
public with sharing class CaseSummarizer {
// Invocable Method allows this to be called from a Flow
@InvocableMethod(label='Get Case Details for Summary' description='Returns the subject and description of a given Case ID.')
public static List<CaseDetails> getCaseDetails(List<Id> caseIds) {
Id caseId = caseIds[0]; // We only expect one ID
Case thisCase = [SELECT Subject, Description FROM Case WHERE Id = :caseId LIMIT 1];
// Prepare the output for the Flow
CaseDetails details = new CaseDetails();
details.caseSubject = thisCase.Subject;
details.caseDescription = thisCase.Description;
return new List<CaseDetails>{ details };
}
// A wrapper class to hold the output variables for the Flow
public class CaseDetails {
@InvocableVariable(label='Case Subject' description='The subject of the case')
public String caseSubject;
@InvocableVariable(label='Case Description' description='The description of the case')
public String caseDescription;
}
}
After creating the Apex logic, we’ll build an Autolaunched Flow that orchestrates the entire process from start to finish.
Flow Configuration
Go to Setup > Flows and create a new Autolaunched Flow.
For this purpose, define an input variable: recordId (Text, Available for Input). This, in turn, will receive the Case ID.
Add an Action element: Call the getCaseDetails Apex method we just created, passing the recordId as the caseIds input.
Store the outputs: Store the caseSubject and caseDescription in new variables within the Flow.
Add a “Post to Chatter” Action:
Message: This is where we bring in AI. We’ll use a Prompt Template here soon, but for now, you can put placeholder text like {!caseSubject}.
Target Name or ID: Set this to {!recordId} to post on the current Case record.
Save and activate the Flow (e.g., as “Post Case Summary to Chatter”).
Step 3: Teaching the AI with a Prompt Template
Furthermore, this step tells the LLM how to generate the summary.
Prompt Builder Setup
Go to Setup > Prompt Builder.
Create a new Prompt Template.
For the prompt, write instructions for the AI. Specifically, use merge fields to bring in your Flow variables.
You are a helpful support team assistant.
Based on the following Case details, write a concise, bulleted summary to be shared with the internal team on Chatter.
Case Subject: {!caseSubject}
Case Description: {!caseDescription}
Summary:
4. Save the prompt (e.g., “Case Summary Prompt”).
Step 4: Connecting Everything with a Copilot Action
Now, this is the crucial step where we tie everything together.
Action Creation
Go to Setup > Copilot Actions.
Click New Action.
Select Salesforce Flow as the action type and choose the Flow you created (“Post Case Summary to Chatter”).
Instead of using a plain text value for the “Message” in your Post to Chatter action, select your “Case Summary Prompt” template.
Follow the prompts to define the language and behavior. For instance, for the prompt, you can use something like: “Summarize the current case and post it to Chatter.”
Activate the Action.
Step 5: Putting Your Copilot Action to the Test
Finally, navigate to any Case record. Open the Salesforce Copilot panel and type your command: “Summarize this case for me.”
Once you issue the command, the magic happens. Specifically, the Copilot will understand your intent, trigger the action, run the Flow, call the Apex, generate the summary using the Prompt Template, and post the final result directly to the Chatter feed on that Case.
Conclusion: The Future of CRM is Action-Oriented
In conclusion, you have successfully built a custom skill for your Salesforce Copilot. This represents a monumental shift from passive data entry to proactive, AI-driven automation. Indeed, by combining the power of Flow, Apex, and the Prompt Builder, you can create sophisticated agents that understand your business and work alongside your team to drive incredible efficiency.
The age of AI chatbots is evolving into the era of AI doers. Instead of just answering questions, modern AI can now execute tasks, interact with systems, and solve multi-step problems. At the forefront of this revolution on the Databricks platform is the Mosaic AI Agent Framework.
This guide will walk you through building your first Databricks AI Agent—a powerful assistant that can understand natural language, inspect your data, and execute Spark SQL queries for you, all powered by the latest GPT-5 model.
What is a Databricks AI Agent?
A Databricks AI Agent is an autonomous system you create using the Mosaic AI Agent Framework. It leverages a powerful Large Language Model (LLM) as its “brain” to reason and make decisions. You equip this brain with a set of “tools” (custom Python functions) that allow it to interact with the Databricks environment.
The agent works in a loop:
Reason: Based on your goal, the LLM decides which tool is needed.
Act: The agent executes the chosen Python function.
Observe: It analyzes the result of that function.
Repeat: It continues this process until it has achieved the final objective.
Our Project: The “Data Analyst” Agent
We will build an agent whose goal is to answer data questions from a non-technical user. To do this, it will need two primary tools:
A tool to get the schema of a table (get_table_schema).
A tool to execute a Spark SQL query and return the result (run_spark_sql).
Let’s start building in a Databricks Notebook.
Step 1: Setting Up Your Tools (Python Functions)
An agent’s capabilities are defined by its tools. In Databricks, these are simply Python functions. Let’s define the two functions our agent needs to do its job.
# Tool #1: A function to get the DDL schema of a table
def get_table_schema(table_name: str) -> str:
"""
Returns the DDL schema for a given Spark table name.
This helps the agent understand the table structure before writing a query.
"""
try:
ddl_result = spark.sql(f"SHOW CREATE TABLE {table_name}").first()[0]
return ddl_result
except Exception as e:
return f"Error: Could not retrieve schema for table {table_name}. Reason: {e}"
# Tool #2: A function to execute a Spark SQL query and return the result as a string
def run_spark_sql(query: str) -> str:
"""
Executes a Spark SQL query and returns the result.
This is the agent's primary tool for interacting with data.
"""
try:
result_df = spark.sql(query)
# Convert the result to a string format for the LLM to understand
return result_df.toPandas().to_string()
except Exception as e:
return f"Error: Failed to execute query. Reason: {e}"
Step 2: Assembling Your Databricks AI Agent
With our tools defined, we can now use the Mosaic AI Agent Framework to create our agent. This involves importing the Agent class, providing our tools, and selecting an LLM from Model Serving.
For this example, we’ll use the newly available openai/gpt-5model endpoint.
from databricks_agents import Agent
# Define the instructions for the agent's "brain"
# This prompt guides the agent on how to behave and use its tools
agent_instructions = """
You are a world-class data analyst. Your goal is to answer user questions by querying data in Spark.
Here is your plan:
1. First, you MUST use the `get_table_schema` tool to understand the columns of the table the user mentions. Do not guess column names.
2. After you have the schema, formulate a Spark SQL query to answer the user's question.
3. Execute the query using the `run_spark_sql` tool.
4. Finally, analyze the result from the query and provide a clear, natural language answer to the user. Do not just return the raw data table. Summarize the findings.
"""
# Create the agent instance
data_analyst_agent = Agent(
model="endpoints:/openai-gpt-5", # Using a Databricks Model Serving endpoint for GPT-5
tools=[get_table_schema, run_spark_sql],
instructions=agent_instructions
)
Step 3: Interacting with Your Agent
Your Databricks AI Agent is now ready. You can interact with it using the .run() method, providing your question as the input.
Let’s use the common samples.nyctaxi.trips table.
# Let's ask our new agent a question
user_question = "What were the average trip distances for trips paid with cash vs. credit card? Use the samples.nyctaxi.trips table."
# Run the agent and get the final answer
final_answer = data_analyst_agent.run(user_question)
print(final_answer)
What Happens Behind the Scenes:
Reason: The agent reads your prompt. It knows it needs to find average trip distances from the samples.nyctaxi.trips table but first needs the schema. It decides to use the get_table_schema tool.
Act: It calls get_table_schema('samples.nyctaxi.trips').
Observe: It receives the table schema and sees columns like trip_distance and payment_type.
Reason: Now it has the schema. It formulates a Spark SQL query like SELECT payment_type, AVG(trip_distance) FROM samples.nyctaxi.trips GROUP BY payment_type. It decides to use the run_spark_sql tool.
Act: It calls run_spark_sql(...) with the generated query.
Observe: It receives the query result as a string (e.g., a small table showing payment types and average distances).
Reason: It has the data. Its final instruction is to summarize the findings.
Final Answer: It generates and returns a human-readable response like: “Based on the data, the average trip distance for trips paid with a credit card was 2.95 miles, while cash-paid trips had an average distance of 2.78 miles.”
Conclusion: Your Gateway to Autonomous Data Tasks
Congratulations! You’ve just built a functional Databricks AI Agent. This simple text-to-SQL prototype is just the beginning. By creating more sophisticated tools, you can build agents that perform data quality checks, manage ETL pipelines, or even automate MLOps workflows, all through natural language commands on the Databricks platform.
Autonomous AI Agents That Transform Customer Engagement
Salesforce Agentforce represents the most significant CRM innovation of 2025, marking the shift from generative AI to truly autonomous agents. Unveiled at Dreamforce 2024, Salesforce Agentforce enables businesses to deploy AI agents that work independently, handling customer inquiries, resolving support tickets, and qualifying leads without human intervention. This comprehensive guide explores how enterprises leverage these intelligent agents to revolutionize customer relationships and operational efficiency.
Traditional chatbots require constant supervision and predefined scripts. Salesforce Agentforce changes everything by introducing agents that reason, plan, and execute tasks autonomously across your entire CRM ecosystem.
What Is Salesforce Agentforce?
Salesforce Agentforce is an advanced AI platform that creates autonomous agents capable of performing complex business tasks across sales, service, marketing, and commerce. Unlike traditional automation tools, these agents understand context, make decisions, and take actions based on your company’s data and business rules.
Core Capabilities
The platform enables agents to:
Resolve customer inquiries autonomously across multiple channels
Qualify and prioritize leads using predictive analytics
Generate personalized responses based on customer history
Execute multi-step workflows without human intervention
Learn from interactions to improve performance over time
Real-world impact: Companies using Salesforce Agentforce report 58% success rates on simple tasks and 35% on complex multi-step processes, significantly reducing response times and operational costs.
Key Features of Agentforce AI
xGen Sales Model
The xGen Sales AI model enhances predictive analytics for sales teams. It accurately forecasts revenue, prioritizes high-value leads, and provides intelligent recommendations that help close deals faster. Sales representatives receive real-time guidance on which prospects to contact and what messaging will resonate.
xLAM Service Model
Designed for complex service workflows, xLAM automates ticket resolution, manages customer inquiries, and predicts service disruptions before they escalate. The model analyzes historical patterns to prevent issues proactively rather than reactively addressing complaints.
Agent Builder
The low-code Agent Builder empowers business users to create custom agents without extensive technical knowledge. Using natural language descriptions, teams can define agent behaviors, set guardrails, and deploy solutions in days rather than months.
How Agentforce Works with Data Cloud
Salesforce Agentforce leverages Data Cloud to access unified customer data across all touchpoints. This integration is critical because AI agents need comprehensive context to make informed decisions.
Unified Data Access
Agents retrieve information from:
Customer relationship history
Purchase patterns and preferences
Support interaction logs
Marketing engagement metrics
Real-time behavioral data
Retrieval Augmented Generation (RAG)
The platform uses RAG technology to extract relevant information from multiple internal systems. This ensures agents provide accurate, contextual responses grounded in your organization’s actual data rather than generic outputs.
Why this matters: 80% of enterprise data is unstructured. Data Cloud harmonizes this information, making it accessible to autonomous agents for better decision-making.
Real-World Use Cases
Use Case 1: Autonomous Customer Service
E-commerce companies deploy service agents that handle common inquiries 24/7. When customers ask about order status, return policies, or product recommendations, agents provide instant, accurate responses by accessing order management systems and customer profiles.
Business impact: Reduces support ticket volume by 40-60% while maintaining customer satisfaction scores.
Use Case 2: Intelligent Lead Qualification
Sales agents automatically engage with website visitors, qualify leads based on predefined criteria, and route high-value prospects to human representatives. The agent asks qualifying questions, scores responses, and updates CRM records in real-time.
Business impact: Sales teams focus on ready-to-buy prospects, increasing conversion rates by 25-35%.
Use Case 3: Proactive Service Management
Service agents monitor system health metrics and customer usage patterns. When potential issues are detected, agents automatically create support tickets, notify relevant teams, and even initiate preventive maintenance workflows.
Business impact: Prevents service disruptions, improving customer retention and reducing emergency support costs.
Getting Started with Agentforce
Step 1: Define Your Use Case
Start with a specific, high-volume process that’s currently manual. Common starting points include:
Customer inquiry responses
Lead qualification workflows
Order status updates
Appointment scheduling
Step 2: Prepare Your Data
Ensure Data Cloud has access to relevant information sources:
CRM data (accounts, contacts, opportunities)
Service Cloud data (cases, knowledge articles)
Commerce Cloud data (orders, products, inventory)
External systems (ERP, marketing automation)
Step 3: Build and Train Your Agent
Use Agent Builder to:
Describe agent purpose and scope
Define decision-making rules
Set guardrails and escalation paths
Test with sample scenarios
Deploy to production with monitoring
Step 4: Monitor and Optimize
Track agent performance using built-in analytics:
Task completion rates
Customer satisfaction scores
Escalation frequency
Resolution time metrics
Continuously refine agent instructions based on performance data and user feedback.
Best Practices for Implementation
Start Small and Scale
Begin with a single, well-defined use case. Prove value before expanding to additional processes. This approach builds organizational confidence and allows teams to learn agent management incrementally.
Establish Clear Guardrails
Define when agents should escalate to humans:
Complex negotiations requiring judgment
Sensitive customer situations
Requests outside defined scope
Regulatory compliance scenarios
Maintain Human Oversight
While agents work autonomously, human supervision remains important during early deployments. Review agent decisions, refine instructions, and ensure quality standards are maintained.
Invest in Data Quality
Agent performance depends directly on data accuracy and completeness. Prioritize data cleansing, deduplication, and enrichment initiatives before deploying autonomous agents.
Pricing and Licensing
Salesforce Agentforce pricing follows a conversation-based model:
Charged per customer interaction
Volume discounts available
Enterprise and unlimited editions include base conversations
Additional conversation packs can be purchased
Organizations should evaluate expected interaction volumes and compare costs against manual handling expenses to calculate ROI.
Integration with Existing Salesforce Tools
Einstein AI Integration
Agentforce builds on Einstein AI capabilities, leveraging existing predictive models and analytics. Organizations with Einstein implementations can extend those investments into autonomous agent scenarios.
Slack Integration
Agents operate within Slack channels, enabling teams to monitor agent activities, intervene when necessary, and maintain visibility into customer interactions directly in collaboration tools.
MuleSoft Connectivity
For enterprises with complex system landscapes, MuleSoft provides pre-built connectors that allow agents to interact with external applications, databases, and legacy systems seamlessly.
Future of Autonomous Agents
Multi-Agent Collaboration
The 2025 roadmap includes enhanced multi-agent orchestration where specialized agents collaborate on complex tasks. For example, a sales agent might work with a finance agent to create custom pricing proposals automatically.
Industry-Specific Agents
Salesforce is developing pre-configured agents for specific industries:
Financial Services: Compliance checking and risk assessment
Healthcare: Patient engagement and appointment optimization
Retail: Inventory management and personalized shopping assistance
Manufacturing: Supply chain coordination and quality control
Continuous Learning Capabilities
Future releases will enable agents to learn from every interaction, automatically improving responses and decision-making without manual retraining.
Common Challenges and Solutions
Challenge 1: Trust and Adoption
Solution: Start with low-risk use cases, maintain transparency about agent involvement, and demonstrate value through metrics before expanding scope.
Challenge 2: Data Silos
Solution: Implement Data Cloud to unify information across systems, ensuring agents have comprehensive context for decision-making.
Challenge 3: Over-Automation
Solution: Maintain balanced automation by defining clear escalation paths and preserving human touchpoints for high-value or sensitive interactions.
Conclusion: Embracing Autonomous AI
Salesforce Agentforce represents a fundamental shift in how businesses automate customer engagement. By moving beyond simple chatbots to truly autonomous agents, organizations can scale personalized service while reducing operational costs and improving customer satisfaction.
Success requires thoughtful implementation—starting with well-defined use cases, ensuring data quality, and maintaining appropriate human oversight. Companies that adopt this technology strategically will gain significant competitive advantages in efficiency, responsiveness, and customer experience.
The future of CRM automation is autonomous, intelligent, and available now through Salesforce Agentforce. Organizations ready to embrace this transformation should begin planning their agent strategy today.
The first time I ran APPROX_TOP_K on a 2-billion-row events table, the query finished in under four seconds. The equivalent GROUP BY / ORDER BY / LIMIT had been timing out at 8 minutes. Same result. Ninety-eight percent the same result, to be precise — but for top-10 product rankings, that margin was irrelevant.
That’s the pattern with Snowflake’s lesser-known aggregation functions: they’re not novelties, and they’re not edge cases for data scientists. They solve real, recurring problems — frequency estimation at scale, distribution analysis, Jaccard similarity across user sets — that the standard SUM/COUNT/AVG toolkit either can’t handle or handles slowly and expensively.
This article covers six of them. Each section includes the practical use case, the SQL, the output, and a note on where I’ve seen it matter — or where I initially got it wrong.
1. APPROX_TOP_K — Frequency Estimation Without GROUP BY Pain
APPROX_TOP_K · APPROX_TOP_K_ARRAY
When you need the top-N most frequent values in a large dataset, a naive GROUP BY ... ORDER BY COUNT(*) DESC LIMIT N forces a full sort of the aggregated result. On hundreds of millions of rows that’s slow, memory-intensive, and often unnecessary — because you don’t need exact counts, you need the right top items.
APPROX_TOP_K uses a space-efficient sketch (the Space-Saving algorithm) to identify the most frequent values with high confidence and a fraction of the compute. The accuracy is configurable. The default counter size gives you the top-K items with at most a small error margin, and in practice I’ve never seen it return a wrong top-10 on real sales or event data.
One thing that tripped me up early: APPROX_TOP_K returns a single JSON object, while APPROX_TOP_K_ARRAY returns an array of JSON objects. If you’re piping the result into a downstream process or flattening it with LATERAL FLATTEN, use the array variant.
-- APPROX_TOP_K_ARRAY: top 3 most sold products
-- Works correctly on tables with billions of rows where GROUP BY times out
WITH top_k AS (
SELECT APPROX_TOP_K(product_group, 3) AS arr
FROM SNOWDQ_DB.PUBLIC.SALES_DATA
)
SELECT f.value[0]::STRING AS product_group,
f.value[1]::INT AS approx_count
FROM top_k, LATERAL FLATTEN(INPUT => top_k.arr) f;
The accuracy guarantee: APPROX_TOP_K can report a false positive — an item that’s not truly in the top K — only if its actual frequency is greater than n/(K + error_rate). In practice on typical sales or click data, the result is reliable for any K ≤ 100.
2. MODE — The Aggregation You’ve Been Writing Manually
MODE()
Before I knew MODE() existed, I was writing this every time I needed the most common value per group:
-- The manual way — verbose and slower
SELECT region, payment_method
FROM (
SELECT region, payment_method,
ROW_NUMBER() OVER (PARTITION BY region ORDER BY COUNT(*) DESC) AS rn
FROM transactions
GROUP BY region, payment_method
)
WHERE rn = 1;
That works, but it’s a subquery with a window function over an aggregation. MODE() does the same job in a single line:
WITH transactions(region, payment_method) AS (
SELECT * FROM VALUES ('North','Credit Card'),('North','Credit Card'),('North','Cash'),
('South','Cash'),('South','Cash'),('South','PayPal'),('East','PayPal'),('East','PayPal'),('East','Cash')
),
manual AS (
SELECT region, payment_method FROM (
SELECT region, payment_method,
ROW_NUMBER() OVER (PARTITION BY region ORDER BY COUNT(*) DESC) AS rn
FROM transactions GROUP BY region, payment_method) WHERE rn = 1
),
m AS (SELECT region, MODE(payment_method) AS most_common_payment FROM transactions GROUP BY region)
SELECT m.region, m.most_common_payment, manual.payment_method AS manual_result
FROM m JOIN manual USING (region) ORDER BY region
The one behaviour to be aware of: when there’s a tie — two values with equal frequency — MODE returns one of them non-deterministically. If tie-breaking matters to your use case, you’ll need to fall back to the ROW_NUMBER pattern with an explicit tiebreaker in the ORDER BY. But for dashboards, feature engineering, and exploratory analysis, MODE is far cleaner.
3. ARRAY_AGG and ARRAY_UNIQUE_AGG: Turning Rows into ArraysARRAY_AGG() · ARRAY_UNIQUE_AGG()
These are among the most useful Snowflake functions for denormalization work. Use them anywhere you need to roll related values up into a single array column. ARRAY_AGG keeps duplicates, and adding WITHIN GROUP (ORDER BY ...) controls the order of the array. ARRAY_UNIQUE_AGG returns distinct values only. Use ARRAY_AGG when you want the full purchase history, including repeat purchases. Use ARRAY_UNIQUE_AGG when you want the distinct set of products a customer has ever bought.
Coming from Spark or Hive?COLLECT_LIST maps to ARRAY_AGG, and COLLECT_SET maps to ARRAY_UNIQUE_AGG. Snowflake didn’t recognize the Spark names when I tested them.
SELECT
customer_id,
ARRAY_AGG(product_name) WITHIN GROUP (ORDER BY sale_date) AS full_purchase_history, -- includes dupes
ARRAY_UNIQUE_AGG(product_name) AS distinct_products_bought -- unique only
FROM sales_data
GROUP BY customer_id
ORDER BY customer_id;
Where I use this most: building ML feature tables where one column needs to represent a user’s interaction history as a variable-length sequence, and in customer 360 pipelines where you want to denormalize order line items back up to the customer level before writing to a serving layer.
4.SKEW and KURTOSIS: Distribution Diagnostics Without Leaving SQLSKEW() · KURTOSIS()
When data engineers want to know whether a numeric column is lopsided or full of outliers, most export it to Python or open a BI tool. Snowflake can answer that in SQL, so you can run distribution checks inside a dbt model or a data quality test without switching tools.
SKEW measures asymmetry. A positive value means a long right tail: a few very high values pull the mean up, which is typical of price data with luxury outliers. A negative value means the tail is on the left. A value near 0 means the distribution is roughly symmetric.
KURTOSIS measures how heavy the tails are. Snowflake returns excess kurtosis, so a normal distribution scores about 0. Tools that report raw kurtosis put a normal distribution at about 3. Positive values mean more extreme outliers than a normal distribution would produce.
Example Use Case: Analyzing Price Distribution
-- Distribution diagnostics on product prices
SELECT
COUNT(price_usd) AS n,
SKEW(price_usd) AS price_skewness,
KURTOSIS(price_usd) AS price_excess_kurtosis -- normal ≈ 0
FROM product_prices;
-- Sample output: seven prices between $10 and $13, plus one $100 outlier
-- N | PRICE_SKEWNESS | PRICE_EXCESS_KURTOSIS
-- --|----------------|----------------------
-- 7 | 2.64 | 6.98
The single $100 outlier creates strong positive skew and heavy tails. That’s a signal to investigate before you feed this column into an ML model.
A caveat about thresholds: kurtosis depends on row count. The same single outlier scored about 5 in a 5-row table, about 7 in a 7-row table, and about 10 in a 10-row table. A fixed rule like “investigate if kurtosis is above 10” can miss problems in small tables and raise too many alarms in large ones. Read the number next to COUNT(), and confirm what you see with MIN, MAX, or APPROX_PERCENTILE before deciding how to handle outliers.
Both functions ignore NULLs, and on very small inputs they can return NULL.
4. HLL_ACCUMULATE — Counting Distinct Values at Billion-Row Scale
HLL_ACCUMULATE() · HLL_COMBINE() · HLL_ESTIMATE()
This is the one I wish more people knew about. COUNT(DISTINCT ...) in Snowflake is exact but expensive — it requires keeping track of every unique value seen, which blows up memory on very large cardinalities. HyperLogLog (HLL_ACCUMULATE) gives you an approximate distinct count using a fixed-size sketch, regardless of how many unique values there are.
The practical accuracy is within 1–2% of the true count in almost all cases. On a table with 500 million rows and 40 million distinct user IDs, the HLL sketch runs in seconds; COUNT(DISTINCT user_id) takes minutes.
SELECT
DATE_TRUNC('day', event_time) AS event_day,
HLL_ESTIMATE(HLL_ACCUMULATE(user_id)) AS approx_distinct_users
FROM user_events
GROUP BY 1
ORDER BY 1 DESC;
-- The real power: combine sketches across time windows without re-scanning
-- "How many distinct users across the last 7 days combined?"
SELECT
HLL_ESTIMATE(
HLL_COMBINE(
ARRAY_AGG(daily_sketch)
)
) AS distinct_users_last_7d
FROM (
SELECT HLL_ACCUMULATE(user_id) AS daily_sketch
FROM user_events
WHERE event_time >= CURRENT_DATE - 7
GROUP BY DATE_TRUNC('day', event_time)
) daily_sketches;
The HLL_COMBINE step is what makes this especially powerful for incremental pipelines: you can pre-compute daily HLL sketches, store them, and merge them at query time without ever re-scanning the raw event table. This is the same technique web analytics platforms use to report 28-day unique visitor counts instantly.
MINHASH and APPROXIMATE_SIMILARITY — Jaccard Similarity Without Cross Joins
MINHASH() · APPROXIMATE_SIMILARITY()
The last one is genuinely rare to see in the wild, and it solves a problem that traditionally required either a self-join or a Python job to handle. MINHASH computes a probabilistic sketch of a set, and APPROXIMATE_SIMILARITY takes two of those sketches and returns the estimated Jaccard similarity — the fraction of items the two sets have in common.
Where this matters: customer segment overlap analysis, duplicate detection across product catalogs, comparing which users two marketing campaigns reached, or detecting near-duplicate content. The traditional approach is a cross join on set membership, which is O(n²) and completely impractical above a few thousand rows.
-- MINHASH + APPROXIMATE_SIMILARITY: Jaccard similarity between two customer segments
-- No cross join required
WITH segment_sketches AS (
SELECT
campaign_id,
MINHASH(100, customer_id) AS customer_sketch -- 100 hash functions = good accuracy
FROM campaign_members
GROUP BY campaign_id
)
SELECT
a.campaign_id AS campaign_a,
b.campaign_id AS campaign_b,
APPROXIMATE_SIMILARITY(a.customer_sketch, b.customer_sketch) AS jaccard_similarity
FROM segment_sketches a
JOIN segment_sketches b ON a.campaign_id < b.campaign_id
ORDER BY jaccard_similarity DESC;
-- Output example:
-- CAMPAIGN_A | CAMPAIGN_B | JACCARD_SIMILARITY
-- -----------|------------|-------------------
-- spring_24 | summer_24 | 0.71
-- summer_24 | fall_24 | 0.43
-- spring_24 | fall_24 | 0.29
A similarity of 0.71 between spring and summer campaigns means 71% of the customers they reached were the same people — which is useful to know before running both again. I’ve used this exact pattern for a media client to detect ad campaign overlap before recommending budget reallocation. The query ran in under 30 seconds on a 50-million-row membership table. The equivalent Python job had been taking 40 minutes.
Which Function for Which Problem
Quick reference for the next time one of these problems comes up:
APPROX_TOP_K — Top-N most frequent values on huge tables where GROUP BY is too slow
MODE() — Most common value per group, in one line instead of a subquery
ARRAY_AGG and ARRAY_UNIQUE_AGG — Aggregate row values into arrays for ML features or 360 views
SKEW / KURTOSIS — Distribution shape diagnostics without leaving SQL
HLL_ACCUMULATE — Approximate distinct counts at scale; combinable across time windows
MINHASH / APPROXIMATE_SIMILARITY — Set similarity without cross joins
All six are available on every Snowflake edition. None require any special configuration. The only cost is the warehouse time to run the query — and in most cases that cost is substantially lower than the standard alternative, which is the point.
Revolutionary Declarative Data Pipelines That Transform ETL
In 2025,Snowflake Dynamic Tables have become the most powerful way to build automated data pipelines. This comprehensive guide covers everything from target lag configuration to incremental refresh strategies, with real-world examples showing how dynamic tables eliminate complex orchestration code and transform pipeline creation through simple SQL statements.
For years, building data pipelines meant wrestling with Streams, Tasks, complex scheduling logic, and dependency management. Dynamic tables changed everything. Now data engineers define the end state they want, and Snowflake handles all the orchestration automatically. The impact is remarkable: pipelines that previously required hundreds of lines of procedural code now need just a single CREATE DYNAMIC TABLE statement.
These tables automatically detect changes in base tables, incrementally update results, and maintain freshness targets—all without external orchestration tools. Leading enterprises use them to build production-ready pipelines processing billions of rows daily, achieving both faster development and lower operational costs.
What Are Snowflake Dynamic Tables and Why They Matter
Snowflake Dynamic Tables are specialized tables that automatically maintain query results through intelligent refresh processes. Unlike traditional tables that require manual updates, dynamic tables continuously monitor source data changes and update themselves based on defined freshness requirements.
Core Concept Explained
When you create a Snowflake Dynamic Table, you define a query that transforms data from base tables. Snowflake then takes full responsibility for refreshing the table, managing dependencies, and optimizing the refresh process. This declarative approach represents a fundamental shift from imperative pipeline coding.
The traditional approach:
sql
-- Old way: Manual orchestration with Streams and Tasks
CREATE STREAM sales_stream ON TABLE raw_sales;
CREATE TASK refresh_daily_sales
WAREHOUSE = compute_wh
SCHEDULE = '5 MINUTE'
WHEN SYSTEM$STREAM_HAS_DATA('sales_stream')
AS
MERGE INTO daily_sales_summary dst
USING (
SELECT product_id,
DATE_TRUNC('day', sale_date) as day,
SUM(amount) as total_sales
FROM sales_stream
GROUP BY 1, 2
) src
ON dst.product_id = src.product_id
AND dst.day = src.day
WHEN MATCHED THEN UPDATE SET total_sales = src.total_sales
WHEN NOT MATCHED THEN INSERT VALUES (src.product_id, src.day, src.total_sales);
The Snowflake Dynamic Tables approach:
sql
-- New way: Simple declarative definition
CREATE DYNAMIC TABLE daily_sales_summary
TARGET_LAG = '5 minutes'
WAREHOUSE = compute_wh
AS
SELECT product_id,
DATE_TRUNC('day', sale_date) as day,
SUM(amount) as total_sales
FROM raw_sales
GROUP BY 1, 2;
The second approach achieves the same result with 80% less code and zero orchestration logic.
How Automated Refresh Works
Snowflake Dynamic Tables use a sophisticated two-step refresh process:
Step 1: Change Detection Snowflake analyzes the dynamic table’s query and creates a Directed Acyclic Graph (DAG) based on dependencies. Behind the scenes, Snowflake creates lightweight streams on base tables to capture change metadata (only ROW_ID, operation type, and timestamp—minimal storage cost).
Step 2: Incremental Merge Only detected changes are incorporated into the dynamic table. This incremental processing dramatically reduces compute consumption compared to full table refreshes. For queries that support it (most aggregations, joins, and filters), Snowflake automatically uses incremental mode.
Real-world example: A global retailer processes 50 million daily transactions. When 10,000 new orders arrive, their Snowflake Dynamic Table refreshes in seconds by processing only those 10,000 rows—not the entire 50 million row history.
Understanding Target Lag Configuration
Target lag defines how fresh your data needs to be. It’s the maximum acceptable delay between changes in base tables and their reflection in the dynamic table.
Target Lag Options and Trade-offs
sql
-- High freshness (low lag) for real-time dashboards
CREATE DYNAMIC TABLE real_time_metrics
TARGET_LAG = '1 minute'
WAREHOUSE = small_wh
AS SELECT * FROM live_events WHERE event_time > CURRENT_TIMESTAMP - INTERVAL '1 hour';
-- Moderate freshness for hourly reports
CREATE DYNAMIC TABLE hourly_summary
TARGET_LAG = '30 minutes'
WAREHOUSE = medium_wh
AS SELECT DATE_TRUNC('hour', ts) as hour, COUNT(*) FROM events GROUP BY 1;
-- Lower freshness (higher lag) for daily aggregates
CREATE DYNAMIC TABLE daily_rollup
TARGET_LAG = '6 hours'
WAREHOUSE = large_wh
AS SELECT DATE(ts) as day, SUM(revenue) FROM sales GROUP BY 1;
Trade-off considerations:
Lower target lag = More frequent refreshes = Higher compute costs = Fresher data
Higher target lag = Less frequent refreshes = Lower compute costs = Older data
Using DOWNSTREAM Lag for Pipeline DAGs
For pipeline DAGs with multiple Snowflake Dynamic Tables, use TARGET_LAG = DOWNSTREAM:
sql
-- Layer 1: Base transformation
CREATE DYNAMIC TABLE customer_events_cleaned
TARGET_LAG = DOWNSTREAM
WAREHOUSE = compute_wh
AS
SELECT customer_id, event_type, event_time
FROM raw_events
WHERE event_time IS NOT NULL;
-- Layer 2: Aggregation (defines the lag requirement)
CREATE DYNAMIC TABLE customer_daily_summary
TARGET_LAG = '15 minutes'
WAREHOUSE = compute_wh
AS
SELECT customer_id,
DATE(event_time) as day,
COUNT(*) as event_count
FROM customer_events_cleaned
GROUP BY 1, 2;
The upstream table (customer_events_cleaned) automatically inherits the 15-minute lag from its downstream consumer. This ensures the entire pipeline maintains consistent freshness without redundant configuration.
Comparing Dynamic Tables vs Streams and Tasks
Understanding when to use Dynamic Tables versus traditional Streams and Tasks is critical for optimal pipeline architecture.
When to Use Dynamic Tables
Choose Dynamic Tables when:
You need declarative, SQL-only transformations without procedural code
Your pipeline has straightforward dependencies that form a clear DAG
You want automatic incremental processing without manual merge logic
Time-based freshness (target lag) meets your requirements
You prefer Snowflake to automatically manage refresh scheduling
Your transformations involve standard SQL operations (joins, aggregations, filters)
Choose Streams and Tasks when:
You need fine-grained control over exact refresh timing
Your pipeline requires complex conditional logic beyond SQL
You need event-driven triggers from external systems
Your workflow involves cross-database operations or external API calls
You require custom error handling and retry logic
Your processing needs transaction boundaries across multiple steps
-- Complex multi-table join with aggregation
CREATE DYNAMIC TABLE customer_lifetime_value
TARGET_LAG = '1 hour'
WAREHOUSE = compute_wh
AS
SELECT
c.customer_id,
c.customer_name,
COUNT(DISTINCT o.order_id) as total_orders,
SUM(o.order_amount) as lifetime_value,
MAX(o.order_date) as last_order_date
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
LEFT JOIN order_items oi ON o.order_id = oi.order_id
WHERE c.customer_status = 'active'
GROUP BY 1, 2;
This query would be impossible in a materialized view but works perfectly in Dynamic Tables.
Incremental vs Full Refresh
Dynamic Tables automatically choose between incremental and full refresh modes based on your query patterns.
Understanding Refresh Modes
Incremental refresh (default for most queries):
Processes only changed rows since last refresh
Dramatically reduces compute costs
Works for most aggregations, joins, and filters
Requires deterministic queries
Full refresh (fallback for complex scenarios):
Reprocesses entire dataset on each refresh
Required for non-deterministic functions
Used when change tracking isn’t feasible
Higher compute consumption
sql
-- This uses incremental refresh automatically
CREATE DYNAMIC TABLE sales_by_region
TARGET_LAG = '10 minutes'
WAREHOUSE = compute_wh
AS
SELECT region,
SUM(sales_amount) as total_sales
FROM transactions
WHERE transaction_date >= '2025-01-01'
GROUP BY region;
-- This forces full refresh (non-deterministic function)
CREATE DYNAMIC TABLE random_sample_data
TARGET_LAG = '1 hour'
WAREHOUSE = compute_wh
REFRESH_MODE = FULL -- Explicitly set to FULL
AS
SELECT *
FROM large_dataset
WHERE RANDOM() < 0.01; -- Non-deterministic
Forcing Incremental Mode
You can explicitly force incremental mode for supported queries:
sql
CREATE DYNAMIC TABLE optimized_pipeline
TARGET_LAG = '5 minutes'
WAREHOUSE = compute_wh
REFRESH_MODE = INCREMENTAL -- Explicitly set
AS
SELECT customer_id,
DATE(order_time) as order_date,
COUNT(*) as order_count,
SUM(order_total) as daily_revenue
FROM orders
WHERE order_time > CURRENT_TIMESTAMP - INTERVAL '90 days'
GROUP BY 1, 2;
Production Best Practices
Building reliable production pipelines requires following proven patterns.
Performance Optimization tips
Break down complex transformations:
sql
-- Bad: Single complex dynamic table
CREATE DYNAMIC TABLE complex_report
TARGET_LAG = '15 minutes'
WAREHOUSE = compute_wh
AS
-- 500 lines of complex SQL with multiple CTEs, joins, window functions
...;
-- Good: Multiple simple dynamic tables
CREATE DYNAMIC TABLE cleaned_events
TARGET_LAG = DOWNSTREAM
WAREHOUSE = compute_wh
AS
SELECT customer_id, event_type, CAST(event_time AS TIMESTAMP) as event_time
FROM raw_events
WHERE event_time IS NOT NULL;
CREATE DYNAMIC TABLE enriched_events
TARGET_LAG = DOWNSTREAM
WAREHOUSE = compute_wh
AS
SELECT e.*, c.customer_segment
FROM cleaned_events e
JOIN customers c ON e.customer_id = c.customer_id;
CREATE DYNAMIC TABLE final_report
TARGET_LAG = '15 minutes'
WAREHOUSE = compute_wh
AS
SELECT customer_segment,
DATE(event_time) as day,
COUNT(*) as event_count
FROM enriched_events
GROUP BY 1, 2;
Monitoring and Debugging
Monitor your Tables through Snowsight or SQL:
sql
-- Show all dynamic tables
SHOW DYNAMIC TABLES;
-- Get detailed information about refresh history
SELECT *
FROM TABLE(INFORMATION_SCHEMA.DYNAMIC_TABLE_REFRESH_HISTORY('daily_sales_summary'))
ORDER BY data_timestamp DESC
LIMIT 10;
-- Check if dynamic table is using incremental refresh
SELECT *
FROM TABLE(INFORMATION_SCHEMA.DYNAMIC_TABLE_GRAPH_HISTORY(
'my_dynamic_table'
))
WHERE refresh_action = 'INCREMENTAL';
-- View the DAG for your pipeline-- In Snowsight: Go to Data → Databases → Your Database → Dynamic Tables-- Click on a dynamic table to see the dependency graph visualization
Cost Optimization Strategies
Right-size your warehouse:
sql
-- Small warehouse for simple transformations
CREATE DYNAMIC TABLE lightweight_transform
TARGET_LAG = '10 minutes'
WAREHOUSE = x_small_wh -- Start small
AS SELECT * FROM source WHERE active = TRUE;
-- Large warehouse only for heavy aggregations
CREATE DYNAMIC TABLE heavy_analytics
TARGET_LAG = '1 hour'
WAREHOUSE = large_wh -- Size appropriately
AS
SELECT product_category,
date,
COUNT(DISTINCT customer_id) as unique_customers,
SUM(revenue) as total_revenue
FROM sales_fact
JOIN product_dim USING (product_id)
GROUP BY 1, 2;
Use clustering keys for large tables:
sql
CREATE DYNAMIC TABLE partitioned_sales
TARGET_LAG = '30 minutes'
WAREHOUSE = medium_wh
CLUSTER BY (sale_date, region) -- Improves refresh performance
AS
SELECT sale_date, region, product_id, SUM(amount) as sales
FROM transactions
GROUP BY 1, 2, 3;
Real-World Use Cases
Use Case 1: Real-Time Analytics Dashboard
Scenario: E-commerce company needs up-to-the-minute sales dashboards
sql
-- Real-time order metrics
CREATE DYNAMIC TABLE real_time_order_metrics
TARGET_LAG = '2 minutes'
WAREHOUSE = reporting_wh
AS
SELECT
DATE_TRUNC('minute', order_time) as minute,
COUNT(*) as order_count,
SUM(order_total) as revenue,
AVG(order_total) as avg_order_value
FROM orders
WHERE order_time >= CURRENT_TIMESTAMP - INTERVAL '24 hours'
GROUP BY 1;
-- Product inventory status
CREATE DYNAMIC TABLE inventory_status
TARGET_LAG = '5 minutes'
WAREHOUSE = operations_wh
AS
SELECT
p.product_id,
p.product_name,
p.stock_quantity,
COALESCE(SUM(o.quantity), 0) as pending_orders,
p.stock_quantity - COALESCE(SUM(o.quantity), 0) as available_stock
FROM products p
LEFT JOIN order_items o ON p.product_id = o.product_id
WHERE o.order_status = 'pending'
GROUP BY 1, 2, 3;
Use Case 2:Change Data Capture Pipelines
Scenario: Financial services company tracks account balance changes
sql
-- Capture all balance changes
CREATE DYNAMIC TABLE account_balance_history
TARGET_LAG = '1 minute'
WAREHOUSE = finance_wh
AS
SELECT
account_id,
transaction_id,
transaction_time,
transaction_amount,
SUM(transaction_amount) OVER (
PARTITION BY account_id
ORDER BY transaction_time
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) as running_balance
FROM transactions
ORDER BY account_id, transaction_time;
-- Daily account summaries
CREATE DYNAMIC TABLE daily_account_summary
TARGET_LAG = '15 minutes'
WAREHOUSE = finance_wh
AS
SELECT
account_id,
DATE(transaction_time) as summary_date,
MIN(running_balance) as min_balance,
MAX(running_balance) as max_balance,
COUNT(*) as transaction_count
FROM account_balance_history
GROUP BY 1, 2;
Use Case 3: Slowly Changing Dimensions
Scenario: Type 2 SCD implementation for customer dimension
sql
-- Customer SCD Type 2 with dynamic table
CREATE DYNAMIC TABLE customer_dimension_scd2
TARGET_LAG = '10 minutes'
WAREHOUSE = etl_wh
AS
WITH numbered_changes AS (
SELECT
customer_id,
customer_name,
customer_address,
customer_segment,
update_timestamp,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY update_timestamp
) as version_number
FROM customer_changes_stream
)
SELECT
customer_id,
version_number,
customer_name,
customer_address,
customer_segment,
update_timestamp as valid_from,
LEAD(update_timestamp) OVER (
PARTITION BY customer_id
ORDER BY update_timestamp
) as valid_to,
CASE
WHEN LEAD(update_timestamp) OVER (
PARTITION BY customer_id
ORDER BY update_timestamp
) IS NULL THEN TRUE
ELSE FALSE
END as is_current
FROM numbered_changes;
Use Case 4:Multi-Layer Data Mart Architecture
Scenario: Building a star schema data mart with automated refresh
sql
-- Bronze layer: Data cleaning
CREATE DYNAMIC TABLE bronze_sales
TARGET_LAG = DOWNSTREAM
WAREHOUSE = etl_wh
AS
SELECT
CAST(sale_id AS NUMBER) as sale_id,
CAST(sale_date AS DATE) as sale_date,
CAST(customer_id AS NUMBER) as customer_id,
CAST(product_id AS NUMBER) as product_id,
CAST(quantity AS NUMBER) as quantity,
CAST(unit_price AS DECIMAL(10,2)) as unit_price
FROM raw_sales
WHERE sale_id IS NOT NULL;
-- Silver layer: Business logic
CREATE DYNAMIC TABLE silver_sales_enriched
TARGET_LAG = DOWNSTREAM
WAREHOUSE = transform_wh
AS
SELECT
s.*,
s.quantity * s.unit_price as total_amount,
c.customer_segment,
p.product_category,
p.product_subcategory
FROM bronze_sales s
JOIN dim_customer c ON s.customer_id = c.customer_id
JOIN dim_product p ON s.product_id = p.product_id;
-- Gold layer: Analytics-ready
CREATE DYNAMIC TABLE gold_sales_summary
TARGET_LAG = '15 minutes'
WAREHOUSE = analytics_wh
AS
SELECT
sale_date,
customer_segment,
product_category,
COUNT(DISTINCT sale_id) as transaction_count,
SUM(total_amount) as revenue,
AVG(total_amount) as avg_transaction_value
FROM silver_sales_enriched
GROUP BY 1, 2, 3;
New features in 2025
Immutability Constraints
New in 2025: Lock specific rows while allowing incremental updates to others
sql
CREATE DYNAMIC TABLE sales_with_closed_periods
TARGET_LAG = '30 minutes'
WAREHOUSE = compute_wh
IMMUTABLE WHERE (sale_date < '2025-01-01') -- Lock historical data
AS
SELECT
sale_date,
region,
SUM(amount) as total_sales
FROM transactions
GROUP BY 1, 2;
This prevents accidental modifications to closed accounting periods while continuing to update current data.
CURRENT_TIMESTAMP Support for incremental mode
New in 2025: Use time-based filters in incremental mode
sql
CREATE DYNAMIC TABLE rolling_30_day_metrics
TARGET_LAG = '10 minutes'
WAREHOUSE = compute_wh
REFRESH_MODE = INCREMENTAL -- Now works with CURRENT_TIMESTAMP
AS
SELECT
customer_id,
COUNT(*) as recent_orders,
SUM(order_total) as recent_revenue
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY customer_id;
Previously, using CURRENT_TIMESTAMP forced full refresh. Now it works with incremental mode.
Backfill from Clone feature
New in 2025: Initialize dynamic tables from historical snapshots
sql
-- Clone existing table with corrected data
CREATE TABLE sales_corrected CLONE sales_with_errors;
-- Apply corrections
UPDATE sales_corrected SET amount = amount * 1.1 WHERE region = 'APAC';
-- Create dynamic table using corrected data as baseline
CREATE DYNAMIC TABLE sales_summary
BACKFILL FROM sales_corrected
IMMUTABLE WHERE (sale_date < '2025-01-01')
TARGET_LAG = '15 minutes'
WAREHOUSE = compute_wh
AS
SELECT sale_date, region, SUM(amount) as total_sales
FROM sales
GROUP BY 1, 2;
Advanced Patterns and Techniques
Pattern 1: Handling Late-Arriving Data
Handle records that arrive out of order:
sql
CREATE DYNAMIC TABLE ordered_events
TARGET_LAG = '30 minutes'
WAREHOUSE = compute_wh
AS
SELECT
event_id,
event_time,
customer_id,
event_type,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY event_time, event_id
) as sequence_number
FROM raw_events
WHERE event_time >= CURRENT_TIMESTAMP - INTERVAL '7 days'
ORDER BY customer_id, event_time;
Pattern 2: Using window Functions for cumulative calculations
Build cumulative calculations automatically:
sql
CREATE DYNAMIC TABLE customer_cumulative_spend
TARGET_LAG = '20 minutes'
WAREHOUSE = analytics_wh
AS
SELECT
customer_id,
order_date,
order_amount,
SUM(order_amount) OVER (
PARTITION BY customer_id
ORDER BY order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) as lifetime_value,
COUNT(*) OVER (
PARTITION BY customer_id
ORDER BY order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) as order_count
FROM orders;
Pattern 3: Automated Data Quality Checks
Automate data validation:
sql
CREATE DYNAMIC TABLE data_quality_metrics
TARGET_LAG = '10 minutes'
WAREHOUSE = monitoring_wh
AS
SELECT
'customers' as table_name,
CURRENT_TIMESTAMP as check_time,
COUNT(*) as total_rows,
COUNT(DISTINCT customer_id) as unique_ids,
SUM(CASE WHEN email IS NULL THEN 1 ELSE 0 END) as missing_emails,
SUM(CASE WHEN LENGTH(phone) < 10 THEN 1 ELSE 0 END) as invalid_phones,
MAX(updated_at) as last_update
FROM customers
UNION ALL
SELECT
'orders' as table_name,
CURRENT_TIMESTAMP as check_time,
COUNT(*) as total_rows,
COUNT(DISTINCT order_id) as unique_ids,
SUM(CASE WHEN order_amount <= 0 THEN 1 ELSE 0 END) as invalid_amounts,
SUM(CASE WHEN customer_id IS NULL THEN 1 ELSE 0 END) as orphaned_orders,
MAX(order_date) as last_update
FROM orders;
Troubleshooting Common Issues
Issue 1: Tables Not Refreshing
Problem: Dynamic table shows “suspended” status
Solution:
sql
-- Check for errors in refresh history
SELECT *
FROM TABLE(INFORMATION_SCHEMA.DYNAMIC_TABLE_REFRESH_HISTORY('my_table'))
WHERE state = 'FAILED'
ORDER BY data_timestamp DESC;
-- Resume the dynamic table
ALTER DYNAMIC TABLE my_table RESUME;
-- Check dependencies
SHOW DYNAMIC TABLES LIKE 'my_table';
Issue 2: Using Full Refresh Instead of Incremental
Problem: Query should support incremental but uses full refresh
Complex nested queries: Simplify or break into multiple dynamic tables
Masking policies on base tables: Consider alternative security approaches
LATERAL FLATTEN: May force full refresh for complex nested structures
sql
-- Check current refresh mode
SELECT refresh_mode
FROM TABLE(INFORMATION_SCHEMA.DYNAMIC_TABLE_GRAPH_HISTORY('my_table'))
LIMIT 1;
-- If full refresh is required, optimize for performance
ALTER DYNAMIC TABLE my_table SET WAREHOUSE = larger_warehouse;
Issue 3: High compute Costs
Problem: Unexpected credit consumption
Solutions:
sql
-- 1. Analyze compute usage
SELECT
name,
warehouse_name,
SUM(credits_used) as total_credits
FROM SNOWFLAKE.ACCOUNT_USAGE.DYNAMIC_TABLE_REFRESH_HISTORY
WHERE start_time >= DATEADD(day, -7, CURRENT_TIMESTAMP)
GROUP BY 1, 2
ORDER BY total_credits DESC;
-- 2. Increase target lag to reduce refresh frequency
ALTER DYNAMIC TABLE expensive_table
SET TARGET_LAG = '30 minutes'; -- Was '5 minutes'-- 3. Use smaller warehouse
ALTER DYNAMIC TABLE expensive_table
SET WAREHOUSE = small_wh; -- Was large_wh-- 4. Check if incremental is being used-- If not, optimize query to support incremental processing
Migration from Streams and Tasks
Converting existing Stream/Task pipelines to Dynamic Tables:
Before (Streams and Tasks):
sql
-- Stream to capture changes
CREATE STREAM order_changes ON TABLE raw_orders;
-- Task to process stream
CREATE TASK process_orders
WAREHOUSE = compute_wh
SCHEDULE = '10 MINUTE'
WHEN SYSTEM$STREAM_HAS_DATA('order_changes')
AS
INSERT INTO processed_orders
SELECT
order_id,
customer_id,
order_date,
order_total,
CASE
WHEN order_total > 1000 THEN 'high_value'
WHEN order_total > 100 THEN 'medium_value'
ELSE 'low_value'
END as value_tier
FROM order_changes
WHERE METADATA$ACTION = 'INSERT';
ALTER TASK process_orders RESUME;
After (Snowflake Dynamic Tables):
sql
CREATE DYNAMIC TABLE processed_orders
TARGET_LAG = '10 minutes'
WAREHOUSE = compute_wh
AS
SELECT
order_id,
customer_id,
order_date,
order_total,
CASE
WHEN order_total > 1000 THEN 'high_value'
WHEN order_total > 100 THEN 'medium_value'
ELSE 'low_value'
END as value_tier
FROM raw_orders;
Benefits of migration:
75% less code to maintain
Automatic dependency management
No manual stream/task orchestration
Automatic incremental processing
Built-in monitoring and observability
Snowflake Dynamic Tables: Comparison with Other Platforms
Feature
Snowflake Dynamic Tables
dbt Incremental Models
Databricks Delta Live Tables
Setup complexity
Low (native Snowflake)
Medium (external tool)
Medium (Databricks-specific)
Automatic orchestration
Yes
No (requires scheduler)
Yes
Incremental processing
Automatic
Manual configuration
Automatic
Query language
SQL
SQL + Jinja
SQL + Python
Dependency management
Automatic DAG
Manual ref() functions
Automatic DAG
Cost optimization
Automatic warehouse sizing
Manual
Automatic cluster sizing
Monitoring
Built-in Snowsight
dbt Cloud or custom
Databricks UI
Multi-cloud
AWS, Azure, GCP
Any Snowflake account
Databricks only
Conclusion: The Future of Data Pipeline develoment
Snowflake Dynamic Tables represent a paradigm shift in data pipeline development. By eliminating complex orchestration code and automating refresh management, they allow data teams to focus on business logic rather than infrastructure.
Key transformations enabled:
80% reduction in pipeline code complexity
Zero orchestration maintenance overhead
Automatic incremental processing without manual merge logic
Self-managing dependencies through intelligent DAG analysis
Built-in monitoring and observability
Cost optimization through intelligent refresh scheduling
As data freshness requirements increase and pipeline complexity grows, dynamic tables provide the declarative approach needed to build scalable, maintainable data infrastructure.
Start with simple use cases, measure performance, and progressively migrate complex pipelines. The investment in learning this technology pays dividends in reduced maintenance burden and faster feature delivery.
In 2025, Snowflake has introduced groundbreaking improvements that fundamentally change how data engineers write queries. This Snowflake SQL tutorial covers the latest features including MERGE ALL BY NAME, UNION BY NAME, and Cortex AISQL. Whether you’re learning Snowflake SQL or optimizing existing code, this tutorial demonstrateshow these enhancements eliminate tedious column mapping, reduce errors, and dramatically simplify complex data operations.
The star feature?MERGE ALL BY NAME—announced on September 29, 2025—automatically matches columns by name, eliminating the need to manually map every column when upserting data. This Snowflake SQL tutorial will show you how this single feature can transform a 50-line MERGE statement into just 5 lines.
But that’s not all.Additionally, this SQL tutorial covers:
UNION BY NAME for flexible data combining
Cortex AISQL for AI-powered SQL functions
Enhanced PIVOT/UNPIVOT with aliasing
Snowflake Scripting UDFs for procedural SQL
Lambda expressions in higher-order functions
For data engineers, these improvements mean less boilerplate code, fewer errors, and more time focused on solving business problems rather than wrestling with SQL syntax.
But that’s not all.Additionally, Snowflake 2025 brings:
UNION BY NAME for flexible data combining
Cortex AISQL for AI-powered SQL functions
Enhanced PIVOT/UNPIVOT with aliasing
Snowflake Scripting UDFs for procedural SQL
Lambda expressions in higher-order functions
For data engineers, these improvements mean less boilerplate code, fewer errors, and more time focused on solving business problems rather than wrestling with SQL syntax.
Snowflake SQL Tutorial: MERGE ALL BY NAME Feature
This Snowflake SQL tutorial begins with the most impactful feature of 2025…
Announced on September 29, 2025, MERGE ALL BY NAME is arguably the most impactful SQL improvement Snowflake has released this year. This feature automatically matches columns between source and target tables based on column names rather than positions.
The SQL Problem MERGE ALL BY NAME Solves
Traditionally, writing a MERGE statement required manually listing and mapping each column:
sql
-- OLD WAY: Manual column mapping (tedious and error-prone)
MERGE INTO customer_target t
USING customer_updates s
ON t.customer_id = s.customer_id
WHEN MATCHED THEN
UPDATE SET
t.first_name = s.first_name,
t.last_name = s.last_name,
t.email = s.email,
t.phone = s.phone,
t.address = s.address,
t.city = s.city,
t.state = s.state,
t.zip_code = s.zip_code,
t.country = s.country,
t.updated_date = s.updated_date
WHEN NOT MATCHED THEN
INSERT (customer_id, first_name, last_name, email, phone,
address, city, state, zip_code, country, updated_date)
VALUES (s.customer_id, s.first_name, s.last_name, s.email,
s.phone, s.address, s.city, s.state, s.zip_code,
s.country, s.updated_date);
This approach suffers from multiple pain points:
Manual mapping for every single column
High risk of typos and mismatches
Difficult maintenance when schemas evolve
Time-consuming for tables with many columns
The Snowflake SQL Solution: MERGE ALL BY NAME
With MERGE ALL BY NAME, the same operation becomes elegantly simple:
sql
-- NEW WAY: Automatic column matching (clean and reliable)
MERGE INTO customer_target
USING customer_updates
ON customer_target.customer_id = customer_updates.customer_id
WHEN MATCHED THEN
UPDATE ALL BY NAME
WHEN NOT MATCHED THEN
INSERT ALL BY NAME;
That’s it!Just 2 lines instead of 20+ lines of column mapping.
How MERGE ALL BY NAME Works
The magic happens through intelligent column name matching:
Snowflake analyzes both target and source tables
It identifies columns with matching names
It automatically maps columns regardless of position
It handles different column orders seamlessly
It executes the MERGE with proper type conversion
Importantly, MERGE ALL BY NAME works even when:
Columns are in different orders
Tables have extra columns in one but not the other
Column names use different casing (Snowflake is case-insensitive by default)
Requirements for MERGE ALL BY NAME
For this feature to work correctly:
Target and source must have the same number of matching columns
Column names must be identical (case-insensitive)
Data types must be compatible (Snowflake handles automatic casting)
However, column order doesn’t matter:
sql
-- This works perfectly!
CREATE TABLE target (
id INT,
name VARCHAR,
email VARCHAR,
created_date DATE
);
CREATE TABLE source (
created_date DATE, -- Different order
email VARCHAR, -- Different order
id INT, -- Different order
name VARCHAR -- Different order
);
MERGE INTO target
USING source
ON target.id = source.id
WHEN MATCHED THEN UPDATE ALL BY NAME
WHEN NOT MATCHED THEN INSERT ALL BY NAME;
Snowflake intelligently matches id with id, name with name, etc., regardless of position.
Real-World Use Case: Slowly Changing Dimensions
Consider implementing a Type 1 SCD (Slowly Changing Dimension) for product data:
sql
-- Product dimension table
CREATE OR REPLACE TABLE dim_product (
product_id INT PRIMARY KEY,
product_name VARCHAR,
category VARCHAR,
price DECIMAL(10,2),
description VARCHAR,
supplier_id INT,
last_updated TIMESTAMP
);
-- Daily product updates from source system
CREATE OR REPLACE TABLE product_updates (
product_id INT,
description VARCHAR, -- Different column order
price DECIMAL(10,2),
product_name VARCHAR,
category VARCHAR,
supplier_id INT,
last_updated TIMESTAMP
);
-- SCD Type 1: Upsert with MERGE ALL BY NAME
MERGE INTO dim_product
USING product_updates
ON dim_product.product_id = product_updates.product_id
WHEN MATCHED THEN
UPDATE ALL BY NAME
WHEN NOT MATCHED THEN
INSERT ALL BY NAME;
This handles:
Updating existing products with latest information
Inserting new products automatically
Different column orders between systems
All columns without manual mapping
Benefits of MERGE ALL BY NAME
Data engineers report significant advantages:
Time Savings:
90% less code for MERGE statements
5 minutes instead of 30 minutes to write complex merges
Faster schema evolution without code changes
Error Reduction:
Zero typos from manual column mapping
No mismatched columns from copy-paste errors
Automatic validation by Snowflake
Maintenance Simplification:
Schema changes don’t require code updates
New columns automatically included
Removed columns handled gracefully
Code Readability:
Clear intent from simple syntax
Easy review in code reviews
Self-documenting logic
Snowflake SQL UNION BY NAME: Flexible Data Combining
This section of our Snowflake SQL tutorial explores how UNION BY NAME Introduced at Snowflake Summit 2025, UNION BY NAME revolutionizes how we combine datasets from different sources by focusing on column names rather than positions.
The Traditional UNION Problem
For years, SQL developers struggled with UNION ALL’s rigid requirements:
sql
-- TRADITIONAL UNION ALL: Requires exact column matching
SELECT id, name, department
FROM employees
UNION ALL
SELECT emp_id, emp_name, dept -- Different names: FAILS!
FROM contingent_workers;
This fails because:
Column names don’t match
Positions matter, not names
Adding columns breaks existing queries
Schema evolution requires constant maintenance
UNION BY NAME Solution
With UNION BY NAME, column matching happens by name:
sql
-- NEW: UNION BY NAME matches columns by name
CREATE TABLE employees (
id INT,
name VARCHAR,
department VARCHAR,
role VARCHAR
);
CREATE TABLE contingent_workers (
id INT,
name VARCHAR,
department VARCHAR
-- Note: No 'role' column
);
SELECT * FROM employees
UNION ALL BY NAME
SELECT * FROM contingent_workers;
-- Result: Combines by name, fills missing 'role' with NULL
Output:
ID | NAME | DEPARTMENT | ROLE
---+---------+------------+--------
1 | Alice | Sales | Manager
2 | Bob | IT | Developer
3 | Charlie | Sales | NULL
4 | Diana | IT | NULL
Key behaviors:
Columns matched by name, not position
Missing columns filled with NULL
Extra columns included automatically
Order doesn’t matter
Use Cases for UNION BY NAME
This feature excels in several scenarios:
Merging Legacy and Modern Systems:
sql
-- Legacy system with old column names
SELECT
cust_id AS customer_id,
cust_name AS name,
phone_num AS phone
FROM legacy_customers
UNION ALL BY NAME
-- Modern system with new column names
SELECT
customer_id,
name,
phone,
email -- New column not in legacy
FROM modern_customers;
Combining Data from Multiple Regions:
sql
-- Different regions have different optional fields
SELECT * FROM us_sales -- Has 'state' column
UNION ALL BY NAME
SELECT * FROM eu_sales -- Has 'country' column
UNION ALL BY NAME
SELECT * FROM asia_sales; -- Has 'region' column
Incremental Schema Evolution:
sql
-- Historical data without new fields
SELECT * FROM sales_2023
UNION ALL BY NAME
-- Current data with additional tracking
SELECT * FROM sales_2024 -- Added 'source_channel' column
UNION ALL BY NAME
SELECT * FROM sales_2025; -- Added 'attribution_id' column
While powerful, UNION BY NAME has slight overhead:
When to use UNION BY NAME:
Schemas differ across sources
Evolution happens frequently
Maintainability matters more than marginal performance
When to use traditional UNION ALL:
Schemas are identical and stable
Maximum performance is critical
Large-scale production queries with billions of rows
Best practice:Use UNION BY NAME for data integration and ELT pipelines where flexibility outweighs marginal performance costs.
Cortex AISQL: AI-Powered SQL Functions
Announced on June 2, 2025, Cortex AISQL brings powerful AI capabilities directly into Snowflake’s SQL engine, enabling AI pipelines with familiar SQL commands.
Revolutionary AI Functions
Cortex AISQL introduces three groundbreaking SQL functions:
AI_FILTER: Intelligent Data Filtering
Filter data using natural language questions instead of complex WHERE clauses:
sql
-- Traditional approach: Complex WHERE clause
SELECT *
FROM customer_reviews
WHERE (
LOWER(review_text) LIKE '%excellent%' OR
LOWER(review_text) LIKE '%amazing%' OR
LOWER(review_text) LIKE '%outstanding%' OR
LOWER(review_text) LIKE '%fantastic%'
) AND (
sentiment_score > 0.7
);
-- AI_FILTER approach: Natural language
SELECT *
FROM customer_reviews
WHERE AI_FILTER(review_text, 'Is this a positive review praising the product?');
Use cases:
Filtering images by content (“Does this image contain a person?”)
Classifying text by intent (“Is this a complaint?”)
Quality control (“Is this product photo high quality?”)
AI_CLASSIFY: Intelligent Classification
Classify text or images into user-defined categories:
sql
-- Classify customer support tickets automatically
SELECT
ticket_id,
subject,
AI_CLASSIFY(
description,
['Technical Issue', 'Billing Question', 'Feature Request',
'Bug Report', 'Account Access']
) AS ticket_category
FROM support_tickets;
-- Multi-label classification
SELECT
product_id,
AI_CLASSIFY(
product_description,
['Electronics', 'Clothing', 'Home & Garden', 'Sports'],
'multi_label'
) AS categories
FROM products;
Advantages:
No training required
Plain-language category definitions
Single or multi-label classification
Works on text and images
AI_AGG: Intelligent Aggregation
Aggregate text columns and extract insights across multiple rows:
sql
-- Traditional: Difficult to get insights from text
SELECT
product_id,
STRING_AGG(review_text, ' | ') -- Just concatenates
FROM reviews
GROUP BY product_id;
-- AI_AGG: Extract meaningful insights
SELECT
product_id,
AI_AGG(
review_text,
'Summarize the common themes in these reviews, highlighting both positive and negative feedback'
) AS review_summary
FROM reviews
GROUP BY product_id;
Key benefit:Not subject to context window limitations—can process unlimited rows.
Cortex AISQL Real-World Example
Complete pipeline for analyzing customer feedback:
sql
-- Step 1: Filter relevant feedback
CREATE OR REPLACE TABLE relevant_feedback AS
SELECT *
FROM customer_feedback
WHERE AI_FILTER(feedback_text, 'Is this feedback about product quality or features?');
-- Step 2: Classify feedback by category
CREATE OR REPLACE TABLE categorized_feedback AS
SELECT
feedback_id,
customer_id,
AI_CLASSIFY(
feedback_text,
['Product Quality', 'Feature Request', 'User Experience',
'Performance', 'Pricing']
) AS feedback_category,
feedback_text
FROM relevant_feedback;
-- Step 3: Aggregate insights by category
SELECT
feedback_category,
COUNT(*) AS feedback_count,
AI_AGG(
feedback_text,
'Summarize the key points from this feedback, identifying the top 3 issues or requests mentioned'
) AS category_insights
FROM categorized_feedback
GROUP BY feedback_category;
This replaces:
Hours of manual review
Complex NLP pipelines with external tools
Expensive ML model training and deployment
Enhanced PIVOT and UNPIVOT with Aliases
Snowflake 2025 adds aliasing capabilities to PIVOT and UNPIVOT operations, improving readability and flexibility.
PIVOT with Column Aliases
Now you can specify aliases for pivot column names:
sql
-- Sample data: Monthly sales by product
CREATE OR REPLACE TABLE monthly_sales (
product VARCHAR,
month VARCHAR,
sales_amount DECIMAL(10,2)
);
INSERT INTO monthly_sales VALUES
('Laptop', 'Jan', 50000),
('Laptop', 'Feb', 55000),
('Laptop', 'Mar', 60000),
('Phone', 'Jan', 30000),
('Phone', 'Feb', 35000),
('Phone', 'Mar', 40000);
-- PIVOT with aliases for readable column names
SELECT *
FROM monthly_sales
PIVOT (
SUM(sales_amount)
FOR month IN ('Jan', 'Feb', 'Mar')
) AS pivot_alias (
product,
january_sales, -- Custom alias instead of 'Jan'
february_sales, -- Custom alias instead of 'Feb'
march_sales -- Custom alias instead of 'Mar'
);
-- OLD: Limited to array elements only
SELECT FILTER(
price_array,
x -> x > 100 -- Can only use array elements
)
FROM products;
Now you can reference table columns:
sql
-- NEW: Reference table columns in lambda
CREATE TABLE products (
product_id INT,
product_name VARCHAR,
prices ARRAY,
discount_threshold FLOAT
);
-- Use table column 'discount_threshold' in lambda
SELECT
product_id,
product_name,
FILTER(
prices,
p -> p > discount_threshold -- References table column!
) AS prices_above_threshold
FROM products;
Real-World Use Case: Dynamic Filtering
sql
-- Inventory table with multiple warehouse locations
CREATE TABLE inventory (
product_id INT,
warehouse_locations ARRAY,
min_stock_level INT,
stock_levels ARRAY
);
-- Filter warehouses where stock is below minimum
SELECT
product_id,
FILTER(
warehouse_locations,
(loc, idx) -> stock_levels[idx] < min_stock_level
) AS understocked_warehouses,
FILTER(
stock_levels,
level -> level < min_stock_level
) AS low_stock_amounts
FROM inventory;
Complex Example: Price Optimization
sql
-- Apply dynamic discounts based on product-specific rules
CREATE TABLE product_pricing (
product_id INT,
base_prices ARRAY,
competitor_prices ARRAY,
max_discount_pct FLOAT,
margin_threshold FLOAT
);
SELECT
product_id,
TRANSFORM(
base_prices,
(price, idx) ->
CASE
-- Don't discount if already below competitor
WHEN price <= competitor_prices[idx] * 0.95 THEN price
-- Apply discount but respect margin threshold
WHEN price * (1 - max_discount_pct / 100) >= margin_threshold
THEN price * (1 - max_discount_pct / 100)
-- Use margin threshold as floor
ELSE margin_threshold
END
) AS optimized_prices
FROM product_pricing;
Additional SQL Improvements in 2025
Beyond the major features, Snowflake 2025 includes numerous enhancements:
Enhanced SEARCH Function Modes
New search modes for more precise text matching:
PHRASE Mode: Match exact phrases with token order
sql
SELECT *
FROM documents
WHERE SEARCH(content, 'data engineering best practices', 'PHRASE');
AND Mode: All tokens must be present
sql
SELECT *
FROM articles
WHERE SEARCH(title, 'snowflake performance optimization', 'AND');
OR Mode: Any token matches (existing, now explicit)
sql
SELECT *
FROM blogs
WHERE SEARCH(content, 'sql python scala', 'OR');
Increased VARCHAR and BINARY Limits
Maximum lengths significantly increased:
VARCHAR: Now 128 MB (previously 16 MB)
VARIANT, ARRAY, OBJECT: Now 128 MB
BINARY, GEOGRAPHY, GEOMETRY: Now 64 MB
This enables:
Storing large JSON documents
Processing big text blobs
Handling complex geographic shapes
Schema-Level Replication for Failover
Selective replication for databases in failover groups:
sql
-- Replicate only specific schemas
ALTER DATABASE production_db
SET REPLICABLE_WITH_FAILOVER_GROUPS = TRUE;
ALTER SCHEMA production_db.critical_schema
SET REPLICABLE_WITH_FAILOVER_GROUPS = TRUE;
-- Other schemas not replicated, reducing costs
XML Format Support (General Availability)
Native XML support for semi-structured data:
sql
-- Load XML files
COPY INTO xml_data
FROM @my_stage/data.xml
FILE_FORMAT = (TYPE = 'XML');
-- Query XML with familiar functions
SELECT
xml_data:customer:@id::STRING AS customer_id,
xml_data:customer:name::STRING AS customer_name
FROM xml_data;
Best Practices for Snowflake SQL 2025
This Snowflake SQL tutorial wouldn’t be complete without best practices…
To maximize the benefits of these improvements:
When to Use MERGE ALL BY NAME
Use it when:
Tables have 5+ columns to map
Schemas evolve frequently
Column order varies across systems
Maintenance is a priority
Avoid it when:
Fine control needed over specific columns
Conditional updates require different logic per column
Performance is absolutely critical (marginal difference)
When to Use UNION BY NAME
Use it when:
Combining data from multiple sources with varying schemas
Schema evolution happens regularly
Missing columns should be NULL-filled
Flexibility outweighs performance
Avoid it when:
Schemas are identical and stable
Maximum performance is required
Large-scale production queries (billions of rows)
Cortex AISQL Performance Tips
Optimize AI function usage:
Filter data first before applying AI functions
Batch similar operations together
Use WHERE clauses to limit rows processed
Cache results when possible
Example optimization:
sql
-- POOR: AI function on entire table
SELECT AI_CLASSIFY(text, categories) FROM large_table;
-- BETTER: Filter first, then classify
SELECT AI_CLASSIFY(text, categories)
FROM large_table
WHERE date >= CURRENT_DATE - 7 -- Only recent data
AND text IS NOT NULL
AND LENGTH(text) > 50; -- Only substantial text
Snowflake Scripting UDF Guidelines
Best practices:
Keep UDFs deterministic when possible
Test thoroughly with edge cases
Document complex logic with comments
Consider performance for frequently-called functions
Use instead of stored procedures when called in SELECT
Migration Guide: Adopting 2025 Features
For teams transitioning to these new features:
Phase 1: Assess Current Code
Identify candidates for improvement:
sql
-- Find MERGE statements that could use ALL BY NAME
SELECT query_text
FROM snowflake.account_usage.query_history
WHERE query_text ILIKE '%MERGE INTO%'
AND query_text ILIKE '%UPDATE SET%'
AND query_text LIKE '%=%' -- Has manual mapping
AND start_time >= DATEADD(month, -3, CURRENT_TIMESTAMP());
Phase 2: Test in Development
Create test cases:
Copy production MERGE to dev
Rewrite using ALL BY NAME
Compare results with original
Benchmark performance differences
Review with team
Phase 3: Gradual Rollout
Prioritize by impact:
Start with non-critical pipelines
Monitor for issues
Expand to production incrementally
Update documentation
Train team on new syntax
Phase 4: Standardize
Update coding standards:
Prefer MERGE ALL BY NAME for new code
Refactor existing MERGE when touched
Document exceptions where old syntax preferred
Include in code reviews
Troubleshooting Common Issues
When adopting new features, watch for these issues:
MERGE ALL BY NAME Not Working
Problem: “Column count mismatch”
Solution:Ensure exact column name matches:
sql
-- Check column names match
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'TARGET_TABLE'
MINUS
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'SOURCE_TABLE';
UNION BY NAME NULL Handling
Problem: Unexpected NULLs in results
Solution:Remember missing columns become NULL:
sql
-- Make NULLs explicit if needed
SELECT
COALESCE(column_name, 'DEFAULT_VALUE') AS column_name,
...
FROM table1
UNION ALL BY NAME
SELECT * FROM table2;
Cortex AISQL Performance
Problem: AI functions running slowly
Solution:Filter data before AI processing:
sql
-- Reduce data volume first
WITH filtered AS (
SELECT * FROM large_table
WHERE conditions_to_reduce_rows
)
SELECT AI_CLASSIFY(text, categories)
FROM filtered;
Future SQL Improvements on Snowflake Roadmap
Based on community feedback and Snowflake’s direction, expect these future enhancements:
2026 Predicted Features:
More AI functions in Cortex AISQL
Enhanced MERGE with more flexible conditions
Additional higher-order functions
Improved query optimization for new syntax
Extended lambda capabilities
Community Requests:
MERGE NOT MATCHED BY SOURCE (like SQL Server)
More flexible PIVOT syntax
Additional string manipulation functions
Graph query capabilities
Conclusion: Embracing Modern SQL in Snowflake
This Snowflake SQL tutorial has covered the revolutionary 2025 improvements represent a significant leap forward in data engineering productivity. MERGE ALL BY NAME alone can save data engineers hours per week by eliminating tedious column mapping.
The key benefits:
Less boilerplate code
Fewer errors from typos
Easier maintenance as schemas evolve
More time for valuable work
For data engineers, these features mean spending less time fighting SQL syntax and more time solving business problems. The tools are more intelligent, the syntax more intuitive, and the results more reliable.
Start today by identifying one MERGE statement you can simplify with ALL BY NAME. Experience the difference these modern SQL features make in your daily work.
The future of SQL is here—and it’s dramatically simpler.
Key Takeaways
MERGE ALL BY NAME automatically matches columns by name, eliminating manual mapping
Announced September 29, 2025, this feature reduces MERGE statements from 50+ lines to 5 lines
UNION BY NAME combines data from sources with different column orders and schemas
Revolutionary Performance Without Lifting a Finger
On October 8, 2025, Snowflake unveiled Snowflake Optima—a groundbreaking optimization engine that fundamentally changes how data warehouses handle performance. Unlike traditional optimization that requires manual tuning, configuration, and ongoing maintenance, Snowflake Optima analyzes your workload patterns in real-time and automatically implements optimizations that deliver dramatically faster queries.
Here’s what makes this revolutionary:
15x performance improvements in real-world customer workloads
Zero additional cost—no extra compute or storage charges
Zero configuration—no knobs to turn, no indexes to manage
Zero maintenance—continuous automatic optimization in the background
For example, an automotive customer experienced queries dropping from 17.36 seconds to just 1.17 seconds after Snowflake Optima automatically kicked in. That’s a 15x acceleration without changing a single line of code or adjusting any settings.
Moreover, this isn’t just about faster queries—it’s about effortless performance. Snowflake Optima represents a paradigm shift where speed is simply an outcome of using Snowflake, not a goal that requires constant engineering effort.
What is Snowflake Optima?
Snowflake Optima is an intelligent optimization engine built directly into the Snowflake platform that continuously analyzes SQL workload patterns and automatically implements the most effective performance strategies. Specifically, it eliminates the traditional burden of manual query tuning, index management, and performance monitoring.
The Core Innovation of Optima:
Traditionally, database optimization requires:
First, DBAs analyzing slow queries
Second, determining which indexes to create
Third, managing index storage and maintenance
Fourth, monitoring for performance degradation
Finally, repeating this cycle continuously
With Optima, however, all of this happens automatically. Instead of requiring human intervention, Snowflake Optima:
Intelligently creates hidden indexes when beneficial
Seamlessly maintains and updates optimizations
Transparently improves performance without user action
Key Principles Behind Snowflake Optima
Fundamentally, Snowflake Optima operates on three design principles:
Performance First:Every query should run as fast as possible without requiring optimization expertise
Simplicity Always:Zero configuration, zero maintenance, zero complexity
Cost Efficiency:No additional charges for compute, storage, or the optimization service itself
Snowflake Optima Indexing: The Breakthrough Feature
At the heart of Snowflake Optima is Optima Indexing—an intelligent feature built on top of Snowflake’s Search Optimization Service. However, unlike traditional search optimization that requires manual configuration, Optima Indexing works completely automatically.
How Snowflake Optima Indexing Works
Specifically, Snowflake Optima Indexing continuously analyzes your SQL workloads to detect patterns and opportunities. When it identifies repetitive operations—such as frequent point-lookup queries on specific tables—it automatically generates hidden indexes designed to accelerate exactly those workload patterns.
For instance:
First, Optima monitors queries running on your Gen2 warehouses
Then, it identifies recurring point-lookup queries with high selectivity
Next, it analyzes whether an index would provide significant benefit
Subsequently, it automatically creates a search index if worthwhile
Finally, it maintains the index as data and workloads evolve
Importantly, these indexes operate on a best-effort basis, meaning Snowflake manages them intelligently based on actual usage patterns and performance benefits. Unlike manually created indexes, they appear and disappear as workload patterns change, ensuring optimization remains relevant.
Real-World Snowflake Optima Performance Gains
Let’s examine actual customer results to understand Snowflake Optima’s impact:
User experience: Slow dashboards, delayed analytics
After Snowflake Optima:
Average query time: 1.17 seconds (15x faster)
Partition pruning rate: 96% of micro-partitions skipped
Warehouse efficiency: Reduced resource contention
User experience: Lightning-fast dashboards, real-time insights
Notably, the improvement wasn’t limited to the directly optimized queries. Because Snowflake Optima reduced resource contention on the warehouse, even queries that weren’t directly accelerated saw a 46% improvement in runtime—almost 2x faster.
Furthermore, average job runtime on the entire warehouse improved from 2.63 seconds to 1.15 seconds—more than 2x faster overall.
The Magic of Micro-Partition Pruning
To understand Snowflake Optima’s power, you need to understand micro-partition pruning:
Snowflake stores data in compressed micro-partitions (typically 50-500 MB). When you run a query, Snowflake first determines which micro-partitions contain relevant data through partition pruning.
Snowflake Optima is exclusively available on Snowflake Generation 2 (Gen2) standard warehouses. Therefore, ensure your infrastructure meets this requirement before expecting Optima benefits.
To check your warehouse generation:
sql
SHOW WAREHOUSES;
-- Look for TYPE column: STANDARD warehouses on Gen2
If needed, migrate to Gen2 warehouses through Snowflake’s upgrade process.
Best-Effort Optimization Model
Unlike manually applied search optimization that guarantees index creation, Snowflake Optima operates on a best-effort basis:
What this means:
Optima creates indexes when it determines they’re beneficial
Indexes may appear and disappear as workloads evolve
Optimization adapts to changing query patterns
Performance improves automatically but variably
When to use manual search optimization instead:
For specialized workloads requiring guaranteed performance—such as:
Emergency response systems (reliability non-negotiable)
In these cases, manually applying search optimization provides consistent index freshness and predictable performance characteristics.
Monitoring Optima Performance
Transparency is crucial for understanding optimization effectiveness. Fortunately, Snowflake provides comprehensive monitoring capabilities through the Query Profile tab in Snowsight.
Query Insights Pane
The Query Insights pane displays detected optimization insights for each query:
What you’ll see:
Each type of insight detected for a query
Every instance of that insight type
Explicit notation when “Snowflake Optima used”
Details about which optimizations were applied
To access:
Navigate to Query History in Snowsight
Select a query to examine
Open the Query Profile tab
Review the Query Insights pane
When Snowflake Optima has optimized a query, you’ll see “Snowflake Optima used” clearly indicated with specifics about the optimization applied.
Statistics Pane: Pruning Metrics
The Statistics pane quantifies Snowflake Optima’s impact through partition pruning metrics:
Key metric: “Partitions pruned by Snowflake Optima”
What it shows:
Number of partitions skipped during query execution
Percentage of total partitions pruned
Improvement in data scanning efficiency
Direct correlation to performance gains
For example:
Total partitions: 10,389
Pruned by Snowflake Optima: 8,343 (80%)
Total pruning rate: 96%
Result: 15x faster query execution
This metric directly correlates to:
Faster query completion times
Reduced compute costs
Lower resource contention
Better overall warehouse efficiency
Use Cases
Let’s explore specific scenarios where Optima delivers exceptional value:
Use Case 1: E-Commerce Analytics
A large retail chain analyzes customer behavior across e-commerce and in-store platforms.
Challenge:
Billions of rows across multiple tables
Frequent point-lookups on customer IDs
Filter-heavy queries on product SKUs
Time-sensitive queries on timestamps
Before Optima:
Dashboard queries: 8-12 seconds average
Ad-hoc analysis: Extremely slow
User experience: Frustrated analysts
Business impact: Delayed decision-making
With Snowflake Optima:
Dashboard queries: Under 1 second
Ad-hoc analysis: Lightning fast
User experience: Delighted analysts
Business impact: Real-time insights driving revenue
Result:10x performance improvement enabling real-time personalization and dynamic pricing strategies.
Use Case 2: Financial Services Risk Analysis
A global bank runs complex risk calculations across portfolio data.
Challenge:
Massive datasets with billions of transactions
Regulatory requirements for rapid risk assessment
Recurring queries on account numbers and counterparties
Performance critical for compliance
Before Snowflake Optima:
Risk calculations: 15-20 minutes
Compliance reporting: Hours to complete
Warehouse costs: High due to long-running queries
Regulatory risk: Potential delays
With Snowflake Optima:
Risk calculations: 2-3 minutes
Compliance reporting: Real-time available
Warehouse costs: 40% reduction through efficiency
Regulatory risk: Eliminated through speed
Result:8x faster risk assessment ensuring regulatory compliance and enabling more sophisticated risk modeling.
Use Case 3: IoT Sensor Data Analysis
A manufacturing company analyzes sensor data from factory equipment.
Challenge:
High-frequency sensor readings (millions per hour)
Integration with other Snowflake intelligent features
Long-term (2027+):
AI-powered optimization using machine learning
Autonomous database management capabilities
Self-healing performance issues automatically
Cognitive optimization understanding business context
Getting Started with Snowflake Optima
The beauty of Snowflake Optima is that getting started requires virtually no effort:
Step 1: Verify Gen2 Warehouses
Check if you’re running Generation 2 warehouses:
sql
SHOW WAREHOUSES;
Look for:
TYPE column: Should show STANDARD
Generation: Contact Snowflake if unsure
If needed:
Contact Snowflake support for Gen2 upgrade
Migration is typically seamless and fast
Step 2: Run Your Normal Workloads
Simply continue running your existing queries:
No configuration needed:
Snowflake Optima monitors automatically
Optimizations apply in the background
Performance improves without intervention
No changes required:
Keep existing query patterns
Maintain current warehouse configurations
Continue normal operations
Step 3: Monitor the Impact
After a few days or weeks, review the results:
In Snowsight:
Go to Query History
Select queries to examine
Open Query Profile tab
Look for “Snowflake Optima used”
Review partition pruning statistics
Key metrics:
Query duration improvements
Partition pruning percentages
Warehouse efficiency gains
Step 4: Share the Success
Document and communicate Snowflake Optima benefits:
For stakeholders:
Performance improvements (X times faster)
Cost savings (reduced compute consumption)
User satisfaction (faster dashboards, better experience)
For technical teams:
Pruning statistics (data scanning reduction)
Workload patterns (which queries optimized)
Best practices (maximizing Optima effectiveness)
Snowflake Optima FAQs
What is Snowflake Optima?
Snowflake Optima is an intelligent optimization engine that automatically analyzes SQL workload patterns and implements performance optimizations without requiring configuration or maintenance. It delivers dramatically faster queries at zero additional cost.
How much does Snowflake Optima cost?
Zero. Snowflake Optima comes at no additional charge beyond your standard Snowflake costs. There are no compute charges, storage charges, or service charges for using Snowflake Optima.
What are the requirements for Snowflake Optima?
Snowflake Optima requires Generation 2 (Gen2) standard warehouses. It’s automatically enabled on qualifying warehouses without any configuration needed.
How does Snowflake Optima compare to manual Search Optimization Service?
Snowflake Optima operates automatically without configuration and at zero cost, while manual Search Optimization Service requires configuration and incurs compute and storage charges. For most workloads, Snowflake Optima is the better choice. However, mission-critical workloads requiring guaranteed performance may still benefit from manual optimization.
How do I monitor Snowflake Optima performance?
Use the Query Profile tab in Snowsight to monitor Snowflake Optima. The Query Insights pane shows when Snowflake Optima was used, and the Statistics pane displays partition pruning metrics showing performance impact.
Can I disable Snowflake Optima?
No, Snowflake Optima cannot be disabled on Gen2 warehouses. However, it operates on a best-effort basis and only creates optimizations when beneficial, so there’s no downside to having it active.
What types of queries benefit from Snowflake Optima?
Snowflake Optima is most effective for point-lookup queries with highly selective filters on large tables, especially recurring query patterns. Queries returning small percentages of rows see the biggest improvements.
Conclusion: The Dawn of Effortless Performance
Snowflake Optima marks a fundamental shift in how organizations approach database performance. For decades, achieving fast query performance required dedicated DBAs, constant tuning, and careful optimization. With Snowflake Optima, however, speed is simply an outcome of using Snowflake.
The results speak for themselves:
15x performance improvements in real-world workloads
Zero additional cost or configuration required
Zero maintenance burden on teams
Continuous improvement as workloads evolve
More importantly, Snowflake Optima represents a strategic advantage for organizations managing complex data operations. By removing the burden of manual optimization, your team can focus on deriving insights rather than tuning infrastructure.
The self-adapting nature of Snowflake Optima means your data warehouse becomes smarter over time, learning from usage patterns and continuously improving without human intervention. This creates a virtuous cycle where performance naturally improves as your workloads evolve and grow.
Snowflake Optima streamlines optimization for data engineers, saving countless hours. Analysts benefit from accelerated insights and smoother user experiences. Meanwhile, executives see improved ROI — all without added investment.
The future of database performance isn’t about smarter DBAs or better optimization tools—it’s about intelligent systems that optimize themselves. Optima is that future, available today.
Are you ready to experience effortless performance?
Key Takeaways
Snowflake Optima delivers automatic query optimization without configuration or cost
Announced October 8, 2025, currently available on Gen2 standard warehouses
Real customers achieve 15x performance improvements automatically
Optima Indexing continuously monitors workloads and creates hidden indexes intelligently
Zero additional charges for compute, storage, or the optimization service
Partition pruning improvements from 30% to 96% drive dramatic speed increases
Best-effort optimization adapts to changing workload patterns automatically
Monitoring available through Query Profile tab in Snowsight
Mission-critical workloads can still use manual search optimization for guaranteed performance
Future roadmap includes AI-powered optimization and autonomous database management