<?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 Databricks Multi-Table Transactions - Part 2 in Community Articles</title>
    <link>https://community.databricks.com/t5/community-articles/databricks-multi-table-transactions-part-2/m-p/152402#M1118</link>
    <description>&lt;DIV style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: #1b3139; line-height: 1.75; font-size: 17px;"&gt;
&lt;P&gt;In &lt;A style="color: #ff3621;" href="https://community.databricks.com/t5/community-articles/databricks-multi-table-transactions-part-1/td-p/151163" target="_blank"&gt;Part 1&lt;/A&gt;, we covered why multi-table transactions matter. Now let's build one.&lt;/P&gt;
&lt;P&gt;We'll create the tables from the claim wrap-up scenario, load sample P&amp;amp;C insurance data, and walk through what happens when the wrap-up succeeds, when it fails, and when two processes try to wrap the same claim at the same time. Everything runs on a Databricks SQL warehouse. You can follow along in a notebook or the SQL editor.&lt;/P&gt;
&lt;H2 style="font-size: 26px; font-weight: bold; color: #1b3139; margin-top: 40px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 3px solid #FF3621; display: inline-block;"&gt;Setting up the demo&lt;/H2&gt;
&lt;P&gt;First, we need a catalog and schema. We'll also enable catalog-managed commits on every table we create, since that's what makes multi-table transactions possible.&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;CREATE CATALOG IF NOT EXISTS&lt;/STRONG&gt; insurance_demo;
&lt;STRONG&gt;USE CATALOG&lt;/STRONG&gt; insurance_demo;
&lt;STRONG&gt;CREATE SCHEMA IF NOT EXISTS&lt;/STRONG&gt; claims_dw;
&lt;STRONG&gt;USE SCHEMA&lt;/STRONG&gt; claims_dw;&lt;/PRE&gt;
&lt;P&gt;Now let's create our four tables. The &lt;CODE style="font-family: 'Courier New', Consolas, monospace; background: #F0F4F6; padding: 2px 6px; border-radius: 4px; font-size: 15px; color: #c03020;"&gt;policies&lt;/CODE&gt; table is our reference data. It holds policy details and never gets written to during the wrap-up. The other three are where the action happens.&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;-- Reference table: policies (read-only during wrap-up)
&lt;STRONG&gt;CREATE OR REPLACE TABLE&lt;/STRONG&gt; policies (
  policy_id STRING,
  customer_name STRING,
  section STRING,
  effective_date DATE,
  expiry_date DATE
)
&lt;STRONG&gt;USING&lt;/STRONG&gt; DELTA
&lt;STRONG&gt;TBLPROPERTIES&lt;/STRONG&gt; ('delta.feature.catalogManaged' = 'supported');

-- Claims table: gets updated with policy FK and wrap status
&lt;STRONG&gt;CREATE OR REPLACE TABLE&lt;/STRONG&gt; claims (
  claim_id STRING,
  policy_number STRING,
  section STRING,
  loss_date DATE,
  claim_amount DECIMAL(12,2),
  status STRING,
  policy_id STRING,
  wrap_date DATE
)
&lt;STRONG&gt;USING&lt;/STRONG&gt; DELTA
&lt;STRONG&gt;TBLPROPERTIES&lt;/STRONG&gt; ('delta.feature.catalogManaged' = 'supported');

-- Wrap log: audit trail of successful wraps
&lt;STRONG&gt;CREATE OR REPLACE TABLE&lt;/STRONG&gt; claim_wrap_log (
  claim_id STRING,
  policy_id STRING,
  wrap_date DATE,
  wrap_method STRING
)
&lt;STRONG&gt;USING&lt;/STRONG&gt; DELTA
&lt;STRONG&gt;TBLPROPERTIES&lt;/STRONG&gt; ('delta.feature.catalogManaged' = 'supported');

-- Claim reserves: initial reserve posted when a claim wraps
&lt;STRONG&gt;CREATE OR REPLACE TABLE&lt;/STRONG&gt; claim_reserves (
  claim_id STRING,
  policy_id STRING,
  section STRING,
  reserve_amount DECIMAL(12,2),
  reserve_date DATE
)
&lt;STRONG&gt;USING&lt;/STRONG&gt; DELTA
&lt;STRONG&gt;TBLPROPERTIES&lt;/STRONG&gt; ('delta.feature.catalogManaged' = 'supported');&lt;/PRE&gt;
&lt;P&gt;Now let's seed some data. We'll add a mix of property and motor policies, and a batch of claims that arrived from the claims system.&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;-- Seed policies
&lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; policies &lt;STRONG&gt;VALUES&lt;/STRONG&gt;
  ('POL-001', 'James Wilson', 'buildings', '2025-01-01', '2026-01-01'),
  ('POL-001', 'James Wilson', 'contents', '2025-01-01', '2026-01-01'),
  ('POL-002', 'Sarah Chen', 'accidental damage', '2025-06-01', '2026-06-01'),
  ('POL-002', 'Sarah Chen', 'third party injury', '2025-06-01', '2026-06-01'),
  ('POL-003', 'David Okafor', 'buildings', '2024-03-01', '2025-03-01'),
  ('POL-004', 'Emily Brown', 'accidental damage', '2025-09-01', '2026-09-01');

-- Seed claims (arrived from claims system, all status = 'open')
&lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claims (claim_id, policy_number, section, loss_date, claim_amount, status) &lt;STRONG&gt;VALUES&lt;/STRONG&gt;
  ('CLM-101', 'POL-001', 'buildings', '2025-08-15', 12000.00, 'open'),
  ('CLM-102', 'POL-002', 'accidental damage', '2025-11-20', 4500.00, 'open'),
  ('CLM-103', 'POL-003', 'buildings', '2025-06-10', 8000.00, 'open'),
  ('CLM-104', 'POL-002', 'windscreen', '2025-12-01', 3200.00, 'open'),
  ('CLM-105', 'POL-999', 'buildings', '2025-07-01', 5500.00, 'open');&lt;/PRE&gt;
