{"id":104941,"date":"2024-12-16T17:45:08","date_gmt":"2024-12-16T17:45:08","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=104941"},"modified":"2025-06-30T12:13:12","modified_gmt":"2025-06-30T12:13:12","slug":"when-should-you-employ-snowflake-materialized-views","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/snowflake\/when-should-you-employ-snowflake-materialized-views\/","title":{"rendered":"When Should You Employ Snowflake Materialized Views?"},"content":{"rendered":"\n<p>I\u2019ve used both Snowflake views and materialized views with varying degrees of success. Both have their place and it\u2019s on us to determine when to use them based on what they offer. This guide will help you in understanding how views and materialized views work in Snowflake and show you practical examples along with potential benefits.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-what-before-the-when\"><a id=\"post-104941-_heading=h.30j0zll\"><\/a>The &#8220;What&#8221; Before the &#8220;When&#8221;<\/h2>\n\n\n\n<p>Before we dive into when to use materialized views, let&#8217;s quickly refresh our understanding. A materialized view in Snowflake is essentially a pre-computed result set of a query that&#8217;s stored and maintained automatically. Think of it as a cache that Snowflake manages for you, keeping it in sync with your source data.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-views-vs-materialized-views-understanding-the-difference\"><a id=\"post-104941-_heading=h.1fob9te\"><\/a>Views vs. Materialized Views: Understanding the Difference<\/h3>\n\n\n\n<p>Let&#8217;s compare traditional views with materialized views:<\/p>\n\n\n\n<p><a id=\"post-104941-_heading=h.3znysh7\"><\/a> Traditional views serve as stored queries that execute at runtime. Their characteristics make them ideal for certain scenarios:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Computed at query time<\/li>\n\n\n\n<li>Always reflect current data<\/li>\n\n\n\n<li>No additional storage cost<\/li>\n\n\n\n<li>Consume compute resources with each query<\/li>\n\n\n\n<li>Best for: Simple transformations, current data requirements, frequently changing data<\/li>\n<\/ul>\n<\/div>\n\n\n<p><a id=\"post-104941-_heading=h.2et92p0\"><\/a> On the other hand, materialized views basically are turned into a table that is refreshed anytime data is changed. Here is an overview of their attributes:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Pre-computed and stored<\/li>\n\n\n\n<li>Automatically maintained by Snowflake<\/li>\n\n\n\n<li>Require additional storage<\/li>\n\n\n\n<li>Minimal compute at query time<\/li>\n\n\n\n<li>Best for: Complex computations, frequent access, relatively stable data<\/li>\n<\/ul>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-understanding-real-world-query-patterns\"><a id=\"post-104941-_heading=h.9ohry26orqfb\"><\/a>Understanding Real-World Query Patterns<\/h3>\n\n\n\n<p>Let&#8217;s examine a typical business intelligence scenario that many organizations face. Consider a sales analysis system that needs to provide:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Daily sales summaries by region<\/li>\n\n\n\n<li>Customer purchase patterns<\/li>\n\n\n\n<li>Product category performance<\/li>\n\n\n\n<li>Year-over-year comparisons<\/li>\n<\/ul>\n<\/div>\n\n\n<p>Here&#8217;s an example query that many organizations might run frequently:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">WITH sales_summary AS (\n    SELECT \n        DATE_TRUNC('month', sale_date) as sale_month,\n        product_category,\n        region,\n        SUM(amount) as total_sales,\n        COUNT(DISTINCT customer_id) as unique_customers,\n        AVG(amount) as avg_sale_amount\n    FROM sales s\n    JOIN products p ON s.product_id = p.product_id\n    JOIN customers c ON s.customer_id = c.customer_id\n    WHERE sale_date &gt;= DATEADD(year, -1, CURRENT_DATE())\n    GROUP BY 1, 2, 3\n)\nSELECT * \nFROM sales_summary;<\/pre>\n\n\n\n<p>This query performs several resource-intensive operations:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Joining three tables (sales, products, customers)<\/li>\n\n\n\n<li>Filtering for the last year&#8217;s data<\/li>\n\n\n\n<li>Grouping by month, product category, and region<\/li>\n\n\n\n<li>Performing multiple aggregations (<code>SUM<\/code>, <code>COUNT<\/code> <code>DISTINCT<\/code>, <code>AVG<\/code>)<\/li>\n<\/ul>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-comparative-performance-analysis-views-vs-materialized-views\"><a id=\"post-104941-_heading=h.tyjcwt\"><\/a>Comparative Performance Analysis: Views vs. Materialized Views<\/h3>\n\n\n\n<p>When comparing regular views and materialized views in Snowflake, several key performance metrics reveal distinct operational characteristics. Let&#8217;s examine their performance profiles using a real-world business intelligence scenario.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-performance-profile\"><a id=\"post-104941-_heading=h.3dy6vkm\"><\/a>Performance profile<\/h2>\n\n\n\n<p>A regular view in Snowflake executes its underlying query logic with each access. For our sample sales analysis query on a medium-sized dataset (about 100 million sales records, 1 million customers, and 10,000 products over multiple years), this means consistently processing raw data through multiple steps. Each execution requires 45-60 seconds of processing time as Snowflake reads from all three source tables, executes join operations, and calculates aggregations from scratch.<\/p>\n\n\n\n<p>This processing consumes approximately 0.5 compute credits per execution due to the repeated full-scale data processing requirements. However, regular views require no additional storage space as they only maintain the query definition. On the other hand, materialized views present a different performance model. The initial creation requires a one-time cost of approximately 60 seconds, comparable to a single execution of the regular view. Subsequent query execution times drop dramatically to 1-2 seconds, representing a 95% improvement over the regular view. This efficiency stems from accessing pre-computed results rather than processing raw data.<\/p>\n\n\n\n<p>One thing that is an interesting trade off is computing costs. While you do have to complete the results, once you are using the materialized views in queries the amount of compute may be quite a bit lower. The compute credit consumption reflects this efficiency, using only 0.02 credits per query\u2014a 96% reduction compared to the regular view. This significant reduction occurs because the materialized view eliminates the need for repeated raw data processing and join operations.<\/p>\n\n\n\n<p>Materialized views introduce two additional operational considerations. First, they require periodic refresh operations, taking a few seconds to update when source data changes. Snowflake optimizes these refreshes through incremental maintenance, processing only changed records. Second, they incur storage costs to store the pre-computed results and aggregated data.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-real-world-performance-impact\"><a id=\"post-104941-_heading=h.2s8eyo1\"><\/a>Real-World Performance Impact<\/h2>\n\n\n\n<p>Consider a scenario where this report runs multiple times daily:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Core business hours: 8 AM to 6 PM<\/li>\n\n\n\n<li>Updates every 15 minutes<\/li>\n\n\n\n<li>Average of 50 concurrent users during peak hours<\/li>\n\n\n\n<li>Each user runs 5-10 different report variations<\/li>\n<\/ul>\n<\/div>\n\n\n<p><strong>Regular View Operation Costs:<\/strong><\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Daily processing time: 83 minutes (5,000 seconds)<\/li>\n\n\n\n<li>Monthly credit consumption: 1,500 credits<\/li>\n\n\n\n<li>Monthly operational cost: $6,000 (at $4 per credit)<\/li>\n\n\n\n<li>Storage cost: None<\/li>\n<\/ul>\n<\/div>\n\n\n<p><strong>Materialized View Operation Costs<\/strong>:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Daily processing time: 2.5 minutes (150 seconds)<\/li>\n\n\n\n<li>Monthly credit consumption: 132 credits (including refresh operations)<\/li>\n\n\n\n<li>Monthly operational cost: $528 (at $4 per credit)<\/li>\n\n\n\n<li>Storage cost: $40 monthly for 100MB<\/li>\n\n\n\n<li>Total monthly cost: $568<\/li>\n<\/ul>\n<\/div>\n\n\n<p>The materialized view implementation delivers a net monthly savings of $5,432, representing a 90% reduction in operational costs.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-optimal-implementation-scenarios\"><a id=\"post-104941-_heading=h.17dp8vu\"><\/a>Optimal Implementation Scenarios<\/h2>\n\n\n\n<p>The materialized view pattern demonstrates maximum effectiveness when a few different things occur. when the query frequency exceeds update frequency of the underlying data, allowing the benefits of pre-computation to outweigh refresh overhead. Implementation supports consistent reporting requirements, such as daily dashboards and regular reports. The queries involve complex calculations that benefit from reuse rather than recalculation. The source datasets are large, but the aggregated results maintain a significantly smaller footprint.<\/p>\n\n\n\n<p>This performance analysis demonstrates that while both view types serve important purposes, materialized views offer substantial performance and cost benefits for frequently accessed, computation-heavy queries where data freshness requirements align with scheduled refresh intervals.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-data-driven-decision-making-let-usage-guide-you\"><a id=\"post-104941-_heading=h.3rdcrjn\"><\/a>Data-Driven Decision Making: Let Usage Guide You<\/h2>\n\n\n\n<p>One of the smartest ways to decide whether you need a materialized view is to look at your query patterns. Snowflake makes this incredibly easy with its <code>SNOWHOUSE.ACCOUNT_USAGE.ACCESS_HISTORY<\/code> view. However, it&#8217;s important to understand how Snowflake tracks and matches queries before diving into the analysis. Snowflake&#8217;s <code>ACCESS_HISTORY<\/code> view tracks queries based on their exact text representation. Here are key points to understand:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Exact Matching: Queries must match exactly to be considered the same, including:<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Spacing and formatting<\/li>\n\n\n\n<li>Case sensitivity<\/li>\n\n\n\n<li>Literal values in <code>WHERE<\/code> clauses<\/li>\n<\/ul>\n<\/div><\/li>\n\n\n\n<li>Common Variations That Create Different Entries:<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Different <code>WHERE<\/code> clause parameters<\/li>\n\n\n\n<li>Changed date ranges<\/li>\n\n\n\n<li>Modified <code>LIMIT<\/code> values<\/li>\n\n\n\n<li>Altered <code>ORDER BY<\/code> clauses<\/li>\n<\/ul>\n<\/div><\/li>\n<\/ul>\n<\/div>\n\n\n<p>For example, these queries would be considered different:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">SELECT * FROM sales WHERE date = '2024-01-01';\n\nSELECT * FROM sales WHERE date = '2024-01-02';<\/pre>\n\n\n\n<p>To identify truly repetitive patterns, you might need to:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Look for similar query structures<\/li>\n\n\n\n<li>Ignore specific parameter values<\/li>\n\n\n\n<li>Consider parameterizing frequently used queries<\/li>\n<\/ul>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-analyzing-query-patterns\"><a id=\"post-104941-_heading=h.26in1rg\"><\/a>Analyzing Query Patterns<\/h3>\n\n\n\n<p>Here&#8217;s a query that will show you the number of times a query has been executed and some additional statistics that will help you to see at a high level how much the query has been used and what it is likely costing you:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">-- Set session variables for configurable parameters\nSET cost_per_credit = 4;\nSET analysis_period_months = 1;\nSET min_execution_count = 100;\nWITH query_metrics AS (\n    SELECT \n        QUERY_TEXT,\n        COUNT(*) as execution_count,\n        AVG(TOTAL_ELAPSED_TIME)\/1000 \n                        as avg_execution_time_seconds,\n        SUM(CREDITS_USED) as total_credits_used,\n        SUM(BYTES_SCANNED)\/POW(1024, 3) as gb_scanned\n    FROM SNOWFLAKE.ACCOUNT_USAGE.ACCESS_HISTORY\n    WHERE START_TIME &gt;= DATEADD(month, -$analysis_period_months, CURRENT_TIMESTAMP())\n    GROUP BY QUERY_TEXT\n    HAVING COUNT(*) &gt; $min_execution_count\n)\nSELECT \n    *,\n    total_credits_used * $cost_per_credit as estimated_monthly_credit_cost\nFROM query_metrics\nORDER BY execution_count DESC;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-interpreting-usage-metrics\"><a id=\"post-104941-_heading=h.lnxbz9\"><\/a>Interpreting Usage Metrics<\/h3>\n\n\n\n<p>When analyzing results, look for certain patterns. While your needs certainly may be different, here is what I do as a starting point:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>High Frequency Queries (&gt;100 executions per month)<\/li>\n\n\n\n<li>Significant Execution Time (&gt;10 seconds average)<\/li>\n\n\n\n<li>Substantial Credit Usage (&gt;0.1 credits per execution)<\/li>\n\n\n\n<li>Large Data Scans (&gt;1GB per query)<\/li>\n<\/ul>\n<\/div>\n\n\n<p>Real-world benchmark from a customer case, where they are implementing a Daily Sales Dashboard.<\/p>\n\n\n\n<p>Before implementing materialized views for commonly reused queries:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Executions: 2,880\/month (every 15 minutes)<\/li>\n\n\n\n<li>Avg Time: 45 seconds<\/li>\n\n\n\n<li>Credits: 1,440 credits\/month<\/li>\n\n\n\n<li>Cost: $5,760\/month<\/li>\n<\/ul>\n<\/div>\n\n\n<p>After identifying and implementing the materialized views:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Executions: 2,880\/month<\/li>\n\n\n\n<li>Avg Time: 2 seconds<\/li>\n\n\n\n<li>Credits: 57.6 credits\/month<\/li>\n\n\n\n<li>Cost: $230.4\/month<\/li>\n\n\n\n<li>MV Refresh: 96 credits\/month<\/li>\n\n\n\n<li>Storage: $20\/month<\/li>\n\n\n\n<li>Net Monthly Savings: $5,413.60<\/li>\n<\/ul>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-sweet-spot-when-to-pull-the-trigger\"><a id=\"post-104941-_heading=h.35nkun2\"><\/a>The Sweet Spot: When to Pull the Trigger<\/h2>\n\n\n\n<p>In this section, I will discuss the sweet spot to realize that you need to use materialized views versus regular views. The sweet spot for materialized views occurs when you have complex queries that are executed frequently but don&#8217;t require real-time data updates. Let&#8217;s explore two common scenarios where materialized views shine.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-complex-aggregations-with-moderate-updates\"><a id=\"post-104941-_heading=h.1ksv4uv\"><\/a>Complex Aggregations with Moderate Updates<\/h3>\n\n\n\n<p>Ideal scenarios for materialized views include complex aggregations that combine data from multiple tables and perform heavy computations are perfect candidates for materialization, especially when the underlying data updates are predictable and moderate. Consider a typical business intelligence scenario where you need daily sales summaries that combine transaction data with customer and product information.<\/p>\n\n\n\n<p>This type of analysis typically involves:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Joining multiple large tables (sales, customers, products)<\/li>\n\n\n\n<li>Performing multiple aggregations (sums, counts, averages)<\/li>\n\n\n\n<li>Grouping by multiple dimensions<\/li>\n\n\n\n<li>Processing millions of records<\/li>\n\n\n\n<li>Running the same calculations repeatedly throughout the day<\/li>\n<\/ul>\n<\/div>\n\n\n<p>Here&#8217;s an example materialized view for such a scenario:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">CREATE MATERIALIZED VIEW daily_sales_summary AS\nSELECT \n    DATE_TRUNC('day', sale_timestamp) as sale_date,\n    product_category,\n    customer_region,\n    customer_segment,\n    SUM(sale_amount) as total_sales,\n    COUNT(DISTINCT customer_id) as unique_customers,\n    SUM(quantity) as units_sold,\n    SUM(sale_amount)\/NULLIF(SUM(quantity), 0) as avg_unit_price\nFROM sales s\n  JOIN customers c ON s.customer_id = c.customer_id\n  JOIN products p ON s.product_id = p.product_id\nGROUP BY 1, 2, 3, 4;<\/pre>\n\n\n\n<p>In a typical enterprise environment, this view might process:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Sales transactions spanning multiple years<\/li>\n\n\n\n<li>Millions of customer records<\/li>\n\n\n\n<li>Thousands of products across multiple categories<\/li>\n\n\n\n<li>Data that updates daily but not in real-time<\/li>\n<\/ul>\n<\/div>\n\n\n<p>The performance impact is substantial when dealing with this scale of data:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Direct queries would need to process and join all historical data each time<\/li>\n\n\n\n<li>Materialized views maintain pre-computed results, only processing new or changed records<\/li>\n\n\n\n<li>Queries against the materialized view access pre-aggregated data, dramatically reducing processing time<\/li>\n<\/ul>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-join-heavy-queries\"><a id=\"post-104941-_heading=h.44sinio\"><\/a>Join-Heavy Queries<\/h3>\n\n\n\n<p>Another sweet spot for materialized views is when you need to combine data from multiple tables to create comprehensive views of your data, often called &#8220;360-degree views.&#8221; These views typically bring together information from across your data warehouse but don&#8217;t require real-time updates.<\/p>\n\n\n\n<p>Consider this example that creates a customer-centric view combining orders, products, and returns data:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">CREATE MATERIALIZED VIEW customer_360 AS\nSELECT \n    c.customer_id,\n    c.region,\n    c.segment,\n    COUNT(DISTINCT o.order_id) as total_orders,\n    SUM(o.order_amount) as lifetime_value,\n    AVG(o.order_amount) as avg_order_value,\n    MAX(o.order_date) as last_order_date,\n    COUNT(DISTINCT p.product_category) as categories_purchased,\n    SUM(CASE WHEN r.return_id IS NOT NULL THEN 1 ELSE 0 END) as returns_count\nFROM customers c\n  LEFT JOIN orders o ON c.customer_id = o.customer_id\n  LEFT JOIN order_items oi ON o.order_id = oi.order_id\n  LEFT JOIN products p ON oi.product_id = p.product_id\n  LEFT JOIN returns r ON o.order_id = r.order_id\nGROUP BY 1, 2, 3;<\/pre>\n\n\n\n<p>This type of view typically processes:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Multiple years of order history<\/li>\n\n\n\n<li>Complex relationships across multiple tables<\/li>\n\n\n\n<li>Large volumes of transaction data<\/li>\n\n\n\n<li>Information that updates daily or weekly<\/li>\n<\/ul>\n<\/div>\n\n\n<p>The benefits of materialization in this scenario include:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Eliminating the need to repeatedly join large tables<\/li>\n\n\n\n<li>Maintaining pre-computed aggregates across multiple dimensions<\/li>\n\n\n\n<li>Providing fast access to commonly needed customer metrics<\/li>\n\n\n\n<li>Reducing the compute resources needed for frequent reporting<\/li>\n<\/ul>\n<\/div>\n\n\n<p>In both scenarios, the materialized views provide significant benefits because:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>The underlying data changes are predictable and less frequent than query access<\/li>\n\n\n\n<li>The computations are complex and resource-intensive<\/li>\n\n\n\n<li>The same data is queried frequently by multiple users or applications<\/li>\n\n\n\n<li>The storage overhead of the materialized results is justified by the performance gains<\/li>\n\n\n\n<li>Real-time data isn&#8217;t a requirement<\/li>\n<\/ul>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-when-to-think-twice\"><a id=\"post-104941-_heading=h.2jxsxqh\"><\/a>When to Think Twice<\/h2>\n\n\n\n<p>While materialized views offer significant benefits in many scenarios, there are specific situations where they might not be the optimal solution. Let&#8217;s explore these cases to help you make informed decisions about when to use alternative approaches.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-highly-volatile-data\"><a id=\"post-104941-_heading=h.didgu9ief8ky\"><\/a>Highly Volatile Data<\/h3>\n\n\n\n<p>One of the primary scenarios where materialized views may not be ideal is when dealing with rapidly changing data. Consider real-time trading systems as an example. In such environments, data can change thousands of times per second, and maintaining materialized views becomes more costly than querying the base tables directly.<\/p>\n\n\n\n<p>To determine if your data is too volatile for materialized views, you can analyze the refresh patterns using this query:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">--Highly Volatile Data:\nSELECT\n    materialized_view_name,\n    COUNT(*) as refresh_count,\n    AVG(TIMESTAMPDIFF('milliseconds', \n                      refresh_start_time, refresh_end_time)) \n                                            as avg_refresh_ms,\n    MAX(rows_updated) as max_rows_updated,\n    SUM(rows_updated) as total_rows_updated,\n    SUM(rows_updated)\/NULLIF(COUNT(*), 0) \n                             as avg_rows_per_refresh\nFROM TABLE(INFORMATION_SCHEMA.MATERIALIZED_VIEW_REFRESH_HISTORY())\nWHERE refresh_end_time &gt;= \n                DATEADD(hour, -24, CURRENT_TIMESTAMP())\nGROUP BY materialized_view_name\nORDER BY refresh_count DESC;<\/pre>\n\n\n\n<p>This query helps you understand:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>How frequently your materialized views are refreshing<\/li>\n\n\n\n<li>The average time spent on refreshes<\/li>\n\n\n\n<li>The volume of data being updated<\/li>\n\n\n\n<li>The pattern of updates (large batches vs. small frequent updates)<\/li>\n<\/ul>\n<\/div>\n\n\n<p>Consider avoiding materialized views when you see patterns like:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Multiple refreshes per minute<\/li>\n\n\n\n<li>High average refresh times relative to query times<\/li>\n\n\n\n<li>Large portions of the dataset being updated in each refresh<\/li>\n\n\n\n<li>Consistent update patterns throughout the day<\/li>\n<\/ul>\n<\/div>\n\n\n<p>For example, in a trading system scenario:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Market data updates could occur thousands of times per second<\/li>\n\n\n\n<li>Position calculations might change hundreds of times per minute<\/li>\n\n\n\n<li>Risk calculations could update dozens of times per hour<\/li>\n<\/ul>\n<\/div>\n\n\n<p>In these cases, consider alternatives such as:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Regular views for real-time access to current data<\/li>\n\n\n\n<li>Streaming solutions for real-time processing<\/li>\n\n\n\n<li>Time-windowed materialized views for historical analysis<\/li>\n\n\n\n<li>Hybrid approaches using both views and materialized views for different time periods<\/li>\n<\/ul>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-storage-cost-analysis\"><a id=\"post-104941-_heading=h.3j2qqm3\"><\/a>Storage Cost Analysis:<\/h2>\n\n\n\n<p>Understanding storage costs is crucial when implementing materialized views in Snowflake. Let&#8217;s explore how to analyze these costs and interpret the results to make informed decisions about materialized view implementations.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-analyzing-storage-usage\"><a id=\"post-104941-_heading=h.8pefmsgcdyrz\"><\/a>Analyzing Storage Usage<\/h2>\n\n\n\n<p>To effectively monitor and analyze storage costs for your materialized views, you can use the following query:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">SELECT\n    table_name,\n    table_type,\n    row_count,\n    bytes\/POW(1024,3) as size_gb,\n    bytes\/POW(1024,3) * 40 as monthly_storage_cost_dollars\nFROM information_schema.tables\nWHERE table_type = 'MATERIALIZED VIEW'\nORDER BY bytes DESC;<\/pre>\n\n\n\n<p><strong>Example result set<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">| table_name          | table_type        | row_count | size_gb | monthly_storage_cost_dollars |\n|---------------------|-------------------|-----------|---------|------------------------------|\n| SALES_SUMMARY_MV    | MATERIALIZED VIEW | 1000000   | 2.5     | 0.10                    \n| CUSTOMER_360_MV     | MATERIALIZED VIEW | 500000    | 5.0     | 0.20                      \n| DAILY_METRICS_MV    | MATERIALIZED VIEW | 2000000   | 10.0    | 0.40  <\/pre>\n\n\n\n<p>This query and result set provides several key metrics that help you understand your storage utilization:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>The name and type of each materialized view<\/li>\n\n\n\n<li>The number of rows stored<\/li>\n\n\n\n<li>The size in gigabytes<\/li>\n\n\n\n<li>The estimated monthly storage cost in dollars<\/li>\n<\/ul>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-understanding-storage-metrics-and-cost-calculations\"><a id=\"post-104941-_heading=h.1y810tw\"><\/a>Understanding Storage Metrics and Cost Calculations<\/h2>\n\n\n\n<p>In this section we will look at some of the ways you can calculate and predict costs of storage.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-storage-size-calculation\"><a id=\"post-104941-_heading=h.lu21t3yh6hmb\"><\/a>Storage Size Calculation<\/h3>\n\n\n\n<p>The fundamental storage calculation in Snowflake converts raw byte measurements into more manageable gigabyte values using the expression bytes\/POW(1024,3). (Note the <a href=\"https:\/\/docs.snowflake.com\/en\/sql-reference\/functions\/pow\">POW<\/a> function raises the number in the first parameter to the power in the second parameter.) This conversion follows the standard binary progression of digital storage units. Starting with the basic unit of a byte, we progress through kilobytes, megabytes, and finally to gigabytes. Each step up multiplies by 1024: a kilobyte contains 1024 bytes, a megabyte encompasses 1024 kilobytes (or 1024\u00b2 bytes), and a gigabyte consists of 1024 megabytes (or 1024\u00b3 bytes).<\/p>\n\n\n\n<p>To illustrate this conversion, consider a materialized view consuming 1073741824 bytes of storage. When we apply our conversion formula (1073741824\/POW(1024,3)), the result simplifies to exactly 1.0 GB, providing a more intuitive measure of the storage footprint.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-cost-calculation-framework\"><a id=\"post-104941-_heading=h.2xcytpi\"><\/a>Cost Calculation Framework<\/h3>\n\n\n\n<p>Snowflake&#8217;s storage cost calculation builds upon this size conversion by incorporating the standard rate of $40 per terabyte per month. The formula bytes\/POW(1024,3) * 40 provides a direct monthly cost estimate in dollars. This calculation takes the size in gigabytes and applies the proportional cost based on Snowflake&#8217;s terabyte pricing.<\/p>\n\n\n\n<p>For practical application, consider a materialized view occupying 100GB of storage. The monthly cost calculation would be: 100 GB * ($40\/1024) \u2248 $3.91. This demonstrates how even relatively large, materialized views can maintain reasonable storage costs while providing significant performance benefits.<\/p>\n\n\n\n<p>This cost framework helps data engineers and architects make informed decisions about materialized view implementations by providing clear visibility into the storage cost implications of their design choices. Understanding these metrics becomes particularly valuable when evaluating the trade-offs between query performance improvements and associated storage costs.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-monitoring-and-maintenance\"><a id=\"post-104941-_heading=h.1ci93xb\"><\/a>Monitoring and Maintenance<\/h2>\n\n\n\n<p>Effective monitoring of materialized views is crucial for maintaining optimal performance and cost efficiency. This section covers key monitoring strategies and maintenance practices to ensure your materialized views continue to provide value over time.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-performance-monitoring-understanding-refresh-operations\"><a id=\"post-104941-_heading=h.3whwml4\"><\/a>Performance Monitoring<a id=\"post-104941-_heading=h.7u2rlh4on7su\"><\/a>Understanding Refresh Operations<\/h3>\n\n\n\n<p>One of the most critical aspects to monitor is how your materialized views handle refresh operations. Refreshes occur when the underlying data changes, and understanding their patterns helps optimize view performance. Here&#8217;s a comprehensive monitoring query that tracks refresh operations:<\/p>\n\n\n\n<p><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">WITH mv_stats AS (\nSELECT\n    materialized_view_name,\n    COUNT(*) as refresh_count,\n    AVG(TIMESTAMPDIFF('seconds', refresh_start_time, refresh_end_time)) as avg_refresh_seconds,\n    SUM(rows_updated) as total_rows_updated,\n    SUM(bytes_updated)\/POW(1024,3) as gb_updated\nFROM TABLE(INFORMATION_SCHEMA.MATERIALIZED_VIEW_REFRESH_HISTORY())\nWHERE refresh_end_time &gt;= DATEADD(day, -7, CURRENT_TIMESTAMP())\nGROUP BY 1\n)\nSELECT \n    m.*,\n    t.bytes\/POW(1024,3) as current_size_gb,\n    t.row_count as total_rows,\n    mv_stats.refresh_count as weekly_refreshes,\n    mv_stats.avg_refresh_seconds,\n    mv_stats.total_rows_updated,\n    mv_stats.gb_updated\nFROM information_schema.materialized_views m\nJOIN information_schema.tables t \n    ON m.table_name = t.table_name\nLEFT JOIN mv_stats \n    ON m.table_name = mv_stats.materialized_view_name;<\/pre>\n\n\n\n<p>This query helps you understand:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>How frequently each view refreshes<\/li>\n\n\n\n<li>The average time taken for refreshes<\/li>\n\n\n\n<li>The volume of data being updated<\/li>\n\n\n\n<li>Patterns in refresh timing<\/li>\n\n\n\n<li>Overall impact on system resources<\/li>\n<\/ul>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-key-monitoring-metrics\"><a id=\"post-104941-_heading=h.2bn6wsx\"><\/a>Key Monitoring Metrics<\/h2>\n\n\n\n<p>When monitoring materialized views, focus on these essential metrics:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-refresh-operations\"><a id=\"post-104941-_heading=h.m61rz7fxhxmf\"><\/a>Refresh Operations<\/h3>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Frequency: Track how often each view refreshes<\/li>\n\n\n\n<li>Duration: Monitor the time taken for refresh operations<\/li>\n\n\n\n<li>Data Volume: Measure the amount of data being processed<\/li>\n\n\n\n<li>Success Rate: Track failed refreshes and their causes<\/li>\n<\/ul>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-performance-impact\"><a id=\"post-104941-_heading=h.g0offw4cog2y\"><\/a>Performance Impact<\/h3>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Query Response Time: Compare query performance before and after materialization<\/li>\n\n\n\n<li>Resource Usage: Monitor compute credits consumed during refreshes<\/li>\n\n\n\n<li>Concurrent Operations: Track impact on other warehouse operations<\/li>\n<\/ul>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-storage-utilization\"><a id=\"post-104941-_heading=h.hp90k5m8fpxk\"><\/a>Storage Utilization<\/h3>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Size Growth: Monitor how view size changes over time<\/li>\n\n\n\n<li>Storage Costs: Track monthly storage expenses<\/li>\n\n\n\n<li>Data Distribution: Analyze clustering effectiveness<\/li>\n<\/ul>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-conclusion\"><a id=\"post-104941-_heading=h.qsh70q\"><\/a>Conclusion<\/h2>\n\n\n\n<p>Materialized views in Snowflake can be incredibly powerful when used correctly. Based on the performance metrics and benchmarks shown above, they can provide significant performance improvements, reduction in cost and greater Return On Investment (ROI). The key is to let your usage patterns guide you &#8211; look at your query history, identify repetitive complex queries, and measure the impact. Start small, monitor the results, and scale up based on real performance improvements. Remember, the goal isn&#8217;t to create materialized views for everything, but to strategically use them where they&#8217;ll make the biggest impact on your performance and costs. Happy materializing!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I\u2019ve used both Snowflake views and materialized views with varying degrees of success. Both have their place and it\u2019s on us to determine when to use them based on what they offer. This guide will help you in understanding how views and materialized views work in Snowflake and show you practical examples along with potential&#8230;&hellip;<\/p>\n","protected":false},"author":345259,"featured_media":104942,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[53,159001],"tags":[],"coauthors":[159232],"class_list":["post-104941","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-featured","category-snowflake"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/104941","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/345259"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=104941"}],"version-history":[{"count":8,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/104941\/revisions"}],"predecessor-version":[{"id":107332,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/104941\/revisions\/107332"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media\/104942"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=104941"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=104941"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=104941"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=104941"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}