<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Re: SQL Scripting  - Procedural Logic in Databricks SQL (Without Leaving SQL) in Community Articles</title>
    <link>https://community.databricks.com/t5/community-articles/sql-scripting-procedural-logic-in-databricks-sql-without-leaving/m-p/144053#M953</link>
    <description>&lt;P&gt;Correction: Preview is available on&amp;nbsp;&lt;SPAN&gt;Databricks Runtime&lt;/SPAN&gt;&lt;SPAN&gt;&amp;nbsp;16.3 and above.&lt;/SPAN&gt;&lt;/P&gt;</description>
    <pubDate>Wed, 14 Jan 2026 15:15:46 GMT</pubDate>
    <dc:creator>AbhaySingh</dc:creator>
    <dc:date>2026-01-14T15:15:46Z</dc:date>
    <item>
      <title>SQL Scripting  - Procedural Logic in Databricks SQL (Without Leaving SQL)</title>
      <link>https://community.databricks.com/t5/community-articles/sql-scripting-procedural-logic-in-databricks-sql-without-leaving/m-p/144048#M951</link>
      <description>&lt;P&gt;We've all been there. You need to loop through a list of tables, apply some transformation, handle a few edge cases, and maybe catch an error or two. In most data platforms, that means switching to Python or Scala—even if 90% of your logic is just SQL anyway.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;SQL Scripting solves this exact problem&lt;/STRONG&gt;, and it's a genuine productivity boost for SQL-first engineers. I've been using it for admin tasks and ELT jobs, and I'm not going back.&lt;/P&gt;