&lt;P&gt;Take a moment to look at the claims. Some will wrap cleanly, some won't:&lt;/P&gt;
&lt;UL style="margin: 16px 0 20px 24px;"&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;CLM-101&lt;/STRONG&gt;: Property buildings claim on POL-001. Loss date is within the policy period and "buildings" is a valid section. This should wrap.&lt;/LI&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;CLM-102&lt;/STRONG&gt;: Motor accidental damage claim on POL-002. Loss date is within the policy period and the section matches. This should wrap too.&lt;/LI&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;CLM-103&lt;/STRONG&gt;: Property buildings claim on POL-003. The policy expired on 2025-03-01, but the loss date is 2025-06-10. Outside the policy period. This should fail.&lt;/LI&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;CLM-104&lt;/STRONG&gt;: Claim on POL-002, but the section is "windscreen". POL-002 only has "accidental damage" and "third party injury" sections. There's no "windscreen" section on this policy. This should fail.&lt;/LI&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;CLM-105&lt;/STRONG&gt;: Says it belongs to POL-999, which doesn't exist at all. This should fail.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2 style="font-size: 26px; font-weight: bold; color: #1b3139; margin-top: 40px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 3px solid #FF3621; display: inline-block;"&gt;Scenario 1: Successful claim wrap-up&lt;/H2&gt;
&lt;P&gt;Let's wrap CLM-101. We'll use a non-interactive transaction with &lt;CODE style="font-family: 'Courier New', Consolas, monospace; background: #F0F4F6; padding: 2px 6px; border-radius: 4px; font-size: 15px; color: #c03020;"&gt;BEGIN ATOMIC...END&lt;/CODE&gt; and validate the business rules with &lt;CODE style="font-family: 'Courier New', Consolas, monospace; background: #F0F4F6; padding: 2px 6px; border-radius: 4px; font-size: 15px; color: #c03020;"&gt;IF&lt;/CODE&gt; and &lt;CODE style="font-family: 'Courier New', Consolas, monospace; background: #F0F4F6; padding: 2px 6px; border-radius: 4px; font-size: 15px; color: #c03020;"&gt;SIGNAL&lt;/CODE&gt;.&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;BEGIN ATOMIC&lt;/STRONG&gt;
  -- Rule 1: Does the policy exist?
  &lt;STRONG&gt;IF NOT EXISTS&lt;/STRONG&gt; (
    &lt;STRONG&gt;SELECT&lt;/STRONG&gt; 1 &lt;STRONG&gt;FROM&lt;/STRONG&gt; policies &lt;STRONG&gt;WHERE&lt;/STRONG&gt; policy_id = 'POL-001'
  ) &lt;STRONG&gt;THEN&lt;/STRONG&gt;
    &lt;STRONG&gt;SIGNAL SQLSTATE&lt;/STRONG&gt; '45000'
    &lt;STRONG&gt;SET&lt;/STRONG&gt; MESSAGE_TEXT = 'Policy not found';
  &lt;STRONG&gt;END IF&lt;/STRONG&gt;;

  -- Rule 2: Is the loss date within the policy period?
  &lt;STRONG&gt;IF NOT EXISTS&lt;/STRONG&gt; (
    &lt;STRONG&gt;SELECT&lt;/STRONG&gt; 1 &lt;STRONG&gt;FROM&lt;/STRONG&gt; policies p
    &lt;STRONG&gt;JOIN&lt;/STRONG&gt; claims c &lt;STRONG&gt;ON&lt;/STRONG&gt; c.policy_number = p.policy_id
    &lt;STRONG&gt;WHERE&lt;/STRONG&gt; c.claim_id = 'CLM-101'
      &lt;STRONG&gt;AND&lt;/STRONG&gt; p.policy_id = 'POL-001'
      &lt;STRONG&gt;AND&lt;/STRONG&gt; p.section = c.section
      &lt;STRONG&gt;AND&lt;/STRONG&gt; c.loss_date &lt;STRONG&gt;BETWEEN&lt;/STRONG&gt; p.effective_date &lt;STRONG&gt;AND&lt;/STRONG&gt; p.expiry_date
  ) &lt;STRONG&gt;THEN&lt;/STRONG&gt;
    &lt;STRONG&gt;SIGNAL SQLSTATE&lt;/STRONG&gt; '45000'
    &lt;STRONG&gt;SET&lt;/STRONG&gt; MESSAGE_TEXT = 'Loss date falls outside policy period or section mismatch';
  &lt;STRONG&gt;END IF&lt;/STRONG&gt;;

  -- All rules passed. Update the claim with policy FK and wrap status.
  &lt;STRONG&gt;UPDATE&lt;/STRONG&gt; claims
  &lt;STRONG&gt;SET&lt;/STRONG&gt; status = 'wrapped',
      wrap_date = current_date(),
      policy_id = 'POL-001'
  &lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-101';

  -- Log the wrap event
  &lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claim_wrap_log
  &lt;STRONG&gt;VALUES&lt;/STRONG&gt; ('CLM-101', 'POL-001', current_date(), 'auto');

  -- Post the initial reserve
  &lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claim_reserves
  &lt;STRONG&gt;VALUES&lt;/STRONG&gt; ('CLM-101', 'POL-001', 'buildings', 12000.00, current_date());
&lt;STRONG&gt;END&lt;/STRONG&gt;;&lt;/PRE&gt;
&lt;P&gt;Both rules pass, so the claim gets updated, the wrap log gets an entry, and the reserve gets posted. One transaction, one commit, three tables updated atomically.&lt;/P&gt;
&lt;P&gt;You can verify:&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;SELECT&lt;/STRONG&gt; * &lt;STRONG&gt;FROM&lt;/STRONG&gt; claims &lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-101';
&lt;STRONG&gt;SELECT&lt;/STRONG&gt; * &lt;STRONG&gt;FROM&lt;/STRONG&gt; claim_wrap_log &lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-101';
&lt;STRONG&gt;SELECT&lt;/STRONG&gt; * &lt;STRONG&gt;FROM&lt;/STRONG&gt; claim_reserves &lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-101';&lt;/PRE&gt;
&lt;P style="text-align: center; margin: 20px 0;"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="s1-claim.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/25435i38FDB0647F0B3620/image-size/large?v=v2&amp;amp;px=999" role="button" title="s1-claim.png" alt="s1-claim.png" /&gt;&lt;/span&gt;&lt;FONT color="#808080"&gt;&lt;EM&gt;&lt;FONT size="2"&gt;&lt;SPAN class="s1"&gt;CLM-101 wrapped: status updated and policy FK attached&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class="lia-align-center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="s1-wraplog.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/25436i41A27F217D952D95/image-size/large?v=v2&amp;amp;px=999" role="button" title="s1-wraplog.png" alt="s1-wraplog.png" /&gt;&lt;/span&gt;&lt;EM&gt;&lt;FONT size="2" color="#808080"&gt;&lt;SPAN class="s1"&gt;CLM-101 wrap log entry with auto method&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/EM&gt;&lt;/P&gt;
&lt;P class="lia-align-center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="s1-reserves.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/25437iB00E7F1046548044/image-size/large?v=v2&amp;amp;px=999" role="button" title="s1-reserves.png" alt="s1-reserves.png" /&gt;&lt;/span&gt;&lt;EM&gt;&lt;FONT size="2" color="#808080"&gt;&lt;SPAN class="s1"&gt;CLM-101 initial reserve posted for the buildings section&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/EM&gt;&lt;/P&gt;
&lt;H2 style="font-size: 26px; font-weight: bold; color: #1b3139; margin-top: 40px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 3px solid #FF3621; display: inline-block;"&gt;Scenario 2: Failed wrap-up with automatic rollback&lt;/H2&gt;
&lt;P&gt;Now let's try CLM-103. Remember, the loss date (2025-06-10) falls after the policy expiry (2025-03-01).&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;BEGIN ATOMIC&lt;/STRONG&gt;
  -- Rule 1: Does the policy exist?
  &lt;STRONG&gt;IF NOT EXISTS&lt;/STRONG&gt; (
    &lt;STRONG&gt;SELECT&lt;/STRONG&gt; 1 &lt;STRONG&gt;FROM&lt;/STRONG&gt; policies &lt;STRONG&gt;WHERE&lt;/STRONG&gt; policy_id = 'POL-003'
  ) &lt;STRONG&gt;THEN&lt;/STRONG&gt;
    &lt;STRONG&gt;SIGNAL SQLSTATE&lt;/STRONG&gt; '45000'
    &lt;STRONG&gt;SET&lt;/STRONG&gt; MESSAGE_TEXT = 'Policy not found';
  &lt;STRONG&gt;END IF&lt;/STRONG&gt;;

  -- Rule 2: Loss date within policy period and section match?
  &lt;STRONG&gt;IF NOT EXISTS&lt;/STRONG&gt; (
    &lt;STRONG&gt;SELECT&lt;/STRONG&gt; 1 &lt;STRONG&gt;FROM&lt;/STRONG&gt; policies p
    &lt;STRONG&gt;JOIN&lt;/STRONG&gt; claims c &lt;STRONG&gt;ON&lt;/STRONG&gt; c.policy_number = p.policy_id
    &lt;STRONG&gt;WHERE&lt;/STRONG&gt; c.claim_id = 'CLM-103'
      &lt;STRONG&gt;AND&lt;/STRONG&gt; p.policy_id = 'POL-003'
      &lt;STRONG&gt;AND&lt;/STRONG&gt; p.section = c.section
      &lt;STRONG&gt;AND&lt;/STRONG&gt; c.loss_date &lt;STRONG&gt;BETWEEN&lt;/STRONG&gt; p.effective_date &lt;STRONG&gt;AND&lt;/STRONG&gt; p.expiry_date
  ) &lt;STRONG&gt;THEN&lt;/STRONG&gt;
    &lt;STRONG&gt;SIGNAL SQLSTATE&lt;/STRONG&gt; '45000'
    &lt;STRONG&gt;SET&lt;/STRONG&gt; MESSAGE_TEXT = 'Loss date falls outside policy period or section mismatch';
  &lt;STRONG&gt;END IF&lt;/STRONG&gt;;

  &lt;STRONG&gt;UPDATE&lt;/STRONG&gt; claims
  &lt;STRONG&gt;SET&lt;/STRONG&gt; status = 'wrapped',
      wrap_date = current_date(),
      policy_id = 'POL-003'
  &lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-103';

  &lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claim_wrap_log
  &lt;STRONG&gt;VALUES&lt;/STRONG&gt; ('CLM-103', 'POL-003', current_date(), 'auto');

  &lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claim_reserves
  &lt;STRONG&gt;VALUES&lt;/STRONG&gt; ('CLM-103', 'POL-003', 'buildings', 8000.00, current_date());
