<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Data Modeling &#8211; Kieran Christensen</title>
	<atom:link href="https://kieran.live/category/data-modeling/feed/" rel="self" type="application/rss+xml" />
	<link>https://kieran.live</link>
	<description>Product analytics champion 🦸 in the DTC world 🌎</description>
	<lastBuildDate>Wed, 12 Mar 2025 22:03:52 +0000</lastBuildDate>
	<language>en-CA</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.7.2</generator>

<image>
	<url>https://kieran.live/wp-content/uploads/2018/03/cropped-Asset-3-49x49.png</url>
	<title>Data Modeling &#8211; Kieran Christensen</title>
	<link>https://kieran.live</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>How to design tables in Snowflake for performance and scalability</title>
		<link>https://kieran.live/how-to-design-tables-in-snowflake-for-performance-and-scalability/</link>
		
		<dc:creator><![CDATA[Kieran]]></dc:creator>
		<pubDate>Tue, 11 Mar 2025 22:09:51 +0000</pubDate>
				<category><![CDATA[Data Modeling]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[data modeling]]></category>
		<category><![CDATA[snowflake]]></category>
		<guid isPermaLink="false">https://kieran.live/?p=191</guid>

					<description><![CDATA[Creating a new table in Snowflake might seem straightforward—after all, it’s just defining columns and data types, right? But if you’ve ever dealt with slow queries, excessive storage costs, or hard-to-maintain schemas, you know that poor table design can lead to long-term headaches.]]></description>
										<content:encoded><![CDATA[
<p>Creating a new table in Snowflake might seem straightforward—after all, it’s just defining columns and data types, right? But if you’ve ever dealt with slow queries, excessive storage costs, or hard-to-maintain schemas, you know that <strong>poor table design can lead to long-term headaches</strong>.</p>



<span id="more-191"></span>



<p>A well-structured table is more than just a place to store data. It affects <strong>query performance, storage efficiency, data governance, and even security</strong>. Making informed choices upfront can save you hours of troubleshooting later and ensure that your analytics workflows run smoothly.</p>



<p>In this guide, I’ll walk through <strong>best practices for designing tables in Snowflake</strong>, covering everything from <strong>schema definition</strong> and <strong>query optimization</strong> to <strong>data loading strategies and access control</strong>. Whether you&#8217;re setting up a fresh database or improving an existing one, these principles will help you build <strong>scalable, high-performance tables</strong> that stand the test of time.</p>





<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading"><strong>Define the Schema Thoughtfully</strong></h2>



<p>The schema defines how data is structured in the table. Choosing the right column types and constraints upfront is crucial for efficiency, query performance, and long-term usability.</p>



<h5 class="wp-block-heading"><strong>Use appropriate data types</strong></h5>



<p>Snowflake supports various data types like <code>INTEGER</code>, <code>NUMBER</code>, <code>VARCHAR</code>, <code>DATE</code>, <code>TIMESTAMP</code>, and <code>VARIANT</code>. Choosing the wrong type can lead to excessive storage usage and slow queries.</p>



<p><em>Example:</em> If a column only stores small whole numbers (like ages), using <code>NUMBER(38,0)</code> (which supports massive numbers) is overkill—<code>INTEGER</code> would be a better choice.</p>



<p>You should also be careful when using <code>VARCHAR</code>. Unlike some databases, Snowflake doesn’t enforce max length on <code>VARCHAR</code>. Defining a reasonable size, such as <code>VARCHAR(255)</code> helps with documentation and clarity.</p>



<h5 class="wp-block-heading"><strong>Default to <code>TIMESTAMP_NTZ</code> unless time zones are needed</strong></h5>



<p>Snowflake has three timestamp types:</p>



<ul class="wp-block-list">
<li><code>TIMESTAMP_NTZ</code> (No Time Zone) – Best when time zones don’t matter.</li>
</ul>



<ul class="wp-block-list">
<li><code>TIMESTAMP_TZ</code> (With Time Zone) – Includes time zone information.</li>
</ul>



<ul class="wp-block-list">
<li><code>TIMESTAMP_LTZ</code> (Local Time Zone) – Converts based on the session time zone.</li>
</ul>



<p><code>TIMESTAMP_NTZ</code> is generally preferred unless you specifically need time zones, as it simplifies calculations and avoids conversion issues.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading"><strong>Optimize for Query Performance</strong></h2>



<p>Snowflake automatically optimizes storage with <strong>micro-partitioning</strong>, but you can further enhance performance with good table design.</p>



<h5 class="wp-block-heading"><strong>Use clustering keys for large tables</strong></h5>



<p>Snowflake <strong>automatically partitions data</strong> into micro-partitions, but queries on large tables can still slow down if they have to scan too many partitions.</p>



<p>If queries frequently filter on a certain column (like <code>ORDER_DATE</code> in an <code>ORDERS</code> table), setting it as a <strong>clustering key</strong> improves query speed.</p>



<p><em>Example:</em></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#39404f;color:#c8d0e0">SQL</span><span role="button" tabindex="0" data-code="ALTER TABLE orders CLUSTER BY (order_date);" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">ALTER</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">TABLE</span><span style="color: #D8DEE9FF"> orders CLUSTER </span><span style="color: #81A1C1">BY</span><span style="color: #D8DEE9FF"> (order_date);</span></span></code></pre></div>



<p>There are times to avoid clustering keys, however. If a table is small (&lt;10M rows), clustering won’t make much difference. If queries don’t consistently filter on a single column, clustering might not help.</p>



<h5 class="wp-block-heading"><strong>Store semi-structured data efficiently with <code>VARIANT</code></strong></h5>



<p>Snowflake supports <strong>semi-structured data</strong> (<code>JSON</code>, <code>Avro</code>, <code>Parquet</code>) in <code>VARIANT</code> columns.</p>



<p>This is useful when dealing with <strong>dynamic schemas</strong> (e.g., data from APIs), but structured tables perform better for reporting.</p>



<p>If a JSON field is frequently queried, <strong>consider extracting it into separate columns</strong> for performance.</p>



<h5 class="wp-block-heading"><strong>Avoid unnecessary row-level locks</strong></h5>



<p>Snowflake <strong>doesn’t use traditional row-level locking</strong>, but excessive updates/deletes can lead to <strong>fragmented micro-partitions</strong>.</p>



<p>Will your table have frequent updates? Consider using <strong><code>MERGE</code></strong> instead of <code>DELETE + INSERT</code>. More specifics on this recommendation <a href="#merge" data-type="link" data-id="https://kieran.live/how-to-design-tables-in-snowflake-for-performance-and-scalability/">down below</a>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading"><strong>Consider Table Type &amp; Retention</strong></h2>



<p>Snowflake offers different table types that impact storage, cost, and fail-safe recovery.</p>



<h5 class="wp-block-heading"><strong>Transient vs. Permanent vs. Temporary Tables</strong></h5>



<ul class="wp-block-list">
<li><strong>Permanent tables</strong> (default): Data is fully recoverable with <strong>Time Travel</strong> (up to 90 days) and Fail-Safe (7 days).</li>
</ul>



<ul class="wp-block-list">
<li><strong>Transient tables</strong>: No Fail-Safe recovery, making them <strong>cheaper</strong> (good for intermediate processing).</li>
</ul>



<ul class="wp-block-list">
<li><strong>Temporary tables</strong>: Exist only for the session, automatically dropped when it ends.</li>
</ul>



<p>Example use cases for table types:</p>



<ul class="wp-block-list">
<li><strong>Permanent</strong> → Business-critical data (<code>ORDERS</code>, <code>CUSTOMERS</code>).</li>



<li><strong>Transient</strong> → ETL processing tables.</li>



<li><strong>Temporary</strong> → Ad-hoc analysis tables.</li>
</ul>



<h5 class="wp-block-heading"><strong>Set appropriate Time Travel retention</strong></h5>



<p>Snowflake <strong>retains deleted data</strong> for a configurable period (<code>1-90 days</code> for Enterprise plans).</p>



<p>Remember: <strong>Longer retention = higher storage costs</strong>.</p>



<p><em>Example: </em></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#39404f;color:#c8d0e0">SQL</span><span role="button" tabindex="0" data-code="CREATE TABLE orders
(id INT, order_date DATE)
DATA_RETENTION_TIME_IN_DAYS = 30;" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">CREATE</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">TABLE</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">orders</span></span>
<span class="line"><span style="color: #D8DEE9FF">(id </span><span style="color: #81A1C1">INT</span><span style="color: #D8DEE9FF">, order_date </span><span style="color: #81A1C1">DATE</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">DATA_RETENTION_TIME_IN_DAYS </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">30</span><span style="color: #D8DEE9FF">;</span></span></code></pre></div>



<p>We can&#8217;t all use Mr. Fusion, so time travel responsibly! If you <strong>don’t need Time Travel</strong>, set it to <code>1</code> day to reduce costs.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading"><strong>Be Intentional with Data Loading</strong></h2>



<p>Efficient data loading ensures better performance and reduces storage fragmentation.</p>



<h5 class="wp-block-heading"><strong>Use bulk inserts over row-by-row operations</strong></h5>



<p>Snowflake is optimized for <strong>bulk inserts</strong> (batching), and inserting data <strong>row by row</strong> is slow.</p>



<p><strong>Good approach</strong>: </p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#39404f;color:#c8d0e0">SQL</span><span role="button" tabindex="0" data-code="INSERT INTO customers (id, name, email)
SELECT * FROM staging_customers;" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">INSERT INTO</span><span style="color: #D8DEE9FF"> customers (id, </span><span style="color: #81A1C1">name</span><span style="color: #D8DEE9FF">, email)</span></span>
<span class="line"><span style="color: #81A1C1">SELECT</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">FROM</span><span style="color: #D8DEE9FF"> staging_customers;</span></span></code></pre></div>



<p><strong>Bad approach</strong>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#39404f;color:#c8d0e0">SQL</span><span role="button" tabindex="0" data-code="INSERT INTO customers VALUES (1, 'Alice', 'alice@email.com');" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">INSERT INTO</span><span style="color: #D8DEE9FF"> customers </span><span style="color: #81A1C1">VALUES</span><span style="color: #D8DEE9FF"> (</span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF">, </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">Alice</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF">, </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">alice@email.com</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF">);</span></span></code></pre></div>



<p>When loading large files, use <code>COPY INTO</code> instead: </p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#39404f;color:#c8d0e0">SQL</span><span role="button" tabindex="0" data-code="COPY INTO orders FROM @my_stage FILE_FORMAT = (FORMAT_NAME = 'my_csv_format');" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">COPY</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">INTO</span><span style="color: #D8DEE9FF"> orders </span><span style="color: #81A1C1">FROM</span><span style="color: #D8DEE9FF"> @my_stage </span><span style="color: #81A1C1">FILE_FORMAT</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> (FORMAT_NAME </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">my_csv_format</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF">);</span></span></code></pre></div>



<h5 class="wp-block-heading" id="merge"><strong>Use <code>MERGE</code> instead of <code>DELETE + INSERT</code></strong></h5>



<p>When updating existing records, <code>DELETE + INSERT</code> creates unnecessary storage overhead.</p>



<p><strong>Better alternative</strong>: </p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#39404f;color:#c8d0e0">SQL</span><span role="button" tabindex="0" data-code="MERGE INTO customers AS target
USING new_customers AS source
ON target.id = source.id
WHEN MATCHED THEN UPDATE SET target.name = source.name
WHEN NOT MATCHED THEN INSERT (id, name) VALUES (source.id, source.name);" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">MERGE</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">INTO</span><span style="color: #D8DEE9FF"> customers </span><span style="color: #81A1C1">AS</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">target</span></span>
<span class="line"><span style="color: #81A1C1">USING</span><span style="color: #D8DEE9FF"> new_customers </span><span style="color: #81A1C1">AS</span><span style="color: #D8DEE9FF"> source</span></span>
<span class="line"><span style="color: #81A1C1">ON</span><span style="color: #D8DEE9FF"> target.id </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> source.id</span></span>
<span class="line"><span style="color: #81A1C1">WHEN</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">MATCHED</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">THEN</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">UPDATE</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">SET</span><span style="color: #D8DEE9FF"> target.name </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> source.name</span></span>
<span class="line"><span style="color: #81A1C1">WHEN</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">NOT</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">MATCHED</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">THEN</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">INSERT</span><span style="color: #D8DEE9FF"> (id, </span><span style="color: #81A1C1">name</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">VALUES</span><span style="color: #D8DEE9FF"> (source.id, source.name);</span></span></code></pre></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading"><strong>Govern Access &amp; Security from the Start</strong></h2>



<p>Security should be a f0undational priority, not an afterthought.</p>



<h5 class="wp-block-heading"><strong>Apply RBAC (Role-Based Access Control)</strong></h5>



<p>Avoid granting privileges directly to users. Instead, use <strong>roles</strong>.</p>



<p><em>Example:</em></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#39404f;color:#c8d0e0">SQL</span><span role="button" tabindex="0" data-code="GRANT SELECT ON orders TO ROLE analyst_role;
GRANT ROLE analyst_role TO USER analyst_user;" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">GRANT</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">SELECT</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">ON</span><span style="color: #D8DEE9FF"> orders </span><span style="color: #81A1C1">TO</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">ROLE</span><span style="color: #D8DEE9FF"> analyst_role;</span></span>
<span class="line"><span style="color: #81A1C1">GRANT</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">ROLE</span><span style="color: #D8DEE9FF"> analyst_role </span><span style="color: #81A1C1">TO</span><span style="color: #D8DEE9FF"> USER analyst_user;</span></span></code></pre></div>



<h5 class="wp-block-heading"><strong>Use row-level security for sensitive data</strong></h5>



<p>Snowflake supports <strong>row access policies</strong> to restrict data visibility based on user roles.</p>



<p><em>Example: </em></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#39404f;color:#c8d0e0">SQL</span><span role="button" tabindex="0" data-code="CREATE OR REPLACE ROW ACCESS POLICY eu_only AS (country STRING) RETURNS BOOLEAN -&gt;
CASE
    WHEN CURRENT_ROLE() = 'EU_ROLE' AND country = 'EU' THEN TRUE
    ELSE FALSE
END;

ALTER TABLE customers ADD ROW ACCESS POLICY eu_only ON (country);" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">CREATE</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">OR</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">REPLACE</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">ROW</span><span style="color: #D8DEE9FF"> ACCESS </span><span style="color: #81A1C1">POLICY</span><span style="color: #D8DEE9FF"> eu_only </span><span style="color: #81A1C1">AS</span><span style="color: #D8DEE9FF"> (country STRING) </span><span style="color: #81A1C1">RETURNS</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">BOOLEAN</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span></span>
<span class="line"><span style="color: #81A1C1">CASE</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">WHEN</span><span style="color: #D8DEE9FF"> CURRENT_ROLE</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">EU_ROLE</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">AND</span><span style="color: #D8DEE9FF"> country </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">EU</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">THEN</span><span style="color: #D8DEE9FF"> TRUE</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">ELSE</span><span style="color: #D8DEE9FF"> FALSE</span></span>
<span class="line"><span style="color: #81A1C1">END</span><span style="color: #D8DEE9FF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">ALTER</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">TABLE</span><span style="color: #D8DEE9FF"> customers </span><span style="color: #81A1C1">ADD</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">ROW</span><span style="color: #D8DEE9FF"> ACCESS </span><span style="color: #81A1C1">POLICY</span><span style="color: #D8DEE9FF"> eu_only </span><span style="color: #81A1C1">ON</span><span style="color: #D8DEE9FF"> (country);</span></span></code></pre></div>



<h5 class="wp-block-heading"><strong>Leverage dynamic data masking for Personally Identifiable Information (PII)</strong></h5>



<p>Snowflake <strong>can mask PII fields</strong> (things like e-mail addresses, IP addresses, etc) so only authorized users see full data.</p>



<p><em>Example: </em></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#39404f;color:#c8d0e0">SQL</span><span role="button" tabindex="0" data-code="CREATE MASKING POLICY mask_email
AS (email STRING) RETURNS STRING -&gt;
CASE
    WHEN CURRENT_ROLE() = 'admin' THEN email
    ELSE '********'
END;

ALTER TABLE users MODIFY COLUMN email SET MASKING POLICY mask_email;" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">CREATE</span><span style="color: #D8DEE9FF"> MASKING </span><span style="color: #81A1C1">POLICY</span><span style="color: #D8DEE9FF"> mask_email</span></span>
<span class="line"><span style="color: #81A1C1">AS</span><span style="color: #D8DEE9FF"> (email STRING) </span><span style="color: #81A1C1">RETURNS</span><span style="color: #D8DEE9FF"> STRING </span><span style="color: #81A1C1">-&gt;</span></span>
<span class="line"><span style="color: #81A1C1">CASE</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">WHEN</span><span style="color: #D8DEE9FF"> CURRENT_ROLE</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">admin</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">THEN</span><span style="color: #D8DEE9FF"> email</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">ELSE</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">********</span><span style="color: #ECEFF4">&#39;</span></span>
<span class="line"><span style="color: #81A1C1">END</span><span style="color: #D8DEE9FF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">ALTER</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">TABLE</span><span style="color: #D8DEE9FF"> users </span><span style="color: #81A1C1">MODIFY</span><span style="color: #D8DEE9FF"> COLUMN email </span><span style="color: #81A1C1">SET</span><span style="color: #D8DEE9FF"> MASKING </span><span style="color: #81A1C1">POLICY</span><span style="color: #D8DEE9FF"> mask_email;</span></span></code></pre></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading"><strong>Final Thoughts</strong></h2>



<p>Creating tables in Snowflake is more than just defining columns. It requires <strong>performance planning, storage optimization, and security governance</strong>. By following these best practices, you can: </p>



<p><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Improve query speed<br><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Reduce storage costs<br><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Ensure data integrity &amp; security</p>



<p>Do you have any other non-negotiable best practices for Snowflake table design? Let me know! </p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