&lt;H2&gt;Prerequisites&lt;/H2&gt;
&lt;UL&gt;
&lt;LI&gt;Databricks workspace (any cloud)&lt;/LI&gt;
&lt;LI&gt;A SQL Warehouse or cluster with DBR 14.3+&lt;/LI&gt;
&lt;LI&gt;Basic SQL knowledge (you don't need to be a PL/SQL veteran)&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;What SQL Scripting Gives You&lt;/H2&gt;
&lt;P&gt;SQL Scripting follows the ANSI SQL/PSM standard, which means the skills transfer if you've ever worked with T-SQL, PL/pgSQL, or similar. Here's the quick rundown:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Local variables&lt;/STRONG&gt; with &lt;SPAN class="inline-code"&gt;DECLARE&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Control flow&lt;/STRONG&gt;: &lt;SPAN class="inline-code"&gt;IF/THEN/ELSE&lt;/SPAN&gt;, &lt;SPAN class="inline-code"&gt;CASE&lt;/SPAN&gt; statements&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Four loop types&lt;/STRONG&gt;: &lt;SPAN class="inline-code"&gt;FOR&lt;/SPAN&gt;, &lt;SPAN class="inline-code"&gt;WHILE&lt;/SPAN&gt;, &lt;SPAN class="inline-code"&gt;REPEAT&lt;/SPAN&gt;, and plain &lt;SPAN class="inline-code"&gt;LOOP&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Dynamic SQL&lt;/STRONG&gt; via &lt;SPAN class="inline-code"&gt;EXECUTE IMMEDIATE&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Exception handling&lt;/STRONG&gt; with condition handlers&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Everything runs inside a &lt;SPAN class="inline-code"&gt;BEGIN...END&lt;/SPAN&gt; compound statement.&lt;/P&gt;
&lt;H2&gt;A Real Example: Bulk-Updating Column Collations&lt;/H2&gt;
&lt;P&gt;Say you've inherited a schema where every &lt;SPAN class="inline-code"&gt;STRING&lt;/SPAN&gt; column uses default collation, and now you need case-insensitive sorting across the board. Manually altering 50 tables? No thanks.&lt;/P&gt;
&lt;P&gt;Here's how it can be done with SQL Scripting: ( sample code - not tested).&amp;nbsp;&lt;/P&gt;
&lt;PRE&gt;&lt;CODE&gt;&lt;SPAN class="keyword"&gt;BEGIN&lt;/SPAN&gt;
  &lt;SPAN class="comment"&gt;-- Declare variables first, then logic&lt;/SPAN&gt;
  &lt;SPAN class="keyword"&gt;DECLARE&lt;/SPAN&gt; v_table_name &lt;SPAN class="keyword"&gt;STRING&lt;/SPAN&gt; &lt;SPAN class="keyword"&gt;DEFAULT&lt;/SPAN&gt; &lt;SPAN class="string"&gt;'employees'&lt;/SPAN&gt;;
  &lt;SPAN class="keyword"&gt;DECLARE&lt;/SPAN&gt; v_collation &lt;SPAN class="keyword"&gt;STRING&lt;/SPAN&gt; &lt;SPAN class="keyword"&gt;DEFAULT&lt;/SPAN&gt; &lt;SPAN class="string"&gt;'UTF8_LCASE'&lt;/SPAN&gt;;
  &lt;SPAN class="keyword"&gt;DECLARE&lt;/SPAN&gt; v_columns &lt;SPAN class="keyword"&gt;ARRAY&lt;/SPAN&gt;&amp;lt;&lt;SPAN class="keyword"&gt;STRING&lt;/SPAN&gt;&amp;gt; &lt;SPAN class="keyword"&gt;DEFAULT&lt;/SPAN&gt; &lt;SPAN class="function"&gt;ARRAY&lt;/SPAN&gt;();
  
  &lt;SPAN class="comment"&gt;-- Set the table's default collation for new columns&lt;/SPAN&gt;
  &lt;SPAN class="keyword"&gt;EXECUTE IMMEDIATE&lt;/SPAN&gt; 
    &lt;SPAN class="string"&gt;'ALTER TABLE '&lt;/SPAN&gt; || v_table_name || &lt;SPAN class="string"&gt;' SET DEFAULT COLLATION '&lt;/SPAN&gt; || v_collation;
  
  &lt;SPAN class="comment"&gt;-- Loop through existing STRING columns and alter each one&lt;/SPAN&gt;
  &lt;SPAN class="keyword"&gt;FOR&lt;/SPAN&gt; col &lt;SPAN class="keyword"&gt;IN&lt;/SPAN&gt; (
    &lt;SPAN class="keyword"&gt;SELECT&lt;/SPAN&gt; column_name 
    &lt;SPAN class="keyword"&gt;FROM&lt;/SPAN&gt; information_schema.columns 
    &lt;SPAN class="keyword"&gt;WHERE&lt;/SPAN&gt; table_name = v_table_name 
      &lt;SPAN class="keyword"&gt;AND&lt;/SPAN&gt; data_type = &lt;SPAN class="string"&gt;'STRING'&lt;/SPAN&gt;
  ) &lt;SPAN class="keyword"&gt;DO&lt;/SPAN&gt;
    &lt;SPAN class="keyword"&gt;EXECUTE IMMEDIATE&lt;/SPAN&gt; 
      &lt;SPAN class="string"&gt;'ALTER TABLE '&lt;/SPAN&gt; || v_table_name || 
      &lt;SPAN class="string"&gt;' ALTER COLUMN '&lt;/SPAN&gt; || col.column_name || 
      &lt;SPAN class="string"&gt;' SET COLLATION '&lt;/SPAN&gt; || v_collation;
    
    &lt;SPAN class="comment"&gt;-- Track which columns we modified&lt;/SPAN&gt;
    &lt;SPAN class="keyword"&gt;SET&lt;/SPAN&gt; v_columns = &lt;SPAN class="function"&gt;array_append&lt;/SPAN&gt;(v_columns, col.column_name);
  &lt;SPAN class="keyword"&gt;END FOR&lt;/SPAN&gt;;
  
  &lt;SPAN class="comment"&gt;-- Refresh stats if we changed anything&lt;/SPAN&gt;
  &lt;SPAN class="keyword"&gt;IF&lt;/SPAN&gt; &lt;SPAN class="function"&gt;size&lt;/SPAN&gt;(v_columns) &amp;gt; 0 &lt;SPAN class="keyword"&gt;THEN&lt;/SPAN&gt;
    &lt;SPAN class="keyword"&gt;EXECUTE IMMEDIATE&lt;/SPAN&gt; 
      &lt;SPAN class="string"&gt;'ANALYZE TABLE '&lt;/SPAN&gt; || v_table_name || 
      &lt;SPAN class="string"&gt;' COMPUTE STATISTICS FOR COLUMNS '&lt;/SPAN&gt; || 
      &lt;SPAN class="function"&gt;array_join&lt;/SPAN&gt;(v_columns, &lt;SPAN class="string"&gt;', '&lt;/SPAN&gt;);
  &lt;SPAN class="keyword"&gt;END IF&lt;/SPAN&gt;;
&lt;SPAN class="keyword"&gt;END&lt;/SPAN&gt;;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The &lt;SPAN class="inline-code"&gt;FOR&lt;/SPAN&gt; loop queries &lt;SPAN class="inline-code"&gt;information_schema.columns&lt;/SPAN&gt; and iterates row by row. &lt;SPAN class="inline-code"&gt;EXECUTE IMMEDIATE&lt;/SPAN&gt; handles the dynamic DDL. Simple.&lt;/P&gt;
&lt;H2&gt;Scaling to an Entire Schema&lt;/H2&gt;
&lt;P&gt;Want to hit every table in a schema? Nest another loop:&lt;/P&gt;
&lt;PRE&gt;&lt;CODE&gt;&lt;SPAN class="keyword"&gt;BEGIN&lt;/SPAN&gt;
  &lt;SPAN class="keyword"&gt;DECLARE&lt;/SPAN&gt; v_schema &lt;SPAN class="keyword"&gt;STRING&lt;/SPAN&gt; &lt;SPAN class="keyword"&gt;DEFAULT&lt;/SPAN&gt; &lt;SPAN class="string"&gt;'my_schema'&lt;/SPAN&gt;;
  &lt;SPAN class="keyword"&gt;DECLARE&lt;/SPAN&gt; v_collation &lt;SPAN class="keyword"&gt;STRING&lt;/SPAN&gt; &lt;SPAN class="keyword"&gt;DEFAULT&lt;/SPAN&gt; &lt;SPAN class="string"&gt;'UTF8_LCASE'&lt;/SPAN&gt;;
  &lt;SPAN class="keyword"&gt;DECLARE&lt;/SPAN&gt; v_skipped &lt;SPAN class="keyword"&gt;ARRAY&lt;/SPAN&gt;&amp;lt;&lt;SPAN class="keyword"&gt;STRING&lt;/SPAN&gt;&amp;gt; &lt;SPAN class="keyword"&gt;DEFAULT&lt;/SPAN&gt; &lt;SPAN class="function"&gt;ARRAY&lt;/SPAN&gt;();
  
  &lt;SPAN class="keyword"&gt;FOR&lt;/SPAN&gt; tbl &lt;SPAN class="keyword"&gt;IN&lt;/SPAN&gt; (
    &lt;SPAN class="keyword"&gt;SELECT&lt;/SPAN&gt; table_name 
    &lt;SPAN class="keyword"&gt;FROM&lt;/SPAN&gt; information_schema.tables 
    &lt;SPAN class="keyword"&gt;WHERE&lt;/SPAN&gt; table_schema = v_schema &lt;SPAN class="keyword"&gt;AND&lt;/SPAN&gt; table_type = &lt;SPAN class="string"&gt;'BASE TABLE'&lt;/SPAN&gt;
  ) &lt;SPAN class="keyword"&gt;DO&lt;/SPAN&gt;
    &lt;SPAN class="keyword"&gt;BEGIN&lt;/SPAN&gt;
      &lt;SPAN class="keyword"&gt;DECLARE&lt;/SPAN&gt; v_columns &lt;SPAN class="keyword"&gt;ARRAY&lt;/SPAN&gt;&amp;lt;&lt;SPAN class="keyword"&gt;STRING&lt;/SPAN&gt;&amp;gt; &lt;SPAN class="keyword"&gt;DEFAULT&lt;/SPAN&gt; &lt;SPAN class="function"&gt;ARRAY&lt;/SPAN&gt;();
      
      &lt;SPAN class="comment"&gt;-- Handler: if ALTER fails (e.g., on a view), log and continue&lt;/SPAN&gt;
      &lt;SPAN class="keyword"&gt;DECLARE CONTINUE HANDLER FOR SQLEXCEPTION&lt;/SPAN&gt;
        &lt;SPAN class="keyword"&gt;SET&lt;/SPAN&gt; v_skipped = &lt;SPAN class="function"&gt;array_append&lt;/SPAN&gt;(v_skipped, tbl.table_name);
      
      &lt;SPAN class="keyword"&gt;EXECUTE IMMEDIATE&lt;/SPAN&gt; 
        &lt;SPAN class="string"&gt;'ALTER TABLE '&lt;/SPAN&gt; || v_schema || &lt;SPAN class="string"&gt;'.'&lt;/SPAN&gt; || tbl.table_name || 
        &lt;SPAN class="string"&gt;' SET DEFAULT COLLATION '&lt;/SPAN&gt; || v_collation;
      
      &lt;SPAN class="keyword"&gt;FOR&lt;/SPAN&gt; col &lt;SPAN class="keyword"&gt;IN&lt;/SPAN&gt; (
        &lt;SPAN class="keyword"&gt;SELECT&lt;/SPAN&gt; column_name 
        &lt;SPAN class="keyword"&gt;FROM&lt;/SPAN&gt; information_schema.columns 
        &lt;SPAN class="keyword"&gt;WHERE&lt;/SPAN&gt; table_schema = v_schema 
          &lt;SPAN class="keyword"&gt;AND&lt;/SPAN&gt; table_name = tbl.table_name 
          &lt;SPAN class="keyword"&gt;AND&lt;/SPAN&gt; data_type = &lt;SPAN class="string"&gt;'STRING'&lt;/SPAN&gt;
      ) &lt;SPAN class="keyword"&gt;DO&lt;/SPAN&gt;
        &lt;SPAN class="keyword"&gt;EXECUTE IMMEDIATE&lt;/SPAN&gt; 
          &lt;SPAN class="string"&gt;'ALTER TABLE '&lt;/SPAN&gt; || v_schema || &lt;SPAN class="string"&gt;'.'&lt;/SPAN&gt; || tbl.table_name || 
          &lt;SPAN class="string"&gt;' ALTER COLUMN '&lt;/SPAN&gt; || col.column_name || 
          &lt;SPAN class="string"&gt;' SET COLLATION '&lt;/SPAN&gt; || v_collation;
        &lt;SPAN class="keyword"&gt;SET&lt;/SPAN&gt; v_columns = &lt;SPAN class="function"&gt;array_append&lt;/SPAN&gt;(v_columns, col.column_name);
      &lt;SPAN class="keyword"&gt;END FOR&lt;/SPAN&gt;;
      
    &lt;SPAN class="keyword"&gt;END&lt;/SPAN&gt;;
  &lt;SPAN class="keyword"&gt;END FOR&lt;/SPAN&gt;;
  
  &lt;SPAN class="keyword"&gt;SELECT&lt;/SPAN&gt; v_skipped &lt;SPAN class="keyword"&gt;AS&lt;/SPAN&gt; skipped_objects;
&lt;SPAN class="keyword"&gt;END&lt;/SPAN&gt;;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The &lt;SPAN class="inline-code"&gt;DECLARE CONTINUE HANDLER FOR SQLEXCEPTION&lt;/SPAN&gt; catches any errors (like trying to alter a view), logs the object name, and moves on. No more script crashes halfway through.&lt;/P&gt;
&lt;H2&gt;Loop Types Cheat Sheet&lt;/H2&gt;
&lt;TABLE&gt;
&lt;THEAD&gt;
&lt;TR&gt;
&lt;TH&gt;Loop&lt;/TH&gt;
&lt;TH&gt;When to Use&lt;/TH&gt;
&lt;/TR&gt;
&lt;/THEAD&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;SPAN class="inline-code"&gt;FOR query DO...END FOR&lt;/SPAN&gt;&lt;/TD&gt;
&lt;TD&gt;Iterating over query results (most common)&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;SPAN class="inline-code"&gt;WHILE cond DO...END WHILE&lt;/SPAN&gt;&lt;/TD&gt;
&lt;TD&gt;Pre-condition check, might run zero times&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;SPAN class="inline-code"&gt;REPEAT...UNTIL cond END REPEAT&lt;/SPAN&gt;&lt;/TD&gt;
&lt;TD&gt;Always runs at least once&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;SPAN class="inline-code"&gt;LOOP...END LOOP&lt;/SPAN&gt;&lt;/TD&gt;
&lt;TD&gt;Infinite loop—break with &lt;SPAN class="inline-code"&gt;LEAVE&lt;/SPAN&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;Use &lt;SPAN class="inline-code"&gt;LEAVE&lt;/SPAN&gt; to exit a loop early and &lt;SPAN class="inline-code"&gt;ITERATE&lt;/SPAN&gt; to skip to the next iteration (like &lt;SPAN class="inline-code"&gt;continue&lt;/SPAN&gt; in Python).&lt;/P&gt;
&lt;H2&gt;Gotchas &amp;amp; Pro Tips&lt;/H2&gt;
&lt;OL&gt;
&lt;LI&gt;&lt;STRONG&gt;Variables must be declared first.&lt;/STRONG&gt; All &lt;SPAN class="inline-code"&gt;DECLARE&lt;/SPAN&gt; statements come before any logic in a &lt;SPAN class="inline-code"&gt;BEGIN...END&lt;/SPAN&gt; block.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;FOR loops don't create a new scope.&lt;/STRONG&gt; If you need a fresh variable per iteration, wrap the body in a nested &lt;SPAN class="inline-code"&gt;BEGIN...END&lt;/SPAN&gt;.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Combine with AI functions.&lt;/STRONG&gt; You can call &lt;SPAN class="inline-code"&gt;ai_fix_grammar()&lt;/SPAN&gt; or &lt;SPAN class="inline-code"&gt;ai_classify()&lt;/SPAN&gt; inside loops for bulk data cleansing. Just watch the API costs.&lt;/LI&gt;
&lt;/OL&gt;
&lt;H2&gt;Wrap Up&lt;/H2&gt;
&lt;P&gt;SQL Scripting isn't trying to replace Python for complex ML pipelines. But for admin automation, schema migrations, and ELT logic? It's exactly what SQL-first engineers have been waiting for.&lt;/P&gt;
&lt;P&gt;Give it a shot on your next bulk update job. You might be surprised how much you can do without switching languages.&lt;/P&gt;
&lt;DIV class="resources"&gt;
&lt;H3&gt;Resources&lt;/H3&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;A href="https://docs.databricks.com/sql/language-manual/sql-ref-scripting" target="_blank" rel="noopener"&gt;SQL Scripting Documentation&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://github.com/databricks-solutions/databricks-blogposts/blob/main/2025-05-announce-sql-scripting/SQL-Scripting-Part-1.sql" target="_blank" rel="noopener"&gt;Example Notebook (Part 1)&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://github.com/databricks-solutions/databricks-blogposts/blob/main/2025-05-announce-sql-scripting/SQL-Scripting-Part-2.sql" target="_blank" rel="noopener"&gt;Example Notebook (Part 2)&lt;/A&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;/DIV&gt;</description>
      <pubDate>Wed, 14 Jan 2026 14:29:40 GMT</pubDate>
      <guid>https://community.databricks.com/t5/community-articles/sql-scripting-procedural-logic-in-databricks-sql-without-leaving/m-p/144048#M951</guid>
      <dc:creator>AbhaySingh</dc:creator>
      <dc:date>2026-01-14T14:29:40Z</dc:date>
    </item>
    <item>
      <title>Re: SQL Scripting  - Procedural Logic in Databricks SQL (Without Leaving SQL)</title>
      <link>https://community.databricks.com/t5/community-articles/sql-scripting-procedural-logic-in-databricks-sql-without-leaving/m-p/144053#M953</link>
      <description>&lt;P&gt;Correction: Preview is available on&amp;nbsp;&lt;SPAN&gt;Databricks Runtime&lt;/SPAN&gt;&lt;SPAN&gt;&amp;nbsp;16.3 and above.&lt;/SPAN&gt;&lt;/P&gt;</description>
      <pubDate>Wed, 14 Jan 2026 15:15:46 GMT</pubDate>
      <guid>https://community.databricks.com/t5/community-articles/sql-scripting-procedural-logic-in-databricks-sql-without-leaving/m-p/144053#M953</guid>
      <dc:creator>AbhaySingh</dc:creator>
      <dc:date>2026-01-14T15:15:46Z</dc:date>
    </item>
  </channel>
</rss>