&lt;STRONG&gt;END&lt;/STRONG&gt;;&lt;/PRE&gt;
&lt;P&gt;This time, the &lt;CODE style="font-family: 'Courier New', Consolas, monospace; background: #F0F4F6; padding: 2px 6px; border-radius: 4px; font-size: 15px; color: #c03020;"&gt;SIGNAL&lt;/CODE&gt; fires because the loss date is outside the policy period. The entire transaction rolls back. Nothing gets written to any of the three tables. The claim stays as an orphan, ready to be picked up in tomorrow's run.&lt;/P&gt;
&lt;P style="text-align: center; margin: 20px 0;"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="s2-error.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/25438iF4BABC871F45F9AD/image-size/large?v=v2&amp;amp;px=999" role="button" title="s2-error.png" alt="s2-error.png" /&gt;&lt;/span&gt;&lt;FONT size="2"&gt;&lt;EM&gt;&lt;FONT color="#808080"&gt;&lt;SPAN class="s1"&gt;SIGNAL fires: loss date falls outside policy period&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;Verify that nothing changed:&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;SELECT&lt;/STRONG&gt; * &lt;STRONG&gt;FROM&lt;/STRONG&gt; claims &lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-103';
-- status should still be 'open', policy_id should be NULL&lt;/PRE&gt;
&lt;P style="text-align: center; margin: 20px 0;"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="s2-unchanged.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/25439i40BAFA2CFDDE9695/image-size/large?v=v2&amp;amp;px=999" role="button" title="s2-unchanged.png" alt="s2-unchanged.png" /&gt;&lt;/span&gt;&lt;FONT size="2"&gt;&lt;EM&gt;&lt;FONT color="#808080"&gt;&lt;SPAN class="s1"&gt;CLM-103 unchanged: still open with no policy attached&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/EM&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2 style="font-size: 26px; font-weight: bold; color: #1b3139; margin-top: 40px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 3px solid #FF3621; display: inline-block;"&gt;Scenario 3: Interactive review of orphaned claims&lt;/H2&gt;
&lt;P&gt;Sometimes an adjuster needs to look at an orphaned claim by hand. Maybe the claim is legitimate but the data needs a correction before it can wrap. Interactive mode is built for this.&lt;/P&gt;
&lt;P&gt;Let's say an adjuster is looking at CLM-104 (the claim with "windscreen" as the section, which doesn't match the policy).&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;BEGIN TRANSACTION&lt;/STRONG&gt;;

-- Check what we're dealing with
&lt;STRONG&gt;SELECT&lt;/STRONG&gt; c.claim_id, c.section &lt;STRONG&gt;AS&lt;/STRONG&gt; claim_section, p.section &lt;STRONG&gt;AS&lt;/STRONG&gt; policy_section
&lt;STRONG&gt;FROM&lt;/STRONG&gt; claims c
&lt;STRONG&gt;JOIN&lt;/STRONG&gt; policies p &lt;STRONG&gt;ON&lt;/STRONG&gt; c.policy_number = p.policy_id
&lt;STRONG&gt;WHERE&lt;/STRONG&gt; c.claim_id = 'CLM-104';&lt;/PRE&gt;
&lt;P style="text-align: center; margin: 20px 0;"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="s3-inspect.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/25440i809AB7DE3AD594A9/image-size/large?v=v2&amp;amp;px=999" role="button" title="s3-inspect.png" alt="s3-inspect.png" /&gt;&lt;/span&gt;&lt;FONT size="2"&gt;&lt;EM&gt;&lt;FONT color="#808080"&gt;&lt;SPAN&gt;CLM-104 mismatch: windscreen vs policy sections&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;The adjuster sees the mismatch: the claim says "windscreen", but POL-002 only has "accidental damage" and "third party injury". After reviewing the original claim form, they determined it should have been "accidental damage". They correct it and wrap:&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;-- Fix the section and wrap
&lt;STRONG&gt;UPDATE&lt;/STRONG&gt; claims
&lt;STRONG&gt;SET&lt;/STRONG&gt; section = 'accidental damage',
    status = 'wrapped',
    wrap_date = current_date(),
    policy_id = 'POL-002'
&lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-104';

&lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claim_wrap_log
&lt;STRONG&gt;VALUES&lt;/STRONG&gt; ('CLM-104', 'POL-002', current_date(), 'manual');

&lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claim_reserves
&lt;STRONG&gt;VALUES&lt;/STRONG&gt; ('CLM-104', 'POL-002', 'accidental damage', 3200.00, current_date());&lt;/PRE&gt;
&lt;P&gt;Before committing, the adjuster checks that everything looks right. This is the whole point of interactive mode: you can see the intermediate state inside the transaction before making anything permanent.&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;-- Verify the correction before committing
&lt;STRONG&gt;SELECT&lt;/STRONG&gt; c.claim_id, c.section, c.status, c.policy_id, r.reserve_amount
&lt;STRONG&gt;FROM&lt;/STRONG&gt; claims c
&lt;STRONG&gt;JOIN&lt;/STRONG&gt; claim_reserves r &lt;STRONG&gt;ON&lt;/STRONG&gt; c.claim_id = r.claim_id
&lt;STRONG&gt;WHERE&lt;/STRONG&gt; c.claim_id = 'CLM-104';&lt;/PRE&gt;
&lt;P style="text-align: center; margin: 20px 0;"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="s3-verify.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/25441i3688EA4151FC8839/image-size/large?v=v2&amp;amp;px=999" role="button" title="s3-verify.png" alt="s3-verify.png" /&gt;&lt;/span&gt;&lt;FONT size="2" color="#808080"&gt;&lt;EM&gt;&lt;SPAN class="s1"&gt;CLM-104 corrected to accidental damage before commit&lt;/SPAN&gt;&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;The section is corrected, the reserve is posted, and the amounts match. The adjuster commits:&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;COMMIT&lt;/STRONG&gt;;&lt;/PRE&gt;
&lt;P&gt;If something looked off, they could have run &lt;CODE style="font-family: 'Courier New', Consolas, monospace; background: #F0F4F6; padding: 2px 6px; border-radius: 4px; font-size: 15px; color: #c03020;"&gt;ROLLBACK&lt;/CODE&gt; instead and nothing would have been saved.&lt;/P&gt;
&lt;H2 style="font-size: 26px; font-weight: bold; color: #1b3139; margin-top: 40px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 3px solid #FF3621; display: inline-block;"&gt;Scenario 4: Conflict detection&lt;/H2&gt;
&lt;P&gt;What happens when two processes try to wrap the same claim at the same time? Let's simulate it.&lt;/P&gt;
&lt;P&gt;To try this yourself, you'll need two separate SQL sessions, like two SQL editor tabs on the same warehouse. The steps below alternate between them.&lt;/P&gt;
&lt;P&gt;Imagine two wrap-up processes running at the same time. Both pick up CLM-102 (the accidental damage claim that should wrap successfully).&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Process A&lt;/STRONG&gt; starts first:&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;BEGIN TRANSACTION&lt;/STRONG&gt;;

&lt;STRONG&gt;UPDATE&lt;/STRONG&gt; claims
&lt;STRONG&gt;SET&lt;/STRONG&gt; status = 'wrapped',
    wrap_date = current_date(),
    policy_id = 'POL-002'
&lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-102';

&lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claim_wrap_log
&lt;STRONG&gt;VALUES&lt;/STRONG&gt; ('CLM-102', 'POL-002', current_date(), 'auto');

&lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claim_reserves
&lt;STRONG&gt;VALUES&lt;/STRONG&gt; ('CLM-102', 'POL-002', 'accidental damage', 4500.00, current_date());&lt;/PRE&gt;
&lt;P&gt;&lt;STRONG&gt;Process B&lt;/STRONG&gt; starts in a separate session before Process A commits:&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;BEGIN TRANSACTION&lt;/STRONG&gt;;

&lt;STRONG&gt;UPDATE&lt;/STRONG&gt; claims
&lt;STRONG&gt;SET&lt;/STRONG&gt; status = 'wrapped',
    wrap_date = current_date(),
    policy_id = 'POL-002'
&lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-102';&lt;/PRE&gt;
&lt;P&gt;Now Process A commits:&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;-- Process A
&lt;STRONG&gt;COMMIT&lt;/STRONG&gt;;  -- succeeds&lt;/PRE&gt;
&lt;P&gt;Process B tries to commit:&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;-- Process B
&lt;STRONG&gt;COMMIT&lt;/STRONG&gt;;  -- fails with a conflict error&lt;/PRE&gt;
&lt;P&gt;Process B gets a conflict error because Process A already committed changes to the same rows. Databricks uses optimistic concurrency control: no locks are held during the transaction, and conflicts are detected at commit time. If two transactions touch overlapping data in the same table, the second one to commit loses and has to retry.&lt;/P&gt;
&lt;P&gt;In most batch processing scenarios, that's exactly what you want. Conflicts are the exception, not the rule. Process B just needs to roll back, re-read the current state, and decide whether it still needs to act.&lt;/P&gt;
&lt;H2 style="font-size: 26px; font-weight: bold; color: #1b3139; margin-top: 40px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 3px solid #FF3621; display: inline-block;"&gt;Key takeaways&lt;/H2&gt;
&lt;P&gt;A few things worth remembering:&lt;/P&gt;
&lt;UL style="margin: 16px 0 20px 24px;"&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;Non-interactive for pipelines, interactive for exploration.&lt;/STRONG&gt; Use &lt;CODE style="font-family: 'Courier New', Consolas, monospace; background: #F0F4F6; padding: 2px 6px; border-radius: 4px; font-size: 15px; color: #c03020;"&gt;BEGIN ATOMIC...END&lt;/CODE&gt; when you know exactly what needs to happen. Use &lt;CODE style="font-family: 'Courier New', Consolas, monospace; background: #F0F4F6; padding: 2px 6px; border-radius: 4px; font-size: 15px; color: #c03020;"&gt;BEGIN TRANSACTION...COMMIT&lt;/CODE&gt; when you need to inspect results before deciding.&lt;/LI&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;SIGNAL is your friend for business rules.&lt;/STRONG&gt; Custom error messages make it obvious why a wrap-up failed, which beats a generic constraint violation every time.&lt;/LI&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;The policy table was read-only throughout.&lt;/STRONG&gt; We joined against it to validate rules but never wrote to it. The transaction still keeps those reads consistent through snapshot isolation.&lt;/LI&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;Orphaned claims are a feature, not a bug.&lt;/STRONG&gt; If the wrap fails, the data is clean. You just try again tomorrow with fresh data. The retry pattern works even better now that you know nothing was half written.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2 style="font-size: 26px; font-weight: bold; color: #1b3139; margin-top: 40px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 3px solid #FF3621; display: inline-block;"&gt;Try it yourself&lt;/H2&gt;
&lt;P&gt;If you missed it, &lt;A style="color: #ff3621;" href="https://community.databricks.com/t5/community-articles/databricks-multi-table-transactions-part-1/td-p/151163" target="_blank"&gt;Part 1&lt;/A&gt; covers the motivation behind this feature and why it matters beyond data engineering.&lt;/P&gt;
&lt;P&gt;The official docs are here: &lt;A style="color: #ff3621;" href="https://docs.databricks.com/aws/en/transactions/" target="_blank"&gt;Multi-statement transactions on Databricks&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;Try adapting this to your own domain. The claim wrap-up is just one example. Anywhere you're writing to multiple tables and need them to stay in sync, this is worth a look.&lt;/P&gt;
&lt;HR /&gt;
&lt;P style="font-style: italic; color: #5a7a86;"&gt;Got a question or spotted something I missed? Let me know in the comments.&lt;/P&gt;
&lt;/DIV&gt;</description>
    <pubDate>Sat, 28 Mar 2026 21:54:20 GMT</pubDate>
    <dc:creator>Ashwin_DSA</dc:creator>
    <dc:date>2026-03-28T21:54:20Z</dc:date>
    <item>
      <title>Databricks Multi-Table Transactions - Part 2</title>
      <link>https://community.databricks.com/t5/community-articles/databricks-multi-table-transactions-part-2/m-p/152402#M1118</link>
      <description>&lt;DIV style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: #1b3139; line-height: 1.75; font-size: 17px;"&gt;
&lt;P&gt;In &lt;A style="color: #ff3621;" href="https://community.databricks.com/t5/community-articles/databricks-multi-table-transactions-part-1/td-p/151163" target="_blank"&gt;Part 1&lt;/A&gt;, we covered why multi-table transactions matter. Now let's build one.&lt;/P&gt;
&lt;P&gt;We'll create the tables from the claim wrap-up scenario, load sample P&amp;amp;C insurance data, and walk through what happens when the wrap-up succeeds, when it fails, and when two processes try to wrap the same claim at the same time. Everything runs on a Databricks SQL warehouse. You can follow along in a notebook or the SQL editor.&lt;/P&gt;
&lt;H2 style="font-size: 26px; font-weight: bold; color: #1b3139; margin-top: 40px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 3px solid #FF3621; display: inline-block;"&gt;Setting up the demo&lt;/H2&gt;
&lt;P&gt;First, we need a catalog and schema. We'll also enable catalog-managed commits on every table we create, since that's what makes multi-table transactions possible.&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;CREATE CATALOG IF NOT EXISTS&lt;/STRONG&gt; insurance_demo;
&lt;STRONG&gt;USE CATALOG&lt;/STRONG&gt; insurance_demo;
&lt;STRONG&gt;CREATE SCHEMA IF NOT EXISTS&lt;/STRONG&gt; claims_dw;
&lt;STRONG&gt;USE SCHEMA&lt;/STRONG&gt; claims_dw;&lt;/PRE&gt;
&lt;P&gt;Now let's create our four tables. The &lt;CODE style="font-family: 'Courier New', Consolas, monospace; background: #F0F4F6; padding: 2px 6px; border-radius: 4px; font-size: 15px; color: #c03020;"&gt;policies&lt;/CODE&gt; table is our reference data. It holds policy details and never gets written to during the wrap-up. The other three are where the action happens.&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;-- Reference table: policies (read-only during wrap-up)
&lt;STRONG&gt;CREATE OR REPLACE TABLE&lt;/STRONG&gt; policies (
  policy_id STRING,
  customer_name STRING,
  section STRING,
  effective_date DATE,
  expiry_date DATE
)
&lt;STRONG&gt;USING&lt;/STRONG&gt; DELTA
&lt;STRONG&gt;TBLPROPERTIES&lt;/STRONG&gt; ('delta.feature.catalogManaged' = 'supported');

-- Claims table: gets updated with policy FK and wrap status
&lt;STRONG&gt;CREATE OR REPLACE TABLE&lt;/STRONG&gt; claims (
  claim_id STRING,
  policy_number STRING,
  section STRING,
  loss_date DATE,
  claim_amount DECIMAL(12,2),
  status STRING,
  policy_id STRING,
  wrap_date DATE
)
&lt;STRONG&gt;USING&lt;/STRONG&gt; DELTA
&lt;STRONG&gt;TBLPROPERTIES&lt;/STRONG&gt; ('delta.feature.catalogManaged' = 'supported');

-- Wrap log: audit trail of successful wraps
&lt;STRONG&gt;CREATE OR REPLACE TABLE&lt;/STRONG&gt; claim_wrap_log (
  claim_id STRING,
  policy_id STRING,
  wrap_date DATE,
  wrap_method STRING
)
&lt;STRONG&gt;USING&lt;/STRONG&gt; DELTA
&lt;STRONG&gt;TBLPROPERTIES&lt;/STRONG&gt; ('delta.feature.catalogManaged' = 'supported');

-- Claim reserves: initial reserve posted when a claim wraps
&lt;STRONG&gt;CREATE OR REPLACE TABLE&lt;/STRONG&gt; claim_reserves (
  claim_id STRING,
  policy_id STRING,
  section STRING,
  reserve_amount DECIMAL(12,2),
  reserve_date DATE
)
&lt;STRONG&gt;USING&lt;/STRONG&gt; DELTA
&lt;STRONG&gt;TBLPROPERTIES&lt;/STRONG&gt; ('delta.feature.catalogManaged' = 'supported');&lt;/PRE&gt;
&lt;P&gt;Now let's seed some data. We'll add a mix of property and motor policies, and a batch of claims that arrived from the claims system.&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;-- Seed policies
&lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; policies &lt;STRONG&gt;VALUES&lt;/STRONG&gt;
  ('POL-001', 'James Wilson', 'buildings', '2025-01-01', '2026-01-01'),
  ('POL-001', 'James Wilson', 'contents', '2025-01-01', '2026-01-01'),
  ('POL-002', 'Sarah Chen', 'accidental damage', '2025-06-01', '2026-06-01'),
  ('POL-002', 'Sarah Chen', 'third party injury', '2025-06-01', '2026-06-01'),
  ('POL-003', 'David Okafor', 'buildings', '2024-03-01', '2025-03-01'),
  ('POL-004', 'Emily Brown', 'accidental damage', '2025-09-01', '2026-09-01');

-- Seed claims (arrived from claims system, all status = 'open')
&lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claims (claim_id, policy_number, section, loss_date, claim_amount, status) &lt;STRONG&gt;VALUES&lt;/STRONG&gt;
  ('CLM-101', 'POL-001', 'buildings', '2025-08-15', 12000.00, 'open'),
  ('CLM-102', 'POL-002', 'accidental damage', '2025-11-20', 4500.00, 'open'),
  ('CLM-103', 'POL-003', 'buildings', '2025-06-10', 8000.00, 'open'),
  ('CLM-104', 'POL-002', 'windscreen', '2025-12-01', 3200.00, 'open'),
  ('CLM-105', 'POL-999', 'buildings', '2025-07-01', 5500.00, 'open');&lt;/PRE&gt;
&lt;P&gt;Take a moment to look at the claims. Some will wrap cleanly, some won't:&lt;/P&gt;
&lt;UL style="margin: 16px 0 20px 24px;"&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;CLM-101&lt;/STRONG&gt;: Property buildings claim on POL-001. Loss date is within the policy period and "buildings" is a valid section. This should wrap.&lt;/LI&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;CLM-102&lt;/STRONG&gt;: Motor accidental damage claim on POL-002. Loss date is within the policy period and the section matches. This should wrap too.&lt;/LI&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;CLM-103&lt;/STRONG&gt;: Property buildings claim on POL-003. The policy expired on 2025-03-01, but the loss date is 2025-06-10. Outside the policy period. This should fail.&lt;/LI&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;CLM-104&lt;/STRONG&gt;: Claim on POL-002, but the section is "windscreen". POL-002 only has "accidental damage" and "third party injury" sections. There's no "windscreen" section on this policy. This should fail.&lt;/LI&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;CLM-105&lt;/STRONG&gt;: Says it belongs to POL-999, which doesn't exist at all. This should fail.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2 style="font-size: 26px; font-weight: bold; color: #1b3139; margin-top: 40px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 3px solid #FF3621; display: inline-block;"&gt;Scenario 1: Successful claim wrap-up&lt;/H2&gt;
&lt;P&gt;Let's wrap CLM-101. We'll use a non-interactive transaction with &lt;CODE style="font-family: 'Courier New', Consolas, monospace; background: #F0F4F6; padding: 2px 6px; border-radius: 4px; font-size: 15px; color: #c03020;"&gt;BEGIN ATOMIC...END&lt;/CODE&gt; and validate the business rules with &lt;CODE style="font-family: 'Courier New', Consolas, monospace; background: #F0F4F6; padding: 2px 6px; border-radius: 4px; font-size: 15px; color: #c03020;"&gt;IF&lt;/CODE&gt; and &lt;CODE style="font-family: 'Courier New', Consolas, monospace; background: #F0F4F6; padding: 2px 6px; border-radius: 4px; font-size: 15px; color: #c03020;"&gt;SIGNAL&lt;/CODE&gt;.&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;BEGIN ATOMIC&lt;/STRONG&gt;
  -- Rule 1: Does the policy exist?
  &lt;STRONG&gt;IF NOT EXISTS&lt;/STRONG&gt; (
    &lt;STRONG&gt;SELECT&lt;/STRONG&gt; 1 &lt;STRONG&gt;FROM&lt;/STRONG&gt; policies &lt;STRONG&gt;WHERE&lt;/STRONG&gt; policy_id = 'POL-001'
  ) &lt;STRONG&gt;THEN&lt;/STRONG&gt;
    &lt;STRONG&gt;SIGNAL SQLSTATE&lt;/STRONG&gt; '45000'
    &lt;STRONG&gt;SET&lt;/STRONG&gt; MESSAGE_TEXT = 'Policy not found';
  &lt;STRONG&gt;END IF&lt;/STRONG&gt;;

  -- Rule 2: Is the loss date within the policy period?
  &lt;STRONG&gt;IF NOT EXISTS&lt;/STRONG&gt; (
    &lt;STRONG&gt;SELECT&lt;/STRONG&gt; 1 &lt;STRONG&gt;FROM&lt;/STRONG&gt; policies p
    &lt;STRONG&gt;JOIN&lt;/STRONG&gt; claims c &lt;STRONG&gt;ON&lt;/STRONG&gt; c.policy_number = p.policy_id
    &lt;STRONG&gt;WHERE&lt;/STRONG&gt; c.claim_id = 'CLM-101'
      &lt;STRONG&gt;AND&lt;/STRONG&gt; p.policy_id = 'POL-001'
      &lt;STRONG&gt;AND&lt;/STRONG&gt; p.section = c.section
      &lt;STRONG&gt;AND&lt;/STRONG&gt; c.loss_date &lt;STRONG&gt;BETWEEN&lt;/STRONG&gt; p.effective_date &lt;STRONG&gt;AND&lt;/STRONG&gt; p.expiry_date
  ) &lt;STRONG&gt;THEN&lt;/STRONG&gt;
    &lt;STRONG&gt;SIGNAL SQLSTATE&lt;/STRONG&gt; '45000'
    &lt;STRONG&gt;SET&lt;/STRONG&gt; MESSAGE_TEXT = 'Loss date falls outside policy period or section mismatch';
  &lt;STRONG&gt;END IF&lt;/STRONG&gt;;

  -- All rules passed. Update the claim with policy FK and wrap status.
  &lt;STRONG&gt;UPDATE&lt;/STRONG&gt; claims
  &lt;STRONG&gt;SET&lt;/STRONG&gt; status = 'wrapped',
      wrap_date = current_date(),
      policy_id = 'POL-001'
  &lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-101';

  -- Log the wrap event
  &lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claim_wrap_log
  &lt;STRONG&gt;VALUES&lt;/STRONG&gt; ('CLM-101', 'POL-001', current_date(), 'auto');

  -- Post the initial reserve
  &lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claim_reserves
  &lt;STRONG&gt;VALUES&lt;/STRONG&gt; ('CLM-101', 'POL-001', 'buildings', 12000.00, current_date());
&lt;STRONG&gt;END&lt;/STRONG&gt;;&lt;/PRE&gt;
&lt;P&gt;Both rules pass, so the claim gets updated, the wrap log gets an entry, and the reserve gets posted. One transaction, one commit, three tables updated atomically.&lt;/P&gt;
&lt;P&gt;You can verify:&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;SELECT&lt;/STRONG&gt; * &lt;STRONG&gt;FROM&lt;/STRONG&gt; claims &lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-101';
&lt;STRONG&gt;SELECT&lt;/STRONG&gt; * &lt;STRONG&gt;FROM&lt;/STRONG&gt; claim_wrap_log &lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-101';
&lt;STRONG&gt;SELECT&lt;/STRONG&gt; * &lt;STRONG&gt;FROM&lt;/STRONG&gt; claim_reserves &lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-101';&lt;/PRE&gt;
&lt;P style="text-align: center; margin: 20px 0;"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="s1-claim.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/25435i38FDB0647F0B3620/image-size/large?v=v2&amp;amp;px=999" role="button" title="s1-claim.png" alt="s1-claim.png" /&gt;&lt;/span&gt;&lt;FONT color="#808080"&gt;&lt;EM&gt;&lt;FONT size="2"&gt;&lt;SPAN class="s1"&gt;CLM-101 wrapped: status updated and policy FK attached&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class="lia-align-center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="s1-wraplog.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/25436i41A27F217D952D95/image-size/large?v=v2&amp;amp;px=999" role="button" title="s1-wraplog.png" alt="s1-wraplog.png" /&gt;&lt;/span&gt;&lt;EM&gt;&lt;FONT size="2" color="#808080"&gt;&lt;SPAN class="s1"&gt;CLM-101 wrap log entry with auto method&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/EM&gt;&lt;/P&gt;
&lt;P class="lia-align-center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="s1-reserves.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/25437iB00E7F1046548044/image-size/large?v=v2&amp;amp;px=999" role="button" title="s1-reserves.png" alt="s1-reserves.png" /&gt;&lt;/span&gt;&lt;EM&gt;&lt;FONT size="2" color="#808080"&gt;&lt;SPAN class="s1"&gt;CLM-101 initial reserve posted for the buildings section&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/EM&gt;&lt;/P&gt;
&lt;H2 style="font-size: 26px; font-weight: bold; color: #1b3139; margin-top: 40px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 3px solid #FF3621; display: inline-block;"&gt;Scenario 2: Failed wrap-up with automatic rollback&lt;/H2&gt;
&lt;P&gt;Now let's try CLM-103. Remember, the loss date (2025-06-10) falls after the policy expiry (2025-03-01).&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;BEGIN ATOMIC&lt;/STRONG&gt;
  -- Rule 1: Does the policy exist?
  &lt;STRONG&gt;IF NOT EXISTS&lt;/STRONG&gt; (
    &lt;STRONG&gt;SELECT&lt;/STRONG&gt; 1 &lt;STRONG&gt;FROM&lt;/STRONG&gt; policies &lt;STRONG&gt;WHERE&lt;/STRONG&gt; policy_id = 'POL-003'
  ) &lt;STRONG&gt;THEN&lt;/STRONG&gt;
    &lt;STRONG&gt;SIGNAL SQLSTATE&lt;/STRONG&gt; '45000'
    &lt;STRONG&gt;SET&lt;/STRONG&gt; MESSAGE_TEXT = 'Policy not found';
  &lt;STRONG&gt;END IF&lt;/STRONG&gt;;

  -- Rule 2: Loss date within policy period and section match?
  &lt;STRONG&gt;IF NOT EXISTS&lt;/STRONG&gt; (
    &lt;STRONG&gt;SELECT&lt;/STRONG&gt; 1 &lt;STRONG&gt;FROM&lt;/STRONG&gt; policies p
    &lt;STRONG&gt;JOIN&lt;/STRONG&gt; claims c &lt;STRONG&gt;ON&lt;/STRONG&gt; c.policy_number = p.policy_id
    &lt;STRONG&gt;WHERE&lt;/STRONG&gt; c.claim_id = 'CLM-103'
      &lt;STRONG&gt;AND&lt;/STRONG&gt; p.policy_id = 'POL-003'
      &lt;STRONG&gt;AND&lt;/STRONG&gt; p.section = c.section
      &lt;STRONG&gt;AND&lt;/STRONG&gt; c.loss_date &lt;STRONG&gt;BETWEEN&lt;/STRONG&gt; p.effective_date &lt;STRONG&gt;AND&lt;/STRONG&gt; p.expiry_date
  ) &lt;STRONG&gt;THEN&lt;/STRONG&gt;
    &lt;STRONG&gt;SIGNAL SQLSTATE&lt;/STRONG&gt; '45000'
    &lt;STRONG&gt;SET&lt;/STRONG&gt; MESSAGE_TEXT = 'Loss date falls outside policy period or section mismatch';
  &lt;STRONG&gt;END IF&lt;/STRONG&gt;;

  &lt;STRONG&gt;UPDATE&lt;/STRONG&gt; claims
  &lt;STRONG&gt;SET&lt;/STRONG&gt; status = 'wrapped',
      wrap_date = current_date(),
      policy_id = 'POL-003'
  &lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-103';

  &lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claim_wrap_log
  &lt;STRONG&gt;VALUES&lt;/STRONG&gt; ('CLM-103', 'POL-003', current_date(), 'auto');

  &lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claim_reserves
  &lt;STRONG&gt;VALUES&lt;/STRONG&gt; ('CLM-103', 'POL-003', 'buildings', 8000.00, current_date());
&lt;STRONG&gt;END&lt;/STRONG&gt;;&lt;/PRE&gt;
&lt;P&gt;This time, the &lt;CODE style="font-family: 'Courier New', Consolas, monospace; background: #F0F4F6; padding: 2px 6px; border-radius: 4px; font-size: 15px; color: #c03020;"&gt;SIGNAL&lt;/CODE&gt; fires because the loss date is outside the policy period. The entire transaction rolls back. Nothing gets written to any of the three tables. The claim stays as an orphan, ready to be picked up in tomorrow's run.&lt;/P&gt;
&lt;P style="text-align: center; margin: 20px 0;"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="s2-error.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/25438iF4BABC871F45F9AD/image-size/large?v=v2&amp;amp;px=999" role="button" title="s2-error.png" alt="s2-error.png" /&gt;&lt;/span&gt;&lt;FONT size="2"&gt;&lt;EM&gt;&lt;FONT color="#808080"&gt;&lt;SPAN class="s1"&gt;SIGNAL fires: loss date falls outside policy period&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;Verify that nothing changed:&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;SELECT&lt;/STRONG&gt; * &lt;STRONG&gt;FROM&lt;/STRONG&gt; claims &lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-103';
-- status should still be 'open', policy_id should be NULL&lt;/PRE&gt;
&lt;P style="text-align: center; margin: 20px 0;"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="s2-unchanged.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/25439i40BAFA2CFDDE9695/image-size/large?v=v2&amp;amp;px=999" role="button" title="s2-unchanged.png" alt="s2-unchanged.png" /&gt;&lt;/span&gt;&lt;FONT size="2"&gt;&lt;EM&gt;&lt;FONT color="#808080"&gt;&lt;SPAN class="s1"&gt;CLM-103 unchanged: still open with no policy attached&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/EM&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2 style="font-size: 26px; font-weight: bold; color: #1b3139; margin-top: 40px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 3px solid #FF3621; display: inline-block;"&gt;Scenario 3: Interactive review of orphaned claims&lt;/H2&gt;
&lt;P&gt;Sometimes an adjuster needs to look at an orphaned claim by hand. Maybe the claim is legitimate but the data needs a correction before it can wrap. Interactive mode is built for this.&lt;/P&gt;
&lt;P&gt;Let's say an adjuster is looking at CLM-104 (the claim with "windscreen" as the section, which doesn't match the policy).&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;BEGIN TRANSACTION&lt;/STRONG&gt;;

-- Check what we're dealing with
&lt;STRONG&gt;SELECT&lt;/STRONG&gt; c.claim_id, c.section &lt;STRONG&gt;AS&lt;/STRONG&gt; claim_section, p.section &lt;STRONG&gt;AS&lt;/STRONG&gt; policy_section
&lt;STRONG&gt;FROM&lt;/STRONG&gt; claims c
&lt;STRONG&gt;JOIN&lt;/STRONG&gt; policies p &lt;STRONG&gt;ON&lt;/STRONG&gt; c.policy_number = p.policy_id
&lt;STRONG&gt;WHERE&lt;/STRONG&gt; c.claim_id = 'CLM-104';&lt;/PRE&gt;
&lt;P style="text-align: center; margin: 20px 0;"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="s3-inspect.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/25440i809AB7DE3AD594A9/image-size/large?v=v2&amp;amp;px=999" role="button" title="s3-inspect.png" alt="s3-inspect.png" /&gt;&lt;/span&gt;&lt;FONT size="2"&gt;&lt;EM&gt;&lt;FONT color="#808080"&gt;&lt;SPAN&gt;CLM-104 mismatch: windscreen vs policy sections&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;The adjuster sees the mismatch: the claim says "windscreen", but POL-002 only has "accidental damage" and "third party injury". After reviewing the original claim form, they determined it should have been "accidental damage". They correct it and wrap:&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;-- Fix the section and wrap
&lt;STRONG&gt;UPDATE&lt;/STRONG&gt; claims
&lt;STRONG&gt;SET&lt;/STRONG&gt; section = 'accidental damage',
    status = 'wrapped',
    wrap_date = current_date(),
    policy_id = 'POL-002'
&lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-104';

&lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claim_wrap_log
&lt;STRONG&gt;VALUES&lt;/STRONG&gt; ('CLM-104', 'POL-002', current_date(), 'manual');

&lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claim_reserves
&lt;STRONG&gt;VALUES&lt;/STRONG&gt; ('CLM-104', 'POL-002', 'accidental damage', 3200.00, current_date());&lt;/PRE&gt;
&lt;P&gt;Before committing, the adjuster checks that everything looks right. This is the whole point of interactive mode: you can see the intermediate state inside the transaction before making anything permanent.&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;-- Verify the correction before committing
&lt;STRONG&gt;SELECT&lt;/STRONG&gt; c.claim_id, c.section, c.status, c.policy_id, r.reserve_amount
&lt;STRONG&gt;FROM&lt;/STRONG&gt; claims c
&lt;STRONG&gt;JOIN&lt;/STRONG&gt; claim_reserves r &lt;STRONG&gt;ON&lt;/STRONG&gt; c.claim_id = r.claim_id
&lt;STRONG&gt;WHERE&lt;/STRONG&gt; c.claim_id = 'CLM-104';&lt;/PRE&gt;
&lt;P style="text-align: center; margin: 20px 0;"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="s3-verify.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/25441i3688EA4151FC8839/image-size/large?v=v2&amp;amp;px=999" role="button" title="s3-verify.png" alt="s3-verify.png" /&gt;&lt;/span&gt;&lt;FONT size="2" color="#808080"&gt;&lt;EM&gt;&lt;SPAN class="s1"&gt;CLM-104 corrected to accidental damage before commit&lt;/SPAN&gt;&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;The section is corrected, the reserve is posted, and the amounts match. The adjuster commits:&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;COMMIT&lt;/STRONG&gt;;&lt;/PRE&gt;
&lt;P&gt;If something looked off, they could have run &lt;CODE style="font-family: 'Courier New', Consolas, monospace; background: #F0F4F6; padding: 2px 6px; border-radius: 4px; font-size: 15px; color: #c03020;"&gt;ROLLBACK&lt;/CODE&gt; instead and nothing would have been saved.&lt;/P&gt;
&lt;H2 style="font-size: 26px; font-weight: bold; color: #1b3139; margin-top: 40px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 3px solid #FF3621; display: inline-block;"&gt;Scenario 4: Conflict detection&lt;/H2&gt;
&lt;P&gt;What happens when two processes try to wrap the same claim at the same time? Let's simulate it.&lt;/P&gt;
&lt;P&gt;To try this yourself, you'll need two separate SQL sessions, like two SQL editor tabs on the same warehouse. The steps below alternate between them.&lt;/P&gt;
&lt;P&gt;Imagine two wrap-up processes running at the same time. Both pick up CLM-102 (the accidental damage claim that should wrap successfully).&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Process A&lt;/STRONG&gt; starts first:&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;BEGIN TRANSACTION&lt;/STRONG&gt;;

&lt;STRONG&gt;UPDATE&lt;/STRONG&gt; claims
&lt;STRONG&gt;SET&lt;/STRONG&gt; status = 'wrapped',
    wrap_date = current_date(),
    policy_id = 'POL-002'
&lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-102';

&lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claim_wrap_log
&lt;STRONG&gt;VALUES&lt;/STRONG&gt; ('CLM-102', 'POL-002', current_date(), 'auto');

&lt;STRONG&gt;INSERT INTO&lt;/STRONG&gt; claim_reserves
&lt;STRONG&gt;VALUES&lt;/STRONG&gt; ('CLM-102', 'POL-002', 'accidental damage', 4500.00, current_date());&lt;/PRE&gt;
&lt;P&gt;&lt;STRONG&gt;Process B&lt;/STRONG&gt; starts in a separate session before Process A commits:&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;&lt;STRONG&gt;BEGIN TRANSACTION&lt;/STRONG&gt;;

&lt;STRONG&gt;UPDATE&lt;/STRONG&gt; claims
&lt;STRONG&gt;SET&lt;/STRONG&gt; status = 'wrapped',
    wrap_date = current_date(),
    policy_id = 'POL-002'
&lt;STRONG&gt;WHERE&lt;/STRONG&gt; claim_id = 'CLM-102';&lt;/PRE&gt;
&lt;P&gt;Now Process A commits:&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;-- Process A
&lt;STRONG&gt;COMMIT&lt;/STRONG&gt;;  -- succeeds&lt;/PRE&gt;
&lt;P&gt;Process B tries to commit:&lt;/P&gt;
&lt;PRE style="background: #1B3139; border-left: 4px solid #FF3621; border-radius: 6px; padding: 20px 24px; margin: 24px 0; font-family: 'Courier New', Consolas, monospace; font-size: 14px; line-height: 1.6; color: #e8ecf0; overflow-x: auto; white-space: pre;"&gt;-- Process B
&lt;STRONG&gt;COMMIT&lt;/STRONG&gt;;  -- fails with a conflict error&lt;/PRE&gt;
&lt;P&gt;Process B gets a conflict error because Process A already committed changes to the same rows. Databricks uses optimistic concurrency control: no locks are held during the transaction, and conflicts are detected at commit time. If two transactions touch overlapping data in the same table, the second one to commit loses and has to retry.&lt;/P&gt;
&lt;P&gt;In most batch processing scenarios, that's exactly what you want. Conflicts are the exception, not the rule. Process B just needs to roll back, re-read the current state, and decide whether it still needs to act.&lt;/P&gt;
&lt;H2 style="font-size: 26px; font-weight: bold; color: #1b3139; margin-top: 40px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 3px solid #FF3621; display: inline-block;"&gt;Key takeaways&lt;/H2&gt;
&lt;P&gt;A few things worth remembering:&lt;/P&gt;
&lt;UL style="margin: 16px 0 20px 24px;"&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;Non-interactive for pipelines, interactive for exploration.&lt;/STRONG&gt; Use &lt;CODE style="font-family: 'Courier New', Consolas, monospace; background: #F0F4F6; padding: 2px 6px; border-radius: 4px; font-size: 15px; color: #c03020;"&gt;BEGIN ATOMIC...END&lt;/CODE&gt; when you know exactly what needs to happen. Use &lt;CODE style="font-family: 'Courier New', Consolas, monospace; background: #F0F4F6; padding: 2px 6px; border-radius: 4px; font-size: 15px; color: #c03020;"&gt;BEGIN TRANSACTION...COMMIT&lt;/CODE&gt; when you need to inspect results before deciding.&lt;/LI&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;SIGNAL is your friend for business rules.&lt;/STRONG&gt; Custom error messages make it obvious why a wrap-up failed, which beats a generic constraint violation every time.&lt;/LI&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;The policy table was read-only throughout.&lt;/STRONG&gt; We joined against it to validate rules but never wrote to it. The transaction still keeps those reads consistent through snapshot isolation.&lt;/LI&gt;
&lt;LI style="margin-bottom: 10px;"&gt;&lt;STRONG&gt;Orphaned claims are a feature, not a bug.&lt;/STRONG&gt; If the wrap fails, the data is clean. You just try again tomorrow with fresh data. The retry pattern works even better now that you know nothing was half written.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2 style="font-size: 26px; font-weight: bold; color: #1b3139; margin-top: 40px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 3px solid #FF3621; display: inline-block;"&gt;Try it yourself&lt;/H2&gt;
&lt;P&gt;If you missed it, &lt;A style="color: #ff3621;" href="https://community.databricks.com/t5/community-articles/databricks-multi-table-transactions-part-1/td-p/151163" target="_blank"&gt;Part 1&lt;/A&gt; covers the motivation behind this feature and why it matters beyond data engineering.&lt;/P&gt;
&lt;P&gt;The official docs are here: &lt;A style="color: #ff3621;" href="https://docs.databricks.com/aws/en/transactions/" target="_blank"&gt;Multi-statement transactions on Databricks&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;Try adapting this to your own domain. The claim wrap-up is just one example. Anywhere you're writing to multiple tables and need them to stay in sync, this is worth a look.&lt;/P&gt;
&lt;HR /&gt;
&lt;P style="font-style: italic; color: #5a7a86;"&gt;Got a question or spotted something I missed? Let me know in the comments.&lt;/P&gt;
&lt;/DIV&gt;</description>
      <pubDate>Sat, 28 Mar 2026 21:54:20 GMT</pubDate>
      <guid>https://community.databricks.com/t5/community-articles/databricks-multi-table-transactions-part-2/m-p/152402#M1118</guid>
      <dc:creator>Ashwin_DSA</dc:creator>
      <dc:date>2026-03-28T21:54:20Z</dc:date>
    </item>
  </channel>
</rss>

