<?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>Modernization Best Practices and Reusable Assets Blog articles</title>
    <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/bg-p/ModernizationBestPracticesBlog</link>
    <description>Modernization Best Practices and Reusable Assets Blog articles</description>
    <pubDate>Thu, 18 Jun 2026 00:41:46 GMT</pubDate>
    <dc:creator>ModernizationBestPracticesBlog</dc:creator>
    <dc:date>2026-06-18T00:41:46Z</dc:date>
    <item>
      <title>Memory-Optimized Table Variables: Performance Under the Microscope</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/memory-optimized-table-variables-performance-under-the/ba-p/4516039</link>
      <description>&lt;H2&gt;Introduction&lt;/H2&gt;
&lt;P&gt;Memory-optimized table variables (MOTVs) have been available since SQL Server 2014, but many workloads still use regular on-disk table variables. The common assumption is that MOTVs are "a bit faster" - but how much faster, and under what conditions? When does the difference justify the change?&lt;/P&gt;
&lt;P&gt;To answer this, we ran a set of benchmarks comparing MOTVs against on-disk table variables across the dimensions that matter in production: caching scenarios, concurrency levels, row counts, and platforms. The results were surprising in several ways - sometimes MOTVs were barely faster, and in one scenario on-disk variables were actually quicker. But under conditions commonly seen in production, the difference was dramatic.&lt;/P&gt;
&lt;P&gt;Throughout this post, we use the shorthand &lt;STRONG&gt;MOTV&lt;/STRONG&gt; for memory-optimized table variables and &lt;STRONG&gt;DiskTV&lt;/STRONG&gt; for regular on-disk table variables. Both are typed table variables created with &lt;CODE&gt;CREATE TYPE ... AS TABLE&lt;/CODE&gt;; the only difference is that MOTVs include &lt;CODE&gt;WITH (MEMORY_OPTIMIZED = ON)&lt;/CODE&gt; in the type definition. The query code that uses them - INSERT, SELECT, JOINs - stays the same. One practical difference is that MOTVs must use an explicitly created table type (&lt;CODE&gt;DECLARE &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3156534" data-lia-user-login="tv" class="lia-mention lia-mention-user"&gt;tv&lt;/a&gt; MyTableType&lt;/CODE&gt;), whereas DiskTVs can also be declared inline (&lt;CODE&gt;DECLARE &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3156534" data-lia-user-login="tv" class="lia-mention lia-mention-user"&gt;tv&lt;/a&gt; TABLE (col1 int, ...)&lt;/CODE&gt;). If your existing code uses inline declarations, switching to MOTVs requires defining a named type first.&lt;/P&gt;
&lt;P&gt;MOTVs are available in SQL Server (2014 and later), Azure SQL Managed Instance, and Azure SQL Database. For brevity, we use &lt;STRONG&gt;Microsoft SQL&lt;/STRONG&gt; throughout this post to refer to all three platforms collectively.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;STRONG&gt;Note:&lt;/STRONG&gt; Results will vary depending on hardware, Microsoft SQL version, database configuration, and workload patterns. We recommend validating performance in your own dev/test environment. All tests were run on SQL Server 2025 (8 logical processors) and Azure SQL Database Hyperscale (4 vCores).&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;HR /&gt;
&lt;H2&gt;Background: Temporary Data Options in Microsoft SQL&lt;/H2&gt;
&lt;P&gt;Microsoft SQL offers several ways to handle temporary data, each with different trade-offs:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Temporary tables&lt;/STRONG&gt; are on-disk tables in tempdb. They can suffer from metadata latch contention and compilation overhead, though &lt;A href="https://www.sql.kiwi/2012/08/temporary-object-caching-explained/" target="_blank" rel="noopener"&gt;object caching&lt;/A&gt; and &lt;A href="https://learn.microsoft.com/en-us/sql/relational-databases/databases/tempdb-database?view=sql-server-ver17#improvements-in-tempdb-for-sql-server" target="_blank" rel="noopener"&gt;ongoing tempdb improvements&lt;/A&gt; have helped significantly.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Table variables&lt;/STRONG&gt; were added in SQL Server 2000 to reduce compilation volumes compared to temp tables. SQL Server 2019 added &lt;A href="https://learn.microsoft.com/en-us/sql/relational-databases/performance/intelligent-query-processing-details?view=sql-server-ver15#table-variable-deferred-compilation" target="_blank" rel="noopener"&gt;deferred compilation&lt;/A&gt; for better cardinality estimates.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Memory-optimized table variables&lt;/STRONG&gt; were introduced with the In-Memory OLTP engine (code-named Hekaton) in SQL Server 2014. They use in-memory structures that bypass tempdb entirely. See &lt;A href="https://learn.microsoft.com/en-us/sql/relational-databases/in-memory-oltp/faster-temp-table-and-table-variable-by-using-memory-optimization?view=sql-server-ver17" target="_blank" rel="noopener"&gt;Memory Optimization for Faster Temp Table and Table Variables&lt;/A&gt;.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Schema-only in-memory OLTP tables&lt;/STRONG&gt; are an alternative for scenarios like &lt;A href="https://learn.microsoft.com/en-us/archive/blogs/sqlserverstorageengine/a-technical-case-study-high-speed-iot-data-ingestion-using-in-memory-oltp-in-azure" target="_blank" rel="noopener"&gt;high-speed data ingestion&lt;/A&gt;, though long-running transactions can block garbage collection.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;&lt;A href="https://learn.microsoft.com/en-us/sql/relational-databases/databases/tempdb-database?view=sql-server-ver17#memory-optimized-tempdb-metadata" target="_blank" rel="noopener"&gt;Memory-optimized tempdb metadata&lt;/A&gt;&lt;/STRONG&gt; addresses tempdb system table contention, but &lt;A href="https://learn.microsoft.com/en-us/troubleshoot/sql/database-engine/performance/memory-optimized-tempdb-out-of-memory" target="_blank" rel="noopener"&gt;has its own risks&lt;/A&gt; with long-running transactions.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Do without&lt;/STRONG&gt; - many temporary objects exist because a query was originally broken into multiple statements that pass intermediate results through temp tables or table variables. This was often a performance workaround for older optimizer limitations. With &lt;A href="https://learn.microsoft.com/en-us/sql/relational-databases/performance/intelligent-query-processing?view=sql-server-ver17" target="_blank" rel="noopener"&gt;intelligent query processing&lt;/A&gt; expanding steadily - adaptive joins, batch mode on rowstore, optimized plan forcing, and others - it is worth revisiting whether the multi-statement pattern is still necessary. A single well-written query that lets the optimizer work end-to-end can eliminate the temp object entirely, removing both the tempdb overhead and the maintenance burden of the staging logic.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;This post focuses specifically on the comparison between MOTVs and DiskTVs.&lt;/P&gt;
&lt;HR /&gt;
&lt;H2&gt;Performance: The Full Picture&lt;/H2&gt;
&lt;P&gt;The headline from existing articles - "MOTVs are faster" - is correct but incomplete. The actual advantage depends heavily on three factors: whether tempdb object caching is active, how many concurrent sessions are running, and whether the workload is executing in a stored procedure or as ad-hoc SQL.&lt;/P&gt;
&lt;H3&gt;The Three Caching Scenarios&lt;/H3&gt;
&lt;P&gt;The most important variable is whether the Microsoft SQL engine can &lt;STRONG&gt;cache the tempdb object&lt;/STRONG&gt; for DiskTVs between executions. There are three distinct scenarios:&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="border-width: 1px;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Scenario&lt;/th&gt;&lt;th&gt;What happens&lt;/th&gt;&lt;th&gt;DiskTV µs&lt;/th&gt;&lt;th&gt;MOTV µs&lt;/th&gt;&lt;th&gt;MOTV speedup&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;STRONG&gt;Stored Procedure&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;Engine caches the tempdb object between calls - only a truncation is needed, not a full create/destroy&lt;/td&gt;&lt;td&gt;1,127&lt;/td&gt;&lt;td&gt;832&lt;/td&gt;&lt;td&gt;1.4x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;STRONG&gt;WHILE Loop&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;The table variable persists across loop iterations within a single batch - neither type is recreated&lt;/td&gt;&lt;td&gt;1,768&lt;/td&gt;&lt;td&gt;907&lt;/td&gt;&lt;td&gt;1.9x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;STRONG&gt;Fresh Batch&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;Each execution compiles a fresh batch and creates the tempdb object from scratch - the pattern for ad-hoc queries, ORMs, and dynamic SQL&lt;/td&gt;&lt;td&gt;19,912&lt;/td&gt;&lt;td&gt;2,248&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;8.9x&lt;/STRONG&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;&lt;EM&gt;(50 rows, single session, local SQL Server 2025)&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;In each scenario, the workload is the same: declare a typed table variable, INSERT 50 rows, and run an aggregate SELECT query. What differs is how Microsoft SQL handles the lifecycle of the underlying tempdb object. In the &lt;STRONG&gt;Stored Procedure&lt;/STRONG&gt; scenario, the procedure is called via &lt;CODE&gt;EXEC&lt;/CODE&gt; and the engine caches the tempdb object between calls - on re-entry it only truncates the existing allocation rather than creating and destroying it. In the &lt;STRONG&gt;WHILE Loop&lt;/STRONG&gt; scenario, a single batch declares the variable before the loop; each iteration inserts, queries, and deletes rows, but the variable itself persists across iterations so neither type incurs creation overhead. In the &lt;STRONG&gt;Fresh Batch&lt;/STRONG&gt; scenario, each iteration passes the SQL text to &lt;CODE&gt;EXEC()&lt;/CODE&gt;, forcing a fresh compile and a new tempdb object every time - this is the pattern produced by ORM frameworks, application-generated SQL, and dynamic SQL.&lt;/P&gt;
&lt;P&gt;In stored procedures, tempdb object caching masks most of the DiskTV overhead, and MOTVs are only 1.4x faster. But a significant share of production table variable usage does not go through stored procedures with cached plans. ORM frameworks, application-generated SQL, and dynamic SQL all hit the &lt;STRONG&gt;fresh batch&lt;/STRONG&gt; path where DiskTVs cost 20ms per iteration and MOTVs cost 2ms - nearly a 9x difference.&lt;/P&gt;
&lt;H3&gt;Concurrency Makes It Worse for DiskTVs&lt;/H3&gt;
&lt;P&gt;Under concurrent sessions, DiskTV performance degrades sharply because multiple sessions compete for tempdb allocation page latches. MOTVs allocate from per-core partitioned memory pools with no shared latches, so their performance stays relatively stable.&lt;/P&gt;
&lt;P&gt;The three-phase breakdown below separates each iteration into DECLARE (object creation), INSERT (populating 50 rows), and SELECT (query). All tests use stored procedure execution (cached):&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="border-width: 1px;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Sessions&lt;/th&gt;&lt;th&gt;Phase&lt;/th&gt;&lt;th&gt;DiskTV µs&lt;/th&gt;&lt;th&gt;MOTV µs&lt;/th&gt;&lt;th&gt;MOTV speedup&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Create&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Insert&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;406&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;377&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;1.1x&lt;/STRONG&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Query&lt;/td&gt;&lt;td&gt;54&lt;/td&gt;&lt;td&gt;65&lt;/td&gt;&lt;td&gt;0.8x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Total&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;461&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;447&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;1.0x&lt;/STRONG&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;Create&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Insert&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;1,757&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;1,335&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;1.3x&lt;/STRONG&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;Query&lt;/td&gt;&lt;td&gt;186&lt;/td&gt;&lt;td&gt;185&lt;/td&gt;&lt;td&gt;1.0x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Total&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;1,949&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;1,525&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;1.3x&lt;/STRONG&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;25&lt;/td&gt;&lt;td&gt;Create&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;25&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Insert&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;1,903&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;993&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;1.9x&lt;/STRONG&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;25&lt;/td&gt;&lt;td&gt;Query&lt;/td&gt;&lt;td&gt;186&lt;/td&gt;&lt;td&gt;135&lt;/td&gt;&lt;td&gt;1.4x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;25&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Total&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;2,094&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;1,133&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;1.8x&lt;/STRONG&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;Three things stand out. First, the DECLARE phase is trivially fast for both types at all concurrency levels - object creation is not the bottleneck. Second, the INSERT phase accounts for 85-91% of total time across every combination. Third, the MOTV INSERT advantage grows steadily with concurrency: from 1.1x at 1 session to 1.9x at 25 sessions, as tempdb latch contention compounds.&lt;/P&gt;
&lt;P&gt;With smaller row counts (5 rows instead of 50), the per-iteration timings drop proportionally but the pattern stays the same - the INSERT phase remains the dominant cost and the MOTV advantage at single session is similar (~1.1x). The difference becomes more pronounced under concurrency, as covered below.&lt;/P&gt;
&lt;H3&gt;The Worst Case: Uncached + Concurrent&lt;/H3&gt;
&lt;P&gt;Combining uncached (fresh batch) execution with concurrent sessions produces the most realistic worst case for typical production workloads:&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="border-width: 1px;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Sessions&lt;/th&gt;&lt;th&gt;Cached DiskTV&lt;/th&gt;&lt;th&gt;Cached MOTV&lt;/th&gt;&lt;th&gt;MOTV speedup&lt;/th&gt;&lt;th&gt;Uncached DiskTV&lt;/th&gt;&lt;th&gt;Uncached MOTV&lt;/th&gt;&lt;th&gt;MOTV speedup&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1,407&lt;/td&gt;&lt;td&gt;1,177&lt;/td&gt;&lt;td&gt;1.2x&lt;/td&gt;&lt;td&gt;11,763&lt;/td&gt;&lt;td&gt;1,495&lt;/td&gt;&lt;td&gt;7.9x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;1,684&lt;/td&gt;&lt;td&gt;1,281&lt;/td&gt;&lt;td&gt;1.3x&lt;/td&gt;&lt;td&gt;9,020&lt;/td&gt;&lt;td&gt;1,542&lt;/td&gt;&lt;td&gt;5.9x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;STRONG&gt;25&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;2,125&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;1,425&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;1.5x&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;68,772&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;1,196&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;57.5x&lt;/STRONG&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 14.29%" /&gt;&lt;col style="width: 14.29%" /&gt;&lt;col style="width: 14.29%" /&gt;&lt;col style="width: 14.29%" /&gt;&lt;col style="width: 14.29%" /&gt;&lt;col style="width: 14.29%" /&gt;&lt;col style="width: 14.29%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;&lt;EM&gt;(50 rows, local, µs per iteration)&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;At 25 concurrent sessions with uncached execution, DiskTV takes &lt;STRONG&gt;68.8ms per iteration&lt;/STRONG&gt; while MOTV takes &lt;STRONG&gt;1.2ms&lt;/STRONG&gt; - a 57x difference. MOTV barely notices concurrency in uncached mode because it never touches tempdb regardless of caching state.&lt;/P&gt;
&lt;H3&gt;Tail Latency&lt;/H3&gt;
&lt;P&gt;Average numbers mask the production impact. The maximum latency per iteration shows the real risk:&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="border-width: 1px;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Concurrency&lt;/th&gt;&lt;th&gt;DiskTV Max&lt;/th&gt;&lt;th&gt;MOTV Max&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;10 sessions&lt;/td&gt;&lt;td&gt;811 ms&lt;/td&gt;&lt;td&gt;81 ms&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;50 sessions&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;10.5 seconds&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;4.7 seconds&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 33.33%" /&gt;&lt;col style="width: 33.33%" /&gt;&lt;col style="width: 33.33%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;A single DiskTV iteration taking 10 seconds in a 50-session workload is a catastrophic tail event for any latency-sensitive application.&lt;/P&gt;
&lt;H3&gt;Small Row Counts (5 Rows)&lt;/H3&gt;
&lt;P&gt;With fewer rows, the INSERT phase shrinks - making the tempdb object creation overhead dominate even more in the uncached case:&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="border-width: 1px;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Test&lt;/th&gt;&lt;th&gt;5-row DiskTV&lt;/th&gt;&lt;th&gt;5-row MOTV&lt;/th&gt;&lt;th&gt;MOTV speedup&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Cached, 1 session&lt;/td&gt;&lt;td&gt;395 µs&lt;/td&gt;&lt;td&gt;316 µs&lt;/td&gt;&lt;td&gt;1.2x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Uncached, 1 session&lt;/td&gt;&lt;td&gt;6,627 µs&lt;/td&gt;&lt;td&gt;293 µs&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;22.6x&lt;/STRONG&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Uncached, 10 sessions&lt;/td&gt;&lt;td&gt;36,240 µs&lt;/td&gt;&lt;td&gt;404 µs&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;89.7x&lt;/STRONG&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 25.00%" /&gt;&lt;col style="width: 25.00%" /&gt;&lt;col style="width: 25.00%" /&gt;&lt;col style="width: 25.00%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;Uncached 5-row DiskTV at 10 sessions (36ms) is actually worse than 50-row (9ms). Each session cycles through create/destroy faster with less INSERT work to do, which means the sessions hammer tempdb metadata contention at a higher rate.&lt;/P&gt;
&lt;H3&gt;Hyperscale Results&lt;/H3&gt;
&lt;P&gt;On a 4-vCore Azure SQL Hyperscale instance, single-session timings using the WHILE loop scenario confirmed that MOTV creation rates of 86K bare-declares per second are achievable. The MOTV populate advantage is also present on Hyperscale, though the uncached (fresh batch) scenario showed narrower differences because compilation overhead - which is the same for both types - dominates on Hyperscale where tempdb is local to the compute node and therefore faster than on a local developer instance.&lt;/P&gt;
&lt;HR /&gt;
&lt;H2&gt;Maximum Creation Rate&lt;/H2&gt;
&lt;P&gt;In stored procedure context with tempdb object caching active, DiskTV bare-declare rates actually exceeded MOTV rates (34K/sec vs 22K/sec) because the cached truncate is cheaper than MOTV's pool allocation and index creation. With single-row INSERTs, MOTVs took the lead at 8.4K/sec vs 5.0K/sec. In the uncached case, MOTVs were 27x faster for 1-row creates (7.9K/sec vs 294/sec), since DiskTVs pay the full tempdb creation penalty on every call.&lt;/P&gt;
&lt;P&gt;The key finding is that raw creation rate is not the main differentiator - it is the data path (INSERT/query) under concurrency and without caching where MOTVs deliver transformative improvement.&lt;/P&gt;
&lt;HR /&gt;
&lt;H2&gt;Memory Usage&lt;/H2&gt;
&lt;P&gt;One common concern about MOTVs is their memory consumption. However, DiskTVs also use memory - they live in the buffer pool. The real question is the difference.&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="border-width: 1px;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Rows&lt;/th&gt;&lt;th&gt;DiskTV (buffer pool)&lt;/th&gt;&lt;th&gt;MOTV NCI&lt;/th&gt;&lt;th&gt;MOTV Hash (15K buckets)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;40 KB&lt;/td&gt;&lt;td&gt;64 KB (1.6x)&lt;/td&gt;&lt;td&gt;128 KB (3.2x)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1,000&lt;/td&gt;&lt;td&gt;680 KB&lt;/td&gt;&lt;td&gt;1,344 KB (2.0x)&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10,000&lt;/td&gt;&lt;td&gt;5,528 KB&lt;/td&gt;&lt;td&gt;7,168 KB (1.3x)&lt;/td&gt;&lt;td&gt;2,368 KB (0.4x)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 25.00%" /&gt;&lt;col style="width: 25.00%" /&gt;&lt;col style="width: 25.00%" /&gt;&lt;col style="width: 25.00%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;At small row counts (10 rows), the difference is just 24 KB - entirely irrelevant. At 10,000 rows with a hash index, MOTVs actually use &lt;STRONG&gt;less&lt;/STRONG&gt; memory than DiskTVs (2,368 KB vs 5,528 KB). For the target use case of high-frequency, small tables, 1,000 concurrent sessions with 10-row MOTVs would use approximately 64 MB of memory - a modest amount on any modern server.&lt;/P&gt;
&lt;P&gt;Hash indexes are significantly more memory-efficient than nonclustered indexes for larger row counts. The bucket array itself is small (8 bytes per bucket), so changing the bucket count has limited impact on total memory consumption. However, bucket count has a &lt;A href="https://techcommunity.microsoft.com/blog/sqlserversupport/importance-of-choosing-correct-bucket-count-of-hash-indexes-on-a-memory-optimize/318905/" target="_blank" rel="noopener"&gt;critical impact on performance&lt;/A&gt;: an undersized count causes long hash chains that severely degrade INSERT and lookup speed. Follow the &lt;A href="https://learn.microsoft.com/en-us/sql/relational-databases/sql-server-index-design-guide?view=sql-server-ver17#hash_index" target="_blank" rel="noopener"&gt;sizing guidance&lt;/A&gt; - set the bucket count to 1-2x the expected number of distinct key values.&lt;/P&gt;
&lt;HR /&gt;
&lt;H2&gt;When to Use MOTVs&lt;/H2&gt;
&lt;P&gt;Following &lt;A href="https://sqlperformance.com/2017/05/sql-performance/sql-server-temporary-object-caching" target="_blank" rel="noopener"&gt;Paul White's guidance&lt;/A&gt; that table variables work best "when the data is small and when plan selection does not depend on the values present", the scenarios where MOTVs help most are:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Very high frequency execution.&lt;/STRONG&gt; Stored procedures that execute thousands of times per second, each using a small table variable, benefit most from MOTV's faster population path.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Concurrent sessions sharing tempdb.&lt;/STRONG&gt; MOTVs are decoupled from tempdb, so they do not contend on shared PFS/GAM allocation latches that slow DiskTVs under concurrency.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Resilience to tempdb stalls.&lt;/STRONG&gt; A tempdb autogrow event pauses all tempdb operations. On an 80-vCore Azure SQL Database running 2,000 batch requests per second with table variables, that can exhaust the worker thread pool in seconds. MOTVs are unaffected.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Data ingestion buffers.&lt;/STRONG&gt; Using MOTVs as table-valued parameter (TVP) buffers avoids the tempdb log throughput that on-disk TVPs generate, as described in &lt;A href="https://learn.microsoft.com/en-us/archive/blogs/sqlserverstorageengine/a-technical-case-study-high-speed-iot-data-ingestion-using-in-memory-oltp-in-azure" target="_blank" rel="noopener"&gt;this IoT ingestion case study&lt;/A&gt;.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Scenarios where MOTVs are &lt;STRONG&gt;not&lt;/STRONG&gt; a good fit:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Plan quality depends on data shape.&lt;/STRONG&gt; If the optimizer needs accurate cardinality estimates - large row counts, complex joins requiring column histograms - table variables (regular or memory-optimized) are the wrong tool. Use temp tables instead.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Low-frequency, large data loads.&lt;/STRONG&gt; MOTVs do not support parallel INSERT, so bulk loading large volumes into a MOTV will be single-threaded. At high execution frequency, going parallel can cause more problems than it solves, but for infrequent large loads, DiskTVs or temp tables are a better choice.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;DML on the table variable after population.&lt;/STRONG&gt; MOTVs use the In-Memory OLTP versioning engine, which retains every version of every row. Garbage collection cannot reclaim these versions while the variable is in scope, so if code loads data into a MOTV and then updates rows - for example, calculating a scoring field - each UPDATE creates a new row version that persists until the variable is destroyed. Memory consumption can double or more depending on the number of update passes. This is a significant anti-pattern for MOTVs.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Long-running transactions.&lt;/STRONG&gt; For the same reason, a MOTV used inside a long-running transaction accumulates row versions that cannot be reclaimed by garbage collection until the variable goes out of scope. A common example is a single-row "next ID" table that is updated on every call - while each individual update is fast, every version is retained, and memory will grow continuously.&lt;/LI&gt;
&lt;/UL&gt;
&lt;HR /&gt;
&lt;H2&gt;When Does It Not Matter?&lt;/H2&gt;
&lt;P&gt;In cached mode (stored procedure) with 10 or fewer concurrent sessions, the MOTV advantage is 1.2-1.3x - meaningful but not dramatic. If your workload uses stored procedures exclusively and runs at low concurrency, the benefit of switching to MOTVs is modest.&lt;/P&gt;
&lt;P&gt;The case for MOTVs is strongest when any of these conditions apply:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Execution is &lt;STRONG&gt;uncached&lt;/STRONG&gt; - ad-hoc queries, ORM-generated SQL, or dynamic SQL&lt;/LI&gt;
&lt;LI&gt;Concurrency is &lt;STRONG&gt;moderate to high&lt;/STRONG&gt; - 10 or more concurrent sessions using table variables&lt;/LI&gt;
&lt;LI&gt;Table variables are used at &lt;STRONG&gt;high frequency&lt;/STRONG&gt; - hundreds or thousands of times per second&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Tail latency matters&lt;/STRONG&gt; - even occasional 10x spikes in DiskTV latency are unacceptable&lt;/LI&gt;
&lt;/UL&gt;
&lt;HR /&gt;
&lt;H2&gt;Conclusion&lt;/H2&gt;
&lt;P&gt;MOTVs deliver a modest but real improvement in stored procedure workloads at low concurrency. Under conditions commonly seen in production - uncached execution, moderate concurrency, and high frequency - the improvement becomes dramatic, reaching up to 57x faster in our tests. The change requires no application code modification beyond the table type definition, and the memory overhead is manageable for the small-table workloads where MOTVs are most beneficial.&lt;/P&gt;
&lt;HR /&gt;
&lt;H2&gt;Test Scripts&lt;/H2&gt;
&lt;P&gt;The scripts used for all benchmarks in this post are included below. Each can be run independently on any Microsoft SQL instance (SQL Server 2019+, Azure SQL MI, or Azure SQL Database) with a database configured with a &lt;CODE&gt;MEMORY_OPTIMIZED_DATA&lt;/CODE&gt; filegroup.&lt;/P&gt;
&lt;H3&gt;Setup: Table Types&lt;/H3&gt;
&lt;LI-CODE lang="sql"&gt;-- Regular (DiskTV) table type
CREATE TYPE dbo.BlogTest_Regular AS TABLE
(
    ID          int            NOT NULL  PRIMARY KEY,
    ProductName nvarchar(50)   NOT NULL,
    Quantity    int            NOT NULL,
    UnitPrice   decimal(10,2)  NOT NULL,
    OrderDate   datetime2(0)   NOT NULL
);
GO

-- Memory-optimized (MOTV) table type
CREATE TYPE dbo.BlogTest_MemOpt AS TABLE
(
    ID          int            NOT NULL  PRIMARY KEY NONCLUSTERED,
    ProductName nvarchar(50)   NOT NULL,
    Quantity    int            NOT NULL,
    UnitPrice   decimal(10,2)  NOT NULL,
    OrderDate   datetime2(0)   NOT NULL
)
WITH (MEMORY_OPTIMIZED = ON);
GO&lt;/LI-CODE&gt;
&lt;H3&gt;Test: Three-Phase Breakdown (Create/Populate/Query)&lt;/H3&gt;
&lt;LI-CODE lang="sql"&gt;-- 3-phase breakdown: single session, 1000 iterations
-- Measures DECLARE, INSERT, and SELECT phases independently
DECLARE @Iterations int = 1000, @Warmup int = 100;
DECLARE &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3147694" data-lia-user-login="RAW" class="lia-mention lia-mention-user"&gt;RAW&lt;/a&gt; TABLE (TestName nvarchar(60), i int, CreateUS bigint,
    PopulateUS bigint, QueryUS bigint, TotalUS bigint);
DECLARE @i int, &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="1546390" data-lia-user-login="T0" class="lia-mention lia-mention-user"&gt;T0&lt;/a&gt; datetime2(7), @t1 datetime2(7),
    &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="583469" data-lia-user-login="t2" class="lia-mention lia-mention-user"&gt;t2&lt;/a&gt; datetime2(7), &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="2955168" data-lia-user-login="T3" class="lia-mention lia-mention-user"&gt;T3&lt;/a&gt; datetime2(7), @sink decimal(18,2);

-- Warmup
SET @i=0;
WHILE @i &amp;lt; @Warmup BEGIN
    DECLARE @w dbo.BlogTest_Regular;
    INSERT INTO @w (ID,ProductName,Quantity,UnitPrice,OrderDate)
    SELECT TOP(50) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)),
        CONCAT('P-', ROW_NUMBER() OVER(ORDER BY(SELECT NULL))),
        ABS(CHECKSUM(NEWID())) % 100 + 1,
        CAST(ABS(CHECKSUM(NEWID())) % 10000 / 100.0 AS decimal(10,2)),
        DATEADD(DAY, -(ABS(CHECKSUM(NEWID())) % 365), SYSDATETIME())
    FROM sys.all_objects;
    SELECT @sink = SUM(Quantity * UnitPrice) FROM @w WHERE Quantity &amp;gt; 20;
    DELETE FROM @w;
    SET @i += 1;
END;

-- DiskTV measured run
SET @i = 0;
WHILE @i &amp;lt; @Iterations BEGIN
    SET &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="1546390" data-lia-user-login="T0" class="lia-mention lia-mention-user"&gt;T0&lt;/a&gt; = SYSDATETIME();
    DECLARE @r dbo.BlogTest_Regular;

    SET @t1 = SYSDATETIME();
    INSERT INTO @r (ID,ProductName,Quantity,UnitPrice,OrderDate)
    SELECT TOP(50) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)),
        CONCAT('P-', ROW_NUMBER() OVER(ORDER BY(SELECT NULL))),
        ABS(CHECKSUM(NEWID())) % 100 + 1,
        CAST(ABS(CHECKSUM(NEWID())) % 10000 / 100.0 AS decimal(10,2)),
        DATEADD(DAY, -(ABS(CHECKSUM(NEWID())) % 365), SYSDATETIME())
    FROM sys.all_objects;

    SET &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="583469" data-lia-user-login="t2" class="lia-mention lia-mention-user"&gt;t2&lt;/a&gt; = SYSDATETIME();
    SELECT @sink = SUM(Quantity * UnitPrice) FROM @r WHERE Quantity &amp;gt; 20;

    SET &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="2955168" data-lia-user-login="T3" class="lia-mention lia-mention-user"&gt;T3&lt;/a&gt; = SYSDATETIME();
    DELETE FROM @r;

    INSERT INTO &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3147694" data-lia-user-login="RAW" class="lia-mention lia-mention-user"&gt;RAW&lt;/a&gt; VALUES('DiskTV', @i,
        DATEDIFF_BIG(MICROSECOND, &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="1546390" data-lia-user-login="T0" class="lia-mention lia-mention-user"&gt;T0&lt;/a&gt;, @t1),
        DATEDIFF_BIG(MICROSECOND, @t1, &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="583469" data-lia-user-login="t2" class="lia-mention lia-mention-user"&gt;t2&lt;/a&gt;),
        DATEDIFF_BIG(MICROSECOND, &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="583469" data-lia-user-login="t2" class="lia-mention lia-mention-user"&gt;t2&lt;/a&gt;, &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="2955168" data-lia-user-login="T3" class="lia-mention lia-mention-user"&gt;T3&lt;/a&gt;),
        DATEDIFF_BIG(MICROSECOND, &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="1546390" data-lia-user-login="T0" class="lia-mention lia-mention-user"&gt;T0&lt;/a&gt;, &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="2955168" data-lia-user-login="T3" class="lia-mention lia-mention-user"&gt;T3&lt;/a&gt;));
    SET @i += 1;
END;

-- Repeat warmup + measured run for MOTV (replace BlogTest_Regular with BlogTest_MemOpt)
-- ... (same pattern, change type name)

-- Results
SELECT TestName,
    AVG(CreateUS) AS Avg_Create_US,
    AVG(PopulateUS) AS Avg_Populate_US,
    AVG(QueryUS) AS Avg_Query_US,
    AVG(TotalUS) AS Avg_Total_US
FROM &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3147694" data-lia-user-login="RAW" class="lia-mention lia-mention-user"&gt;RAW&lt;/a&gt;
GROUP BY TestName
ORDER BY TestName;&lt;/LI-CODE&gt;
&lt;H3&gt;Test: Caching Scenarios (Cached vs Uncached)&lt;/H3&gt;
&lt;LI-CODE lang="sql"&gt;-- Scenario A: Stored Procedure (cached)
CREATE PROCEDURE dbo.usp_Test_DiskTV AS BEGIN
    SET NOCOUNT ON;
    DECLARE &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3156534" data-lia-user-login="tv" class="lia-mention lia-mention-user"&gt;tv&lt;/a&gt; dbo.BlogTest_Regular;
    INSERT INTO &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3156534" data-lia-user-login="tv" class="lia-mention lia-mention-user"&gt;tv&lt;/a&gt; (ID,ProductName,Quantity,UnitPrice,OrderDate)
    SELECT TOP(50) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)),
        CONCAT('P-', ROW_NUMBER() OVER(ORDER BY(SELECT NULL))),
        ABS(CHECKSUM(NEWID())) % 100 + 1,
        CAST(ABS(CHECKSUM(NEWID())) % 10000 / 100.0 AS decimal(10,2)),
        DATEADD(DAY, -(ABS(CHECKSUM(NEWID())) % 365), SYSDATETIME())
    FROM sys.all_objects;
    DECLARE @sink decimal(18,2);
    SELECT @sink = SUM(Quantity * UnitPrice) FROM &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3156534" data-lia-user-login="tv" class="lia-mention lia-mention-user"&gt;tv&lt;/a&gt; WHERE Quantity &amp;gt; 20;
END;
GO

-- Scenario A (MOTV variant): same procedure using the memory-optimized type
CREATE PROCEDURE dbo.usp_Test_MOTV AS BEGIN
    SET NOCOUNT ON;
    DECLARE &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3156534" data-lia-user-login="tv" class="lia-mention lia-mention-user"&gt;tv&lt;/a&gt; dbo.BlogTest_MemOpt;
    INSERT INTO &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3156534" data-lia-user-login="tv" class="lia-mention lia-mention-user"&gt;tv&lt;/a&gt; (ID,ProductName,Quantity,UnitPrice,OrderDate)
    SELECT TOP(50) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)),
        CONCAT('P-', ROW_NUMBER() OVER(ORDER BY(SELECT NULL))),
        ABS(CHECKSUM(NEWID())) % 100 + 1,
        CAST(ABS(CHECKSUM(NEWID())) % 10000 / 100.0 AS decimal(10,2)),
        DATEADD(DAY, -(ABS(CHECKSUM(NEWID())) % 365), SYSDATETIME())
    FROM sys.all_objects;
    DECLARE @sink decimal(18,2);
    SELECT @sink = SUM(Quantity * UnitPrice) FROM &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3156534" data-lia-user-login="tv" class="lia-mention lia-mention-user"&gt;tv&lt;/a&gt; WHERE Quantity &amp;gt; 20;
END;
GO

-- Scenario C: Fresh batch (uncached) - run each iteration via EXEC()
DECLARE @sql nvarchar(max) = N'
    DECLARE &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3156534" data-lia-user-login="tv" class="lia-mention lia-mention-user"&gt;tv&lt;/a&gt; dbo.BlogTest_Regular;
    INSERT INTO &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3156534" data-lia-user-login="tv" class="lia-mention lia-mention-user"&gt;tv&lt;/a&gt; (ID,ProductName,Quantity,UnitPrice,OrderDate)
    SELECT TOP(50) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)),
        CONCAT(''P-'', ROW_NUMBER() OVER(ORDER BY(SELECT NULL))),
        ABS(CHECKSUM(NEWID())) % 100 + 1,
        CAST(ABS(CHECKSUM(NEWID())) % 10000 / 100.0 AS decimal(10,2)),
        DATEADD(DAY, -(ABS(CHECKSUM(NEWID())) % 365), SYSDATETIME())
    FROM sys.all_objects;
    DECLARE @sink decimal(18,2);
    SELECT @sink = SUM(Quantity*UnitPrice) FROM &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3156534" data-lia-user-login="tv" class="lia-mention lia-mention-user"&gt;tv&lt;/a&gt; WHERE Quantity &amp;gt; 20;';

DECLARE @i int = 0, &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="1546390" data-lia-user-login="T0" class="lia-mention lia-mention-user"&gt;T0&lt;/a&gt; datetime2(7), @t1 datetime2(7);
SET &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="1546390" data-lia-user-login="T0" class="lia-mention lia-mention-user"&gt;T0&lt;/a&gt; = SYSDATETIME();
WHILE @i &amp;lt; 1000 BEGIN EXEC(@sql); SET @i += 1; END;
SET @t1 = SYSDATETIME();
SELECT DATEDIFF_BIG(MICROSECOND, &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="1546390" data-lia-user-login="T0" class="lia-mention lia-mention-user"&gt;T0&lt;/a&gt;, @t1) / 1000 AS Avg_US_Per_Iteration;&lt;/LI-CODE&gt;
&lt;H3&gt;Test: Concurrent Sessions (PowerShell driver)&lt;/H3&gt;
&lt;LI-CODE lang="powershell"&gt;# Run N concurrent sessions, each executing a stored procedure
$server = "YOUR-SERVER-INSTANCE"  # e.g. "localhost\sql25" or "myserver.database.windows.net"
$db = "YOUR-DATABASE"             # database with MEMORY_OPTIMIZED_DATA filegroup

foreach ($level in @(1, 10, 25)) {
    foreach ($type in @('DiskTV','MOTV')) {
        $proc = "dbo.usp_Test_$type"
        Write-Host "--- $type at $level sessions ---"
        $jobs = @()
        for ($s = 0; $s -lt $level; $s++) {
            $sql = "EXEC $proc @Iterations=500, @Warmup=50;"
            $jobs += Start-Job -ScriptBlock {
                param($svr, $database, $query)
                sqlcmd -S $svr -d $database -Q $query -t 600
            } -ArgumentList $server, $db, $sql
        }
        $jobs | Wait-Job | Out-Null
        $jobs | Remove-Job -Force
        Write-Host "  Done."
    }
}&lt;/LI-CODE&gt;
&lt;H3&gt;Test: Memory Usage Comparison&lt;/H3&gt;
&lt;LI-CODE lang="sql"&gt;-- Helper function: current MEMORYCLERK_XTP total (KB)
CREATE OR ALTER FUNCTION dbo.fn_XTP_MemoryKB()
RETURNS bigint
AS
BEGIN
    DECLARE &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="2763994" data-lia-user-login="KB" class="lia-mention lia-mention-user"&gt;KB&lt;/a&gt; bigint;
    SELECT &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="2763994" data-lia-user-login="KB" class="lia-mention lia-mention-user"&gt;KB&lt;/a&gt; = SUM(pages_kb + virtual_memory_committed_kb
                   + awe_allocated_kb + shared_memory_committed_kb)
    FROM sys.dm_os_memory_clerks
    WHERE type = 'MEMORYCLERK_XTP';
    RETURN ISNULL(@kb, 0);
END;
GO

-- Measure MOTV memory: capture XTP clerk before and after populating
DECLARE &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="1260039" data-lia-user-login="Baseline" class="lia-mention lia-mention-user"&gt;Baseline&lt;/a&gt; bigint = dbo.fn_XTP_MemoryKB();

DECLARE &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3156534" data-lia-user-login="tv" class="lia-mention lia-mention-user"&gt;tv&lt;/a&gt; dbo.BlogTest_MemOpt;
INSERT INTO &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3156534" data-lia-user-login="tv" class="lia-mention lia-mention-user"&gt;tv&lt;/a&gt; (ID, ProductName, Quantity, UnitPrice, OrderDate)
SELECT TOP(50) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)),
    CONCAT('P-', ROW_NUMBER() OVER(ORDER BY(SELECT NULL))),
    ABS(CHECKSUM(NEWID())) % 100 + 1,
    CAST(ABS(CHECKSUM(NEWID())) % 10000 / 100.0 AS decimal(10,2)),
    DATEADD(DAY, -(ABS(CHECKSUM(NEWID())) % 365), SYSDATETIME())
FROM sys.all_objects;

DECLARE @after bigint = dbo.fn_XTP_MemoryKB();
SELECT @after - &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="1260039" data-lia-user-login="Baseline" class="lia-mention lia-mention-user"&gt;Baseline&lt;/a&gt; AS MOTV_Memory_KB;
GO

-- Measure DiskTV memory: count buffer pool pages in tempdb
-- Run this in a stored proc so the TV is alive during measurement
CREATE OR ALTER PROCEDURE dbo.usp_MeasureDiskTV @RowCount int
AS BEGIN
    SET NOCOUNT ON;
    DECLARE &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3156534" data-lia-user-login="tv" class="lia-mention lia-mention-user"&gt;tv&lt;/a&gt; dbo.BlogTest_Regular;

    INSERT INTO &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3156534" data-lia-user-login="tv" class="lia-mention lia-mention-user"&gt;tv&lt;/a&gt; (ID, ProductName, Quantity, UnitPrice, OrderDate)
    SELECT TOP(@RowCount) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)),
        CONCAT('P-', ROW_NUMBER() OVER(ORDER BY(SELECT NULL))),
        ABS(CHECKSUM(NEWID())) % 100 + 1,
        CAST(ABS(CHECKSUM(NEWID())) % 10000 / 100.0 AS decimal(10,2)),
        DATEADD(DAY, -(ABS(CHECKSUM(NEWID())) % 365), SYSDATETIME())
    FROM sys.all_objects a CROSS JOIN sys.all_objects b;

    -- Count tempdb pages allocated to this session's internal objects
    SELECT (internal_objects_alloc_page_count
          - internal_objects_dealloc_page_count) * 8 AS DiskTV_Memory_KB
    FROM sys.dm_db_task_space_usage
    WHERE session_id = @@SPID;
END;
GO
EXEC dbo.usp_MeasureDiskTV @RowCount = 50;&lt;/LI-CODE&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;STRONG&gt;Note:&lt;/STRONG&gt; DiskTV memory measurement via &lt;CODE&gt;dm_db_task_space_usage&lt;/CODE&gt; can return 0 for small row counts when pages are cached. For DiskTV, the buffer pool query from &lt;CODE&gt;dm_os_buffer_descriptors&lt;/CODE&gt; filtered to tempdb is the most reliable method, but requires a second session to query while the first holds the TV alive. The MOTV measurement via &lt;CODE&gt;MEMORYCLERK_XTP&lt;/CODE&gt; differencing works reliably on a quiet instance.&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;HR /&gt;
&lt;H2&gt;Feedback and Suggestions&lt;/H2&gt;
&lt;P&gt;If you have feedback or suggestions, please contact the Databases SQL Customer Success Engineering (Ninja) Team (&lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;datasqlninja@microsoft.com&lt;/A&gt;).&lt;/P&gt;
&lt;P&gt;Note: For additional information about migrating various source databases to Azure, see the &lt;A href="https://datamigration.microsoft.com/" target="_blank" rel="noopener"&gt;Azure Database Migration Guide&lt;/A&gt;.&lt;/P&gt;</description>
      <pubDate>Wed, 17 Jun 2026 23:24:50 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/memory-optimized-table-variables-performance-under-the/ba-p/4516039</guid>
      <dc:creator>David_Lyth</dc:creator>
      <dc:date>2026-06-17T23:24:50Z</dc:date>
    </item>
    <item>
      <title>Microsoft Options to Migrate SQL Server Databases</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/microsoft-options-to-migrate-sql-server-databases/ba-p/4521637</link>
      <description>&lt;H1&gt;&lt;U&gt;Overview&lt;/U&gt;&lt;/H1&gt;
&lt;P&gt;This blog supplements the &lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/migration-guides/?view=azuresql" target="_blank" rel="noopener"&gt;Migrate to Azure SQL documentation&lt;/A&gt; and tries to summarize the Microsoft first party ways to copy or migrate SQL Server databases from on‑premises environments to one of the flavours of &lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/azure-sql-iaas-vs-paas-what-is-overview?view=azuresql" target="_blank" rel="noopener"&gt;SQL Server running in Azure&lt;/A&gt;. It is organized first by &lt;STRONG&gt;target platform&lt;/STRONG&gt; (Azure SQL Database/Hyperscale, Azure SQL Managed Instance and Azure SQL Virtual Machine) and then by &lt;STRONG&gt;tool&lt;/STRONG&gt; (for example Azure DMS, Managed Instance Link, Distributed Availability Groups, SqlPackage, and data copy utilities). For each tool, you’ll find supported targets, prerequisites, whether it can run &lt;STRONG&gt;online&lt;/STRONG&gt; (continuous change replication) or requires &lt;STRONG&gt;offline&lt;/STRONG&gt; downtime, and the common limitations that influence tool choice.&lt;/P&gt;
&lt;H1&gt;&lt;U&gt;Azure SQL Target Platforms&lt;/U&gt;&lt;/H1&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Azure SQL Database (including Hyperscale)&lt;/STRONG&gt; – Fully managed by Microsoft running as Platform as a Service (PaaS), the service provides database scoped access with no visibility between databases on a logical server. Some SQL Server features are not available (i.e. backup/restore) so migrations need to use logical transfer or export/import of data.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Azure SQL Managed Instance (MI)&lt;/STRONG&gt; – Fully managed by Microsoft running as Platform as a Service (PaaS), the service provides SQL Server instance scoped access and near-full SQL Server surface area. MI supports specialized migration/replication options such as &lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/managed-instance/managed-instance-link-feature-overview?view=azuresql" target="_blank" rel="noopener"&gt;&lt;STRONG&gt;Managed Instance Link&lt;/STRONG&gt;&lt;/A&gt; as well as backup/restore based (&lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/managed-instance/log-replay-service-overview?view=azuresql" target="_blank" rel="noopener"&gt;Log Replay Service (LRS)&lt;/A&gt;) approaches.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Azure SQL Virtual Machine (SQL Server on Azure VM)&lt;/STRONG&gt; – This option uses Azure Infrastructure as a Service (IaaS). You manage the OS (Windows or Linux) and SQL Server. Since you are installing the full box product of SQL Server, this target supports all native SQL Server features on that OS platform.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;SQL Server everywhere else – For completeness, this includes SQL Server running on-premises, another VM, or other cloud providers. Tools such as &lt;A href="https://learn.microsoft.com/en-us/sql/tools/sqlpackage/sqlpackage-download?view=sql-server-ver17" target="_blank" rel="noopener"&gt;SqlPackage&lt;/A&gt;, &lt;A href="https://learn.microsoft.com/en-us/samples/azure-samples/smartbulkcopy/smart-bulk-copy/" target="_blank" rel="noopener"&gt;SmartBulkCopy&lt;/A&gt;, and &lt;A href="https://www.microsoft.com/en-us/download/details.aspx?id=105024" target="_blank" rel="noopener"&gt;SqlDBCopy&lt;/A&gt; can copy data to any SQL Server targets when connectivity between the servers is available.&lt;/STRONG&gt;&lt;/P&gt;
&lt;H1&gt;&lt;U&gt;Key Decision Factors&lt;/U&gt;&lt;/H1&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Downtime tolerance:&lt;/STRONG&gt; Do you need an online migration (continuous replication) with a brief cutover phase or is an offline data migration approach acceptable?&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Scope:&lt;/STRONG&gt; One database, many databases, or full instance and feature usage (replication, CLR, agent jobs, cross database query, linked servers, SSIS, SSAS, SSRS etc.)?&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Size and throughput:&lt;/STRONG&gt; The PaaS offerings have maximum databases sizes that vary across each service. Ensure your growth and performance requirements are met by the service you select. For migrations, large databases may favor backup/restore and log shipping type approaches (if available) over row-by-row copy.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Network connectivity:&lt;/STRONG&gt; VPN/ExpressRoute, firewall rules, and whether you can deploy a self-hosted Integration Runtime (IR) or agents.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Schema compatibility:&lt;/STRONG&gt; Azure SQL Database/Hyperscale has the most feature differences; MI and SQL VM are closer to SQL Server.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Operational constraints:&lt;/STRONG&gt; Change control on production, ability to install agents, and security requirements for credentials and encryption.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Cost model:&lt;/STRONG&gt; Some approaches incur ongoing compute costs (for example ADF pipeline runs, IRs, interim storage, or extra replicas during migration).&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1&gt;&lt;U&gt;Migration Assessments (SSMS 22+ and Azure Arc)&lt;/U&gt;&lt;/H1&gt;
&lt;P&gt;Before moving an on‑premises SQL Server database to &lt;STRONG&gt;Azure SQL Database/Hyperscale&lt;/STRONG&gt; or &lt;STRONG&gt;Azure SQL Managed Instance&lt;/STRONG&gt;, you should run a &lt;STRONG&gt;migration assessment&lt;/STRONG&gt;. Assessments identify feature incompatibilities, behavior differences, and configuration gaps that are easy to miss until late in the project (for example unsupported instance features in Azure SQL Database, cross-database dependencies, SQL Agent usage, CLR/extended procedures, or deprecated syntax). Running an assessment early reduces rework, helps you choose the right PaaS target (SQL DB vs. MI), and informs remediation tasks and sizing/performance planning.&lt;/P&gt;
&lt;P&gt;Starting with &lt;STRONG&gt;SSMS 22+&lt;/STRONG&gt;, assessment capabilities are built into the primary management tool many DBAs already use, making this a low-friction, repeatable step. The assessment results typically highlight blocking issues, warnings, and recommended changes, along with links to guidance so teams can prioritize remediation. Treat this as a &lt;STRONG&gt;gating step&lt;/STRONG&gt; for PaaS migrations: run it for every candidate database, track findings as work items, and re-run after remediation and again before cutover.&lt;/P&gt;
&lt;P&gt;For organizations managing multiple on‑premises and edge SQL Servers, &lt;STRONG&gt;Azure Arc-enabled SQL Server&lt;/STRONG&gt; can extend Azure management capabilities to your existing estate. Arc can provide &lt;STRONG&gt;centralized inventory&lt;/STRONG&gt;, &lt;STRONG&gt;performance monitoring&lt;/STRONG&gt;, policy-based governance, and (where configured) &lt;STRONG&gt;automatic/recurring assessments&lt;/STRONG&gt; so you can continuously measure readiness for Azure SQL PaaS.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;When to assess:&lt;/STRONG&gt; Run an assessment as soon as a database is a candidate for Azure SQL DB/HS or MI, then re-run after fixes and before cutover.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;What you learn:&lt;/STRONG&gt; Compatibility blockers, feature gaps, and refactoring guidance; inputs to target selection (SQL DB vs. MI) and to migration method choice.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;SSMS 22+ workflow:&lt;/STRONG&gt; Use the built-in assessment experience to generate findings you can track and share with dev/app teams.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Arc value-add:&lt;/STRONG&gt; Enable Arc on on‑premises SQL Server to get centralized management, performance monitoring, and ongoing assessments across your estate.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Planning integrations:&lt;/STRONG&gt; Use assessment outputs to drive remediation backlogs and to inform Azure DMS-based migration projects (and associated connectivity/identity planning). For larger estates, &lt;STRONG&gt;Azure Migrate&lt;/STRONG&gt; assessments can complement SSMS/Arc by providing discovery at scale plus readiness, sizing, and cost estimates for Azure SQL targets.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1&gt;&lt;U&gt;Tooling by Target (at a glance)&lt;/U&gt;&lt;/H1&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="border-width: 1px;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;Target&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;Online (minimal downtime) options&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;Offline (requires downtime) options&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;Azure SQL Database / Hyperscale&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;ADF with CDC/CT, Transactional Replication (&lt;EM&gt;push subscription required&lt;/EM&gt;), SqlDBCopy with CT&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Azure Migrate (discovery/assessment), Azure Arc SQL Migration Tooling (Arc-enabled source assessment/orchestration), Azure DMS offline, ADF copy, SqlDBCopy, SmartBulkCopy, SSIS, SqlPackage (BACPAC)&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;Azure SQL Managed Instance&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Managed Instance Link, Azure DMS online, Log Replay Service (LRS), Transactional Replication, ADF copy with CDC/CT, BlobShipper, SqlDBCopy with CT&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Azure Migrate (discovery/assessment), Azure Arc SQL Migration Tooling (Arc-enabled source assessment/orchestration), LRS, Azure DMS (offline), ADF copy, SqlDBCopy, SmartBulkCopy, SSIS, SqlPackage (BACPAC)&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;Azure SQL VM&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Distributed Availability Groups (DAG), Transactional Replication, BlobShipper, Log Shipping, ADF with CDC/CT, SqlDBCopy with CT&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Azure Migrate (discovery/assessment), Azure Arc SQL Migration Tooling (Arc-enabled source assessment/orchestration), Backup/restore, Azure DMS (offline), Transactional Replication, ADF copy with CDC/CT, SqlDBCopy, SmartBulkCopy, SSIS, SqlPackage (BACPAC)&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;SQL Server anywhere&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Possibly Distributed AG, Transactional Replication, SqlDBCopy with CT&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Backup/Restore, SqlDBCopy, SmartBulkCopy, SSIS, SqlPackage (BACPAC)&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 33.33%" /&gt;&lt;col style="width: 33.33%" /&gt;&lt;col style="width: 33.33%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;/DIV&gt;
&lt;P&gt;&lt;STRONG&gt;Note:&lt;/STRONG&gt; Exact capabilities depend on SQL Server version/edition, database features in use, and target service limitations. Some approaches copy &lt;EM&gt;data only&lt;/EM&gt; (table-level) while others move a full database or instance. Always validate feature compatibility and run a proof-of-concept with production-like data volumes.&lt;/P&gt;
&lt;H1&gt;&lt;U&gt;Tool Deep Dives&lt;/U&gt;&lt;/H1&gt;
&lt;H2&gt;Azure Migrate&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;What it is:&lt;/STRONG&gt; Azure Migrate is the Azure hub for &lt;STRONG&gt;discovery&lt;/STRONG&gt;, &lt;STRONG&gt;assessment&lt;/STRONG&gt;, &lt;STRONG&gt;migration planning&lt;/STRONG&gt;, and &lt;STRONG&gt;lift and shift migrations&lt;/STRONG&gt; across servers, apps, and data platforms. For SQL Server specifically, Azure Migrate can&amp;nbsp;&lt;STRONG&gt;discover SQL instances and databases&lt;/STRONG&gt; (via an appliance or other supported discovery methods) and produce &lt;STRONG&gt;Azure SQL assessments&lt;/STRONG&gt; that estimate readiness, right-size recommendations, and cost for targets such as &lt;STRONG&gt;SQL Server on Azure VM&lt;/STRONG&gt;, &lt;STRONG&gt;Azure SQL Managed Instance&lt;/STRONG&gt;, and &lt;STRONG&gt;Azure SQL Database&lt;/STRONG&gt;.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Where it fits:&lt;/STRONG&gt; Use Azure Migrate early to build an inventory, group related workloads, understand dependencies, and generate sizing/cost guidance before you choose an execution tool (for example Azure DMS, MI Link, backup/restore, replication, or table-level copy).&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Discovery approach:&lt;/STRONG&gt; Typically uses an &lt;STRONG&gt;Azure Migrate appliance&lt;/STRONG&gt; for agentless discovery of servers and workloads (including SQL Server instances/databases) and ongoing performance collection; assessment quality improves when you collect representative performance data over time.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Assessment outputs:&lt;/STRONG&gt; Readiness (ready/conditional/not ready), recommended target (SQL VM vs. MI vs. SQL DB where applicable), right-sized SKU/tier, and cost estimates based on configuration-only or performance-based sizing.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Strengths:&lt;/STRONG&gt; Works well at &lt;STRONG&gt;estate scale&lt;/STRONG&gt;; provides a consistent, repeatable assessment methodology and helps prioritize migration waves and modernization candidates.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Limitations / considerations:&lt;/STRONG&gt; Azure Migrate is primarily a &lt;STRONG&gt;planning&lt;/STRONG&gt; and &lt;STRONG&gt;assessment&lt;/STRONG&gt; tool for SQL; the actual database move is typically executed with tools like &lt;STRONG&gt;Azure DMS&lt;/STRONG&gt;, &lt;STRONG&gt;MI Link&lt;/STRONG&gt;, backup/restore-based approaches, replication, or data copy utilities. Treat Azure Migrate recommendations as inputs—validate with workload-specific testing and SQL feature compatibility checks (for example SSMS assessment results) before cutover.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Azure Database Migration Service (Azure DMS)&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;What it is:&lt;/STRONG&gt; A managed Azure service that orchestrates database migrations using guided workflows. Depending on the source/target combination, it can perform &lt;STRONG&gt;offline&lt;/STRONG&gt; (one-time move) or &lt;STRONG&gt;online&lt;/STRONG&gt; (continuous sync + cutover) migrations.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Typical targets:&lt;/STRONG&gt; Azure SQL Database/Hyperscale (offline), Azure SQL Managed Instance (online), and SQL Server on Azure VM.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Prerequisites:&lt;/STRONG&gt; Network connectivity from DMS to source/target (often via VPN/ExpressRoute), required firewall openings, and appropriate permissions on both ends.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Strengths:&lt;/STRONG&gt; Guided experience, assessment integration, and support for multiple database moves with monitoring.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Limitations / considerations:&lt;/STRONG&gt; Feature support varies by scenario; some migrations are offline only. DMS orchestrates the process but still depends on connectivity and may have constraints around file placement for restore-based workflows. Plan for validation, cutover steps, and potential reconfiguration tasks (logins, jobs, SSIS, etc.) outside the database move itself.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Azure SQL Managed Instance Link (MI Link)&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;What it is:&lt;/STRONG&gt; A managed replication capability designed specifically for migrating from SQL Server to &lt;STRONG&gt;Azure SQL Managed Instance&lt;/STRONG&gt; with near-real-time synchronization and a controlled cutover experience.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Supported targets:&lt;/STRONG&gt; &lt;STRONG&gt;Azure SQL Managed Instance only.&lt;/STRONG&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Online/offline:&lt;/STRONG&gt; &lt;STRONG&gt;Online&lt;/STRONG&gt; synchronization with a short cutover window.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Prerequisites:&lt;/STRONG&gt; Supported SQL Server versions/editions and configurations, network connectivity to the MI endpoint, and permissions to configure the link. (Review current product documentation for the exact supported matrix.)&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Strengths:&lt;/STRONG&gt; Designed for MI migrations; reduces the need for custom change-capture pipelines; supports validation and coordinated cutover.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Limitations:&lt;/STRONG&gt; Not usable for Azure SQL VM or Azure SQL Database targets. Feature support and prerequisites are scenario-specific; plan for testing and for migrating instance-level objects and application connectivity during cutover.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Log Replay Service (LRS)&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;What it is:&lt;/STRONG&gt; LRS is a managed Azure SQL Managed Instance migration service that restores native SQL Server full, differential, and log backups from Azure Blob Storage to keep a target database catching up until cutover. It is useful when you want a backup/restore-based migration pattern with more control over orchestration, or when other guided migration tooling cannot be used because of environment, network, or operational constraints.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Supported targets:&lt;/STRONG&gt; &lt;STRONG&gt;Azure SQL Managed Instance only.&lt;/STRONG&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Online/offline:&lt;/STRONG&gt; Commonly used as a &lt;STRONG&gt;minimal-downtime&lt;/STRONG&gt; migration method in &lt;STRONG&gt;continuous&lt;/STRONG&gt; mode by repeatedly restoring new log backups until cutover. It can also be used in an &lt;STRONG&gt;autocomplete&lt;/STRONG&gt; pattern when the full backup chain is already available and you simply want the restore to run to completion.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Prerequisites:&lt;/STRONG&gt; Source databases must use the &lt;STRONG&gt;full recovery model&lt;/STRONG&gt;, and you need a valid backup chain (full, optional differential, and log backups) accessible in &lt;STRONG&gt;Azure Blob Storage&lt;/STRONG&gt;. Plan storage layout carefully—each database should use its own folder path—and validate backup integrity (CHECKSUM recommended) before migration.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Strengths:&lt;/STRONG&gt; Supports migrations from SQL Server hosted almost anywhere, including on-premises, VMs, and other clouds; supports &lt;STRONG&gt;differential backups&lt;/STRONG&gt;; and gives you direct control through PowerShell, Azure CLI, or APIs for custom orchestration and hybrid migration patterns.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Limitations / considerations:&lt;/STRONG&gt; Databases being restored through LRS are &lt;STRONG&gt;not usable until migration finishes&lt;/STRONG&gt; (no read-only access during restore). After cutover, the migration is final and can’t be resumed with additional differential backups. Review current service-tier and feature limitations—especially for Business Critical—and schedule maintenance windows and cutover steps carefully.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Azure Arc SQL Migration Tooling&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;What it is:&lt;/STRONG&gt; Azure Arc-enabled SQL Server adds an Azure portal experience for &lt;STRONG&gt;continuous migration assessment&lt;/STRONG&gt; and, for supported scenarios, integrated migration workflows. It helps you discover Arc-enabled SQL Server instances, evaluate readiness for Azure SQL targets, compare recommended destinations and sizing, and then move into guided migration execution without stitching together separate tools by hand.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Where it fits:&lt;/STRONG&gt; Best for organizations that have already &lt;STRONG&gt;Azure Arc-enabled&lt;/STRONG&gt; their SQL Server estate and want a more centralized migration journey covering discovery, readiness, target recommendation, and guided execution from the Azure portal.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Assessment outputs:&lt;/STRONG&gt; Arc can continuously generate migration readiness results, identify risks and mitigation guidance, recommend a target type (&lt;STRONG&gt;Azure SQL Managed Instance&lt;/STRONG&gt;, &lt;STRONG&gt;Azure SQL Database&lt;/STRONG&gt;, or &lt;STRONG&gt;SQL Server on Azure VM&lt;/STRONG&gt;), suggest sizing/service tier, and provide retail cost estimates to support migration planning.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Supported migration direction:&lt;/STRONG&gt; Arc-based SQL migration tooling is centered on &lt;STRONG&gt;Arc-enabled source SQL Server instances&lt;/STRONG&gt;. Current portal experiences focus on integrated migration to Azure SQL targets, including &lt;STRONG&gt;Azure SQL Managed Instance&lt;/STRONG&gt;, with expanding support for &lt;STRONG&gt;SQL Server on Azure VM&lt;/STRONG&gt; scenarios.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Strengths:&lt;/STRONG&gt; Consolidates assessment and migration activities into one operational surface, reduces context switching, helps compare modernization-vs.-cost strategies, and keeps assessments refreshed on a schedule so you can track readiness over time.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Limitations / considerations:&lt;/STRONG&gt; Requires the source SQL Server to be &lt;STRONG&gt;enabled by Azure Arc&lt;/STRONG&gt;, and some migration experiences are still scenario- or platform-specific and at the time of this article limited to 10 databases per migration workflow. Treat Arc guidance as a planning and orchestration layer—validate the recommended target, chosen migration method, feature compatibility, and operational prerequisites with workload-specific testing before production cutover.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Transactional Replication&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;What it is:&lt;/STRONG&gt; A SQL Server replication technology that continuously streams committed changes from a &lt;STRONG&gt;Publisher&lt;/STRONG&gt; to one or more &lt;STRONG&gt;Subscribers&lt;/STRONG&gt; via a &lt;STRONG&gt;Distributor&lt;/STRONG&gt;. For migrations, it is commonly used to keep a target synchronized with minimal downtime, then cut over applications to the subscriber.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Supported targets:&lt;/STRONG&gt; SQL Server on Azure VM, Azure SQL Managed Instance, Azure SQL Database/Hyperscale, and SQL Server anywhere - subject to replication feature support and version compatibility.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;PaaS subscription model:&lt;/STRONG&gt; When the &lt;STRONG&gt;subscriber is a PaaS target&lt;/STRONG&gt; (Azure SQL Database or Azure SQL Managed Instance), you typically use a &lt;STRONG&gt;push subscription&lt;/STRONG&gt; so that the on‑premises publisher/distributor (or an IaaS SQL Server acting as distributor) connects outbound to the PaaS subscriber.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Distributor placement:&lt;/STRONG&gt; You can host the Distributor &lt;STRONG&gt;on‑premises&lt;/STRONG&gt; (common when outbound connectivity to Azure is allowed and you want to keep replication infrastructure local), or on a &lt;STRONG&gt;SQL Server on Azure VM&lt;/STRONG&gt; (common when you want replication infrastructure closer to the cloud target, to simplify network paths, or to avoid routing large snapshot/distribution traffic across the WAN). In both cases, ensure the Distributor can reach the Publisher and can make outbound connections to the PaaS subscriber for push subscriptions.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Online/offline:&lt;/STRONG&gt; &lt;STRONG&gt;Online&lt;/STRONG&gt; data movement after initialization; cutover requires a brief downtime window to quiesce writes, allow the subscriber to catch up, and switch application connections.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Prerequisites:&lt;/STRONG&gt; A configured Distributor, reliable network connectivity, and appropriate permissions. Articles generally need a &lt;STRONG&gt;primary key&lt;/STRONG&gt; (or a defined unique row identifier) for updatable patterns; initialization uses a snapshot that must be delivered to the subscriber.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Strengths:&lt;/STRONG&gt; Proven low-latency replication for many OLTP workloads; supports one-to-many topologies; useful when you want to keep a cloud target warm for validation and phased cutover.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Limitations:&lt;/STRONG&gt; Operational overhead (monitoring agents, distribution database growth, troubleshooting latency). Not a full instance migration (logins, jobs, etc. are separate). Some schema changes require careful coordination and may force reinitialization. Large objects, high churn, or very large initial snapshots can increase time and storage requirements. Plan identity range management, constraints, and replication of DDL based on your workload and target.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Distributed Availability Groups (DAG)&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;What it is:&lt;/STRONG&gt; A SQL Server feature that replicates databases between two separate Availability Groups. It can be used for migration by establishing asynchronous data movement to a secondary AG and then performing a controlled cutover.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Supported targets:&lt;/STRONG&gt; &lt;STRONG&gt;SQL Server only&lt;/STRONG&gt; targets—commonly SQL Server on Azure VM. (Not applicable to Azure SQL Database or Managed Instance.)&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Online/offline:&lt;/STRONG&gt; &lt;STRONG&gt;Online&lt;/STRONG&gt; replication with a short cutover window; can provide very low data loss when configured appropriately.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Prerequisites:&lt;/STRONG&gt; Availability Groups and underlying clustering infrastructure (WSFC on Windows or Pacemaker on Linux), compatible SQL Server editions, and network connectivity between replicas.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Strengths:&lt;/STRONG&gt; Mature HA/DR technology with robust monitoring; suitable for larger databases and for keeping a warm secondary environment.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Limitations:&lt;/STRONG&gt; Higher complexity and operational overhead (clusters, AG configuration, certificates/endpoints). Not a lightweight option when change control or infrastructure constraints are tight.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;BlobShipper Service (log shipping via Azure Blob Storage)&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;What it is:&lt;/STRONG&gt; The &lt;A href="https://www.microsoft.com/en-us/download/details.aspx?id=108400" target="_blank" rel="noopener"&gt;&lt;STRONG&gt;BlobShippe&lt;/STRONG&gt;r&lt;/A&gt; application is a Windows service that automates SQL Server full and log backups to Azure Blob Storage and restores them to a target SQL Server (commonly on an Azure SQL VM). It is a backup-driven approach intended to minimize production changes while enabling low-downtime cutover.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Supported targets:&lt;/STRONG&gt; SQL Server on Azure VM (primary), and generally SQL Server-to-SQL Server scenarios where backup/restore is available.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Online/offline:&lt;/STRONG&gt; &lt;STRONG&gt;Online&lt;/STRONG&gt; up to cutover (continuous log shipping), then a short downtime window for the final log backup/restore and application cutover.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Prerequisites:&lt;/STRONG&gt; Ability to take full + log backups, Azure Storage account access, and connectivity from the target to read backups from Blob Storage.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Strengths:&lt;/STRONG&gt; Minimal change to production beyond taking ownership of the backup/log chain; controlled file placement on the target; can optionally apply logs with a delay to provide a read-only fallback window.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Limitations:&lt;/STRONG&gt; Target must support restoring native SQL Server backups (therefore not Azure SQL Database). Log chain control means you typically stop other backup jobs for the migrated databases while the service runs.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Log Shipping (native SQL Server)&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;What it is:&lt;/STRONG&gt; A built-in SQL Server feature that keeps a secondary database synchronized by repeatedly backing up transaction log backups on the primary, copying them to a secondary location, and restoring them on the secondary. For migrations to Azure, Log Shipping is commonly used from on‑premises SQL Server to &lt;STRONG&gt;SQL Server on Azure VM&lt;/STRONG&gt; to achieve low downtime cutover.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Supported targets:&lt;/STRONG&gt; &lt;STRONG&gt;SQL Server only - &lt;/STRONG&gt;including SQL Server on Azure VM. (Not applicable to Azure SQL Database or Azure SQL Managed Instance.)&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Online/offline:&lt;/STRONG&gt; &lt;STRONG&gt;Online&lt;/STRONG&gt; log restore cycles up to cutover; downtime is typically limited to the final log backup/restore and application connection switch.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Prerequisites:&lt;/STRONG&gt; The source database must be in full recovery model with a healthy log chain, and you need a mechanism to &lt;STRONG&gt;copy log backups&lt;/STRONG&gt; from on‑premises to the Azure VM (file share, storage account, or other transfer path). Agent jobs run on both ends to schedule backup/copy/restore.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Strengths:&lt;/STRONG&gt; Simple, well-understood, and works with very large databases; provides a warm standby you can validate before cutover.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Limitations:&lt;/STRONG&gt; Asynchronous by design (some data loss is possible if you cut over before the last logs are applied). Requires ongoing management of backup files, schedules, and retention. You must coordinate other backup jobs so they don’t break the log chain and ensure you can meet RPO/RTO.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Relationship to BlobShipper:&lt;/STRONG&gt; BlobShipper automates a similar log-shipping pattern using Azure Blob Storage as the transfer medium and adds operational conveniences (for example, controlled restore timing and simplified blob-based transport).&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Azure Data Factory (ADF) copy pipelines&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;What it is:&lt;/STRONG&gt; A cloud data integration service that can copy data from on‑premises SQL Server to Azure data stores (including Azure SQL Database, Managed Instance, and SQL Server on Azure VM) using configurable pipelines.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Prerequisites:&lt;/STRONG&gt; For on‑premises sources you typically deploy a &lt;STRONG&gt;Self-hosted Integration Runtime (IR)&lt;/STRONG&gt; in your network (or a connected network) to enable secure connectivity. You also configure linked services, datasets, and credentials.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Online/offline:&lt;/STRONG&gt; ADF copy itself is often &lt;STRONG&gt;offline&lt;/STRONG&gt; for consistent point-in-time results, but you can implement &lt;STRONG&gt;incremental&lt;/STRONG&gt; patterns (for example, watermark columns, Change Tracking/CDC-based extracts, or time-window batching) to reduce cutover downtime. Our team has a custom tool to make this process much easier - if you are interested, reach out to us.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Strengths:&lt;/STRONG&gt; Works across many sources/targets; good for selective table moves, transformations, and repeatable pipeline operations.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Limitations / considerations:&lt;/STRONG&gt; Throughput is influenced by IR sizing, source/target performance, network bandwidth, and parallelism settings. Pipeline executions incur &lt;STRONG&gt;costs&lt;/STRONG&gt; (activity runs, data movement, and IR compute if you use Azure-hosted resources). You are responsible for schema management and for handling constraints/ordering, as well as for designing incremental logic and validation.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;SSIS (SQL Server Integration Services)&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;What it is:&lt;/STRONG&gt; A data integration and ETL technology in SQL Server that can extract data from an on‑premises SQL Server and load it into many targets. SSIS is often used as an &lt;STRONG&gt;offline&lt;/STRONG&gt; copy mechanism for selected tables (or full database data via multiple packages) when you already have SSIS skills/infrastructure or when you need transformations during the move.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Supported targets:&lt;/STRONG&gt; SQL Server on Azure VM, Azure SQL Managed Instance, Azure SQL Database/Hyperscale, and many other destinations via connectors/drivers.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Online/offline:&lt;/STRONG&gt; Most SSIS-driven copies are &lt;STRONG&gt;offline&lt;/STRONG&gt; point-in-time loads. Incremental loads are possible but require package design (watermarks, CDC/CT patterns, staging, and merge logic).&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Prerequisites:&lt;/STRONG&gt; An execution environment for SSIS packages (for example SQL Server Integration Services on-prem, or an Azure-hosted SSIS runtime where applicable), connectivity from the runtime to source and target, and appropriate drivers/connectors and credentials.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Strengths:&lt;/STRONG&gt; Mature tooling, rich transformation support, and strong control over batching, error handling, and staging.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Limitations / considerations:&lt;/STRONG&gt; Package development and operations can be complex at scale. Performance depends on runtime sizing and network throughput. Like other table-level copy approaches, SSIS does not automatically migrate schema/instance objects unless you build that into the process. For cloud-first pipelines, ADF may be preferred for managed scheduling/monitoring, but SSIS remains a solid choice when you already have packages and patterns.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;SqlDBCopy (offline copy and Change Tracking–based incremental copy)&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;What it is: &lt;A href="https://www.microsoft.com/en-us/download/details.aspx?id=105024" target="_blank" rel="noopener"&gt;SqlDBCopy&lt;/A&gt;&lt;/STRONG&gt; is a utility that can copy tables between SQL Server endpoints either as a one-time offline operation or incrementally by leveraging &lt;STRONG&gt;SQL Server Change Tracking&lt;/STRONG&gt; to move only changed rows.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Supported targets:&lt;/STRONG&gt; SQL Server, Azure SQL VM, Azure SQL Managed Instance, and Azure SQL Database (depending on connectivity).&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Offline mode:&lt;/STRONG&gt; Copies a static snapshot of data; best when you can stop writes or when the data set is reference/static.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Incremental mode (Change Tracking):&lt;/STRONG&gt; Can continuously or periodically apply changes for tables where &lt;STRONG&gt;Change Tracking is enabled&lt;/STRONG&gt; and where tables have a &lt;STRONG&gt;primary key&lt;/STRONG&gt;. This enables lower-downtime migration patterns for selected tables.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Prerequisites:&lt;/STRONG&gt; Target schema and keys must exist; Change Tracking must be enabled on the source database and tables used for incremental copy; plan retention/cleanup so changes aren’t lost before they’re applied.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Limitations:&lt;/STRONG&gt; Change Tracking does not capture all DDL changes—schema evolution must be managed separately. Tables without primary keys are not suitable for Change Tracking–based incremental copy. Large change volumes may require tuning batch sizes and scheduling to keep up.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;SmartBulkCopy (table-level bulk copy utility)&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;What it is:&lt;/STRONG&gt; A table-level bulk copy approach for moving data between SQL Server-compatible endpoints using high-throughput bulk operations. It is useful when you want to copy selected tables (not a whole database) or when backup/restore-based methods aren’t available.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Supported targets:&lt;/STRONG&gt; SQL Server, Azure SQL VM, Azure SQL Managed Instance, and Azure SQL Database (connectivity and permissions permitting).&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Online/offline:&lt;/STRONG&gt; Commonly &lt;STRONG&gt;offline&lt;/STRONG&gt; for consistent results unless you implement your own change capture and replay pattern.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Prerequisites:&lt;/STRONG&gt; The &lt;STRONG&gt;target schema must already exist&lt;/STRONG&gt; (tables, indexes as needed). You also need appropriate permissions for bulk insert/copy and sufficient target throughput.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Strengths:&lt;/STRONG&gt; Fast for large table copies; you can scope to a subset of tables and parallelize work.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Limitations:&lt;/STRONG&gt; Does not automatically migrate schema, users/logins, or instance-level objects. Maintaining referential integrity and identity values may require careful ordering and settings. If you need minimal downtime, you must add a separate mechanism to capture and apply ongoing changes.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;SqlPackage (BACPAC export/import and DAC deployment)&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;What it is:&lt;/STRONG&gt; A command-line utility used to export a database to a &lt;STRONG&gt;BACPAC&lt;/STRONG&gt; (schema + data) and import it into a target, or to publish schema changes using a DACPAC. It is commonly used to move databases to &lt;STRONG&gt;Azure SQL Database&lt;/STRONG&gt; or to create a portable logical copy for SQL Server targets.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Supported targets:&lt;/STRONG&gt; Azure SQL Database/Hyperscale, Azure SQL Managed Instance, and SQL Server (including SQL Server on Azure VM) depending on options used.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Online/offline:&lt;/STRONG&gt; Typically &lt;STRONG&gt;offline&lt;/STRONG&gt; for a full data move—export from the source represents a point-in-time snapshot. To avoid data drift, you usually stop writes or accept a reconciliation step.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Strengths:&lt;/STRONG&gt; Simple packaging model; good for smaller databases, dev/test cloning, and scenarios where logical export is preferred over backup/restore.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Limitations:&lt;/STRONG&gt; Can be &lt;STRONG&gt;slow&lt;/STRONG&gt; for large databases (export/import is largely single-threaded and sensitive to network and target performance). Requires enough local or intermediate storage for the BACPAC. It is not a minimal downtime option. Some objects/features may not be supported on the target platform (especially Azure SQL Database), and you may need to resolve compatibility issues before import.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1&gt;&lt;U&gt;Security Considerations&lt;/U&gt;&lt;/H1&gt;
&lt;P&gt;Security planning is a critical part of any on‑premises to cloud migration. In addition to securing network paths (VPN/ExpressRoute, firewall rules, Private Link where applicable) and protecting credentials used by migration tools, you should plan for how &lt;STRONG&gt;identity and access&lt;/STRONG&gt; will work on the target platform after cutover.&lt;/P&gt;
&lt;P&gt;For Azure SQL PaaS targets (Azure SQL Database and Azure SQL Managed Instance), many organizations choose to standardize on &lt;STRONG&gt;Microsoft Entra ID&lt;/STRONG&gt; (formerly Azure AD) authentication. This can require translating existing &lt;STRONG&gt;on‑premises Active Directory&lt;/STRONG&gt; logins and database users into Entra-based identities and groups and then remapping database permissions, so applications, users and administrators retain the correct access.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Inventory and map identities:&lt;/STRONG&gt; List server logins, database users, role memberships, and explicit GRANT/DENY permissions on the source. Decide which should become &lt;STRONG&gt;Entra users&lt;/STRONG&gt; vs. &lt;STRONG&gt;Entra groups&lt;/STRONG&gt; (recommended for manageability) and confirm synchronization/hybrid identity requirements with your identity team.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;&lt;A href="https://www.microsoft.com/en-us/download/details.aspx?id=108534" target="_blank" rel="noopener"&gt;MoveLogins&lt;/A&gt;:&lt;/STRONG&gt; Use this PowerShell script to help script existing logins, users, roles and permissions and use the AD lookup function to map the AD logins to Entra UPNs. The script can then be reviewed and applied to the target server. (Contact our team if you need this script).&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://www.microsoft.com/en-us/download/details.aspx?id=106382" target="_blank" rel="noopener"&gt;&lt;STRONG&gt;ScriptLoginsAndUsers&lt;/STRONG&gt;&lt;/A&gt;&lt;STRONG&gt;:&lt;/STRONG&gt; Use this utility to script out Entra and SQL logins, users, roles and permission so you can recreate the security structure on the target. This is especially helpful when you are moving PaaS databases from one Tenant to another.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1&gt;&lt;U&gt;Dev/Test with Obfuscated Data&lt;/U&gt;&lt;/H1&gt;
&lt;P&gt;Many migration projects benefit from creating a &lt;STRONG&gt;development or test&lt;/STRONG&gt; copy of production data to validate application behavior, performance, and migration tooling. When production contains sensitive information (PII/PHI/PCI or confidential business data), you may need to &lt;STRONG&gt;obfuscate&lt;/STRONG&gt; that data before it can be used outside tightly controlled production environments.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Typical use cases:&lt;/STRONG&gt; Building realistic dev/test environments, rehearsing migrations and cutovers, enabling troubleshooting by broader teams, and sharing data with partners/vendors where raw production data is not permitted.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Obfuscate tool:&lt;/STRONG&gt; Use the &lt;A href="https://www.microsoft.com/en-us/download/details.aspx?id=108635" target="_blank" rel="noopener"&gt;Obfuscate&lt;/A&gt; utility to transform sensitive columns (for example names, emails, phone numbers, identifiers, free-text fields) into non-sensitive substitutes while preserving useful characteristics (such as format, referential integrity, and distribution patterns) as required by testing scenarios.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Plan the masking strategy:&lt;/STRONG&gt; Define which tables/columns are sensitive, which must remain consistent across tables (keys/lookup values), and what level of realism is needed (synthetic values vs. deterministic masking). Document the rules so results are repeatable for refreshes.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Sequence and constraints:&lt;/STRONG&gt; Obfuscation often needs to run in a controlled order to preserve foreign keys and uniqueness constraints. Consider disabling/re-enabling constraints or using staging tables if your process requires it.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Security hygiene:&lt;/STRONG&gt; Treat the obfuscation process and outputs as sensitive until validated. Restrict access to the obfuscation configuration, logs, and any intermediate exports that may contain raw values.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Verify effectiveness:&lt;/STRONG&gt; Add validation steps to confirm that sensitive values are masked, that key relationships still hold, and that the application behaves correctly with the obfuscated dataset.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1&gt;&lt;U&gt;Data validation&lt;/U&gt;&lt;/H1&gt;
&lt;P&gt;Regardless of which copy or migration mechanism you choose, you may want to plan for &lt;STRONG&gt;data validation&lt;/STRONG&gt; immediately before cutover. Validation reduces the risk of silent data drift and missed objects, and it helps you quantify exactly what is left to reconcile before switching applications to the new target.&lt;/P&gt;
&lt;H2&gt;Database Compare Utility&lt;/H2&gt;
&lt;P&gt;The &lt;A href="https://www.microsoft.com/en-us/download/details.aspx?id=103016" target="_blank" rel="noopener"&gt;&lt;STRONG&gt;Database Compare Utility&lt;/STRONG&gt;&lt;/A&gt; can validate that source and target have the same schema objects and contain identical data with the option to generate a change script to align the target to the source. It supports comparisons between other heterogeneous database platforms, but it fully supports any SQL Server to SQL Server comparison assuming there is connectivity between the two. The latest version includes a &lt;STRONG&gt;GUI&lt;/STRONG&gt; or it can also be executed in batch/console mode. The tool is &lt;STRONG&gt;multi-threaded&lt;/STRONG&gt; (configurable parallelism) but can generate significant load on databases and the network. It can do row count verification, table hash verification or row by row verification. For tables with primary keys, it can also generate change scripts. Advanced scenarios can be driven by an &lt;STRONG&gt;Excel input&lt;/STRONG&gt; that maps different schema/table/column names, supplies filters to split large tables into parallel tasks, and defines alternate keys or column exclusions.&lt;/P&gt;
&lt;H2&gt;CPQR Utility&lt;/H2&gt;
&lt;P&gt;The Compare Query Performance and Result sets (&lt;STRONG&gt;&lt;A href="https://www.microsoft.com/en-us/download/details.aspx?id=105992" target="_blank" rel="noopener"&gt;CPQR&lt;/A&gt;&lt;/STRONG&gt;) utility’s purpose is to compare the execution times and result sets (i.e. every column value for every row) generated by running two queries against two different data sources; one any OLEDB data source and the other a SQL Server data source. Note that although the performance of queries is measured, since multiple queries are being run by the application concurrently, the performance may be affected by other queries that are being run in parallel (to mitigate this you can turn off parallelism).&lt;/P&gt;
&lt;H1&gt;&lt;U&gt;How to choose an approach&lt;/U&gt;&lt;/H1&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;If your target is Azure SQL VM and you need minimal downtime:&lt;/STRONG&gt; prefer log/replication approaches such as BlobShipper (log shipping), &lt;STRONG&gt;Transactional Replication&lt;/STRONG&gt;, or Distributed Availability Groups, depending on how much infrastructure and change you can accept.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;If your target is Azure SQL Managed Instance and you need minimal downtime:&lt;/STRONG&gt; start with Managed Instance Link (when supported) or DMS online migrations. &lt;STRONG&gt;Transactional Replication&lt;/STRONG&gt; (&lt;EM&gt;push subscription&lt;/EM&gt;) can also work for many patterns when you can manage distributor operations and table/article constraints; use table-level incremental copy (SqlDBCopy/ADF patterns) only when a full database move isn’t feasible.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;If your target is Azure SQL Database/Hyperscale:&lt;/STRONG&gt; prioritize compatibility assessment early. For smaller databases, SqlPackage (BACPAC) may be acceptable. For larger or lower-downtime needs, consider DMS online (supported scenarios), &lt;STRONG&gt;Transactional Replication&lt;/STRONG&gt; (&lt;EM&gt;push subscription&lt;/EM&gt;) for suitable table/article sets, or ADF incremental patterns.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;If you only need certain tables or you’re consolidating data:&lt;/STRONG&gt; table-level tools (ADF, SmartBulkCopy, SqlDBCopy) can be simpler than full database migrations—just plan schema creation and data consistency.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Always plan the non-data work:&lt;/STRONG&gt; logins/users, SQL Agent jobs, maintenance plans, SSIS/SSRS, linked servers, certificates/keys, and application connection changes often dominate the overall cutover effort.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1&gt;Conclusion&lt;/H1&gt;
&lt;P&gt;There is no single best migration method for every SQL Server workload. The right choice depends primarily on the Azure target, downtime tolerance, and how closely you need to preserve SQL Server instance features. Use the matrix above to narrow options by target, then validate prerequisites and limitations in a proof-of-concept. For production migrations, include performance testing, data validation, cutover rehearsal, and a rollback plan appropriate to your business risk.&lt;/P&gt;
&lt;H1&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc227937169"&gt;&lt;/A&gt;Feedback and suggestions&lt;/H1&gt;
&lt;P&gt;If you have feedback or suggestions for improving this data migration asset, please contact the Azure Databases SQL Customer Success Engineering (Ninja) Team (&lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;datasqlninja@microsoft.com&lt;/A&gt;).&lt;/P&gt;
&lt;P&gt;Thank you for your support!&lt;/P&gt;</description>
      <pubDate>Wed, 17 Jun 2026 15:04:50 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/microsoft-options-to-migrate-sql-server-databases/ba-p/4521637</guid>
      <dc:creator>Mitch_van_Huuksloot</dc:creator>
      <dc:date>2026-06-17T15:04:50Z</dc:date>
    </item>
    <item>
      <title>Unlocking the Power of Azure Resource Graph Explorer for Comprehensive Database Inventory</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/unlocking-the-power-of-azure-resource-graph-explorer-for/ba-p/4392345</link>
      <description>&lt;H1&gt;Why Taking Inventory is Crucial&lt;/H1&gt;
&lt;P&gt;Taking inventory of databases in Azure is a critical practice for any organization managing cloud resources. It ensures that all databases are accounted for, providing a comprehensive overview of the organization’s data assets. This visibility is essential for effective management and optimization of resources, as it allows for better planning and allocation based on actual usage and requirements.&lt;/P&gt;
&lt;P&gt;An accurate inventory also plays a key role in identifying potential vulnerabilities and compliance issues. By knowing exactly what databases exist and where they reside, organizations can take proactive measures to secure sensitive data and adhere to regulatory standards.&lt;/P&gt;
&lt;P&gt;Additionally, maintaining an up-to-date inventory facilitates efficient troubleshooting and maintenance. When issues arise, having detailed insights into the configuration and status of each database significantly reduces resolution time.&lt;/P&gt;
&lt;P&gt;In short, keeping an accurate inventory of Azure databases supports operational efficiency, security, and compliance.&lt;/P&gt;
&lt;H1&gt;Who Does This Matter To&lt;/H1&gt;
&lt;P&gt;For roles such as product owners or operations managers, this task becomes even more critical. You may need to query the entire data estate to consolidate information for governance, reporting, or strategic planning. Without a centralized view, managing large-scale environments becomes challenging and prone to errors.&lt;/P&gt;
&lt;H1&gt;How Azure Resource Graph Explorer Helps&lt;/H1&gt;
&lt;P&gt;&lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/governance/resource-graph/overview" target="_blank" rel="noopener"&gt;Azure Resource Graph Explorer&lt;/A&gt; is a powerful tool designed to extend Azure Resource Management by enabling efficient and performant resource exploration across multiple subscriptions. It allows users to:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Query resources with complex filtering, grouping, and sorting by resource properties.&lt;/LI&gt;
&lt;LI&gt;Explore resources at scale across multiple subscriptions.&lt;/LI&gt;
&lt;LI&gt;Iterate queries based on governance requirements.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;This makes ARG an ideal solution for managing and governing large-scale environments where visibility and control are paramount.&lt;/P&gt;
&lt;P&gt;An Azure Resource Graph query like this one can help users to get an inventory of Azure SQL installations from data estates spanning across multiple subscriptions.&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;resources | where type =~ "microsoft.sqlvirtualmachine/sqlvirtualmachines" | project SubscriptionId = subscriptionId, ResourceGroup = resourceGroup, Name = name, type, Location = location, tags, elasticPoolId = "", ServiceType = "SQL VM", Edition=tostring(properties.sqlImageSku), Environment=tostring(tags.opEnvironment), PricingModel="vCores", ServiceTier=tostring(properties.sqlImageOffer), IsAHB = iff(tostring(properties.sqlServerLicenseType) =~ "AHUB", "Yes", iff(tostring(properties.sqlServerLicenseType) =~ "PAYG" and tostring(tolower(properties.sqlImageSku)) =~ "Developer", "Yes", "No")) | join kind=inner (resources | where ['type'] =~ 'microsoft.compute/virtualmachines' | project SubscriptionId = subscriptionId, ResourceGroup = resourceGroup, Name = name, Location = location, Num=tostring(properties.hardwareProfile.vmSize)) on Name, ResourceGroup, Location, SubscriptionId | project-away SubscriptionId1,ResourceGroup1,Name1,Location1 | union ( Resources | project SubscriptionId = subscriptionId, ResourceGroup = resourceGroup, Name = name, type, Location = location, tags, elasticPoolId = tolower(tostring(properties.elasticPoolId)), ServiceType = case(type =~ "microsoft.sql/managedinstances", "Azure SQL MI", type =~ "microsoft.sql/servers/databases", iff(tolower(properties.currentSku.tier)=~"datawarehouse", "Synapse", "Azure SQL DB"), type =~ "microsoft.sql/servers/elasticpools", "Azure SQL DB Elastic Pools", "Azure SQL VM"), Edition = case(type in~ ("microsoft.sql/managedinstances","microsoft.sql/servers/databases","microsoft.sql/servers/elasticpools"), iff(tolower(properties.currentSku.tier)=~"datawarehouse", "Dedicated Pools", "Azure SQL") , "SQL VM"), Environment=tostring(tags.opEnvironment), Num = tostring(case(type =~ "microsoft.sql/managedinstances", toint(sku.capacity), type =~ "microsoft.sql/servers/databases", toint(properties.currentSku.capacity), type =~ "microsoft.sql/servers/elasticpools", toint(properties.perDatabaseSettings.maxCapacity), 0)), PricingModel = case(type =~ "microsoft.sql/managedinstances", "vCores", type =~ "microsoft.sql/servers/databases", iff(tolower(properties.currentSku.tier)=~"datawarehouse", "DWU", iff((tolower(sku.tier) in ("basic","standard","premium")), "DTU", "vCores")), type =~ "microsoft.sql/servers/elasticpools", iff((tolower(sku.tier) in ("basic","standard","premium")), "DTU", "vCores"), "vCores"), ServiceTier = case(type =~ "microsoft.sql/managedinstances", sku.tier, tolower(properties.currentSku.tier)), MaxDatabaseSizeGB = iff(tostring(properties.currentSku.tier)=~"hyperscale", 1000,properties.maxSizeBytes/(1024 * 1024 * 1024)), IsAHB = case(properties.licenseType == 'BasePrice', "Yes", "No"), Is_Replica=iff(tostring(properties.secondaryType) in("Geo","Standby"), "Yes","No"), Is_Standby_Replica=iff(tostring(properties.secondaryType)=~ "Standby", "Yes","No") | where (type =~ 'Microsoft.Sql/servers/databases' or type =~ "microsoft.sql/managedinstances" or type =~ "microsoft.sql/servers/elasticpools") and ServiceTier !in~ ("system") | join kind=leftouter ( Resources | where type =~ 'microsoft.sql/servers/elasticpools' | project elasticPoolId = tolower(id), elasticPoolName = name, elasticPoolSize=tostring(properties.perDatabaseSettings.maxCapacity)) on elasticPoolId | project-away elasticPoolId1 ) | order by ['Name'] asc&lt;/LI-CODE&gt;
&lt;P&gt;This process can be further bolstered by &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/governance/resource-graph/tutorials/logic-app-calling-arg" target="_blank" rel="noopener"&gt;automating &lt;/A&gt;the running of Azure Resource Graph queries and even acting on the results.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Let us know if any of these resonate with you or if have more suggestions!&lt;/P&gt;
&lt;H1&gt;Feedback&lt;/H1&gt;
&lt;P&gt;If you have feedback or suggestions for improving this data migration asset, please comment here or contact the Azure Databases SQL Customer Success Engineering Team (&lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;datasqlninja@microsoft.com&lt;/A&gt;) directly. Thanks for your support!&lt;/P&gt;
&lt;P&gt;Note: For additional information about migrating various source databases to Azure, see the&amp;nbsp;&lt;A href="https://datamigration.microsoft.com/" target="_blank" rel="noopener"&gt;Azure Database Migration Guide&lt;/A&gt;.&lt;/P&gt;</description>
      <pubDate>Fri, 29 May 2026 16:03:38 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/unlocking-the-power-of-azure-resource-graph-explorer-for/ba-p/4392345</guid>
      <dc:creator>akumaranchath</dc:creator>
      <dc:date>2026-05-29T16:03:38Z</dc:date>
    </item>
    <item>
      <title>The Odd Couple - Latches and KEEP_NULLS</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/the-odd-couple-latches-and-keep-nulls/ba-p/4516048</link>
      <description>&lt;H2&gt;Introduction&lt;/H2&gt;
&lt;P&gt;Bulk loading is one of the most heavily optimised data paths in SQL Server. When it works well, hundreds of concurrent sessions can push millions of rows per minute into large tables with minimal CPU overhead. But when it doesn't, the symptoms can be baffling: CPU sits almost idle while every session waits, and throughput collapses by orders of magnitude.&lt;/P&gt;
&lt;P&gt;We recently worked with a customer running a large-scale data processing platform on Azure SQL Hyperscale Database. The workload used Spark to bulk-insert data from hundreds of concurrent sessions into multi-billion-row tables, each using &lt;CODE&gt;SEQUENCE&lt;/CODE&gt; objects to generate surrogate keys. After the team switched from row-by-row inserts to parallel bulk loading - a change that should have been a clear win - job durations went from minutes to over an hour. CPU utilisation sat at 2–5% while latch waits consumed over 98% of elapsed time.&lt;/P&gt;
&lt;P&gt;The root cause turned out to be a surprising interaction between two seemingly unrelated SQL Server features: &lt;STRONG&gt;SEQUENCE object latch modes&lt;/STRONG&gt; and the &lt;STRONG&gt;KEEP_NULLS bulk insert option&lt;/STRONG&gt;. Each is individually well-documented, but their combined effect under high-concurrency bulk insert workloads produced catastrophic serialisation that was not obvious from the documentation alone.&lt;/P&gt;
&lt;P&gt;This post distils the lessons learnt from that engagement into a reusable guide. We explain the mechanism, demonstrate the issue with reproducible tests on SQL Server 2025, and provide four independent fixes - the simplest of which is a single client-side configuration change that requires no schema modifications, no table rebuilds, and no downtime.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;STRONG&gt;Note:&lt;/STRONG&gt; Results may vary depending on hardware, SQL Server version, and workload patterns. All tests in this post were run on SQL Server 2025 (CU3). We recommend validating in your own dev/test environment.&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;HR /&gt;
&lt;H2&gt;Background: The Ingredients&lt;/H2&gt;
&lt;P&gt;The workload used a common pattern for generating surrogate keys during bulk inserts:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;&lt;STRONG&gt;SEQUENCE objects&lt;/STRONG&gt; -&amp;nbsp;&lt;CODE&gt;CREATE SEQUENCE ... AS NUMERIC(28,0)&lt;/CODE&gt; - provided unique IDs&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;DEFAULT constraints&lt;/STRONG&gt; -&amp;nbsp;&lt;CODE&gt;ALTER TABLE ... ADD CONSTRAINT ... DEFAULT (NEXT VALUE FOR dbo.MySequence)&lt;/CODE&gt; - auto-populated IDs for rows that didn't supply them&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;INSERT BULK&lt;/STRONG&gt; - Spark's JDBC &lt;CODE&gt;SQLServerBulkCopy&lt;/CODE&gt; loaded data in parallel from ~350 concurrent sessions&lt;/LI&gt;
&lt;LI&gt;The application called &lt;CODE&gt;sp_sequence_get_range&lt;/CODE&gt; to &lt;STRONG&gt;pre-allocate&lt;/STRONG&gt; ID ranges and supplied IDs with every row - the DEFAULT was there as a safety net, not the primary path&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;Each component is sensible on its own. The problem emerges from their interaction.&lt;/P&gt;
&lt;HR /&gt;
&lt;H2&gt;The Problem: Two Layers of Serialisation&lt;/H2&gt;
&lt;H3&gt;Layer 1: INSERT BULK Fires the DEFAULT - Even When You Supply Values&lt;/H3&gt;
&lt;P&gt;When &lt;CODE&gt;INSERT BULK&lt;/CODE&gt; (the TDS protocol used by BCP, &lt;CODE&gt;BULK INSERT&lt;/CODE&gt;, and JDBC &lt;CODE&gt;SQLServerBulkCopy&lt;/CODE&gt;) runs &lt;STRONG&gt;without&lt;/STRONG&gt; the &lt;CODE&gt;KEEP_NULLS&lt;/CODE&gt; option, the engine sets an internal flag (&lt;CODE&gt;HNT_KEEPDEFAULT&lt;/CODE&gt;) that wraps every DEFAULT-constrained column as:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;ISNULL(source_value, default_expression)&lt;/LI-CODE&gt;
&lt;P&gt;The &lt;CODE&gt;default_expression&lt;/CODE&gt; - in this case &lt;CODE&gt;NEXT VALUE FOR&lt;/CODE&gt; - is evaluated by a &lt;STRONG&gt;Sequence Project&lt;/STRONG&gt; iterator that runs &lt;STRONG&gt;unconditionally per row&lt;/STRONG&gt;, before the &lt;CODE&gt;ISNULL&lt;/CODE&gt; decides whether to use it. Even though every row in our workload supplied a valid non-NULL ID, the sequence latch was still acquired, the counter was incremented, and the generated value was discarded.&lt;/P&gt;
&lt;P&gt;This is fundamentally different from a regular &lt;CODE&gt;INSERT ... SELECT&lt;/CODE&gt; statement, where &lt;CODE&gt;NEXT VALUE FOR&lt;/CODE&gt; only fires if the column is actually omitted from the column list.&lt;/P&gt;
&lt;H3&gt;Layer 2: NUMERIC Sequences Take Exclusive Latches&lt;/H3&gt;
&lt;P&gt;SQL Server's sequence generator uses different synchronisation strategies depending on the sequence data type:&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="border-width: 1px;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Sequence Type&lt;/th&gt;&lt;th&gt;Latch Mode&lt;/th&gt;&lt;th&gt;Concurrency&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;CODE&gt;NUMERIC&lt;/CODE&gt; / &lt;CODE&gt;DECIMAL&lt;/CODE&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Exclusive (EX)&lt;/STRONG&gt; per row&lt;/td&gt;&lt;td&gt;Fully serialised - one thread at a time&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;CODE&gt;BIGINT&lt;/CODE&gt; / &lt;CODE&gt;INT&lt;/CODE&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Shared (SH)&lt;/STRONG&gt; + atomic CPU increment&lt;/td&gt;&lt;td&gt;Concurrent - multiple threads progress&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 33.33%" /&gt;&lt;col style="width: 33.33%" /&gt;&lt;col style="width: 33.33%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;The difference is not in the sequence &lt;EM&gt;cache&lt;/EM&gt; (which only reduces disk I/O) but in the in-memory latch that protects the counter. With a &lt;CODE&gt;NUMERIC(28,0)&lt;/CODE&gt; sequence, every single row - across all 350 concurrent sessions - must wait in line for one exclusive latch.&lt;/P&gt;
&lt;H3&gt;The Combined Effect&lt;/H3&gt;
&lt;P&gt;Combining both layers produces catastrophic serialisation:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;350 sessions × 50,000 rows each = &lt;STRONG&gt;17.5 million unnecessary exclusive latch acquisitions&lt;/STRONG&gt; per bulk load cycle&lt;/LI&gt;
&lt;LI&gt;Each acquisition blocks all other sessions&lt;/LI&gt;
&lt;LI&gt;CPU sits idle while threads wait in the latch queue&lt;/LI&gt;
&lt;LI&gt;With &lt;CODE&gt;NUMERIC(28,0)&lt;/CODE&gt; + no &lt;CODE&gt;KEEP_NULLS&lt;/CODE&gt;, the workload was 98% wait time and 2% CPU time&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The diagnostic signature is unmistakable: &lt;CODE&gt;LATCH_EX&lt;/CODE&gt; dominant in wait stats, low CPU utilisation, and all waiting sessions blocked on &lt;CODE&gt;METADATA_SEQUENCE_GENERATOR&lt;/CODE&gt;.&lt;/P&gt;
&lt;HR /&gt;
&lt;H2&gt;Proving It: The KEEP_NULLS Experiment&lt;/H2&gt;
&lt;P&gt;The simplest proof is to BCP 10,000 rows - all with application-supplied IDs - and check whether the sequence counter advances.&lt;/P&gt;
&lt;H3&gt;Setup&lt;/H3&gt;
&lt;LI-CODE lang="sql"&gt;CREATE SEQUENCE dbo.Seq_Orders
    AS NUMERIC(28, 0)
    START WITH 1 INCREMENT BY 1 CACHE 50000000;
GO

CREATE TABLE dbo.Orders
(
    OrderID     NUMERIC(28, 0)  NOT NULL
        CONSTRAINT DF_Orders_ID DEFAULT (NEXT VALUE FOR dbo.Seq_Orders),
    CustomerID  INT             NOT NULL,
    OrderDate   DATE            NOT NULL,
    Amount      DECIMAL(10, 2)  NOT NULL,
    Filler      CHAR(80)        NOT NULL DEFAULT ('x'),
    CONSTRAINT PK_Orders PRIMARY KEY CLUSTERED (OrderID)
);
GO&lt;/LI-CODE&gt;
&lt;P&gt;Generate a BCP data file with IDs 1-10,000 pre-assigned by the application.&lt;/P&gt;
&lt;H3&gt;Test 1: BCP Without KEEP_NULLS (Default Behaviour)&lt;/H3&gt;
&lt;LI-CODE lang="bash"&gt;bcp MyDB.dbo.Orders in orders.dat -c -t"," -S localhost\sql25 -T&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;Result:&lt;/STRONG&gt;&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="border-width: 1px;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Rows stored&lt;/td&gt;&lt;td&gt;10,000&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Stored OrderIDs&lt;/td&gt;&lt;td&gt;1-10,000 (app-supplied - correct)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sequence current_value&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;20,000&lt;/STRONG&gt; (advanced from 1)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 50.00%" /&gt;&lt;col style="width: 50.00%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;The sequence advanced by at least 10,000 values, even though the stored IDs are entirely from the application. The &lt;CODE&gt;NEXT VALUE FOR&lt;/CODE&gt; default fired per-row, consumed sequence values, and discarded them all.&lt;/P&gt;
&lt;H3&gt;Test 2: BCP With KEEP_NULLS&lt;/H3&gt;
&lt;LI-CODE lang="bash"&gt;bcp MyDB.dbo.Orders in orders.dat -c -t"," -S localhost\sql25 -T -k&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;Result:&lt;/STRONG&gt;&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="border-width: 1px;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Rows stored&lt;/td&gt;&lt;td&gt;10,000&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Stored OrderIDs&lt;/td&gt;&lt;td&gt;1-10,000 (app-supplied - correct)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sequence current_value&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;1&lt;/STRONG&gt; (unchanged)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 50.00%" /&gt;&lt;col style="width: 50.00%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;With &lt;CODE&gt;-k&lt;/CODE&gt; (KEEP_NULLS), the sequence did not advance at all. The &lt;CODE&gt;ISNULL&lt;/CODE&gt; wrapper was not applied, the Sequence Project iterator was not added to the plan, and all 10,000 latch acquisitions were eliminated.&lt;/P&gt;
&lt;H3&gt;Test 3: Table Without DEFAULT (Control)&lt;/H3&gt;
&lt;LI-CODE lang="bash"&gt;bcp MyDB.dbo.Orders_NoDefault in orders.dat -c -t"," -S localhost\sql25 -T&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;Result:&lt;/STRONG&gt; Sequence unchanged at 1. No DEFAULT means no Sequence Project, regardless of &lt;CODE&gt;KEEP_NULLS&lt;/CODE&gt;.&lt;/P&gt;
&lt;H3&gt;Summary&lt;/H3&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="border-width: 1px;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Scenario&lt;/th&gt;&lt;th&gt;DEFAULT Exists&lt;/th&gt;&lt;th&gt;KEEP_NULLS&lt;/th&gt;&lt;th&gt;Sequence Advances?&lt;/th&gt;&lt;th&gt;Latch Per Row?&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;BCP (default behaviour)&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Yes - wasted&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Yes - serialised&lt;/STRONG&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;BCP with &lt;CODE&gt;-k&lt;/CODE&gt;&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Yes&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;BCP, no DEFAULT&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Regular INSERT...SELECT&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;No (column supplied)&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;The fourth row is important: a standard &lt;CODE&gt;INSERT ... SELECT&lt;/CODE&gt; that includes the &lt;CODE&gt;OrderID&lt;/CODE&gt; column does &lt;STRONG&gt;not&lt;/STRONG&gt; fire the DEFAULT. This behaviour is specific to the INSERT BULK protocol.&lt;/P&gt;
&lt;HR /&gt;
&lt;H2&gt;Proving It: NUMERIC vs BIGINT Latch Contention&lt;/H2&gt;
&lt;P&gt;Even when the DEFAULT must fire (for example, when the application genuinely relies on it), the sequence data type determines the severity of the contention.&lt;/P&gt;
&lt;P&gt;We tested three configurations at 32 concurrent sessions, each inserting 20,000 rows via &lt;CODE&gt;INSERT ... DEFAULT VALUES&lt;/CODE&gt;:&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="border-width: 1px;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;NUMERIC(28,0)&lt;/th&gt;&lt;th&gt;BIGINT→NUMERIC&lt;/th&gt;&lt;th&gt;BIGINT&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Latch wait requests&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;137,321&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;7&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;23&lt;/STRONG&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Latch total wait (ms)&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;37,661&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;210&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;384&lt;/STRONG&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Latch max single wait (ms)&lt;/td&gt;&lt;td&gt;53&lt;/td&gt;&lt;td&gt;54&lt;/td&gt;&lt;td&gt;18&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 25.00%" /&gt;&lt;col style="width: 25.00%" /&gt;&lt;col style="width: 25.00%" /&gt;&lt;col style="width: 25.00%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;&lt;EM&gt;(32 sessions × 20,000 rows, SQL Server 2025, localhost)&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;The "BIGINT→NUMERIC" column uses a &lt;CODE&gt;BIGINT&lt;/CODE&gt; sequence feeding a &lt;CODE&gt;NUMERIC(28,0)&lt;/CODE&gt; column. This proves that &lt;STRONG&gt;the latch mode follows the SEQUENCE type, not the column type&lt;/STRONG&gt;. Changing the sequence from &lt;CODE&gt;NUMERIC(28,0)&lt;/CODE&gt; to &lt;CODE&gt;BIGINT&lt;/CODE&gt; is a metadata-only operation (&lt;CODE&gt;DROP SEQUENCE&lt;/CODE&gt; + &lt;CODE&gt;CREATE SEQUENCE AS BIGINT&lt;/CODE&gt;) that delivers a &lt;STRONG&gt;99.4% reduction in latch waits&lt;/STRONG&gt; without requiring any table rebuild.&lt;/P&gt;
&lt;H3&gt;IDENTITY Columns Have the Same Issue&lt;/H3&gt;
&lt;P&gt;The NUMERIC-vs-BIGINT latch behaviour is not limited to SEQUENCE objects - it applies equally to &lt;STRONG&gt;IDENTITY columns&lt;/STRONG&gt;. An &lt;CODE&gt;IDENTITY&lt;/CODE&gt; column defined as &lt;CODE&gt;NUMERIC(28,0)&lt;/CODE&gt; or &lt;CODE&gt;DECIMAL(28,0)&lt;/CODE&gt; takes an exclusive latch on the identity counter for every inserted row, while &lt;CODE&gt;BIGINT IDENTITY&lt;/CODE&gt; uses a shared latch with atomic increments. The identity counter latch class (&lt;CODE&gt;IDENTITYVALUE_GENERATOR&lt;/CODE&gt;) follows the same exclusive-vs-shared pattern as &lt;CODE&gt;METADATA_SEQUENCE_GENERATOR&lt;/CODE&gt;.&lt;/P&gt;
&lt;P&gt;For tables that use &lt;CODE&gt;NUMERIC IDENTITY&lt;/CODE&gt; under high-concurrency insert workloads, changing the column type to &lt;CODE&gt;BIGINT&lt;/CODE&gt; eliminates the serialisation. Unlike sequences, changing an identity column's data type requires &lt;CODE&gt;ALTER TABLE ... ALTER COLUMN&lt;/CODE&gt;, which triggers a table rebuild for large tables. Plan this as a maintenance window operation for billion-row tables.&lt;/P&gt;
&lt;HR /&gt;
&lt;H2&gt;The Fixes&lt;/H2&gt;
&lt;P&gt;Four independent fixes address different layers of the problem. Any one of them helps; the combination eliminates the issue entirely.&lt;/P&gt;
&lt;H3&gt;Fix 1: Enable KEEP_NULLS in the Bulk Copy Client&lt;/H3&gt;
&lt;P&gt;This is the single highest-impact fix. When the application supplies all column values, enabling &lt;CODE&gt;KEEP_NULLS&lt;/CODE&gt; prevents the engine from evaluating any DEFAULT constraint during &lt;CODE&gt;INSERT BULK&lt;/CODE&gt;.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Spark JDBC:&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang="java"&gt;.option("bulkCopyForBatchInsertKeepNulls", "true")&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;JDBC directly:&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang="java"&gt;SQLServerBulkCopyOptions opts = new SQLServerBulkCopyOptions();
opts.setKeepNulls(true);&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;BCP:&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang="bash"&gt;bcp ... -k&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;BULK INSERT:&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;BULK INSERT dbo.Orders FROM 'data.dat' WITH (KEEPNULLS);&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;Impact:&lt;/STRONG&gt; Eliminates 100% of unnecessary sequence latch acquisitions during bulk insert. If the application already supplies the ID column, this fix has zero behavioural side effects.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;STRONG&gt;Warning:&lt;/STRONG&gt; &lt;CODE&gt;KEEP_NULLS&lt;/CODE&gt; applies to &lt;STRONG&gt;all&lt;/STRONG&gt; columns, not just the sequence column. When enabled, any column where the source data contains NULL will store NULL rather than the column's DEFAULT value. If your table has other DEFAULT constraints that the application relies on - for example, &lt;CODE&gt;CreatedDate DATE DEFAULT GETDATE()&lt;/CODE&gt; or &lt;CODE&gt;Status VARCHAR(20) DEFAULT 'Pending'&lt;/CODE&gt; - those defaults will &lt;STRONG&gt;not&lt;/STRONG&gt; fire for NULL values when &lt;CODE&gt;KEEP_NULLS&lt;/CODE&gt; is active. Before enabling this option, verify that the application supplies values for every column, or that NULL is acceptable for columns the application omits. If some columns genuinely need their defaults, Fix 2 (dropping only the sequence DEFAULT) is the safer server-side alternative.&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;H3&gt;Fix 2: Drop the DEFAULT Constraint&lt;/H3&gt;
&lt;P&gt;If every insert path supplies the ID column, the DEFAULT constraint serves no purpose. Dropping it is an instant metadata-only DDL operation:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;ALTER TABLE dbo.Orders DROP CONSTRAINT DF_Orders_ID;&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;Impact:&lt;/STRONG&gt; Same as Fix 1, but enforced server-side. No client configuration required. Consider this if multiple applications insert into the table and you cannot guarantee all of them will set &lt;CODE&gt;KEEP_NULLS&lt;/CODE&gt;.&lt;/P&gt;
&lt;H3&gt;Fix 3: Change NUMERIC Sequences to BIGINT&lt;/H3&gt;
&lt;P&gt;For scenarios where the DEFAULT must fire - for example, ad-hoc inserts, SSMS, or stored procedures that omit the ID column - changing the sequence type from &lt;CODE&gt;NUMERIC(28,0)&lt;/CODE&gt; to &lt;CODE&gt;BIGINT&lt;/CODE&gt; switches from exclusive to shared latching:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;-- Wrap in a transaction so the workload never sees a moment without the DEFAULT.
-- Without the transaction, inserts that rely on the DEFAULT would fail between
-- the DROP CONSTRAINT and the ADD CONSTRAINT.

BEGIN TRANSACTION;

    -- Step 1: Record current value
    DECLARE @current BIGINT;
    SELECT @current = CAST(current_value AS BIGINT)
    FROM sys.sequences WHERE name = 'Seq_Orders';

    -- Step 2: Drop DEFAULT, drop sequence, recreate as BIGINT, re-add DEFAULT
    ALTER TABLE dbo.Orders DROP CONSTRAINT DF_Orders_ID;
    DROP SEQUENCE dbo.Seq_Orders;

    CREATE SEQUENCE dbo.Seq_Orders
        AS BIGINT
        START WITH @current INCREMENT BY 1 CACHE 50000000;

    ALTER TABLE dbo.Orders
        ADD CONSTRAINT DF_Orders_ID DEFAULT (NEXT VALUE FOR dbo.Seq_Orders) FOR OrderID;

COMMIT TRANSACTION;&lt;/LI-CODE&gt;
&lt;P&gt;Wrapping the four statements in a single transaction ensures that concurrent inserts never see a state where the DEFAULT constraint is missing. Without the transaction, any insert that relies on the DEFAULT would fail with an error between the &lt;CODE&gt;DROP CONSTRAINT&lt;/CODE&gt; and the &lt;CODE&gt;ADD CONSTRAINT&lt;/CODE&gt;. All four operations are metadata-only and the transaction completes in under a second. The column type (&lt;CODE&gt;NUMERIC(28,0)&lt;/CODE&gt;) does not need to change - SQL Server implicitly converts &lt;CODE&gt;BIGINT&lt;/CODE&gt; to &lt;CODE&gt;NUMERIC&lt;/CODE&gt; on insert.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Impact:&lt;/STRONG&gt; 99.4% reduction in latch wait time when the DEFAULT does fire. &lt;CODE&gt;BIGINT&lt;/CODE&gt; covers values up to 9.2 × 10^18, which is sufficient for virtually all surrogate key scenarios.&lt;/P&gt;
&lt;H3&gt;Fix 4: Right-Size Sequence Cache&lt;/H3&gt;
&lt;P&gt;The sequence &lt;CODE&gt;CACHE&lt;/CODE&gt; setting controls how many values are pre-allocated in memory before the next disk write to the system catalog. The default cache is 50, which is far too small for high-volume bulk inserts:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;ALTER SEQUENCE dbo.Seq_Orders CACHE 50000000;&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;Impact:&lt;/STRONG&gt; Reduces disk I/O during sequence value generation. This does &lt;STRONG&gt;not&lt;/STRONG&gt; change the latch mode - it only reduces how often the latch must also perform a catalog write. Fix 3 addresses the latch mode; this fix addresses the I/O.&lt;/P&gt;
&lt;HR /&gt;
&lt;H2&gt;When Each Fix Applies&lt;/H2&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="border-width: 1px;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Scenario&lt;/th&gt;&lt;th&gt;Fix 1 (KEEP_NULLS)&lt;/th&gt;&lt;th&gt;Fix 2 (Drop DEFAULT)&lt;/th&gt;&lt;th&gt;Fix 3 (BIGINT)&lt;/th&gt;&lt;th&gt;Fix 4 (Cache)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;App supplies IDs via bulk insert&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Primary fix&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;Backup/server-side&lt;/td&gt;&lt;td&gt;Reduces residual contention&lt;/td&gt;&lt;td&gt;Helpful&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;App relies on DEFAULT for IDs&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Primary fix&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Required&lt;/STRONG&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Mixed: some paths supply, some don't&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;For bulk paths&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;For DEFAULT paths&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Required&lt;/STRONG&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SEQUENCE used in sp_sequence_get_range only&lt;/td&gt;&lt;td&gt;Not needed&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Drop it&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;Helpful&lt;/td&gt;&lt;td&gt;Helpful&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;HR /&gt;
&lt;H2&gt;How to Detect This Pattern&lt;/H2&gt;
&lt;H3&gt;Wait Stats&lt;/H3&gt;
&lt;P&gt;Query &lt;CODE&gt;sys.dm_os_wait_stats&lt;/CODE&gt; or &lt;CODE&gt;sys.dm_os_latch_stats&lt;/CODE&gt; for the signature:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;-- Check for sequence latch contention
SELECT
    latch_class,
    waiting_requests_count,
    wait_time_ms,
    max_wait_time_ms
FROM sys.dm_os_latch_stats
WHERE latch_class = 'METADATA_SEQUENCE_GENERATOR'
  AND waiting_requests_count &amp;gt; 0;&lt;/LI-CODE&gt;
&lt;P&gt;If &lt;CODE&gt;METADATA_SEQUENCE_GENERATOR&lt;/CODE&gt; appears with high &lt;CODE&gt;waiting_requests_count&lt;/CODE&gt; alongside &lt;CODE&gt;LATCH_EX&lt;/CODE&gt; dominating &lt;CODE&gt;sys.dm_os_wait_stats&lt;/CODE&gt;, this pattern is likely active.&lt;/P&gt;
&lt;H3&gt;Sequence Consumption Rate&lt;/H3&gt;
&lt;P&gt;Compare the sequence advance rate against actual row insertions:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;-- If sequence advances much faster than rows are inserted,
-- DEFAULT is firing unnecessarily during bulk inserts
SELECT
    s.name AS sequence_name,
    CAST(s.current_value AS BIGINT) AS current_value,
    s.data_type,
    s.cache_size
FROM sys.sequences s
WHERE s.data_type IN ('numeric', 'decimal');&lt;/LI-CODE&gt;
&lt;P&gt;Sequences typed as &lt;CODE&gt;NUMERIC&lt;/CODE&gt; or &lt;CODE&gt;DECIMAL&lt;/CODE&gt; under high-concurrency workloads are the risk profile.&lt;/P&gt;
&lt;H3&gt;Execution Plans&lt;/H3&gt;
&lt;P&gt;Look for a &lt;STRONG&gt;Sequence Project&lt;/STRONG&gt; iterator in the execution plan of &lt;CODE&gt;INSERT BULK&lt;/CODE&gt; operations. If the application supplies the ID column, a Sequence Project indicates the DEFAULT is being evaluated unnecessarily.&lt;/P&gt;
&lt;HR /&gt;
&lt;H2&gt;Test Scripts&lt;/H2&gt;
&lt;P&gt;The scripts used for all benchmarks in this post are included below. Each can be run on any SQL Server 2019+ instance.&lt;/P&gt;
&lt;H3&gt;Setup: Database, Sequences, and Tables&lt;/H3&gt;
&lt;LI-CODE lang="sql"&gt;USE master;
GO

IF DB_ID('LatchKeepNullsDemo') IS NOT NULL
BEGIN
    ALTER DATABASE LatchKeepNullsDemo SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
    DROP DATABASE LatchKeepNullsDemo;
END
GO

CREATE DATABASE LatchKeepNullsDemo;
GO
USE LatchKeepNullsDemo;
GO

-- Table WITH NEXT VALUE FOR DEFAULT (demonstrates the problem)
CREATE SEQUENCE dbo.Seq_Orders_Numeric
    AS NUMERIC(28, 0)
    START WITH 1 INCREMENT BY 1 CACHE 50000000;
GO

CREATE TABLE dbo.Orders_WithDefault
(
    OrderID     NUMERIC(28, 0)  NOT NULL
        CONSTRAINT DF_Orders_ID DEFAULT (NEXT VALUE FOR dbo.Seq_Orders_Numeric),
    CustomerID  INT             NOT NULL,
    OrderDate   DATE            NOT NULL,
    Amount      DECIMAL(10, 2)  NOT NULL,
    Filler      CHAR(80)        NOT NULL DEFAULT ('x'),
    CONSTRAINT PK_Orders_WithDefault PRIMARY KEY CLUSTERED (OrderID)
);
GO

-- Table WITHOUT DEFAULT (control)
CREATE TABLE dbo.Orders_NoDefault
(
    OrderID     NUMERIC(28, 0)  NOT NULL,
    CustomerID  INT             NOT NULL,
    OrderDate   DATE            NOT NULL,
    Amount      DECIMAL(10, 2)  NOT NULL,
    Filler      CHAR(80)        NOT NULL DEFAULT ('x'),
    CONSTRAINT PK_Orders_NoDefault PRIMARY KEY CLUSTERED (OrderID)
);
GO

-- NUMERIC(28,0) sequence for latch test
CREATE SEQUENCE dbo.Seq_Latch_Numeric
    AS NUMERIC(28, 0) START WITH 1 INCREMENT BY 1 CACHE 50000000;
GO

CREATE TABLE dbo.Inserts_Numeric
(
    ID      NUMERIC(28, 0)  NOT NULL
        DEFAULT (NEXT VALUE FOR dbo.Seq_Latch_Numeric),
    Filler  CHAR(100)       NOT NULL DEFAULT ('x'),
    CONSTRAINT PK_Inserts_Numeric PRIMARY KEY CLUSTERED (ID)
);
GO

-- BIGINT sequence for latch test
CREATE SEQUENCE dbo.Seq_Latch_BigInt
    AS BIGINT START WITH 1 INCREMENT BY 1 CACHE 50000000;
GO

CREATE TABLE dbo.Inserts_BigInt
(
    ID      BIGINT          NOT NULL
        DEFAULT (NEXT VALUE FOR dbo.Seq_Latch_BigInt),
    Filler  CHAR(100)       NOT NULL DEFAULT ('x'),
    CONSTRAINT PK_Inserts_BigInt PRIMARY KEY CLUSTERED (ID)
);
GO

-- BIGINT sequence + NUMERIC column (proves latch follows sequence type)
CREATE SEQUENCE dbo.Seq_Latch_Mixed
    AS BIGINT START WITH 1 INCREMENT BY 1 CACHE 50000000;
GO

CREATE TABLE dbo.Inserts_Mixed
(
    ID      NUMERIC(28, 0)  NOT NULL
        DEFAULT (NEXT VALUE FOR dbo.Seq_Latch_Mixed),
    Filler  CHAR(100)       NOT NULL DEFAULT ('x'),
    CONSTRAINT PK_Inserts_Mixed PRIMARY KEY CLUSTERED (ID)
);
GO
&lt;/LI-CODE&gt;&lt;LI-CODE lang="sql"&gt;-- Helper: Generate BCP test data with app-supplied IDs
CREATE OR ALTER PROCEDURE dbo.GenerateBCPData @RowCount INT = 10000
AS
BEGIN
    SET NOCOUNT ON;
    ;WITH Nums AS (
        SELECT TOP(@RowCount) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n
        FROM sys.all_objects a CROSS JOIN sys.all_objects b
    )
    SELECT
        CAST(n AS NUMERIC(28,0))                                        AS OrderID,
        ABS(CHECKSUM(NEWID())) % 10000 + 1                              AS CustomerID,
        CAST(DATEADD(DAY, -(n % 365), '2026-03-31') AS DATE)            AS OrderDate,
        CAST(ABS(CHECKSUM(NEWID())) % 100000 / 100.0 AS DECIMAL(10,2))  AS Amount,
        REPLICATE('x', 80)                                              AS Filler
    FROM Nums;
END
GO&lt;/LI-CODE&gt;
&lt;H3&gt;Test: BCP KEEP_NULLS Comparison&lt;/H3&gt;
&lt;LI-CODE lang="bash"&gt;# Step 1: Export test data
bcp "EXEC LatchKeepNullsDemo.dbo.GenerateBCPData @RowCount=10000" \
    queryout orders.dat -c -t"," -S localhost\sql25 -T

# Step 2: Record sequence starting value
sqlcmd -S localhost\sql25 -d LatchKeepNullsDemo -Q \
    "SELECT name, CAST(current_value AS VARCHAR(30)) AS val
     FROM sys.sequences WHERE name = 'Seq_Orders_Numeric';"

# Step 3a: BCP WITHOUT KEEP_NULLS (DEFAULT fires per-row)
bcp LatchKeepNullsDemo.dbo.Orders_WithDefault in orders.dat \
    -c -t"," -S localhost\sql25 -T

# Check: sequence should have advanced (values wasted)
sqlcmd -S localhost\sql25 -d LatchKeepNullsDemo -Q \
    "SELECT name, CAST(current_value AS VARCHAR(30)) AS val
     FROM sys.sequences WHERE name = 'Seq_Orders_Numeric';"

# Step 3b: Reset, then BCP WITH KEEP_NULLS (DEFAULT suppressed)
sqlcmd -S localhost\sql25 -d LatchKeepNullsDemo -Q \
    "TRUNCATE TABLE dbo.Orders_WithDefault;
     ALTER SEQUENCE dbo.Seq_Orders_Numeric RESTART WITH 1;"

bcp LatchKeepNullsDemo.dbo.Orders_WithDefault in orders.dat \
    -c -t"," -S localhost\sql25 -T -k

# Check: sequence should NOT have advanced
sqlcmd -S localhost\sql25 -d LatchKeepNullsDemo -Q \
    "SELECT name, CAST(current_value AS VARCHAR(30)) AS val
     FROM sys.sequences WHERE name = 'Seq_Orders_Numeric';"&lt;/LI-CODE&gt;
&lt;H3&gt;Test: Concurrent Latch Comparison (PowerShell)&lt;/H3&gt;
&lt;LI-CODE lang="powershell"&gt;# Compare NUMERIC vs BIGINT sequence latch contention
# under N concurrent sessions, each inserting via INSERT...DEFAULT VALUES

param(
    [int]$Sessions = 16,
    [int]$RowsPerSession = 10000,
    [string]$Server = "localhost\sql25"
)

$db = "LatchKeepNullsDemo"

function Invoke-ConcurrentInserts {
    param([string]$TableName, [string]$Label)

    sqlcmd -S $Server -E -d $db -Q "TRUNCATE TABLE dbo.$TableName;" -b 2&amp;gt;&amp;amp;1 | Out-Null
    sqlcmd -S $Server -E -d $db `
        -Q "DBCC SQLPERF('sys.dm_os_latch_stats', CLEAR) WITH NO_INFOMSGS;" `
        -b 2&amp;gt;&amp;amp;1 | Out-Null

    $sql = "SET NOCOUNT ON; DECLARE @i INT = 0; " +
           "WHILE @i &amp;lt; $RowsPerSession BEGIN " +
           "INSERT INTO dbo.$TableName DEFAULT VALUES; SET @i += 1; END"

    $sw = [System.Diagnostics.Stopwatch]::StartNew()
    $jobs = @()
    for ($s = 0; $s -lt $Sessions; $s++) {
        $jobs += Start-Job -ScriptBlock {
            param($srv, $database, $query)
            sqlcmd -S $srv -E -d $database -Q $query -b 2&amp;gt;&amp;amp;1 | Out-Null
        } -ArgumentList $Server, $db, $sql
    }
    $jobs | Wait-Job | Out-Null
    $sw.Stop()
    $jobs | Remove-Job -Force

    # Capture latch stats
    $raw = sqlcmd -S $Server -E -d $db `
        -Q "SELECT latch_class, waiting_requests_count, wait_time_ms,
            max_wait_time_ms FROM sys.dm_os_latch_stats
            WHERE latch_class = 'METADATA_SEQUENCE_GENERATOR';" `
        -W -s "," -h -1 2&amp;gt;&amp;amp;1
    $line = $raw | Where-Object { $_ -match 'METADATA' } | Select-Object -First 1

    $requests = 0; $waitMs = 0; $maxMs = 0
    if ($line -match 'METADATA_SEQUENCE_GENERATOR\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)') {
        $requests = [long]$Matches[1]; $waitMs = [long]$Matches[2]; $maxMs = [long]$Matches[3]
    }

    Write-Host ("  {0,-20} Elapsed: {1,6} ms | Latch waits: {2,8} | Wait: {3,6} ms" `
        -f $Label, $sw.ElapsedMilliseconds, $requests, $waitMs)

    return @{ Label = $Label; Elapsed = $sw.ElapsedMilliseconds;
              Requests = $requests; WaitMs = $waitMs; MaxMs = $maxMs }
}

Write-Host "Sessions: $Sessions | Rows/session: $RowsPerSession"

$rNum = Invoke-ConcurrentInserts -TableName "Inserts_Numeric" -Label "NUMERIC(28,0)"
$rMix = Invoke-ConcurrentInserts -TableName "Inserts_Mixed"   -Label "BIGINT-&amp;gt;NUMERIC"
$rBig = Invoke-ConcurrentInserts -TableName "Inserts_BigInt"  -Label "BIGINT"

if ($rNum.WaitMs -gt 0) {
    $mixPct = [math]::Round((1 - $rMix.WaitMs / $rNum.WaitMs) * 100, 1)
    $bigPct = [math]::Round((1 - $rBig.WaitMs / $rNum.WaitMs) * 100, 1)
    Write-Host "`nBIGINT-&amp;gt;NUMERIC latch reduction: $mixPct%"
    Write-Host "BIGINT latch reduction: $bigPct%"
}&lt;/LI-CODE&gt;&lt;HR /&gt;
&lt;H2&gt;Key Takeaways&lt;/H2&gt;
&lt;OL&gt;
&lt;LI&gt;&lt;STRONG&gt;&lt;CODE&gt;INSERT BULK&lt;/CODE&gt; without &lt;CODE&gt;KEEP_NULLS&lt;/CODE&gt; evaluates DEFAULT expressions per-row&lt;/STRONG&gt; - even when the source data supplies non-NULL values. For &lt;CODE&gt;NEXT VALUE FOR&lt;/CODE&gt; defaults, this means one latch acquisition per row, per session, regardless of whether the value is used. This is &lt;A href="https://learn.microsoft.com/en-us/sql/t-sql/statements/bulk-insert-transact-sql#keepnulls" target="_blank" rel="noopener"&gt;documented behaviour&lt;/A&gt;, but the performance implications for sequence-based defaults under concurrency are rarely discussed.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;&lt;CODE&gt;NUMERIC&lt;/CODE&gt; / &lt;CODE&gt;DECIMAL&lt;/CODE&gt; sequences use exclusive latches; &lt;CODE&gt;BIGINT&lt;/CODE&gt; / &lt;CODE&gt;INT&lt;/CODE&gt; use shared latches with atomic increments.&lt;/STRONG&gt; The sequence cache setting reduces disk I/O only - it does not change the latch mode. The latch mode is determined by the &lt;STRONG&gt;sequence type&lt;/STRONG&gt;, not the column type.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;The combination creates a force multiplier.&lt;/STRONG&gt; Each factor is tolerable in isolation. Exclusive latching with a few sessions is fine. Unnecessary DEFAULT evaluation at low concurrency has negligible cost. But 350 sessions × exclusive latch × per-row evaluation = complete serialisation.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;The diagnostic signature is clear.&lt;/STRONG&gt; &lt;CODE&gt;LATCH_EX&lt;/CODE&gt; dominant in wait stats + &lt;CODE&gt;METADATA_SEQUENCE_GENERATOR&lt;/CODE&gt; in &lt;CODE&gt;sys.dm_os_latch_stats&lt;/CODE&gt; + low CPU utilisation = this pattern. Low CPU under high concurrency means serialisation, not low demand.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;The fix is simple.&lt;/STRONG&gt; Setting &lt;CODE&gt;KeepNulls = true&lt;/CODE&gt; in the bulk copy options eliminates the issue entirely when the application supplies all values. No schema changes required, no table rebuilds, no downtime.&lt;/LI&gt;
&lt;/OL&gt;
&lt;HR /&gt;
&lt;H2&gt;Documentation References&lt;/H2&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;A href="https://learn.microsoft.com/en-us/sql/t-sql/statements/bulk-insert-transact-sql#keepnulls" target="_blank" rel="noopener"&gt;BULK INSERT (Transact-SQL) - KEEPNULLS&lt;/A&gt; - Documents the KEEPNULLS behaviour: "Specifies that empty columns should retain a null value during the bulk import operation, instead of having any default values for the columns inserted."&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://learn.microsoft.com/en-us/sql/tools/bcp-utility#k" target="_blank" rel="noopener"&gt;bcp Utility -k flag&lt;/A&gt; - BCP command-line equivalent of KEEPNULLS.&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://learn.microsoft.com/en-us/sql/connect/jdbc/using-bulk-copy-with-the-jdbc-driver" target="_blank" rel="noopener"&gt;SQLServerBulkCopyOptions.setKeepNulls&lt;/A&gt; - JDBC bulk copy configuration for the &lt;CODE&gt;keepNulls&lt;/CODE&gt; option.&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://learn.microsoft.com/en-us/sql/relational-databases/sequence-numbers/sequence-numbers" target="_blank" rel="noopener"&gt;Sequence Numbers (SQL Server)&lt;/A&gt; - Overview of SEQUENCE objects, caching behaviour, and usage patterns.&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-sequence-get-range-transact-sql" target="_blank" rel="noopener"&gt;sp_sequence_get_range&lt;/A&gt; - Pre-allocating contiguous ID ranges with a single latch acquisition.&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://learn.microsoft.com/en-us/sql/t-sql/statements/create-sequence-transact-sql" target="_blank" rel="noopener"&gt;CREATE SEQUENCE (Transact-SQL)&lt;/A&gt; - Sequence data type options and cache configuration.&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-os-latch-stats-transact-sql" target="_blank" rel="noopener"&gt;sys.dm_os_latch_stats&lt;/A&gt; - DMV for diagnosing latch contention by class.&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property" target="_blank" rel="noopener"&gt;IDENTITY (Property)&lt;/A&gt; - IDENTITY column behaviour and data type considerations.&lt;/LI&gt;
&lt;/UL&gt;
&lt;HR /&gt;
&lt;H2&gt;Feedback and Suggestions&lt;/H2&gt;
&lt;P&gt;If you have feedback or suggestions, please contact the Databases SQL Customer Success Engineering (Ninja) Team (&lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;datasqlninja@microsoft.com&lt;/A&gt;).&lt;/P&gt;
&lt;P&gt;Note: For additional information about migrating various source databases to Azure, see the &lt;A href="https://datamigration.microsoft.com/" target="_blank" rel="noopener"&gt;Azure Database Migration Guide&lt;/A&gt;.&lt;/P&gt;</description>
      <pubDate>Fri, 29 May 2026 15:58:10 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/the-odd-couple-latches-and-keep-nulls/ba-p/4516048</guid>
      <dc:creator>David_Lyth</dc:creator>
      <dc:date>2026-05-29T15:58:10Z</dc:date>
    </item>
    <item>
      <title>Redefining Database Maintenance after Migrating from Db2 on Mainframe to Azure SQL DB Hyperscale</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/redefining-database-maintenance-after-migrating-from-db2-on/ba-p/4471207</link>
      <description>&lt;H3&gt;&lt;STRONG&gt;Introduction&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;Migrating from Db2 z/OS to Azure SQL Database Hyperscale is a major step toward modernizing your mainframe relational data infrastructure to Azure Data SQL managed relational database offering. But what happens to all those daily and periodic Db2 database maintenance tasks you used to perform on Mainframe?&lt;/P&gt;
&lt;P&gt;In this post, to make migration easier we have provided the recommended mapping between Db2 zOS maintenance and Azure SQL DB Hyperscale maintenance tasks that shows:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Tasks you must &lt;EM&gt;still&lt;/EM&gt; &lt;EM&gt;schedule&lt;/EM&gt; or trigger yourself&lt;/LI&gt;
&lt;LI&gt;Tasks that are only needed after &lt;EM&gt;specific&lt;/EM&gt; events (like large data loads)&lt;/LI&gt;
&lt;LI&gt;Tasks that are now fully handled by &lt;EM&gt;Azure SQL Database PaaS&lt;/EM&gt; or are simply &lt;EM&gt;not applicable&lt;/EM&gt; anymore&lt;/LI&gt;
&lt;LI&gt;Best practices that, while not mandatory, are strongly &lt;EM&gt;recommended&lt;/EM&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3&gt;&lt;STRONG&gt;Mainframe Db2 vs Azure SQL Database Hyperscale Database Maintenance&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;For each maintenance activity, you will find actionable guidance on how to perform it in Azure SQL Database Hyperscale, helping you streamline operations and focus on what matters most.&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table class="lia-align-left lia-border-color-21 lia-border-style-solid" border="0.5" style="width: 99.2593%; height: 7119px; border-width: 0.5px;"&gt;&lt;thead&gt;&lt;tr style="height: 102px;"&gt;&lt;td class="lia-border-color-21" style="height: 102px; border-width: 0.5px;"&gt;
&lt;P&gt;&lt;STRONG&gt;Db2 z/OS Task / Concept&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21" style="height: 102px; border-width: 0.5px;"&gt;
&lt;P&gt;&lt;STRONG&gt;Purpose on Db2&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21" style="height: 102px; border-width: 0.5px;"&gt;
&lt;P&gt;&lt;STRONG&gt;Azure SQL DB Hyperscale Equivalent&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21" style="height: 102px; border-width: 0.5px;"&gt;
&lt;P&gt;&lt;STRONG&gt;Responsibility&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21" style="height: 102px; border-width: 0.5px;"&gt;
&lt;P&gt;&lt;STRONG&gt;Recommended Frequency (Post‑Migration)&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21" style="height: 102px; border-width: 0.5px;"&gt;
&lt;P&gt;&lt;STRONG&gt;Remark&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 32px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 32px; border-width: 0.5px;"&gt;
&lt;PRE&gt;&lt;STRONG&gt;&amp;nbsp;&lt;/STRONG&gt;&lt;/PRE&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 32px; border-width: 0.5px;"&gt;
&lt;PRE&gt;&lt;STRONG&gt;&amp;nbsp;&lt;/STRONG&gt;&lt;/PRE&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 32px; border-width: 0.5px;"&gt;
&lt;PRE&gt;&lt;STRONG&gt;&amp;nbsp;&lt;/STRONG&gt;&lt;/PRE&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 32px; border-width: 0.5px;"&gt;
&lt;PRE&gt;&lt;STRONG&gt;&amp;nbsp;&lt;/STRONG&gt;&lt;/PRE&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 32px; border-width: 0.5px;"&gt;
&lt;PRE&gt;&lt;STRONG&gt;&amp;nbsp;&lt;/STRONG&gt;&lt;/PRE&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 32px; border-width: 0.5px;"&gt;
&lt;PRE&gt;&lt;STRONG&gt;&amp;nbsp;&lt;/STRONG&gt;&lt;/PRE&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr class="lia-align-left" style="height: 399px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 399px; border-width: 0.5px;"&gt;
&lt;P&gt;Full Image COPY (Backup)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 399px; border-width: 0.5px;"&gt;
&lt;P&gt;Recoverability&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Note&lt;/STRONG&gt;: Full image copy needs to be taken for data objects like Table space, index space etc.&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 399px; border-width: 0.5px;"&gt;
&lt;P&gt;&lt;SPAN style="background-color: rgba(0, 0, 0, 0); color: rgb(30, 30, 30);"&gt;Azure SQL DB Hyperscale utilizes&amp;nbsp;&lt;/SPAN&gt;&lt;STRONG style="background-color: rgba(0, 0, 0, 0); color: rgb(30, 30, 30);"&gt;storage snapshot technology&lt;/STRONG&gt;&lt;SPAN style="background-color: rgba(0, 0, 0, 0); color: rgb(30, 30, 30);"&gt; to capture a full, complete copy of the database's data files. The &lt;/SPAN&gt;&lt;STRONG style="background-color: rgba(0, 0, 0, 0); color: rgb(30, 30, 30);"&gt;transaction logs&lt;/STRONG&gt;&lt;SPAN style="background-color: rgba(0, 0, 0, 0); color: rgb(30, 30, 30);"&gt; generated since the last snapshot are kept unchanged ("as is") for the set retention period to ensure point-in-time recovery.&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 399px; border-width: 0.5px;"&gt;
&lt;P&gt;Azure&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 399px; border-width: 0.5px;"&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/hyperscale-automated-backups-overview?view=azuresql" target="_blank" rel="noopener"&gt;Continuous&lt;/A&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 399px; border-width: 0.5px;"&gt;
&lt;P&gt;In Azure SQL DB HS default short term retention period of database backup is 7 days which can be configured till 35 days. You can configure retain backup for up to 10 years by configuring Long Term Retention Policy through Azure&amp;nbsp;&lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/long-term-backup-retention-configure?view=azuresql&amp;amp;tabs=portal" target="_blank" rel="noopener"&gt;Portal&lt;/A&gt; / Azure &lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/long-term-backup-retention-configure?view=azuresql&amp;amp;tabs=azure-cli" target="_blank" rel="noopener"&gt;CLI&lt;/A&gt; / &lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/long-term-backup-retention-configure?view=azuresql&amp;amp;tabs=powershell" target="_blank" rel="noopener"&gt;PowerShell&lt;/A&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 95px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Incremental / Delta COPY&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Reduce backup window&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;No incremental backup in HS&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Azure&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;N/A&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 95px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Log Archive / Dual Logging&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Point-in-time recovery&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Transaction log backups automatic&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Azure&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Continuous&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 2037px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 2037px; border-width: 0.5px;"&gt;
&lt;P&gt;RUNSTATS (Table/Index)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 2037px; border-width: 0.5px;"&gt;
&lt;P&gt;Optimizer statistics&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 2037px; border-width: 0.5px;"&gt;
&lt;P&gt;Auto Create/Auto Update Statistics&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;For specific cases schedule STATS update&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 2037px; border-width: 0.5px;"&gt;
&lt;P&gt;Platform&lt;/P&gt;
&lt;P&gt;+ You&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 2037px; border-width: 0.5px;"&gt;
&lt;P&gt;Continuous&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 2037px; border-width: 0.5px;"&gt;
&lt;P&gt;DBCC &lt;A href="https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-show-statistics-transact-sql?view=sql-server-ver17" target="_blank" rel="noopener"&gt;SHOW_STATISTICS&lt;/A&gt; command displays current query optimization statistics for a table or indexed view.&lt;/P&gt;
&lt;P&gt;The Query Optimizer determines when statistics might be out-of-date and then updates them when they're needed for a query plan when AUTO_UPDATE_STATISTICS ON or AUTO_UPDATE_STATISTICS_ASYNC is enabled.&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-database-scoped-configuration-transact-sql?view=sql-server-ver17#async_stats_update_wait_at_low_priority---on--off-" target="_blank" rel="noopener"&gt;ASYNC_STATS_UPDATE_WAIT_AT_LOW_PRIORITY&lt;/A&gt; allows for updates&amp;nbsp;of statistics&amp;nbsp;asynchronously which can wait for the schema modification lock on a low priority queue. This improves concurrency for workloads with frequent query plan (re)compilations.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Asynchronous Auto Update Statistics&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Leave it OFF (default) for most OLTP/reporting hybrids unless you see blocking on STATMAN operations. Consider enabling async if:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;You have latency-sensitive OLTP queries that frequently block on synchronous stat refresh.&lt;/LI&gt;
&lt;LI&gt;Trade-off: First executions after threshold may use stale stats until async update finishes.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;ALTER DATABASE CURRENT SET AUTO_UPDATE_STATISTICS_ASYNC ON;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Statistics update for partitioned table.&amp;nbsp;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Option INCREMENTAL = { ON | OFF } &lt;/STRONG&gt;allows for creation of statistics per partition of the table.&lt;/P&gt;
&lt;P&gt;If only some partitions are changed recently use below statistics update command for specific partition stats update.&lt;/P&gt;
&lt;P&gt;UPDATE STATISTICS dbo.Fact PARTITION = n&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Updating Statistics manually:&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;UPDATE STATISTICS schema.table WITH FULLSCAN / SAMPLE N ROWS / SAMPLE N PERCENT&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Update Stats after:&amp;nbsp;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;·&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Bulk load (insert millions of rows) into existing large table&lt;/P&gt;
&lt;P&gt;·&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Large data purge (delete/archival)&lt;/P&gt;
&lt;P&gt;·&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Partition switch-in / switch-out&lt;/P&gt;
&lt;P&gt;·&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Parameter sniffing + big, estimated vs actual row mismatch &lt;EM&gt;repeatedly&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Identifying Which Stats Need Attention&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Query to rank stats by modification ratio:&lt;/P&gt;
&lt;P&gt;Pick those with (&lt;STRONG&gt;general guidance tune according to specific workload and performance expectations&lt;/STRONG&gt;):&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;modification_pct &amp;gt; 15% on large fact tables&lt;/LI&gt;
&lt;LI&gt;or absolute modification_counter very high (e.g., &amp;gt; 100K changes) even if pct smaller (extreme scale tables)&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;Simple Maintenance Script (DMV-Driven)&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;This updates only stats with &amp;gt;15% modification and &amp;gt;100K row changes:&lt;/P&gt;
&lt;table border="1" style="width: 100.052%; height: 473px; border-width: 1px;"&gt;&lt;tbody&gt;&lt;tr style="height: 473px;"&gt;&lt;td style="height: 473px;"&gt;
&lt;PRE class="language-sql line-numbers" contenteditable="false" data-lia-code-value="DECLARE @sql nvarchar(max) = N'';
WITH c AS (
  SELECT
    s.object_id, s.stats_id,
    QUOTENAME(OBJECT_SCHEMA_NAME(s.object_id)) + '.' + QUOTENAME(OBJECT_NAME(s.object_id)) AS full_table_name,
    QUOTENAME(s.name) AS stat_name,
    sp.rows, sp.modification_counter,
    1.0 * sp.modification_counter / NULLIF(sp.rows,0) AS mod_ratio
  FROM sys.stats s
  CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) sp
  WHERE sp.modification_counter IS NOT NULL
    AND OBJECTPROPERTY(s.object_id,'IsMsShipped') = 0
    AND sp.rows &amp;gt;= 100000
    AND sp.modification_counter &amp;gt;= 100000
    AND (1.0 * sp.modification_counter / NULLIF(sp.rows,0)) &amp;gt;= 0.15
)
SELECT @sql = STRING_AGG(
   N'UPDATE STATISTICS ' + full_table_name + N' ' + stat_name + N' WITH SAMPLE 30 PERCENT;',' ')
FROM c;
PRINT @sql;  -- Review first
EXEC sp_executesql @sql;"&gt;&lt;CODE&gt;DECLARE @sql nvarchar(max) = N'';
WITH c AS (
  SELECT
    s.object_id, s.stats_id,
    QUOTENAME(OBJECT_SCHEMA_NAME(s.object_id)) + '.' + QUOTENAME(OBJECT_NAME(s.object_id)) AS full_table_name,
    QUOTENAME(s.name) AS stat_name,
    sp.rows, sp.modification_counter,
    1.0 * sp.modification_counter / NULLIF(sp.rows,0) AS mod_ratio
  FROM sys.stats s
  CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) sp
  WHERE sp.modification_counter IS NOT NULL
    AND OBJECTPROPERTY(s.object_id,'IsMsShipped') = 0
    AND sp.rows &amp;gt;= 100000
    AND sp.modification_counter &amp;gt;= 100000
    AND (1.0 * sp.modification_counter / NULLIF(sp.rows,0)) &amp;gt;= 0.15
)
SELECT @sql = STRING_AGG(
   N'UPDATE STATISTICS ' + full_table_name + N' ' + stat_name + N' WITH SAMPLE 30 PERCENT;',' ')
FROM c;
PRINT @sql;  -- Review first
EXEC sp_executesql @sql;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;P&gt;&lt;STRONG&gt;Decision Matrix&lt;/STRONG&gt;&lt;/P&gt;
&lt;table border="1" style="border-width: 1px;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;Question&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;Answer&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;Should I keep AUTO_CREATE/UPDATE ON?&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Often Yes. For big tables with millions of records with sizes 100 GB+ if auto create / update / asynchronous stats update is having&amp;nbsp;performance impact, you can disable this and schedule daily / weekly process to perform STATAS UPDATE.&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;Do I need a nightly job?&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Often no; maybe lightweight sp_updatestats for very volatile&lt;/P&gt;
&lt;P&gt;workloads.&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;After heavy ETL?&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Yes, targeted stats refresh (especially dimensions/facts touched).&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;After index rebuild?&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;No&lt;/STRONG&gt; extra stats on that index; other column stats unaffected may still&lt;/P&gt;
&lt;P&gt;need update.&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;Use FULLSCAN often?&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Only for a handful of skewed, performance-critical tables with proven&lt;/P&gt;
&lt;P&gt;cardinality issues.&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 273px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 273px; border-width: 0.5px;"&gt;
&lt;P&gt;REORG TABLE (Tablespace)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 273px; border-width: 0.5px;"&gt;
&lt;P&gt;Eliminate overflow / reclaim space&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 273px; border-width: 0.5px;"&gt;
&lt;P&gt;- Azure SQL does not have REORG TABLE command&lt;/P&gt;
&lt;P&gt;- Similar result can achieved by using REBUILD of the clustered index&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 273px; border-width: 0.5px;"&gt;
&lt;P&gt;Not usually required. Should be done if there is large (&amp;gt;30 %) index defragmentation&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Note&lt;/STRONG&gt;: 30% is just guidance.&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 273px; border-width: 0.5px;"&gt;
&lt;P&gt;N/A&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 273px; border-width: 0.5px;"&gt;
&lt;P&gt;Db2 has table level REORG Option, Azure SQL DB HS has index level REORG options.&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 551px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 551px; border-width: 0.5px;"&gt;
&lt;P&gt;REORG INDEX / REBUILD INDEX&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 551px; border-width: 0.5px;"&gt;
&lt;P&gt;Defragment indexes&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 551px; border-width: 0.5px;"&gt;
&lt;P&gt;REBUILD / REORGANIZE index&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 551px; border-width: 0.5px;"&gt;
&lt;P&gt;You (Needed / Conditional)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 551px; border-width: 0.5px;"&gt;
&lt;P&gt;Weekly or when fragmentation &amp;gt; thresholds&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 551px; border-width: 0.5px;"&gt;
&lt;P&gt;Check current fragmentation: SELECT avg_fragmentation_in_percent FROM sys.dm_db_index_physical_stats;&lt;/P&gt;
&lt;P&gt;Reorganize (5–30%): ALTER INDEX ix ON tbl REORGANIZE&lt;/P&gt;
&lt;P&gt;Rebuild (&amp;gt;30%): ALTER INDEX ix ON tbl REBUILD WITH (ONLINE=ON , RESUMABLE = ON, MAXDOP=1/4/8);&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Above numbers (5 % to 30 % REORG / &amp;gt; 30 % REBUILD) are just guidelines in many cases you may not need index REBUILD due to latest storage technologies or you may need to perform REBUILD after &amp;gt; 60-70 % fragmentation. Also, you may not need REORG. STATS update needs to be done diligently for query performance improvement. Perform through test compare before and after execution plans to derive conclusion on this.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Large Index Rebuild special handling:&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;ALTER INDEX ix ON tbl REBUILD WITH (ONLINE=ON , RESUMABLE = ON, MAX_DURATION = 60,MAXDOP=1/4/8);&lt;/P&gt;
&lt;P&gt;-- Pause after current batch&lt;/P&gt;
&lt;P&gt;ALTER INDEX ix ON tbl PAUSE;&lt;/P&gt;
&lt;P&gt;-- Resume with lower DOP&lt;/P&gt;
&lt;P&gt;ALTER INDEX ix ON tbl RESUME WITH (MAXDOP = 2, MAX_DURATION = 60);&lt;/P&gt;
&lt;P&gt;-- Abort and roll back if required&lt;/P&gt;
&lt;P&gt;ALTER INDEX ix ON tbl ABORT;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Monitor progress of Index rebuild operation using below query:&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;SELECT r.session_id, r.command, r.percent_complete, r.start_time, r.estimated_completion_time&lt;/P&gt;
&lt;P&gt;FROM sys.dm_exec_requests r&lt;/P&gt;
&lt;P&gt;WHERE r.command LIKE '%INDEX%';&lt;/P&gt;
&lt;P&gt;Perform Index Rebuild using example below approaches:&lt;/P&gt;
&lt;P&gt;a)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Ola Hallengren maintenance Scripts :&amp;nbsp;&lt;A href="https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html" target="_blank" rel="noopener"&gt;link&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Or&lt;/P&gt;
&lt;P&gt;b)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Adaptive Index Defragmentation: &lt;A href="https://github.com/Microsoft/tigertoolbox/tree/master/AdaptiveIndexDefrag" target="_blank" rel="noopener"&gt;link&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Or&lt;/P&gt;
&lt;P&gt;c)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;How to maintain Azure SQL Indexes and Statistics :&amp;nbsp;&lt;A href="https://techcommunity.microsoft.com/blog/azuredbsupport/how-to-maintain-azure-sql-indexes-and-statistics/368787" target="_blank" rel="noopener"&gt;link&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;Automating Azure SQL DB index and statistics maintenance using Azure Automation : &lt;A href="https://techcommunity.microsoft.com/blog/azuredbsupport/automating-azure-sql-db-index-and-statistics-maintenance-using-azure-automation/368974" target="_blank" rel="noopener"&gt;link&lt;/A&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 123px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;COPY / QUIESCE utilities&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Consistent copy state&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Not needed (transactionally consistent snapshots)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Not Applicable&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;N/A&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;N/A&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 115px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 115px; border-width: 0.5px;"&gt;
&lt;P&gt;CHECK DATA / CHECK INDEX&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 115px; border-width: 0.5px;"&gt;
&lt;P&gt;Structural consistency&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 115px; border-width: 0.5px;"&gt;
&lt;P&gt;DBCC CHECKDB&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 115px; border-width: 0.5px;"&gt;
&lt;P&gt;You (Best Practice)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 115px; border-width: 0.5px;"&gt;
&lt;P&gt;Monthly or Weekly (off-peak)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 115px; border-width: 0.5px;"&gt;
&lt;P&gt;Azure SQL Database automatically runs internal consistency checks, but you may still run it manually if you want:&lt;/P&gt;
&lt;P&gt;T-SQL Query: DBCC CHECKDB(DatabaseName);&lt;/P&gt;
&lt;P&gt;For large DB: run on geo-secondary or named replica then review&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 123px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;RUN QUERY EXPLAIN SNAPSHOT&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Capture access paths&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Query Store captures execution plans&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Platform + You (Analysis)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Ongoing&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;UL&gt;
&lt;LI&gt;Ensure Query Store ON with Operation Mode DEFAULT i.e. READ_WRITE (collects and persists query stats).&lt;/LI&gt;
&lt;LI&gt;Easy analysis of Query store details using SSMS&lt;/LI&gt;
&lt;/UL&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 123px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;REBIND Packages&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Refresh access plans&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Not needed (no static package binding)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Not Applicable&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;N/A&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;N/A&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 95px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Catalog Statistics Maintenance&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Optimizer health&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;System metadata auto maintained&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Platform&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;N/A&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;N/A&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 123px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Buffer Pool Sizing (BP0/BP32K etc.)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Memory tuning&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Managed by service tier&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Platform&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;N/A&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;N/A&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 95px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Storage Space Preallocation&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Avoid space issues&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Storage allocated dynamically (i.e. auto-grow page servers)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Platform&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;N/A&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Monitor size: sys.database_files; optionally purge/archive data&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 95px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Archive Log Space Monitoring&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Prevent log fill&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Log scaling&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Platform (capacity) + You (workload)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Monitor during bursts&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;sys.dm_db_resource_stats; watch log_write_percent&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 271px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 271px; border-width: 0.5px;"&gt;
&lt;P&gt;Partition Maintenance (ROLL-IN/OUT)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 271px; border-width: 0.5px;"&gt;
&lt;P&gt;Lifecycle management&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 271px; border-width: 0.5px;"&gt;
&lt;P&gt;Table partitioning (if used)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 271px; border-width: 0.5px;"&gt;
&lt;P&gt;You (Conditional)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 271px; border-width: 0.5px;"&gt;
&lt;P&gt;During data lifecycle events&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 271px; border-width: 0.5px;"&gt;
&lt;UL&gt;
&lt;LI&gt;Switching Partitions (Fast Load/Unload) : Move data in/out of a partitioned table without large DELETE/INSERT operations:&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="lia-indent-padding-left-60px"&gt;ALTER TABLE [PartitionedTable] SWITCH&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-indent-padding-left-60px"&gt;PARTITION N TO [StagingTable];&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Rebuilding/Defragmenting Indexes per Partition&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="lia-indent-padding-left-60px"&gt;Instead of rebuilding the entire table, you can rebuild indexes on a single partition, which is faster and less blocking:&lt;/P&gt;
&lt;P class="lia-indent-padding-left-60px"&gt;ALTER INDEX [IX_YourIndex] ON [YourPartitionedTable] REBUILD PARTITION = N&lt;/P&gt;
&lt;P class="lia-indent-padding-left-60px"&gt;WITH (ONLINE = ON);&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 125px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 125px; border-width: 0.5px;"&gt;
&lt;P&gt;Compression (ROW/PAGE) MGMT&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 125px; border-width: 0.5px;"&gt;
&lt;P&gt;Space and IO&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 125px; border-width: 0.5px;"&gt;
&lt;P&gt;ROW / PAGE / ColumnStore compression (if enabled)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 125px; border-width: 0.5px;"&gt;
&lt;P&gt;You (Optional)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 125px; border-width: 0.5px;"&gt;
&lt;P&gt;At design / periodic review&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 125px; border-width: 0.5px;"&gt;
&lt;P&gt;&lt;STRONG&gt;Row compression&lt;/STRONG&gt; stores fixed-length data as variable-length, ideal for CHAR, INT, etc.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Page compression&lt;/STRONG&gt; adds prefix and dictionary compression for repeated values in large tables.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Columnstore compression&lt;/STRONG&gt; stores data column-wise for huge analytical tables with millions of rows.&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 207px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 207px; border-width: 0.5px;"&gt;
&lt;P&gt;Security: RACF/External Auth Integration&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 207px; border-width: 0.5px;"&gt;
&lt;P&gt;Access control&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 207px; border-width: 0.5px;"&gt;
&lt;P&gt;Microsoft Entra ID Auth, Managed Identities, Microsoft Entra Service Principal&amp;nbsp;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 207px; border-width: 0.5px;"&gt;
&lt;P&gt;You (Needed)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 207px; border-width: 0.5px;"&gt;
&lt;P&gt;At onboarding + periodic review&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 207px; border-width: 0.5px;"&gt;
&lt;P&gt;Example authentication using Microsoft Entra ID:&lt;/P&gt;
&lt;P&gt;DECLARE @EntraIDUser NVARCHAR(128) = 'john@contoso.com';&lt;/P&gt;
&lt;P&gt;CREATE USER [@EntraIDUser] FROM EXTERNAL PROVIDER;&lt;/P&gt;
&lt;P&gt;ALTER ROLE db_datareader ADD MEMBER [@EntraIDUser];&lt;/P&gt;
&lt;P&gt;ALTER ROLE db_datawriter ADD MEMBER [@EntraIDUser];&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 95px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Encryption (Dataset / Log)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Compliance&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;TDE auto-enabled&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Platform&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;N/A&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Optionally manage customer-managed keys (CMK) via Key Vault&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 95px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Auditing / SMF / IFI&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Compliance logging&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Azure SQL Auditing / Defender&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;You (Needed)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Enable once; review monthly&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;&lt;STRONG&gt;Azure SQL Database / Managed Instance&lt;/STRONG&gt; supports auditing at the &lt;STRONG&gt;server&lt;/STRONG&gt; or &lt;STRONG&gt;database&lt;/STRONG&gt; level. Audits can be sent to &lt;STRONG&gt;Log Analytics&lt;/STRONG&gt;, &lt;STRONG&gt;Storage Account&lt;/STRONG&gt;, or &lt;STRONG&gt;Event Hub&lt;/STRONG&gt;.&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 235px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 235px; border-width: 0.5px;"&gt;
&lt;P&gt;Performance Trace (IFCID)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 235px; border-width: 0.5px;"&gt;
&lt;P&gt;Problem diagnostics&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 235px; border-width: 0.5px;"&gt;
&lt;P&gt;Query Store + Extended Events, &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/azure-sql/database-watcher-overview?view=azuresql&amp;amp;tabs=americas" target="_blank" rel="noopener"&gt;Database Watcher&lt;/A&gt;, DMVs, Azure Portal Monitoring&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 235px; border-width: 0.5px;"&gt;
&lt;P&gt;You (Conditional)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 235px; border-width: 0.5px;"&gt;
&lt;P&gt;When diagnosing issues&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 235px; border-width: 0.5px;"&gt;
&lt;P&gt;Example: Use SSMS to browse Query Store view; Use Azure Portal to monitor performance; or use DMVs for detailed troubleshooting.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 275px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 275px; border-width: 0.5px;"&gt;
&lt;P&gt;Job Scheduling (JCL)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 275px; border-width: 0.5px;"&gt;
&lt;P&gt;Orchestrate utilities&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 275px; border-width: 0.5px;"&gt;
&lt;P&gt;Azure Automation /&lt;/P&gt;
&lt;P&gt;Elastic Jobs in Azure SQL DB/&lt;/P&gt;
&lt;P&gt;ADF Pipelines /&lt;/P&gt;
&lt;P&gt;Azure Logic Apps / Azure Functions&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 275px; border-width: 0.5px;"&gt;
&lt;P&gt;You (As per need)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 275px; border-width: 0.5px;"&gt;
&lt;P&gt;Per task (daily/weekly)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 275px; border-width: 0.5px;"&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Azure Automation&amp;nbsp;&lt;/STRONG&gt;– Serverless runbooks or scripts run in the cloud using managed identities or credentials.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Elastic Job in Azure SQL DB - &lt;/STRONG&gt;You can create and schedule elastic jobs that could be periodically executed against one or many Azure SQL databases to run Transact-SQL (T-SQL) queries and perform maintenance tasks.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;ADF / Fabric Pipelines&lt;/STRONG&gt; – Orchestrate queries and maintenance as scheduled or triggered pipeline activities.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Logic Apps / Functions&lt;/STRONG&gt; – Event-driven query execution in response to timers or Azure events.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 123px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Index Design Advisor&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Workload tuning&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Automatic Tuning (create/drop indexes)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Platform (optional)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;Continuous&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 123px; border-width: 0.5px;"&gt;
&lt;P&gt;ALTER DATABASE CURRENT SET AUTOMATIC_TUNING (CREATE_INDEX=ON, DROP_INDEX=ON)&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 95px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Plan Regression Detection&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Stability&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Automatic Plan Correction&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Platform (if enabled)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Continuous&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;ALTER DATABASE CURRENT SET AUTOMATIC_TUNING (FORCE_LAST_GOOD_PLAN=ON)&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 151px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 151px; border-width: 0.5px;"&gt;
&lt;P&gt;HA / DR (Dual site, GDPS)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 151px; border-width: 0.5px;"&gt;
&lt;P&gt;Availability&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 151px; border-width: 0.5px;"&gt;
&lt;P&gt;Built-in HA + Geo-Replication / Failover Groups&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 151px; border-width: 0.5px;"&gt;
&lt;P&gt;Platform + You (DR config)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 151px; border-width: 0.5px;"&gt;
&lt;P&gt;Configure once; drills semi-annual&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 151px; border-width: 0.5px;"&gt;
&lt;UL&gt;
&lt;LI&gt;You can create up to 4 HA replicas. HA replica uses same page servers as the primary replica, so no data copy is required to add an HA replica. Information for High Availability replica is present at documentation:&amp;nbsp;&lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/service-tier-hyperscale-replicas?view=azuresql#high-availability-replica" target="_blank" rel="noopener"&gt;link&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;Named replica just like an HA uses the same page servers as the primary replica. Named replica can have their own SLO and you can create up to 30 named replicas for read scale out purpose. Create Hyperscale named replica by following documentation:&amp;nbsp;&lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/hyperscale-named-replica-configure?view=azuresql&amp;amp;tabs=portal#create-a-hyperscale-named-replica" target="_blank" rel="noopener"&gt;link&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;For DR replica easily configure geo replication / failover group for Azure SQL Database as explained at location :&amp;nbsp;&lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/failover-group-configure-sql-db?view=azuresql&amp;amp;tabs=azure-portal%2Cazure-powershell-manage&amp;amp;pivots=azure-sql-single-db" target="_blank" rel="noopener"&gt;link&lt;/A&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 130px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 130px; border-width: 0.5px;"&gt;
&lt;P&gt;Backup Retention / Offsite Vault&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 130px; border-width: 0.5px;"&gt;
&lt;P&gt;Long-term retention&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 130px; border-width: 0.5px;"&gt;
&lt;P&gt;LTR (Long-Term Retention)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 130px; border-width: 0.5px;"&gt;
&lt;P&gt;You (Optional)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 130px; border-width: 0.5px;"&gt;
&lt;P&gt;Configure at migration; review annually&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 130px; border-width: 0.5px;"&gt;
&lt;P&gt;Set LTR via&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Azure Portal or&lt;/LI&gt;
&lt;LI&gt;Azure CLI : az sql db ltr-policy set or&lt;/LI&gt;
&lt;LI&gt;Azure Powershell : Set-AzSqlDatabaseBackupLongTermRetentionPolicy&lt;/LI&gt;
&lt;/UL&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 127px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 127px; border-width: 0.5px;"&gt;
&lt;P&gt;Monitoring (OMEGAMON, Tivoli)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 127px; border-width: 0.5px;"&gt;
&lt;P&gt;Health &amp;amp; capacity&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 127px; border-width: 0.5px;"&gt;
&lt;P&gt;Azure Monitor / Log Analytics / Database Watcher&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 127px; border-width: 0.5px;"&gt;
&lt;P&gt;You (Needed)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 127px; border-width: 0.5px;"&gt;
&lt;P&gt;Daily dashboard; alert reaction&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 127px; border-width: 0.5px;"&gt;
&lt;UL&gt;
&lt;LI&gt;Using Azure Monitor create Alerts on CPU %, Memory %, Transaction Log throughput %, Storage %, blocking, failed logins etc.&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database-watcher-overview?view=azuresql&amp;amp;tabs=americas" target="_blank" rel="noopener"&gt;Database Watcher&lt;/A&gt; collects in-depth workload monitoring data to give you a detailed view of database performance, configuration, and health.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 138px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 138px; border-width: 0.5px;"&gt;
&lt;P&gt;Deadlock / Lock Escalation Review&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 138px; border-width: 0.5px;"&gt;
&lt;P&gt;Concurrency tuning&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 138px; border-width: 0.5px;"&gt;
&lt;P&gt;Extended Events + DMVs&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 138px; border-width: 0.5px;"&gt;
&lt;P&gt;You (Conditional)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 138px; border-width: 0.5px;"&gt;
&lt;P&gt;Investigate alerts&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 138px; border-width: 0.5px;"&gt;
&lt;UL&gt;
&lt;LI&gt;sys.dm_tran_locks: Current (transient) lock inventory. Useful for spotting patterns leading to deadlocks (e.g., two sessions holding incompatible locks in different order) and for capturing blocking chains before a deadlock forms. It does NOT retain history; deadlocks often resolve in milliseconds, so you rarely see the actual deadlock moment here.&lt;/LI&gt;
&lt;LI&gt;system_health Extended Event: Always running; captures xml_deadlock_report events with a full deadlock graph (processes, resource nodes, lock modes, victim). Gives you post‑mortem detail even if you missed it live.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 120px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 120px; border-width: 0.5px;"&gt;
&lt;P&gt;Batch Window Management&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 120px; border-width: 0.5px;"&gt;
&lt;P&gt;Avoid contention&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 120px; border-width: 0.5px;"&gt;
&lt;P&gt;Scale compute / workload smoothing&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 120px; border-width: 0.5px;"&gt;
&lt;P&gt;You (Best Practice)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 120px; border-width: 0.5px;"&gt;
&lt;P&gt;Before large ETL&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 120px; border-width: 0.5px;"&gt;
&lt;P&gt;If required, scale up database resources before batch execution to shorten batch execution:&lt;/P&gt;
&lt;P&gt;ALTER DATABASE ... MODIFY (SERVICE_OBJECTIVE='HS_Gen5_8');&lt;/P&gt;
&lt;P&gt;scale down database resources after batch completion&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 266px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 266px; border-width: 0.5px;"&gt;
&lt;P&gt;ETL Load&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 266px; border-width: 0.5px;"&gt;
&lt;P&gt;Faster bulk load&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 266px; border-width: 0.5px;"&gt;
&lt;P&gt;Minimal logging depends on model (Always FULL)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 266px; border-width: 0.5px;"&gt;
&lt;P&gt;You (Conditional)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 266px; border-width: 0.5px;"&gt;
&lt;P&gt;During large loads&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 266px; border-width: 0.5px;"&gt;
&lt;P&gt;On large data load to a given table (&amp;gt; 100 GB)&lt;/P&gt;
&lt;P&gt;Preferably:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;&amp;nbsp;Keep Clustered Column Store Indexes on table as it is. Use batch size of &amp;gt;102, 400 rows for better performance.&lt;/LI&gt;
&lt;LI&gt;&amp;nbsp;Drop Clustered Row store and non-clustered indexes before load if possible and recreate them after load.&lt;/LI&gt;
&lt;LI&gt;&amp;nbsp;While recreating indexes on large tables use these options : ONLINE = ON, RESUMABLE = ON, MAXDOP = 4 / 8&lt;/LI&gt;
&lt;LI&gt;&amp;nbsp;Monitor the progress of index creation using:&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;SELECT object_name(object_id) AS TableName, index_id,&amp;nbsp; percent_complete,&amp;nbsp; state_desc FROM sys.index_resumable_operations&lt;/P&gt;
&lt;P&gt;WHERE state_desc = 'IN_PROGRESS';&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 95px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Object Ownership / Schema Sync&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Governance&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;VS Code extension&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;You (Needed)&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;On deployment&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;&lt;STRONG&gt;Schema compare between Db2 and SQL&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;ADF based solution for Db2 and SQL Schema comparison:&amp;nbsp;&lt;A href="https://techcommunity.microsoft.com/blog/modernizationbestpracticesblog/database-schema-compare-tool/4118537" target="_blank" rel="noopener"&gt;link&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Schema compare between Azure SQL databases&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The schema comparison tooling enables you to compare two database definitions:&lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/sql/tools/sql-database-projects/concepts/schema-comparison?view=sql-server-ver17&amp;amp;pivots=sq1-visual-studio" target="_blank" rel="noopener"&gt;link&lt;/A&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 95px;"&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Database Comparison&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Governance&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;Database Compare Utility&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;You&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;On-demand&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-left lia-border-color-21 lia-vertical-align-top" style="height: 95px; border-width: 0.5px;"&gt;
&lt;P&gt;If you want to compare data between Db2 and Azure SQL DB HS you can use Database Compare Utility which is available for download at location : &lt;A href="https://www.microsoft.com/en-us/download/details.aspx?id=103016&amp;amp;msockid=1996a9980f02689d0a7cbfe00ea069a8" target="_blank" rel="noopener"&gt;link&lt;/A&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;H3&gt;&lt;STRONG&gt;Minimal Practical Post-Migration Maintenance Set (Typical Cadence)&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;&lt;STRONG&gt;Daily&lt;/STRONG&gt;:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Review alerts (performance, error rates, failed logins)&lt;/LI&gt;
&lt;LI&gt;Optional lightweight index fragmentation check if workload highly write-intensive&lt;/LI&gt;
&lt;LI&gt;Monitor Query Store for top regressions&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;After Bulk Loads&lt;/STRONG&gt;:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;UPDATE STATISTICS table WITH FULLSCAN (only for large changes)&lt;/LI&gt;
&lt;LI&gt;Consider index rebuild if fragmentation spiked&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;Weekly&lt;/STRONG&gt;:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Targeted&lt;/STRONG&gt; index maintenance (only fragmented ones)&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Targeted&lt;/STRONG&gt; statistics maintenance (only for stale stats)&lt;/LI&gt;
&lt;LI&gt;Query Store review: force known good plan if auto correction off&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;Monthly&lt;/STRONG&gt; (or quarterly for very large DB):&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;DBCC CHECKDB (prefer off-peak or on a geo-secondary). If database size is large, perform this operation on business-critical tables or use specific options like PHYSICAL_ONLY.&lt;/LI&gt;
&lt;LI&gt;Security/audit review&lt;/LI&gt;
&lt;LI&gt;Cost/compute tier right-sizing&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;Annual / Semi-annual&lt;/STRONG&gt;:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;DR failover test (Failover Group)&lt;/LI&gt;
&lt;LI&gt;LTR retention policy review&lt;/LI&gt;
&lt;LI&gt;Compression strategy review&lt;/LI&gt;
&lt;LI&gt;Automatic Tuning effectiveness assessment&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;Migration Mindset Tips&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Don’t lift-and-shift Db2 utility cadence; you might over-maintain and waste resources.&lt;/LI&gt;
&lt;LI&gt;Replace "daily job list" with "monitor + exception-based actions."&lt;/LI&gt;
&lt;LI&gt;For very large Hyperscale DBs, consider named replicas for offloading DBCC CHECKDB or reporting.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H6&gt;&lt;STRONG class="lia-align-justify"&gt;&lt;SPAN data-teams="true"&gt;Disclaimer: The guidance, recommendations, and examples provided in this blog are based on our experience with migrating Db2 from Mainframe to Azure SQL Database Hyperscale and may not be universally applicable to all environments. Every customer’s workload, configuration, and performance characteristics are unique. &lt;STRONG class="lia-align-justify" data-start="455" data-end="603"&gt;You should thoroughly test and validate these recommendations in your own development or staging environment before applying them in production. &lt;/STRONG&gt;We do not assume any responsibility or liability for any issues, downtime, or impacts resulting from the use of the information in this blog.&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/H6&gt;
&lt;H3&gt;&lt;STRONG&gt;Feedback and suggestions&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;If you have feedback or suggestions for improving this data migration asset, please send an email to&amp;nbsp;&lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;Database Platform Engineering Team&lt;/A&gt;.&lt;/P&gt;</description>
      <pubDate>Wed, 25 Mar 2026 17:05:12 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/redefining-database-maintenance-after-migrating-from-db2-on/ba-p/4471207</guid>
      <dc:creator>Sandip-Khandelwal</dc:creator>
      <dc:date>2026-03-25T17:05:12Z</dc:date>
    </item>
    <item>
      <title>Azure SQL’s Native JSON Type: Optimized for Performance</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/azure-sql-s-native-json-type-optimized-for-performance/ba-p/4486952</link>
      <description>&lt;H2&gt;Introduction&lt;/H2&gt;
&lt;P&gt;JSON has become the de-facto format for modern applications from web APIs to microservices and event-driven systems. Azure SQL has supported JSON for years, but JSON was treated just like text (stored as nvarchar or varchar). That meant every query involving JSON required parsing, which could get expensive as data volume grew.&lt;/P&gt;
&lt;P&gt;The new &lt;STRONG&gt;native JSON binary type&lt;/STRONG&gt; changes that story. Instead of saving JSON as raw text, Azure SQL can store it in a binary representation that’s optimized for fast reads, efficient in-place updates, and compact storage. You get the flexibility of JSON with performance that behaves more like a structured column.&lt;/P&gt;
&lt;P&gt;Learn more about the JSON data type in the documentation –&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Textual data format - &lt;A href="https://learn.microsoft.com/en-us/sql/relational-databases/json/json-data-sql-server?view=sql-server-ver17" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/en-us/sql/relational-databases/json/json-data-sql-server?view=sql-server-ver17&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;Native binary format - &lt;A href="https://learn.microsoft.com/en-us/sql/t-sql/data-types/json-data-type?view=sql-server-ver17" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/en-us/sql/t-sql/data-types/json-data-type?view=sql-server-ver17&lt;/A&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;BR /&gt;A few useful things to know upfront:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;JSON data is stored in a &lt;STRONG&gt;native binary format&lt;/STRONG&gt;, not as plain text&lt;/LI&gt;
&lt;LI&gt;Reads are faster because the JSON is &lt;STRONG&gt;already parsed&lt;/STRONG&gt;&lt;/LI&gt;
&lt;LI&gt;Improved write efficiency, since queries can &lt;STRONG&gt;update individual values &lt;/STRONG&gt;without accessing the entire document.&lt;/LI&gt;
&lt;LI&gt;Storage is &lt;STRONG&gt;more compact, &lt;/STRONG&gt;optimized for compression&lt;/LI&gt;
&lt;LI&gt;Existing JSON functions continue to work so &lt;STRONG&gt;app changes are minimal&lt;/STRONG&gt;&lt;/LI&gt;
&lt;LI&gt;Internally, JSON is stored using &lt;STRONG&gt;UTF-8 encoding&lt;/STRONG&gt; (Latin1_General_100_BIN2_UTF8)&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;This blog shares the performance gains observed after migrating JSON from nvarchar/varchar to the native JSON binary type. Results will vary across JSON structures and workloads, so consider this a guide rather than a universal benchmark.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;EM&gt;Note:&lt;/EM&gt;&lt;/STRONG&gt;&lt;EM&gt; The purpose of this blog is to introduce the native JSON binary data type. We are not covering JSON indexes or JSON functions at this time in order to maintain clarity and focus.&lt;/EM&gt;&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;H2&gt;Test Environment Details:&lt;/H2&gt;
&lt;P&gt;To measure the performance impact of migrating JSON from nvarchar/varchar to the native JSON binary type, a test environment was set up with six tables on Azure SQL Database (General Purpose, Gen5, 2 vCores).&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;EM&gt;Note: &lt;/EM&gt;&lt;/STRONG&gt;&lt;EM&gt;The dataset used in the testing was generated using AI for demonstration purposes.&lt;BR /&gt;&lt;/EM&gt;&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;BR /&gt;JSON data stored as nvarchar/varchar data types:&lt;/STRONG&gt;&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN lia-align-center"&gt;&lt;table class="lia-border-color-21 lia-border-style-solid" border="1" style="border-width: 1px;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;&lt;STRONG&gt;Table Name&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;&lt;STRONG&gt;Number of Records&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;&lt;STRONG&gt;Size (GB)&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="lia-align-left lia-border-color-21"&gt;
&lt;P&gt;InventoryTrackingJSON&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;400,003&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;4.21&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="lia-align-left lia-border-color-21"&gt;
&lt;P&gt;OrderDetailsJSON&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;554,153&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;1.29&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="lia-align-left lia-border-color-21"&gt;
&lt;P&gt;CustomerProfileJSON&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P class="lia-align-center"&gt;55,001&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;0.16&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="lia-align-left lia-border-color-21"&gt;
&lt;P&gt;ProductCatalogJSON&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;100,001&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;0.10&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="lia-align-left lia-border-color-21"&gt;
&lt;P&gt;SalesAnalyticsJSON&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;10,000&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;0.08&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="lia-align-left lia-border-color-21"&gt;
&lt;P&gt;EmployeeRecordsJSON&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;5,000&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;0.02&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 33.33%" /&gt;&lt;col style="width: 33.33%" /&gt;&lt;col style="width: 33.33%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Total database size:&lt;/STRONG&gt; &lt;STRONG&gt;5.94 GB&lt;/STRONG&gt; (59.43% used), based on a maximum configured size of 10 GB, with JSON stored as nvarchar/varchar.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-clear-both"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Example schema (OrderDetailsJSON)&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;One of the core tables used in testing:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;CREATE TABLE JSONTest.OrderDetailsJSON (
    OrderDetailID INT IDENTITY(1,1) PRIMARY KEY,
    OrderMetadata NVARCHAR(MAX),           -- JSON: order info,source, salesperson
    ShippingDetails NVARCHAR(MAX),         -- JSON: carrier, priority, addresses
    CustomizationOptions NVARCHAR(MAX),    -- JSON: customizations and add-ons
    CreatedDate DATETIME2 DEFAULT SYSDATETIME(),
    ModifiedDate DATETIME2 DEFAULT SYSDATETIME()
);
&lt;/LI-CODE&gt;
&lt;P&gt;&lt;BR /&gt;Each JSON column simulated realistic business structure - for example:&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;OrderMetadata&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang="json"&gt;{
  "orderSource": "Mobile App",
  "salesPerson": "Jane Smith",
  "orderDate": "2025-11-14T10:30:00Z",
  "customerType": "Premium"
}
&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;ShippingDetails&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang="json"&gt;{
  "carrier": "FedEx",
  "priority": "standard",
  "address": { "city": "Anytown", "state": "CA" }
}
&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;CustomizationOptions&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang="json"&gt;{
  "color": "Green",
  "size": "Medium",
  "giftWrap": true
}
&lt;/LI-CODE&gt;
&lt;H2&gt;Performance before migration:&lt;/H2&gt;
&lt;P&gt;To measure performance differences accurately, a continuous &lt;STRONG&gt;12-minute test session&lt;/STRONG&gt; was run. The load sizes referenced in the results (500, 1K, 2.5K, 5K, 10K, and 25K) represent the number of records read, and each record goes through the following operations:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Multiple JSON_VALUE extractions&lt;/LI&gt;
&lt;LI&gt;JSON validation using ISJSON&lt;/LI&gt;
&lt;LI&gt;Safe type conversions using TRY_CONVERT&lt;/LI&gt;
&lt;LI&gt;Aggregation logic&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;During the 12-minute continuous workload, JSON stored as nvarchar/varchar showed consistent resource pressure, primarily on CPU and storage IO. The monitoring tools reported:&lt;/P&gt;
&lt;img&gt;&lt;STRONG&gt;&lt;EM&gt;Disclaimer:&lt;/EM&gt;&lt;/STRONG&gt;&lt;EM&gt; These results are for illustration purposes only. Actual performance will vary depending on system hardware (CPU cores, memory, disk I/O), database configurations, network latency, and table structures. We recommend validating performance in dev/test to establish a baseline.&lt;/EM&gt;&lt;/img&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;&lt;BR /&gt;&lt;BR /&gt;Data migration to native JSON binary data type&lt;/H2&gt;
&lt;P&gt;For testing, native JSON columns were added to the existing tables, and JSON data stored in nvarchar/varchar columns was migrated to the new native JSON binary columns using the CAST function.&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Migration Script (example used for all tables)&lt;/STRONG&gt;&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Add native JSON columns&lt;BR /&gt;&lt;LI-CODE lang="sql"&gt;ALTER TABLE JSONTest.OrderDetailsJSON ADD OrderMetadata_native JSON, ShippingDetails_native JSON, CustomizationOptions_native JSON;&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;/LI&gt;
&lt;LI&gt;Migrate existing NVARCHAR/VARCHAR JSON into native JSON&lt;BR /&gt;&lt;LI-CODE lang="sql"&gt;UPDATE JSONTest.OrderDetailsJSON SET OrderMetadata_native = CAST(OrderMetadata AS JSON), ShippingDetails_native = CAST(ShippingDetails AS JSON), CustomizationOptions_native = CAST(CustomizationOptions AS JSON);&lt;/LI-CODE&gt;&lt;/LI&gt;
&lt;/OL&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;EM&gt;&lt;STRONG&gt;Note:&lt;/STRONG&gt; After validating that the migrated data was consistent, the original nvarchar/varchar JSON columns were dropped. A &lt;STRONG&gt;rebuild index operation&lt;/STRONG&gt; was then performed to remove fragmentation and reclaim space, ensuring that the subsequent storage comparison reflected the true storage footprint of the native JSON binary type.&lt;/EM&gt;&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;P&gt;The same pattern was repeated for all tables.&amp;nbsp;&lt;BR /&gt;&lt;BR /&gt;Storage footprint after migration:&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table class="lia-border-color-21 lia-border-style-solid" border="1" style="width: 642px; border-width: 1px;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;&lt;STRONG&gt;Table Name&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;&lt;STRONG&gt;Number of Records&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;&lt;STRONG&gt;Size_Before (GB)&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;&lt;STRONG&gt;Size_After (GB)&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;InventoryTrackingJSON&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;400,003&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;4.21&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;0.60&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;OrderDetailsJSON&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;554,153&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;1.29&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;0.27&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;ProductCatalogJSON&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;100,001&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;0.16&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;0.11&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;SalesAnalyticsJSON&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;10,000&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;0.10&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;0.04&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;CustomerProfileJSON&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;55,001&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;0.08&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;0.01&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="lia-border-color-21"&gt;
&lt;P&gt;EmployeeRecordsJSON&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;5,000&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;0.02&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21"&gt;
&lt;P&gt;0.00&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 25.00%" /&gt;&lt;col style="width: 25.00%" /&gt;&lt;col style="width: 25.00%" /&gt;&lt;col style="width: 25.00%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;&lt;BR /&gt;Total&amp;nbsp;&lt;STRONG&gt;database size: 1.06 GB&lt;/STRONG&gt; (10.64% used), based on a maximum configured size of 10 GB, with JSON in native binary data type.&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-clear-both"&gt;After migrating all JSON columns from nvarchar/varchar to the native JSON type, the total database size dropped from 5.94 GB to 1.06 GB - an &lt;STRONG&gt;~82% reduction in storage&lt;/STRONG&gt;.&lt;/P&gt;
&lt;H2&gt;Performance after migration&lt;/H2&gt;
&lt;P&gt;After moving all JSON columns from nvarchar/varchar to native JSON, the exact same 12-minute workload was rerun - same query patterns, same workload distribution. Only the JSON storage format was different. Here are the results:&amp;nbsp;&amp;nbsp;&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;BR /&gt;&lt;BR /&gt;Key Metrics (Before vs. After)&lt;/STRONG&gt;&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-clear-both"&gt;The migration didn’t just shrink storage - it made JSON workloads easier for the engine to process. With the native JSON type, the same workload completed with &lt;STRONG&gt;~27% lower CPU&lt;/STRONG&gt; and &lt;STRONG&gt;~80% lower Data IO.&lt;/STRONG&gt;&lt;/P&gt;
&lt;H2&gt;Query duration, Throughput, &amp;amp; Logical Reads&lt;/H2&gt;
&lt;H3&gt;Query duration&lt;/H3&gt;
&lt;P&gt;A comparison was conducted using the same workload, dataset, indexes, and query patterns - with the &lt;STRONG&gt;only variable being the JSON storage format&lt;/STRONG&gt;. The outcome showed a clear trend in query duration.&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Across every single load level,&amp;nbsp;&lt;STRONG&gt;native JSON cut query duration by 2.5x - 4x&lt;/STRONG&gt;. Even more interesting: as the workload scaled 50x, &lt;STRONG&gt;native JSON latency stayed almost flat&lt;/STRONG&gt;, while text JSON steadily slowed down.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;EM&gt;Note:&lt;/EM&gt;&lt;/STRONG&gt;&lt;EM&gt; The duration values shown represent the average across multiple runs within the performance test described earlier.&lt;/EM&gt;&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;H3&gt;Throughput improvement&lt;/H3&gt;
&lt;P&gt;The benefits also translated directly into throughput. Overall, native JSON enabled &lt;STRONG&gt;20x to 40x more records processed per second (rps)&lt;/STRONG&gt;. For example:&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table class="lia-border-color-21 lia-border-style-solid" border="1" style="width: 49.537%; height: 154.667px; border-width: 1px;"&gt;&lt;tbody&gt;&lt;tr style="height: 38.6667px;"&gt;&lt;td class="lia-align-center lia-border-color-21" style="height: 38.6667px;"&gt;
&lt;P&gt;&lt;STRONG&gt;Load&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21" style="height: 38.6667px;"&gt;
&lt;P&gt;&lt;STRONG&gt;Throughput Before (rps)&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21" style="height: 38.6667px;"&gt;
&lt;P&gt;&lt;STRONG&gt;Throughput After (rps)&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 38.6667px;"&gt;&lt;td class="lia-border-color-21" style="height: 38.6667px;"&gt;
&lt;P&gt;Small load&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21" style="height: 38.6667px;"&gt;
&lt;P&gt;~60&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21" style="height: 38.6667px;"&gt;
&lt;P&gt;~240&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 38.6667px;"&gt;&lt;td class="lia-border-color-21" style="height: 38.6667px;"&gt;
&lt;P&gt;High load&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21" style="height: 38.6667px;"&gt;
&lt;P&gt;~690&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21" style="height: 38.6667px;"&gt;
&lt;P&gt;~2300&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 38.6667px;"&gt;&lt;td class="lia-border-color-21" style="height: 38.6667px;"&gt;
&lt;P&gt;Peak load&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21" style="height: 38.6667px;"&gt;
&lt;P&gt;~1360&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center lia-border-color-21" style="height: 38.6667px;"&gt;
&lt;P&gt;~4700&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 33.33%" /&gt;&lt;col style="width: 33.33%" /&gt;&lt;col style="width: 33.33%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;H3&gt;Logical reads improvement&lt;/H3&gt;
&lt;P&gt;Native JSON significantly reduced I/O work as well:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Logical reads per run dropped from &lt;STRONG&gt;~168,507 → ~33,880&lt;/STRONG&gt;&lt;/LI&gt;
&lt;LI&gt;An &lt;STRONG&gt;~80% reduction in pages read&lt;/STRONG&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Lower logical reads directly correlate with improved scalability - fewer pages scanned means less work required to serve each request, especially under increasing load.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;BR /&gt;Sample results:&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; JSON (nvarchar/varchar)&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; JSON (native binary)&lt;/P&gt;
&lt;img /&gt;&lt;img /&gt;
&lt;H3&gt;&amp;nbsp;&lt;/H3&gt;
&lt;H3&gt;&amp;nbsp;&lt;/H3&gt;
&lt;H3&gt;&amp;nbsp;&lt;/H3&gt;
&lt;H3&gt;Cache management&lt;/H3&gt;
&lt;P&gt;To ensure the performance improvement was not simply a result of native JSON fitting more easily in memory, the test cleared the cache at regular intervals using &lt;EM&gt;DBCC DROPCLEANBUFFERS&lt;/EM&gt;, forcing repeated cold-start execution. As expected, query duration increased immediately after each cache clear for both text JSON and native JSON, yet the relative benefit remained consistent: native JSON continued to show a 2.5x–4x reduction in duration across all load levels. This confirms that the gains are not due to buffer pool residency alone, but from reduced JSON parsing work during execution.&lt;BR /&gt;&lt;BR /&gt;For example, in the chart below for the small load, runs &lt;STRONG&gt;3&lt;/STRONG&gt; and &lt;STRONG&gt;6&lt;/STRONG&gt; were executed right after clearing cache. Although both formats show higher duration, the relative performance advantage remains unchanged.&lt;/P&gt;
&lt;img /&gt;
&lt;H2&gt;&amp;nbsp;&lt;/H2&gt;
&lt;H2&gt;&amp;nbsp;&lt;/H2&gt;
&lt;H2&gt;&amp;nbsp;&lt;/H2&gt;
&lt;H2&gt;&amp;nbsp;&lt;/H2&gt;
&lt;H2&gt;&amp;nbsp;&lt;/H2&gt;
&lt;H2&gt;&amp;nbsp;&lt;/H2&gt;
&lt;H2&gt;Conclusion&lt;/H2&gt;
&lt;P&gt;Native JSON storage in Azure SQL isn’t just a new way to store semi-structured data - it delivers &lt;STRONG&gt;tangible performance and efficiency gains&lt;/STRONG&gt;. In our case, migrating JSON from NVARCHAR to the new binary JSON type resulted in:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;If your workload involves frequent reading or updating of JSON documents - especially large or deeply nested ones, the native JSON type is worth evaluating. Your gains may vary based on JSON structure, indexing strategy, and workload patterns - but the benefits of&amp;nbsp;&lt;STRONG&gt;eliminating repeated text parsing + reducing storage cost&lt;/STRONG&gt; are difficult to ignore.&lt;/P&gt;
&lt;P&gt;As SQL workloads continue to blend structured and semi-structured data, native JSON brings Azure SQL more in line with modern application design while preserving the maturity and stability of the relational engine.&lt;/P&gt;
&lt;H5&gt;Feedback and Suggestions&lt;/H5&gt;
&lt;P&gt;If you have feedback or suggestions, please contact the Databases SQL Customer Success Engineering (Ninja) Team (&lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;datasqlninja@microsoft.com&lt;/A&gt;).&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Note: For additional information about migrating various source databases to Azure, see the&amp;nbsp;&lt;A href="https://datamigration.microsoft.com/" target="_blank" rel="noopener"&gt;Azure Database Migration Guide.&lt;/A&gt;&lt;/P&gt;</description>
      <pubDate>Mon, 09 Feb 2026 09:00:00 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/azure-sql-s-native-json-type-optimized-for-performance/ba-p/4486952</guid>
      <dc:creator>ShrustiKolsur</dc:creator>
      <dc:date>2026-02-09T09:00:00Z</dc:date>
    </item>
    <item>
      <title>Handling Sybase BIGTIME Data Type During Migration to Azure SQL</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/handling-sybase-bigtime-data-type-during-migration-to-azure-sql/ba-p/4480157</link>
      <description>&lt;H4&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc297286694"&gt;&lt;/A&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc11766963"&gt;&lt;/A&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc216866210"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-15"&gt;Introduction&lt;/SPAN&gt;&lt;/H4&gt;
&lt;P&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc11766964"&gt;&lt;/A&gt;Migrating databases from Sybase to SQL Server or Azure SQL is a common modernization scenario. However, not all Sybase data types have direct equivalents in SQL Server, and one such challenge is the BIGTIME data type. The BIGTIME data type in Sybase stores time-of-day values with microsecond precision (format: hh:mm:ss.SSSSSS). It is commonly used in applications that require high-precision time tracking.&lt;/P&gt;
&lt;P&gt;&lt;BR /&gt;To unblock and accelerate this conversion, we have developed an script&amp;nbsp;&lt;STRONG&gt;(sybase_bigtime_migration.sh)&lt;/STRONG&gt; that provides automation to migrate schemas from Sybase ASE to SQL Server specifically where tables contain the BIGTIME datatype. It systematically discovers affected tables, then generates ALTER statements to convert BIGTIME columns to SQL Server’s TIME (6) with a controlled, auditable flow.&lt;/P&gt;
&lt;H4&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc216866211"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-15"&gt;General Guidelines&lt;/SPAN&gt;&lt;/H4&gt;
&lt;P&gt;The purpose of this blog is to provide end‑to‑end flow for discovering BIGTIME columns in Sybase and converting them to SQL Server’s TIME (6). Run the scripts on a host that has Sybase ASE installed and running and SQL Server tools ("sqlcmd") installed and available on the PATH. Provide accurate connection details, passwords are read securely without echoing to the terminal.&lt;/P&gt;
&lt;H4&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc216866212"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-15"&gt;Functionality of the scripts&lt;/SPAN&gt;&lt;/H4&gt;
&lt;P&gt;The script &lt;STRONG&gt;(sybase_bigtime_migration.sh)&lt;/STRONG&gt; validates and sources the Sybase environment, then locates "isql" to query system catalogs for tables with BIGTIME columns. It writes a clean, header-free list to "tablist.txt", ensuring a usable input for the next steps. For each table, it generates an individual ALTER script converting BIGTIME → TIME (6) so you can review or apply changes per object. When SQL migration is enabled, it detects "sqlcmd", tests connectivity, executes each ALTER script, and saves rich logs for verification and troubleshooting.&lt;/P&gt;
&lt;H4&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc216866213"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-15"&gt;Prerequisites&lt;/SPAN&gt;&lt;/H4&gt;
&lt;P&gt;The script &lt;STRONG&gt;(sybase_bigtime_migration.sh)&lt;/STRONG&gt; must be executed from the same host where Sybase ASE is installed and running, to ensure reliable access to system catalogs and local client utilities. The schema conversion of all tables must be performed using &lt;A href="https://www.microsoft.com/en-us/download/details.aspx?id=54256" target="_blank" rel="noopener"&gt;SQL Server Migration Assistant (SSMA)&lt;/A&gt; prior to running this script, ensuring that all non-BIGTIME columns are properly migrated and aligned with Azure SQL standards.&lt;/P&gt;
&lt;P&gt;Ensure access to Sybase ASE instance with permissions to query metadata in "sysobjects", "syscolumns", and "systypes". If you plan to apply changes, you must have SQL Server client tools installed and permissions to run "ALTER TABLE" on the target database objects. Network connectivity from the host to both Sybase and SQL Server is required.&lt;/P&gt;
&lt;P&gt;If you want to run the script only for a specific set of BIGTIME tables in Sybase, create a file named tablist.txt in the same directory as the script. This file should contain the list of BIGTIME tables (one table name per line) that the script should process.&lt;/P&gt;
&lt;PRE&gt;&lt;U&gt;Sybase datatype:&lt;/U&gt;&lt;/PRE&gt;
&lt;img /&gt;&lt;img /&gt;
&lt;PRE&gt;&lt;U&gt;Schema conversion using SSMA:&lt;/U&gt;&lt;/PRE&gt;
&lt;img /&gt;&lt;img /&gt;&lt;img /&gt;&lt;img /&gt;&lt;img /&gt;&lt;img /&gt;
&lt;PRE&gt;&lt;U&gt;Azure SQL datatype after schema conversion using SSMA:&lt;/U&gt;&lt;/PRE&gt;
&lt;img /&gt;&lt;img /&gt;
&lt;H4&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc216866214"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-15"&gt;How to Use&lt;/SPAN&gt;&lt;/H4&gt;
&lt;P&gt;Run the script &lt;STRONG&gt;(sybase_bigtime_migration.sh)&lt;/STRONG&gt; and provide Sybase server, username, password, and database when prompted. Choose whether to perform migration against SQL Server; if yes, supply SQL Server host, credentials, and database. After the detection step, confirm whether to proceed with all tables that have BIGTIME in the specified Sybase database. Selecting “yes” triggers script generation and optional application, selecting “no” exits after guidance, letting you tailor "tablist.txt" before rerunning.&lt;/P&gt;
&lt;img /&gt;&lt;img /&gt;&lt;img /&gt;
&lt;H4&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc2708575"&gt;&lt;/A&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc2790239"&gt;&lt;/A&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc216866215"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-15"&gt;Output Files&lt;/SPAN&gt;&lt;/H4&gt;
&lt;P&gt;&lt;STRONG&gt;"tablist_Final.txt"&lt;/STRONG&gt; output file contains the clean list of tables with BIGTIME columns and is regenerated on each run to reflect the current database. Each run writes an overall validation report, including per-table status and counts to &lt;STRONG&gt;"validation_summary_timestamp.log"&lt;/STRONG&gt; where valid=tables with BIGTIME columns, missing=tables not found in DB, no_bigtime=tables without BIGTIME columns, unverified=validations errors, total_tablist_count=total tables checked from "tablist.txt". Per table ALTER scripts are created as&amp;nbsp;&lt;STRONG&gt;"alter_&amp;lt;SYB_DB&amp;gt;_&amp;lt;TABLE&amp;gt;.sql",&lt;/STRONG&gt; enabling fine-grained review and targeted application. When executing against SQL Server, output logs are saved under &lt;STRONG&gt;"sql_outputs/alter_&amp;lt;SYB_DB&amp;gt;_&amp;lt;TABLE&amp;gt;.out"&lt;/STRONG&gt;. These logs assist with validating results, identifying failures.&lt;A class="lia-anchor" target="_blank" name="_Toc11766971"&gt;&lt;/A&gt;&lt;/P&gt;
&lt;img /&gt;&lt;img /&gt;
&lt;PRE&gt;&lt;U&gt;Final Azure SQL datatype output:&lt;/U&gt;&lt;/PRE&gt;
&lt;img /&gt;&lt;img /&gt;
&lt;H4&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc216866216"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-15"&gt;Data Migration Strategy&lt;/SPAN&gt;&lt;/H4&gt;
&lt;P&gt;After the schema conversion and BIGTIME data type handling are completed, the data migration should be performed as a separate activity. The migration can be executed using Azure Data Factory (ADF) or a custom BCP-based export and import process, based on factors such as data volume, performance requirements, and operational considerations. Separating schema preparation from data movement provides greater flexibility, improved control, and reduced risk during the data migration phase.&lt;/P&gt;
&lt;H4&gt;&lt;SPAN class="lia-text-color-15"&gt;Steps to Download the script&lt;/SPAN&gt;&lt;/H4&gt;
&lt;P&gt;&lt;SPAN class="lia-text-color-21"&gt;Please send an email to the alias &lt;SPAN class="lia-text-color-20"&gt;&lt;A class="lia-external-url" href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;&lt;SPAN class="lia-text-color-15"&gt;datasqlninja@microsoft.com&lt;/SPAN&gt;&lt;/A&gt;,&lt;/SPAN&gt; and we will share the download link along with instructions.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H4&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc216866217"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-15"&gt;Feedback and suggestions&lt;/SPAN&gt;&lt;/H4&gt;
&lt;P&gt;If you have feedback or suggestions for improving this data migration asset, please contact the Databases SQL Customer Success Engineering (Ninja) Team (&lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;datasqlninja@microsoft.com&lt;/A&gt;). Thanks for your support!&lt;/P&gt;
&lt;P&gt;Note: For additional information about migrating various source databases to Azure, see the &lt;A class="lia-external-url" href="https://datamigration.microsoft.com/" target="_blank" rel="noopener"&gt;Azure Database Migration Guide.&lt;/A&gt;&lt;/P&gt;</description>
      <pubDate>Mon, 12 Jan 2026 09:00:00 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/handling-sybase-bigtime-data-type-during-migration-to-azure-sql/ba-p/4480157</guid>
      <dc:creator>saikat_dey</dc:creator>
      <dc:date>2026-01-12T09:00:00Z</dc:date>
    </item>
    <item>
      <title>Implementing Oracle Autonomous Transactions in Azure SQL for Seamless Logging</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/implementing-oracle-autonomous-transactions-in-azure-sql-for/ba-p/4448130</link>
      <description>&lt;P&gt;In Oracle PL/SQL, the directive PRAGMA AUTONOMOUS_TRANSACTION allows a block of code such as a procedure, function, or trigger to run in its own independent transaction. This means:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;It can &lt;STRONG&gt;commit or rollback&lt;/STRONG&gt; changes without affecting the main transaction.&lt;/LI&gt;
&lt;LI&gt;It is &lt;STRONG&gt;fully isolated&lt;/STRONG&gt; from the calling transactions, no shared locks or dependencies.&lt;/LI&gt;
&lt;LI&gt;It is ideal for &lt;STRONG&gt;logging, auditing, and error tracking&lt;/STRONG&gt;, where you want to preserve data even if the main transaction fails.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;Example in Oracle PL/SQL&lt;/STRONG&gt;:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;CREATE OR REPLACE PROCEDURE log_error(p_message VARCHAR2) IS
  PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  INSERT INTO error_log (log_time, message) VALUES (SYSDATE, p_message);
  COMMIT;
END;&lt;/LI-CODE&gt;
&lt;P&gt;This ensures that the error log is saved even if the main transaction rolls back.&lt;/P&gt;
&lt;H3&gt;How to Implement Autonomous Transactions in Azure SQL&lt;/H3&gt;
&lt;P&gt;If you need logs that persist through rollbacks in Azure SQL, you can recreate the behavior of Oracle’s PRAGMA AUTONOMOUS_TRANSACTION using external logging via Azure Function&amp;nbsp;&lt;/P&gt;
&lt;UL data-line="12"&gt;
&lt;LI data-line="12"&gt;External logging via &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-invoke-external-rest-endpoint-transact-sql?view=sql-server-ver17&amp;amp;tabs=request-headers" target="_blank" rel="noopener"&gt;sp_invoke_external_rest_endpoint&lt;/A&gt; to an&amp;nbsp;&lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-overview" target="_blank" rel="noopener"&gt;Azure Function&lt;/A&gt;&amp;nbsp;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P data-line="15"&gt;This blog post shows concrete code for Oracle PL/SQL vs. T‑SQL, and provides a secure, sample Azure Function example.&lt;/P&gt;
&lt;UL data-line="18"&gt;
&lt;LI data-line="18"&gt;There’s no direct PRAGMA AUTONOMOUS_TRANSACTION in Azure SQL as on the date when this blog is written.&lt;/LI&gt;
&lt;LI data-line="19"&gt;To persist logs even if the current transaction rolls back, consider calling an external logger (e.g., Azure Function) using sp_invoke_external_rest_endpoint.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2 data-line="22"&gt;Oracle vs. Azure SQL at a glance&lt;/H2&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table class="lia-align-left" border="1" style="width: 96.2963%; border-width: 1px; border-spacing: 5px;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style="padding: 5px;"&gt;Feature&lt;/th&gt;&lt;th style="padding: 5px;"&gt;Oracle (PRAGMA AUTONOMOUS_TRANSACTION)&lt;/th&gt;&lt;th style="padding: 5px;"&gt;Azure SQL (workarounds)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="padding: 5px;"&gt;Native support&lt;/td&gt;&lt;td style="padding: 5px;"&gt;Yes&lt;/td&gt;&lt;td style="padding: 5px;"&gt;No&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="padding: 5px;"&gt;Isolation from caller txn&lt;/td&gt;&lt;td style="padding: 5px;"&gt;True&lt;/td&gt;&lt;td style="padding: 5px;"&gt;Partial (via external logging)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="padding: 5px;"&gt;Rollback independence&lt;/td&gt;&lt;td style="padding: 5px;"&gt;Yes&lt;/td&gt;&lt;td style="padding: 5px;"&gt;Yes (external logging only)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="padding: 5px;"&gt;Complexity&lt;/td&gt;&lt;td style="padding: 5px;"&gt;Low&lt;/td&gt;&lt;td style="padding: 5px;"&gt;Moderate (extra components)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="padding: 5px;"&gt;Logging persistence&lt;/td&gt;&lt;td style="padding: 5px;"&gt;Immediate&lt;/td&gt;&lt;td style="padding: 5px;"&gt;External or deferred&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="padding: 5px;"&gt;Common use cases&lt;/td&gt;&lt;td style="padding: 5px;"&gt;Audit, error logging, notifications&lt;/td&gt;&lt;td style="padding: 5px;"&gt;Audit, error logging, compliance&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;H3 data-line="33"&gt;External logging via Azure Function and sp_invoke_external_rest_endpoint&lt;/H3&gt;
&lt;P data-line="35"&gt;In SQL use this when the application might wrap your stored procedure in its own transaction. If the app rolls back after your proc finishes, any in-database transactions (including your logs) also roll back. An external logger runs outside the database transaction and remains durable.&lt;/P&gt;
&lt;H3 data-line="37"&gt;Prerequisites and notes&lt;/H3&gt;
&lt;UL data-line="38"&gt;
&lt;LI data-line="38"&gt;sp_invoke_external_rest_endpoint&amp;nbsp;is available in Azure SQL Database and Azure SQL Managed Instance. Ensure outbound network access to your Function endpoint.&lt;/LI&gt;
&lt;LI data-line="39"&gt;Prefer Azure AD (Managed Identity) auth for your Function endpoint. If you must use function keys, pass them via header (x-functions-key), not query strings.&lt;/LI&gt;
&lt;LI data-line="40"&gt;Set reasonable timeouts and add retry/backoff for transient failures.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3 data-line="177"&gt;Minimal SQL prerequisites for Function App Managed Identity&lt;/H3&gt;
&lt;P&gt;Grant your Function App’s managed identity access to the database and tables:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;-- In your Azure SQL Database (connected as an Entra admin) 
CREATE USER [&amp;lt;FUNCTION_APP_MI_NAME&amp;gt;] FROM EXTERNAL PROVIDER; 
ALTER ROLE db_datawriter ADD MEMBER [&amp;lt;FUNCTION_APP_MI_NAME&amp;gt;]; 
-- Or grant explicit permissions 
GRANT INSERT ON dbo.ErrorLogs TO [&amp;lt;FUNCTION_APP_MI_NAME&amp;gt;];&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H3 data-line="42"&gt;T‑SQL: emit a log via REST&lt;/H3&gt;
&lt;LI-CODE lang="sql"&gt;CREATE OR ALTER PROCEDURE dbo.add_numbers
    @x INT,
    @y INT,
     INT OUTPUT
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRY
        -- Log process start
        INSERT INTO dbo.ProcessLogs (LogMessage, ProcedureName)
        VALUES (CONCAT('Started adding numbers: ', @x, ' + ', @y), 'add_numbers');

        -- Add numbers
        SET  = @x + @y;

        -- Log process success
        INSERT INTO dbo.ProcessLogs (LogMessage, ProcedureName)
        VALUES (CONCAT('Successfully calculated sum = ', ), 'add_numbers');
    END TRY

    BEGIN CATCH
        -- Log error details locally
        INSERT INTO dbo.ErrorLogs (ErrorMessage, ErrorProcedure, ErrorLine)
        VALUES (ERROR_MESSAGE(), ERROR_PROCEDURE(), ERROR_LINE());

        -- Prepare JSON payload
        DECLARE @jsonPayload NVARCHAR(MAX) = N'{
            "LogMessage": "Some error occurred",
            "ProcedureName": "add_numbers",
            "ErrorMessage": "' + ERROR_MESSAGE() + N'"
        }';

        -- Call external REST endpoint (Azure Function)
        EXEC sp_invoke_external_rest_endpoint
            @method = 'POST',
            @url = 'https://yourfunctionapp.azurewebsites.net/api/WriteLog',
            @headers = '{"Content-Type": "application/json", "x-functions-key": "&amp;lt;FUNCTION_KEY&amp;gt;"}',
             = @jsonPayload;

        -- Re-throw original error to caller
        THROW;
    END CATCH;
END;
GO
&lt;/LI-CODE&gt;
&lt;H3 data-line="74"&gt;Azure Function: HTTP-triggered logger (C#)&lt;/H3&gt;
&lt;UL data-line="75"&gt;
&lt;LI data-line="75"&gt;Auth: use AuthorizationLevel.Function (or Azure AD), avoid Anonymous in production.&lt;/LI&gt;
&lt;LI data-line="76"&gt;DB access: use Managed Identity User to request an access token for Azure SQL. Once the request token is generated, next step is to insert logs/messages into a permanent log/error table.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Below is an end-to-end Function App code in C# which demonstrates above.&lt;/P&gt;
&lt;LI-CODE lang="csharp"&gt;using Azure.Core;
using Azure.Identity;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace FunctionApp_MI_HTTPRequest
{
    public static class ManagedInstance_Http_Request
    {
        private static readonly string SqlConnectionString = Environment.GetEnvironmentVariable("SqlConnectionString");

        public class ProcedureLog
        {
            public string ProcedureName { get; set; }
            public string LogMessage { get; set; }
        }

        [FunctionName("WriteLog")]
        public static async Task&amp;lt;IActionResult&amp;gt; Run(
            [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("WriteLog invoked.");

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            log.LogInformation($"Received Log Request: {requestBody}");

            ProcedureLog logData;

            try
            {
                logData = JsonSerializer.Deserialize&amp;lt;ProcedureLog&amp;gt;(requestBody, new JsonSerializerOptions
                {
                    PropertyNameCaseInsensitive = true
                });

                if (logData == null || string.IsNullOrWhiteSpace(logData.ProcedureName))
                {
                    return new BadRequestObjectResult(new
                    {
                        status = "Error",
                        message = "Invalid log data."
                    });
                }
            }
            catch (JsonException)
            {
                return new BadRequestObjectResult(new
                {
                    status = "Error",
                    message = "Invalid JSON format."
                });
            }

            try
            {
                // Acquire access token for Azure SQL using Managed Identity
                var tokenCredential = new DefaultAzureCredential();
                var accessToken = await tokenCredential.GetTokenAsync(
                    new TokenRequestContext(new[] { "https://database.windows.net/.default" }),
                    CancellationToken.None);

                using var conn = new SqlConnection(SqlConnectionString)
                {
                    AccessToken = accessToken.Token
                };

                await conn.OpenAsync();
                using var transaction = conn.BeginTransaction();

                var query = @"
                    INSERT INTO dbo.ErrorLogs (ErrorMessage, ErrorProcedure)
                    VALUES (@LogMessage, @ProcedureName);";

                try
                {
                    using var cmd = new SqlCommand(query, conn, transaction);
                    cmd.Parameters.AddWithValue("@LogMessage", (object?)logData.LogMessage ?? string.Empty);
                    cmd.Parameters.AddWithValue("@ProcedureName", logData.ProcedureName);

                    await cmd.ExecuteNonQueryAsync();
                    transaction.Commit();

                    log.LogInformation($"Log inserted: {logData.ProcedureName} | {logData.LogMessage}");

                    return new OkObjectResult(new
                    {
                        status = "Success",
                        message = "Log inserted successfully."
                    });
                }
                catch (SqlException ex)
                {
                    transaction.Rollback();
                    log.LogError(ex, "Database transaction failed.");

                    return new ObjectResult(new
                    {
                        status = "Error",
                        message = "Database transaction failed."
                    })
                    { StatusCode = 500 };
                }
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Internal Server Error");

                return new ObjectResult(new
                {
                    status = "Error",
                    message = "Internal Server Error"
                })
                { StatusCode = 500 };
            }
        }
    }
}
&lt;/LI-CODE&gt;
&lt;H4 data-line="188"&gt;Calling the Function from T‑SQL&lt;/H4&gt;
&lt;P data-line="189"&gt;Header-based key (preferred):&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;DECLARE @jsonPayload NVARCHAR(MAX) = N'{
    "LogMessage": "Some error occurred",
    "ProcedureName": "ProcessData"
}';

EXEC sp_invoke_external_rest_endpoint
    @method  = 'POST',
    @url     = 'https://yourfunctionapp.azurewebsites.net/api/WriteLog',
    @headers = '{"Content-Type": "application/json", "x-functions-key": "&amp;lt;FUNCTION_KEY&amp;gt;"}',
     = @jsonPayload;
&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;CREATE TABLE dbo.ErrorLogs
(
    Id             INT IDENTITY(1,1) PRIMARY KEY,
    ErrorMessage   NVARCHAR(MAX),
    ErrorProcedure NVARCHAR(255),
    ErrorLine      INT NULL,
    ErrorDateTime  DATETIME2 DEFAULT SYSDATETIME()
);
GO

CREATE TABLE dbo.ProcessLogs
(
    Id             INT IDENTITY(1,1) PRIMARY KEY,
    LogMessage     NVARCHAR(MAX),
    ProcedureName  NVARCHAR(255),
    LogDateTime    DATETIME2 DEFAULT SYSDATETIME()
);
GO
&lt;/LI-CODE&gt;
&lt;H2 data-line="277"&gt;Architecture diagram&lt;/H2&gt;
&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2 data-line="327"&gt;PL/SQL reference (Oracle)&lt;/H2&gt;
&lt;P&gt;For comparison, here’s the autonomous transaction pattern in Oracle. Note the autonomous pragma on the logger procedure.&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;-- =========================================
-- Table: ERRORLOGS
-- =========================================
CREATE TABLE errorlogs (
    log_id       NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    procedure_nm VARCHAR2(100),
    error_msg    VARCHAR2(4000),
    log_time     TIMESTAMP DEFAULT SYSTIMESTAMP
);
/

-- =========================================
-- Procedure: LOG_ERROR (Autonomous Transaction Logger)
-- =========================================
CREATE OR REPLACE PROCEDURE log_error (
    p_procedure VARCHAR2,
    p_error     VARCHAR2
) IS
    PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
    INSERT INTO errorlogs (procedure_nm, error_msg)
    VALUES (p_procedure, p_error);

    COMMIT;
END;
/
-- =========================================
-- Procedure: ADD_NUMBERS (Sample Procedure)
-- =========================================
CREATE OR REPLACE PROCEDURE add_numbers (
    p_x   NUMBER,
    p_y   NUMBER,
    p_sum OUT NUMBER
) IS
BEGIN
    p_sum := p_x + p_y;
EXCEPTION
    WHEN OTHERS THEN
        log_error('add_numbers', SQLERRM);
        RAISE;
END;
/
&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Troubleshooting and FAQ -&amp;nbsp;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Why aren’t my logs saved after a rollback in Azure SQL?&amp;nbsp;&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Inserts performed inside the same transaction are rolled back together. Use external logging via 'sp_invoke_external_rest_endpoint' to guarantee persistence regardless of the caller’s transaction.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;Can I use CLR or linked servers for autonomous transactions in Azure SQL? &lt;/STRONG&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;No. CLR integration and linked servers for DML aren’t supported in Azure SQL Database. Prefer external REST endpoints.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;What are the security considerations for external logging?&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Avoid 'Anonymous' Functions. Prefer Azure AD; second-best is 'AuthorizationLevel.Function' with keys in headers. Validate payloads and avoid logging sensitive data unless encrypted. Store secrets in Key Vault.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;How do I monitor and validate the logging pipeline?&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Use Application Insights in your Function. Emit structured logs for both success and failure. Configure alerts on failure rates and latency.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;What performance impact should I expect?&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&amp;nbsp;External calls add network latency. For high-volume scenarios, batch logs, use async fire-and-forget patterns on the app side, and set sensible timeouts.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3&gt;&lt;STRONG&gt;Summary&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;In summary, external logging offers durable visibility across services, making it ideal for distributed systems. Be sure to implement retries and validation, set up monitoring and alerts, and document the behavior clearly for your team to ensure everyone understands the trade-offs involved.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;If you have any feedback or suggestion, please reach out to us at &lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;datasqlninja@microsoft.com&lt;/A&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Mon, 05 Jan 2026 12:00:00 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/implementing-oracle-autonomous-transactions-in-azure-sql-for/ba-p/4448130</guid>
      <dc:creator>Vijay_Kumar</dc:creator>
      <dc:date>2026-01-05T12:00:00Z</dc:date>
    </item>
    <item>
      <title>Data Migration Strategies for Large-Scale Sybase to SQL Migrations Using SSMA, SSIS and ADF- Part 1</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/data-migration-strategies-for-large-scale-sybase-to-sql/ba-p/4455711</link>
      <description>&lt;H2 class="lia-align-justify"&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc297286694"&gt;&lt;/A&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc205974328"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Introduction&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class="lia-align-justify"&gt;In today’s data-driven landscape, the migration of databases is a crucial task that requires meticulous planning and execution. Our recent project for migrating data from Sybase ASE to MSSQL set out to illuminate this process, using tools like Microsoft SQL Server Migration Assistant (SSMA),&amp;nbsp;Microsoft SQL Server&amp;nbsp;Integration Service (SSIS)&amp;nbsp;packages, and Azure Data Factory&amp;nbsp;(ADF).&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;We carefully designed our tests to cover a range of database sizes: 1GB, 10GB, 100GB, and 500GB. Each size brought its own set of challenges and valuable insights, guiding our migration strategies for a smooth transition.&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;For the smaller databases (1GB and 10GB), we utilized SSMA, which&amp;nbsp;demonstrated&amp;nbsp;strong efficiency and reliability in handling straightforward migrations. SSMA was particularly effective in converting database schemas and moving data with minimal complication. As we&amp;nbsp;scaled&amp;nbsp;up&amp;nbsp;larger datasets (100GB and 500GB), we incorporated SSIS packages alongside Azure Data Factory to address the increased complexity and ensure robust performance throughout the migration process.&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;Our exercises yielded important findings related to performance metrics such as data throughput, error rates during transfer, and overall execution times for each migration approach. These insights helped us refine our methodologies and underscored the necessity of selecting the right tools for each migration scenario when transitioning from Sybase to MSSQL.&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;Ultimately, our experience highlighted that thorough analysis is essential for identifying potential bottlenecks and optimizing workflows, enabling successful and efficient migrations across databases of all sizes. The results reassure stakeholders that, with a well-considered approach and comprehensive testing, migrations can be executed seamlessly while maintaining the integrity of all datasets involved.&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;There are 2 parts in the Strategy, the 1st part covers the overview, source database environment setup for Sybase, target database environment setup for Azure SQL, migration steps, time consumed and key learnings for SSMA and SSIS, the remaining sections are covered in Part 2 here :- &lt;A class="lia-internal-link lia-internal-url lia-internal-url-content-type-blog" href="https://techcommunity.microsoft.com/blog/modernizationbestpracticesblog/data-migration-strategies-for-large-scale-sybase-to-sql-migrations-using-ssma-ss/4471308" target="_blank" rel="noopener" data-lia-auto-title="Part 2" data-lia-auto-title-active="0"&gt;Part 2&lt;/A&gt;&lt;/P&gt;
&lt;H2 class="lia-align-justify"&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc205974329"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Objectives of the Evaluation&lt;/SPAN&gt;&lt;/H2&gt;
&lt;UL class="lia-align-justify"&gt;
&lt;LI class="lia-align-justify"&gt;Assess the migration duration for various database sizes.&amp;nbsp;&lt;/LI&gt;
&lt;LI class="lia-align-justify"&gt;Evaluate the capabilities and limitations of each tool.&lt;/LI&gt;
&lt;LI&gt;Identify optimal patterns for bulk data movement.&lt;/LI&gt;
&lt;LI&gt;Recommend the best tool or parameter combination for various scenarios which gives the best results for different sample datasets.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2 class="lia-align-justify"&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc205974330"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Overview of Tools&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG class="lia-align-justify"&gt;SQL Server Migration Assistant (SSMA) for SAP ASE&lt;/STRONG&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;Microsoft SQL Server Migration Assistant (SSMA) for Sybase Adaptive Server Enterprise (ASE) is a tool for migrating SAP ASE databases to SQL Server 2012 (11.x) through SQL Server 2022 (16.x) on Windows and Linux, Azure SQL Database or Azure SQL Managed Instance.  It supports schema conversion, data migration, and limited post-migration testing. SSMA converts Sybase ASE database objects (tables, views, stored procedures, etc.) to Azure SQL-compatible formats and migrates data using a client-side or server-side engine.&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;SQL Server Integration Services (SSIS)&lt;/STRONG&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;SQL Server Integration Services is a platform for building enterprise-level data integration and data transformations solutions. It offers flexibility in handling non-compatible objects, custom transformations, and large-scale data transfers through its pipeline architecture. SSIS is particularly useful when SSMA cannot migrate certain objects or when additional data transformation is required like Unparsed SQL, SET option conversion issues, Identifier conversion and issues, date format conversion and with NON-ANSI joins&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Azure Data Factory (ADF)&lt;/STRONG&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;ADF is a fully managed, serverless, cloud-based data integration service for orchestrating and automating data movement and transformation across on-premises and cloud environments. It is well-suited for hybrid migrations, large-scale data pipelines, and integration with Azure services like Azure SQL. ADF excels in scenarios requiring scalability and parallel processing.&amp;nbsp;&lt;/P&gt;
&lt;H2 class="lia-align-justify"&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc205974331"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Environment Setup&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class="lia-align-justify"&gt;For testing, we used the following setup:&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Source Environment&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL class="lia-align-justify"&gt;
&lt;LI&gt;&lt;STRONG&gt;Sybase ASE Version&lt;/STRONG&gt;: 16.0 SP03&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;OS&lt;/STRONG&gt;: SUSE Linux Enterprise Server 15 SP6&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;VM Size&lt;/STRONG&gt;: Standard B12ms (12 vcpus, 48 GiB memory)&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Target Environment&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL class="lia-align-justify"&gt;
&lt;LI&gt;&lt;STRONG&gt;SQL Server 2022:&lt;/STRONG&gt; hosted on Azure VM&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;OS&lt;/STRONG&gt;: Windows Server 2022&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;VM Size&lt;/STRONG&gt;: Standard D16as v4 (16 vcpus, 64 GiB memory)&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Network&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL class="lia-align-justify"&gt;
&lt;LI&gt;Both VMs hosted in &lt;STRONG&gt;same Azure region&lt;/STRONG&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Premium SSD LRS disks&lt;/STRONG&gt; for source and target&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Metrics&lt;/STRONG&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;We evaluated the tools based on:&lt;/P&gt;
&lt;OL class="lia-align-justify"&gt;
&lt;LI&gt;&lt;STRONG&gt;Data Migration Time&lt;/STRONG&gt;: Time to migrate 1 GB, 10 GB, 100 GB (1 x 100 GB table) and 500 GB (5 x 100 GB tables) of data.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Scalability&lt;/STRONG&gt;: Performance with increased data volumes (up to 500 GB).&lt;/LI&gt;
&lt;/OL&gt;
&lt;H2 class="lia-align-justify"&gt;&lt;SPAN class="lia-text-color-10"&gt;SQL Server Migration Assistant (SSMA)&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Migration steps:&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL class="lia-align-justify"&gt;
&lt;LI&gt;Install SSMA for SAP ASE 10.3 or above and required drivers (Sybase ASE ODBC/ADO.NET providers).&lt;/LI&gt;
&lt;LI&gt;Create an SSMA project, configure source (Sybase ASE) and target (Azure SQL) connections.&lt;/LI&gt;
&lt;LI&gt;Assess database compatibility, customize data type mappings (e.g., Sybase TEXT to SQL Server NVARCHAR(MAX)), and convert schema.&lt;/LI&gt;
&lt;LI&gt;Migrate data using SSMA’s client-side or server-side engine.&lt;/LI&gt;
&lt;LI&gt;Validate migrated objects and data.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Test Results:&lt;/STRONG&gt;&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table class="lia-align-center lia-border-style-solid" border="1" style="width: 100%; height: 280.666px; border-width: 1px;"&gt;&lt;tbody&gt;&lt;tr style="height: 39.3333px;"&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Test Scenario&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Data Size&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Time Taken&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Throughput&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Threads&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Notes&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 39.3333px;"&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Single Copy&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;1 GB&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;28 seconds&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;~36 Mbps&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;1&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;&amp;nbsp;Single Threaded&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 67.3333px;"&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Single Copy&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;10 GB&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;4 minutes 36 seconds&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;~37 Mbps&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;1&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;&amp;nbsp;Single Threaded&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 67.3333px;"&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Parallel Copy&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;100 GB&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;44 minutes&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;~38 Mbps&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;40&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Parallel Threads&amp;nbsp;- Using project level setting changes, like Migration engine, batch size, parallel data migration mode and multi loading&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 67.3333px;"&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Scalability Test&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;500 GB&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;3 hours 44 minutes&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;~38 Mbps&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;40&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Parallel Threads&amp;nbsp;-&amp;nbsp;&amp;nbsp;Same&amp;nbsp;performance as of&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN data-contrast="auto"&gt;sequential nature of SSMA’s processing engine,&amp;nbsp;which limits parallelism&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Key learnings:&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL class="lia-align-justify"&gt;
&lt;LI&gt;&lt;STRONG&gt;Excellent&lt;/STRONG&gt; for schema conversion and small data sets&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Not scalable&lt;/STRONG&gt; beyond 100 GB, memory issues and slow single-threaded loads&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Limited configuration&lt;/STRONG&gt; for tuning bulk inserts&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Performance Tuning Insights:&lt;/STRONG&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Migration Engine: &lt;/STRONG&gt;Configured server-side data migration to optimize performance and reduce processing overhead. Client-side data migration refers to SSMA client retrieving the data from the source and bulk inserting that data into Azure SQL. Server-side data migration refers to SSMA data migration engine (bulk copy program) running on the Azure SQL box as a SQL Agent job retrieving data from the source and inserting directly into Azure SQL thus avoiding an extra client-hop (better performance). When choosing this method, you will need to specify which version of the BCP is intended to use (32bit or 64bit):&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Batch Size: &lt;/STRONG&gt;Data is migrated in batches from the source tables into Azure SQL tables within transactions. The batch size option determines how many rows are loaded into Azure SQL per transaction. By default, it is 10,000, but we increased it to 270,000 since our dataset contained 26,214,400 rows (around 100 GB).&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Parallel Data Migration Mode: &lt;/STRONG&gt;This option is available only when using the Client Side Data Migration Engine mode. It defines the number of parallel threads to be used during migration. By default, it is set to Auto (10 threads). To modify it, select Custom and specify the desired number of parallel threads. We changed this to 40 to get the best results.&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Multi loading: &lt;/STRONG&gt;With Multi-Loading enabled, SSMA uses multiple parallel threads to load data batches at the same time, which can significantly speed up migration for large tables. It essentially breaks the source data into chunks (based on your batch size setting) and loads them concurrently.&lt;/P&gt;
&lt;img /&gt;
&lt;H2 class="lia-align-justify"&gt;&lt;SPAN class="lia-text-color-10"&gt;SQL Server Integration Services (SSIS)&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Migration steps:&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL class="lia-align-justify"&gt;
&lt;LI&gt;Create an SSIS project in SQL Server Data Tools (SSDT).&lt;/LI&gt;
&lt;LI&gt;Configure ODBC / ADO .NET Source (Sybase ASE) and ADO .NET/OLE DB Destination (Azure SQL) with appropriate drivers.&lt;/LI&gt;
&lt;LI&gt;Build Control flow and data flow tasks for each table, applying transformations for incompatible data types or business logic.&lt;/LI&gt;
&lt;LI&gt;Execute packages in parallel for large tables, optimizing buffer size and commit intervals.&lt;/LI&gt;
&lt;LI&gt;Monitor and log errors for troubleshooting.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Test Results:&lt;/STRONG&gt;&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table class="lia-align-center lia-border-style-solid" border="1" style="width: 100%; height: 280.666px; border-width: 1px;"&gt;&lt;tbody&gt;&lt;tr style="height: 39.3333px;"&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Test Scenario&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Data Size&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Time Taken&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Throughput&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Threads&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Notes&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 39.3333px;"&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Single Copy&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;1 GB&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;31 seconds&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;~33 Mbps&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;1&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 39.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;&amp;nbsp;Single Threaded&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 67.3333px;"&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Single Copy&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;10 GB&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;4 minutes 16 seconds&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;~40 Mbps&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;1&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;&amp;nbsp;Single Threaded&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 67.3333px;"&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Parallel Copy&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;100 GB&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;38 minutes&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;~44 Mbps&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;5&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Parallel Threads&amp;nbsp;– Regulating&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN data-contrast="auto"&gt;MaxConcurrentExecutable, DefaultBufferMaxRows, Engine threads and Azure SQL maximum server memory&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 67.3333px;"&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Scalability Test&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;500 GB&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;3 hours 12 minutes&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;~44 Mbps&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;5&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td style="height: 67.3333px;"&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Parallel Threads&amp;nbsp;-&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN data-contrast="auto"&gt;Performance improved with larger buffer sizes (100 MB) and SSD I/O.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Key learnings:&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL class="lia-align-justify"&gt;
&lt;LI&gt;&lt;STRONG&gt;Very fast&lt;/STRONG&gt; for large data volumes with tuning&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Requires development time&lt;/STRONG&gt; (package design, error handling)&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Bottleneck&lt;/STRONG&gt;: network throughput on 500 GB run&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Best suited&lt;/STRONG&gt; for 10–500 GB on-prem or IaaS migrations&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Performance Tuning Insights:&lt;/STRONG&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;DefaultBufferSize: &lt;/STRONG&gt;The task’s buffer settings can be configured using the DefaultBufferSize property, which defines the buffer size, and the &lt;STRONG&gt;DefaultBufferMaxRows&lt;/STRONG&gt; property, which specifies the maximum number of rows per buffer. By default, the buffer size is 10 MB (with a maximum of 100 MB), and the default maximum number of rows is 10,000.&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;DefaultBufferMaxRows: &lt;/STRONG&gt;The data flow engine begins the task of sizing its buffers by calculating the estimated size of a single row of data. It then multiplies the estimated size of a row by the value of &lt;STRONG&gt;DefaultBufferMaxRows&lt;/STRONG&gt; to obtain a preliminary value for the buffer size.&lt;/P&gt;
&lt;UL&gt;
&lt;LI class="lia-align-justify"&gt;&amp;nbsp; If the result is greater than the value of &lt;STRONG&gt;DefaultBufferSize&lt;/STRONG&gt;, the engine reduces the number of rows.&lt;/LI&gt;
&lt;LI class="lia-align-justify"&gt;&amp;nbsp; If the result is less than the minimum buffer size calculated inside the engine increases the number of rows.&lt;/LI&gt;
&lt;LI class="lia-align-justify"&gt;&amp;nbsp; If the result falls between the minimum buffer size and the value of &lt;STRONG&gt;DefaultBufferSize&lt;/STRONG&gt;, the engine sizes the buffer as close as possible to the estimated row size times the value of &lt;STRONG&gt;DefaultBufferMaxRows&lt;/STRONG&gt;.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="lia-align-justify"&gt;If sufficient memory is available, it is better to use fewer large buffers instead of many small ones. In other words, performance improves when the number of buffers is minimized, and each buffer holds as many rows as possible.&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;IsSorted: &lt;/STRONG&gt;Sorting using a SSIS task is a slow operation by definition. Avoiding unnecessary sorting can enhance the performance of data flow in the package. Set the &lt;STRONG&gt;IsSorted&lt;/STRONG&gt; property of a component in the output data flow upstream to &lt;STRONG&gt;True&lt;/STRONG&gt;.&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;&amp;nbsp;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;ADO .NET Source:&lt;/STRONG&gt; When retrieving data from a view using an OLE DB data source, choose &lt;EM&gt;SQL command&lt;/EM&gt; as the data access mode and provide a SELECT statement. Using a SELECT statement ensures that the view is accessed in the most efficient way.&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;OLEDB Destination: &lt;/STRONG&gt;Several OLE DB Destination settings can significantly impact data transfer performance:&lt;/P&gt;
&lt;UL class="lia-align-justify"&gt;
&lt;LI&gt;Data Access Mode – This setting offers the &lt;EM&gt;Fast Load&lt;/EM&gt; option, which internally uses a BULK INSERT statement to load data into the destination table, instead of executing individual INSERT statements for each row. Unless you have a specific reason to change it, keep the default &lt;EM&gt;Fast Load&lt;/EM&gt; option enabled. When using &lt;EM&gt;Fast Load&lt;/EM&gt;, additional performance-related settings become available (listed below).&lt;/LI&gt;
&lt;LI&gt;Keep Identity – By default, this is unchecked. If the destination table has an identity column, SQL Server generates identity values automatically. Checking this option ensures that identity values from the source are preserved and inserted into the destination.&lt;/LI&gt;
&lt;LI&gt;Keep Nulls – By default, this is unchecked. If a NULL value is encountered in the source and the target column has a default constraint, the default value will be inserted. Enabling this option preserves the NULL values from the source instead of applying the default constraint.&lt;/LI&gt;
&lt;LI&gt;Table Lock – This is checked by default, meaning a table-level lock is acquired during data load instead of multiple row-level locks. This prevents lock escalation issues and generally improves performance. Keep this enabled unless the table is actively being used by other processes at the same time.&lt;/LI&gt;
&lt;LI&gt;Check Constraints – Checked by default. This validates incoming data against the destination table’s constraints. If you are confident the data will not violate constraints, unchecking this option can improve performance by skipping the validation step.&lt;/LI&gt;
&lt;LI&gt;Rows per Batch – The default value is -1, which means all incoming rows are treated as a single batch. You can change this to a positive integer to divide the incoming rows into multiple batches, where the value specifies the maximum number of rows per batch.&lt;/LI&gt;
&lt;LI&gt;Maximum Insert Commit Size – The default value is 2147483647 (the maximum for a 4-byte integer), which commits all rows in a single transaction once the load completes successfully. You can set this to a positive integer to commit data in smaller chunks. While committing more frequently does add overhead to the data flow engine, it helps reduce pressure on the transaction log and tempdb, preventing excessive growth during high-volume data loads.&lt;/LI&gt;
&lt;/UL&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;&amp;nbsp;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;DelayValidation: &lt;/STRONG&gt;SSIS uses validation to determine if the package could fail at runtime. SSIS uses two types of validation. First is package validation (early validation) which validates the package and all its components before starting the execution of the package.&amp;nbsp; Second SSIS uses component validation (late validation), which validates the components of the package once started. If you set it to TRUE, early validation will be skipped and the component will be validated only at the component level (late validation) which is during package execution.&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;&amp;nbsp;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;MaxConcurrentExecutables&lt;/STRONG&gt;: It's the property of the SSIS package and specifies the number of executables (different tasks inside the package) that can run in parallel within a package or in other words, the number of threads SSIS runtime engine can create to execute the executables of the package in parallel.&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;&amp;nbsp;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;EngineThreads&lt;/STRONG&gt;: This property specifies the number of source threads (does data pull from source) and worker thread (does transformation and upload into the destination) that can be created by data flow pipeline engine to manage the flow of data and data transformation inside a data flow task, it means if the EngineThreads has value 5 then up to 5 source threads and also up to 5 worker threads can be created. Please note, this property is just a suggestion to the data flow pipeline engine, the pipeline engine may create less or more threads if required.&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;IsolationLevel&lt;/STRONG&gt;: In SQL Server Integration Services (SSIS),&amp;nbsp;IsolationLevel&amp;nbsp;is a property that defines how a transaction within a package interacts with other concurrent transactions in the database.&amp;nbsp;It determines the degree to which one transaction is isolated from the effects of other transactions. Default is ‘Serializable’ which Locks the entire data set being read and keeps the lock until the transaction completes. Instead set it to ‘ReadUncommited’ or ‘ReadCommited’. ‘ReadUncommited’ Reads data without waiting for other transactions to finish. Can read rows that are not yet committed (a.k.a. &lt;EM&gt;dirty reads&lt;/EM&gt;). While ‘ReadCommited’ only reads committed rows. Prevents dirty reads by waiting until other transactions commit or roll back. Use ReadUncommited when you need speed and can tolerate inaccurate data else use ReadCommited but it is slower than Read Uncommitted because it must wait for locks to release.&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;TransactionOption: &lt;/STRONG&gt;The TransactionOption property in SQL Server Integration Services (SSIS) is used to control how tasks, containers, or the entire package participate in transactions to ensure data integrity. It determines whether a task or container starts a transaction, joins an existing one, or does not participate in any transaction. The property is available at the package level, container level (e.g., For Loop, Foreach Loop, Sequence), and for individual Control Flow tasks (e.g., Execute SQL Task, Data Flow Task). Set this to ‘NotSupported’ by which the task or container does not participate in any transaction, even if a parent container or package has started one. If a transaction exists at a higher level (e.g., package or parent container), the task or container operates outside of it. It is the fastest because there’s no transaction overhead.&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Database Recovery Model:&lt;/STRONG&gt; The source database is configured to use the Bulk-logged recovery model, which aims to minimally&amp;nbsp;log bulk operations.&amp;nbsp;This should be significantly more performant than the Full recovery model, assuming the bulk insert meets the criteria required to be minimally logged. The criteria for the target&amp;nbsp;table are as follows –&lt;/P&gt;
&lt;UL class="lia-align-justify"&gt;
&lt;LI&gt;The target table is not being replicated.&lt;/LI&gt;
&lt;LI&gt;Table locking is specified (using TABLOCK).&lt;/LI&gt;
&lt;LI&gt;If the table has no indexes, data pages are minimally logged.&lt;/LI&gt;
&lt;LI&gt;If the table does not have a clustered index but has one or more non-clustered indexes, data pages are always minimally logged. How index pages are logged, however, depends on whether the table is empty –&lt;/LI&gt;
&lt;LI&gt;If the table is empty, index pages are minimally logged.&lt;/LI&gt;
&lt;LI&gt;If table is non-empty, index pages are fully logged.&lt;/LI&gt;
&lt;LI&gt;If the table has a clustered index and is empty, both data and index pages are minimally logged. In contrast, if a table has a clustered index and is non-empty, data pages and index pages are both fully logged regardless of the recovery model.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Pulling High Volumes of Data: &lt;/STRONG&gt;To enhance the speed and efficiency of the data migration process, we devised an optimized approach that involves transforming the target table into a heap by dropping all its indexes at the outset, thereby eliminating the overhead of index maintenance during data insertion. Following this, we transfer the data to the heap table, which is significantly faster due to the absence of indexes. Once the data transfer is complete, we recreate the indexes on the target table to restore its original structure and optimize query performance. This streamlined method of dropping indexes, transferring data to a heap, and then recreating indexes substantially accelerates the overall migration process compared to maintaining indexes throughout.&lt;/P&gt;
&lt;H2 class="lia-align-justify"&gt;&lt;SPAN class="lia-text-color-10"&gt;Feedback and suggestions&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class="lia-align-justify"&gt;We hope this post has helped you configure your migration solution and choose the right options to successfully migrate your databases.&amp;nbsp;The remaining steps are covered in Part 2 here :-&amp;nbsp;&lt;A class="lia-internal-link lia-internal-url lia-internal-url-content-type-blog" href="https://techcommunity.microsoft.com/blog/modernizationbestpracticesblog/data-migration-strategies-for-large-scale-sybase-to-sql-migrations-using-ssma-ss/4471308" data-lia-auto-title="Part 2" data-lia-auto-title-active="0" target="_blank"&gt;&lt;STRONG&gt;Part 2&lt;/STRONG&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;If you have feedback or suggestions for improving this data migration asset, please contact the Databases SQL Customer Success Engineering (Ninja) Team (&lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;datasqlninja@microsoft.com&lt;/A&gt;). Thanks for your support!&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;Note: For additional information about migrating various source databases to Azure, see the &lt;A class="lia-external-url" href="https://datamigration.microsoft.com/" target="_blank" rel="noopener"&gt;Azure Database Migration Guide&lt;/A&gt;.&lt;/P&gt;</description>
      <pubDate>Fri, 12 Dec 2025 16:33:49 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/data-migration-strategies-for-large-scale-sybase-to-sql/ba-p/4455711</guid>
      <dc:creator>Ankur_Sinha</dc:creator>
      <dc:date>2025-12-12T16:33:49Z</dc:date>
    </item>
    <item>
      <title>Data Migration Strategies for Large-Scale Sybase to SQL Migrations Using SSMA, SSIS and ADF- Part 2</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/data-migration-strategies-for-large-scale-sybase-to-sql/ba-p/4471308</link>
      <description>&lt;H2 class="lia-align-justify"&gt;&lt;SPAN class="lia-text-color-10"&gt;Introduction&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class="lia-align-justify"&gt;In today’s data-driven landscape, the migration of databases is a crucial task that requires meticulous planning and execution. Our recent project for migrating data from Sybase ASE to MSSQL set out to illuminate this process, using tools like Microsoft SQL Server Migration Assistant (SSMA),&amp;nbsp;Microsoft SQL Server&amp;nbsp;Integration Service (SSIS)&amp;nbsp;packages, and Azure Data Factory&amp;nbsp;(ADF).&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;We carefully designed our tests to cover a range of database sizes: 1GB, 10GB, 100GB, and 500GB. Each size brought its own set of challenges and valuable insights, guiding our migration strategies for a smooth transition.&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;For the smaller databases (1GB and 10GB), we utilized SSMA, which&amp;nbsp;demonstrated&amp;nbsp;strong efficiency and reliability in handling straightforward migrations. SSMA was particularly effective in converting database schemas and moving data with minimal complication. As we&amp;nbsp;scaled&amp;nbsp;up&amp;nbsp;larger datasets (100GB and 500GB), we incorporated SSIS packages alongside Azure Data Factory to address the increased complexity and ensure robust performance throughout the migration process.&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;Our exercises yielded important findings related to performance metrics such as data throughput, error rates during transfer, and overall execution times for each migration approach. These insights helped us refine our methodologies and underscored the necessity of selecting the right tools for each migration scenario when transitioning from Sybase to MSSQL.&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;Ultimately, our experience highlighted that thorough analysis is essential for identifying potential bottlenecks and optimizing workflows, enabling successful and efficient migrations across databases of all sizes. The results reassure stakeholders that, with a well-considered approach and comprehensive testing, migrations can be executed seamlessly while maintaining the integrity of all datasets involved.&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;There are 2 parts in the solution. &lt;A class="lia-internal-link lia-internal-url lia-internal-url-content-type-blog" href="https://techcommunity.microsoft.com/blog/modernizationbestpracticesblog/data-migration-strategies-for-large-scale-sybase-to-sql-migrations-using-ssma-ss/4455711" data-lia-auto-title="Part 1" data-lia-auto-title-active="0" target="_blank"&gt;&lt;STRONG&gt;Part 1&lt;/STRONG&gt;&lt;/A&gt; covers SSMA and SSIS. Part 2 covers the Azure Data Factory (ADF), tests results, performance improvement guidelines and conclusion.&lt;/P&gt;
&lt;H2 class="lia-align-justify"&gt;&lt;SPAN class="lia-text-color-10"&gt;Azure Data Factory (ADF)&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Migration steps:&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL class="lia-align-justify"&gt;
&lt;LI&gt;Set up an Azure Data Factory instance and Integration Runtime (IR) for on-premises connectivity.&lt;/LI&gt;
&lt;LI&gt;Create pipelines with Copy data activities, mapping Sybase ASE tables to SQL Server/Azure SQL.&lt;/LI&gt;
&lt;LI&gt;Configure parallel copy settings and staging storage (Azure Blob Storage) for large datasets (if required).&lt;/LI&gt;
&lt;LI&gt;Monitor pipeline execution and retry failed activities.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;&amp;nbsp;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Test Results:&amp;nbsp;&lt;/STRONG&gt;&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table class="lia-align-center lia-border-style-solid" border="1" style="border-width: 1px;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Test Scenario&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Data Size&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Time Taken&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Throughput&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Threads&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Notes&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Single Copy&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;1 GB&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;40 seconds&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;~25 Mbps&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;1&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;&amp;nbsp;Single Threaded&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Single Copy&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;10 GB&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;4 minutes 22 seconds&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;~40 Mbps&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;1&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;&amp;nbsp;Single Threaded&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Parallel Copy&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;100 GB&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;39 minutes&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;~44 Mbps&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;16&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Parallel Threads&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Scalability Test&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;500 GB&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;3 hours 10 minutes&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;~45 Mbps&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;16&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;SPAN data-contrast="none"&gt;Parallel Threads - ADF scaled better due to cloud elasticity&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;201341983&amp;quot;:0,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:160,&amp;quot;335559740&amp;quot;:276}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Key learnings:&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL class="lia-align-justify"&gt;
&lt;LI&gt;&lt;STRONG&gt;Cloud-native, scalable&lt;/STRONG&gt;: handled 500 GB with ease&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Parallel copy&lt;/STRONG&gt; and &lt;STRONG&gt;batch tuning&lt;/STRONG&gt; make it faster as volume increases&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Auto-scaling&lt;/STRONG&gt; prevents many typical OOM (Out of Memory) errors seen with SSMA&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Recommended&lt;/STRONG&gt; for cloud targets (Azure SQL MI, Azure SQL DB)&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Performance Tuning Insights:&lt;/STRONG&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;In our setup we added a lookup, a foreach and one copy data within our pipeline.&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Lookup: &lt;/STRONG&gt;In the Settings tab, select the SQL dataset as the source. Instead of choosing the entire table, go to the “Use query” section and provide the query shown below. This query uses a recursive Common Table Expression (CTE) to dynamically generate partition ranges across a large integer sequence (representing the total number of rows).&amp;nbsp;&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;Here is a sample output of this query for top 20 rows–&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;ForEach: &lt;/STRONG&gt;Use this query in the Pipeline expression builder which is a reference to the Lookup output in our pipeline.&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt; &lt;/STRONG&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;Copydata: &lt;/STRONG&gt;Use below query at source which allows you to dynamically query different partitions of a large table (like Table_1_100GB) in parallel or sequentially.&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;On Sink, change “write batch size” and “Max concurrent connections as per the rowset and assigned memory. Optimizing these values can improve performance and reduce overhead during data transfers. For small rows, increase writeBatchSize to reduce batch overhead and improve throughput. For large rows, use a smaller value to avoid memory or database overload. If the source data exceeds the specified batch size, ADF processes data in multiple batches automatically.&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;Additionally adjust “Maximum data integration unit” and “Degree of copy parallelism”. A Data Integration Unit (DIU) is a measure that represents the power of a single unit in Azure Data Factory and Synapse pipelines. Power is a combination of CPU, memory, and network resource allocation. DIU only applies to Azure integration runtime. DIU doesn't apply to self-hosted integration runtime. While Degree of copy parallelism determines how many parallel copy activities can run simultaneously, optimizing the data transfer process.&lt;/P&gt;
&lt;H2 class="lia-align-justify"&gt;&lt;SPAN class="lia-text-color-10"&gt;Cumulative Tests Result&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class="lia-align-justify"&gt;&lt;STRONG&gt;&lt;EM&gt;&lt;U&gt;Disclaimer&lt;/U&gt;&lt;/EM&gt;:&lt;/STRONG&gt; &lt;EM&gt;All test results published herein are provided solely for reference purposes and reflect performance under ideal conditions within our controlled environment. Actual performance in the user's environment may vary significantly due to factors including, but not limited to, network speed, system bottlenecks, hardware limitations, CPU cores, memory, disk I/O, firewall configurations, and other environmental variables. On the source and target databases also multiple&amp;nbsp;&lt;/EM&gt;&lt;EM&gt;performance optimization and configuration adjustments have been implemented&lt;/EM&gt;&lt;EM&gt; to enhance the migration efficiency. We strongly recommend that users conduct their own testing to determine performance under their specific conditions.&lt;/EM&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN lia-align-justify"&gt;&lt;table class="lia-border-style-ridge" border="1" style="width: 1010px; border-width: 1px;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;Data Size&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;Best Performer&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;Observation&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;1 GB&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P class="lia-align-left"&gt;All tools performed efficiently&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Minimal overhead; no significant performance difference.&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;10 GB&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;SSIS&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Optimized batch processing led to better performance.&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;100 GB&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;SSIS and ADF&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Both benefited from parallelism.&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;500 GB&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;ADF and SSIS&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;ADF's scalability and retry mechanisms proved valuable. SSIS was equivalent with tuned data flow components.&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN lia-align-justify"&gt;&lt;table class="lia-border-style-ridge" border="1" style="width: 1010px; border-width: 1px;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG class="lia-align-center"&gt;Data Volume&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;Row Count&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;SSMA Time / Speed&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;SSIS Time / Speed&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;ADF Time / Speed&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;1 GB&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;262,144&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;28 sec / ⚡ &lt;STRONG&gt;36 MB/s&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;31 sec / ⚡33 MB/s&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;40 sec / ⚡25 MB/s&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;10 GB&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;2,621,440&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;4 min 36 sec / ⚡37 MB/s&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;4 min 16 sec / ⚡&lt;STRONG&gt;40 MB/s&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;4 min 22 sec / ⚡40 MB/s&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;100 GB&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;26,214,400&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;44 min / ⚡38 MB/s&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;38 min / ⚡&lt;STRONG&gt;44 MB/s&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;39 min / ⚡44 MB/s&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;500 GB&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;26,214,400 x 5&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;3 hr 44 min / ⚡38 MB/s&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;3 hr 12 min / ⚡44 MB/s&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;3 hr 10 min / ⚡&lt;STRONG&gt;45 MB/s&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;H2&gt;&lt;SPAN class="lia-text-color-10"&gt;Performance Improvement Guidelines&lt;/SPAN&gt;&lt;/H2&gt;
&lt;OL&gt;
&lt;LI&gt;&lt;STRONG&gt;Use SSMA for Schema Conversion&lt;/STRONG&gt;: SSMA is the primary tool for automating schema conversion. Customize data type mappings (e.g., Sybase DATETIME to SQL Server DATETIME2) and handle case-sensitive databases carefully.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Leverage SSIS for Complex Data Transformations&lt;/STRONG&gt;: Use SSIS for tables with non-compatible data types or when business logic requires transformation. Optimize performance with parallel tasks and appropriate buffer settings.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Use ADF for Large-Scale or Hybrid Migrations&lt;/STRONG&gt;: ADF is ideal for large datasets or migrations to Azure SQL Database. Use staging storage and parallel copy to maximize throughput. Ensure stable network connectivity for on-premises to cloud transfers.
&lt;P&gt;&lt;STRONG&gt;&lt;EM&gt;Tips to improve ADF performance&lt;/EM&gt;&lt;/STRONG&gt;&lt;EM&gt;:&lt;/EM&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Use &lt;STRONG&gt;staging areas&lt;/STRONG&gt; (e.g., Azure Blob Storage) to offload source systems and speed up data transfers.&lt;/LI&gt;
&lt;LI&gt;Enable &lt;STRONG&gt;parallel copy&lt;/STRONG&gt; in Copy Activity to increase throughput.&lt;/LI&gt;
&lt;LI&gt;Use the &lt;STRONG&gt;Integration Runtime&lt;/STRONG&gt; closest to your data source to reduce network latency.&lt;/LI&gt;
&lt;LI&gt;Enable &lt;STRONG&gt;data partitioning&lt;/STRONG&gt; on large tables to parallelize read/write operations.&lt;/LI&gt;
&lt;LI&gt;Adjust &lt;STRONG&gt;degree of parallelism&lt;/STRONG&gt; to match your compute capacity.&lt;/LI&gt;
&lt;LI&gt;Use &lt;STRONG&gt;Self-Hosted IR&lt;/STRONG&gt; or &lt;STRONG&gt;Azure IR with higher compute&lt;/STRONG&gt; for large or complex migrations.&lt;/LI&gt;
&lt;LI&gt;Enable &lt;STRONG&gt;Auto-Scaling&lt;/STRONG&gt; where supported to handle spikes efficiently.&lt;/LI&gt;
&lt;LI&gt;Monitor IR utilization to avoid under-provisioning or over-provisioning.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Please refer below links for more details related to ADF –&lt;/P&gt;
&lt;P&gt;&lt;A class="lia-external-url" href="https://aka.ms/SybaseASETransferTableDataCopyToSQL" target="_blank" rel="noopener"&gt;Sybase ASE to Azure SQL full and incremental data copy using ASE Transfer Table Tool and ADF&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/data-factory/copy-activity-performance-features#parallel-copy" target="_blank" rel="noopener"&gt;Copy activity performance optimization features - Azure Data Factory &amp;amp; Azure Synapse | Microsoft Learn&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A class="lia-internal-link lia-internal-url lia-internal-url-content-type-blog" href="https://techcommunity.microsoft.com/blog/modernizationbestpracticesblog/db2-to-azure-sql-db-parallel-data-copy-by-generating-adf-copy-activities-dynamic/3605541" target="_blank" rel="noopener" data-lia-auto-title="Db2 to Azure SQL fast data copy using ADF" data-lia-auto-title-active="0"&gt;Db2 to Azure SQL fast data copy using ADF&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Migration Readiness Testing:&lt;/STRONG&gt; Conduct performance testing on production-scale environments prior to the actual migration to obtain an accurate baseline of system behavior and identify potential bottlenecks under real workload conditions.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Hybrid Approach:&lt;/STRONG&gt; Combine SSMA for schema conversion, SSIS for complex data migrations, and ADF for orchestration in large-scale or cloud-based scenarios. For example, use SSMA to convert schemas, SSIS to migrate problematic tables, and ADF to orchestrate the overall pipeline.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Validation&lt;/STRONG&gt;: Post-migration, validate data integrity using checksums or row counts and test stored procedures for functional equivalence. Use SQL Server Management Studio (SSMS) for debugging. In the end we can use Microsoft Database Compare Utility that allows comparison of multiple source and target databases.&lt;/LI&gt;
&lt;/OL&gt;
&lt;H2 class="lia-align-justify"&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc205974336"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Challenges and Mitigations&lt;/SPAN&gt;&lt;/H2&gt;
&lt;UL class="lia-align-justify"&gt;
&lt;LI&gt;&lt;STRONG&gt;Sybase-Specific Syntax&lt;/STRONG&gt;: SSMA may fail to convert complex stored procedures with Sybase-specific T-SQL. Manually rewrite these using SQL Server T-SQL.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;LOB Data&lt;/STRONG&gt;: Large Object (LOB) data types (e.g., TEXT, IMAGE) may cause truncation errors. Map to NVARCHAR(MAX) or VARBINARY(MAX) and validate data post-migration.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Network Latency in ADF&lt;/STRONG&gt;: For on-premises to Azure migrations, ensure high-bandwidth connectivity or use Azure ExpressRoute to minimize latency.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Case Sensitivity&lt;/STRONG&gt;: Sybase ASE databases may be case-sensitive, while SQL Server defaults to case-insensitive. Configure SQL Server collations (e.g. SQL_Latin1_General_CP1_CS_AS) to match source behavior.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2 class="lia-align-justify"&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc205974337"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Conclusion&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class="lia-align-justify"&gt;SSMA, SSIS, and ADF each offer unique strengths for migrating Sybase ASE to SQL Server, Azure SQL Database &lt;SPAN data-contrast="auto"&gt;or Azure SQL Managed Instance&lt;/SPAN&gt;. SSMA excels in schema conversion, SSIS in complex data transformations, and ADF in scalability and cloud integration. A hybrid approach, leveraging SSMA for schema conversion, SSIS for problematic data, and ADF for orchestration, often yields the best results. Evaluation shows ADF’s superior scalability for large datasets, while SSIS provides flexibility for complex migrations.&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;Proper planning, including schema assessment, data type mapping, and performance tuning, is critical for a successful migration. For further details refer to Microsoft’s official documentation:&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;SSMA for Sybase: &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/sql/ssma/sybase/sql-server-migration-assistant-for-sybase-sybasetosql?view=sql-server-ver17" target="_blank" rel="noopener"&gt;SQL Server Migration Assistant for Sybase (SybaseToSQL) - SQL Server | Microsoft Learn&lt;/A&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;A href="https://learn.microsoft.com/en-us/sql/ssma/sybase/project-settings-migration-sybasetosql?view=sql-server-ver17" target="_blank" rel="noopener"&gt;&lt;SPAN class="lia-text-color-21"&gt;SSMA Project Settings:&lt;/SPAN&gt;&lt;/A&gt; &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/sql/ssma/sybase/project-settings-migration-sybasetosql?view=sql-server-ver17" target="_blank" rel="noopener"&gt;Project Settings (Migration) (SybaseToSQL) - SQL Server | Microsoft Learn&lt;/A&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;SSIS: &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/sql/integration-services/sql-server-integration-services?view=sql-server-ver17" target="_blank" rel="noopener"&gt;SQL Server Integration Services - SQL Server Integration Services (SSIS) | Microsoft Learn&lt;/A&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;ADF: &lt;A class="lia-external-url" href="https://azure.microsoft.com/en-in/products/data-factory" target="_blank" rel="noopener"&gt;Azure Data Factory - Data Integration Service | Microsoft Azure&lt;/A&gt;&lt;/P&gt;
&lt;H2 class="lia-align-justify"&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc205974338"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Feedback and suggestions&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class="lia-align-justify"&gt;If you have feedback or suggestions for improving this data migration asset, please contact the Databases SQL Customer Success Engineering (Ninja) Team (&lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;datasqlninja@microsoft.com&lt;/A&gt;). Thanks for your support!&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;Note: For additional information about migrating various source databases to Azure, see the &lt;A class="lia-external-url" href="https://datamigration.microsoft.com/" target="_blank" rel="noopener"&gt;Azure Database Migration Guide&lt;/A&gt;.&lt;/P&gt;</description>
      <pubDate>Fri, 12 Dec 2025 16:29:05 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/data-migration-strategies-for-large-scale-sybase-to-sql/ba-p/4471308</guid>
      <dc:creator>Ankur_Sinha</dc:creator>
      <dc:date>2025-12-12T16:29:05Z</dc:date>
    </item>
    <item>
      <title>Faster Data Copy between Source and target for partitioned table using Partition Switch in ADF</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/faster-data-copy-between-source-and-target-for-partitioned-table/ba-p/4456585</link>
      <description>&lt;H1&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc207650893"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Introduction&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;This blog post presents a comprehensive Azure Data Factory (ADF) solution for automating the migration of partitioned tables from IBM Db2 z/OS to Azure SQL Database. The solution consists of two main components: a Partition Discovery &amp;amp; Preparation Pipeline and a Parallel Copy Pipeline with partition switching capabilities. This approach significantly reduces migration time and ensures data integrity while maintaining partition structure in the target environment. As we use one separate table per partition for data copy, it divides the workload into multiple different tables hence reducing the complexity in data migration. &amp;nbsp;For few partitioned/non-partitioned tables, there is &lt;A class="lia-internal-link lia-internal-url lia-internal-url-content-type-blog" href="https://techcommunity.microsoft.com/blog/modernizationbestpracticesblog/db2-to-azure-sql-db-parallel-data-copy-by-generating-adf-copy-activities-dynamic/3605541" target="_blank" rel="noopener" data-lia-auto-title="another approach" data-lia-auto-title-active="0"&gt;another approach&lt;/A&gt; where parallel copy activities can be implemented.&lt;/P&gt;
&lt;H1&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc207650894"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Challenges in migrating of large, partitioned tables&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;Migrating large, partitioned tables from IBM Db2 z/OS to Azure SQL Database presents unique challenges:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Complex Partition Structures: Db2 z/OS supports various partitioning schemes (range, hash, list) that need to be properly mapped&lt;/LI&gt;
&lt;LI&gt;Large Data Volumes: Enterprise tables can contain billions of rows across hundreds of partitions&lt;/LI&gt;
&lt;LI&gt;Minimal Downtime Requirements: Business-critical applications require near-zero downtime migrations&lt;/LI&gt;
&lt;LI&gt;Data Integrity: Ensuring consistency across all partitions during migration&lt;/LI&gt;
&lt;LI&gt;Performance Optimization: Maximizing throughput while managing resource consumption&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;This approach addresses most of the challenges above in an intelligent, automated approach:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Automatically discovers partition metadata from Db2 system tables&lt;/LI&gt;
&lt;LI&gt;Creates optimized migration plans based on partition characteristics&lt;/LI&gt;
&lt;LI&gt;Create one temporary persistent table per partition(Which can be dropped after the process is complete) with the same partition function and scheme to help in migration(&lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/sql/relational-databases/tables/create-check-constraints?view=sql-server-ver17" target="_blank" rel="noopener"&gt;Check constraint&lt;/A&gt; is another way to implement this, however for the sake of simplicity, we are cloning the base table)&lt;/LI&gt;
&lt;LI&gt;Executes parallel data transfer with partition-level granularity&lt;/LI&gt;
&lt;LI&gt;Implements &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-table-transact-sql?view=sql-server-ver17#c-switch-partitions-between-tables" target="_blank" rel="noopener"&gt;partition switching&lt;/A&gt; for minimal downtime&lt;/LI&gt;
&lt;LI&gt;Provides comprehensive monitoring and error handling&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1&gt;&lt;SPAN class="lia-text-color-10"&gt;Solution Overview&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;The solution has two phases both implemented using an automated ADF Pipeline.&lt;/P&gt;
&lt;H2&gt;&lt;SPAN class="lia-text-color-10"&gt;Phase 1: Discovery &amp;amp; Preparation&lt;/SPAN&gt;&lt;/H2&gt;
&lt;UL&gt;
&lt;LI&gt;Using a &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/data-factory/connector-db2?tabs=data-factory" target="_blank" rel="noopener"&gt;Db2 copy activity&lt;/A&gt;, extract necessary data from Db2 System tables.&lt;/LI&gt;
&lt;LI&gt;Extracts partition metadata (boundaries, row counts, sizes)&lt;/LI&gt;
&lt;LI&gt;Creates migration control tables in Azure SQL Database&lt;/LI&gt;
&lt;LI&gt;Generates optimized copy strategies per partition&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;&lt;SPAN class="lia-text-color-10"&gt;Phase 2: Copy and Partition switch&lt;/SPAN&gt;&lt;/H2&gt;
&lt;UL&gt;
&lt;LI&gt;Executes partition-level data transfers in parallel&lt;/LI&gt;
&lt;LI&gt;Implements partition switching for seamless integration&lt;/LI&gt;
&lt;LI&gt;Provides real-time status tracking and error handling&lt;/LI&gt;
&lt;LI&gt;Supports restart capabilities for failed partitions&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc207650895"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Prerequisites&lt;/SPAN&gt;&lt;/H1&gt;
&lt;OL&gt;
&lt;LI&gt;&lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/data-factory/introduction" target="_blank" rel="noopener"&gt;Azure data factory&lt;/A&gt; instance&lt;/LI&gt;
&lt;LI&gt;&lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/data-factory/create-self-hosted-integration-runtime?tabs=data-factory" target="_blank" rel="noopener"&gt;Self-Hosted Integration Runtime (SHIR)&lt;/A&gt; with Db2 connectivity&lt;/LI&gt;
&lt;LI&gt;Access to System tables on Db2.&lt;/LI&gt;
&lt;LI&gt;Schema of Db2 Source Partitioned table should be migrated to SQL(SQL table will be used to clone temporary persistent tables per partition)&lt;/LI&gt;
&lt;/OL&gt;
&lt;H1&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc207650896"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;ADF Pipeline Phase 1: Partition Discovery &amp;amp; Preparation Pipeline&lt;/SPAN&gt;&lt;/H1&gt;
&lt;img&gt;&lt;STRONG&gt;Fig 1.1 Partition Preparation Pipeline&lt;/STRONG&gt;&lt;/img&gt;
&lt;P&gt;The diagram above illustrates the initial phase of the solution, which sets up the necessary tables and extracts essential information from Db2 into Azure SQL. Here’s a breakdown of each step:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;The first script establishes a control table in SQL called Db2_PARTITION_STATUS.&lt;/LI&gt;
&lt;LI&gt;The second script adds indexes to Db2_PARTITION_STATUS to improve access speed.&lt;/LI&gt;
&lt;LI&gt;The third step uses copy activities to retrieve partition details from Db2 system tables and populate the Db2_PARTITION_STATUS table.&lt;/LI&gt;
&lt;LI&gt;The fourth step creates a stored procedure named ClonePartitionedTable, which automates the cloning of SQL tables—one for each partition—making it easier to handle hundreds of partitions.&lt;/LI&gt;
&lt;LI&gt;The fifth step fetches relevant rows from Db2_PARTITION_STATUS. Users can decide which tables to migrate by updating the MIGRATE column in this table.&lt;/LI&gt;
&lt;LI&gt;The final step generates a clone of the source table for each partition, supporting the overall migration process.&lt;/LI&gt;
&lt;/OL&gt;
&lt;H1&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc207650897"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;ADF Pipeline Phase 2: Data Copy &amp;amp; Partition Switch&lt;/SPAN&gt;&lt;/H1&gt;
&lt;img&gt;&lt;STRONG&gt;Fig 1.2 Parallel copy and Switch pipeline&lt;/STRONG&gt;&lt;/img&gt;
&lt;P&gt;The next phase involves the actual data copy between partitions&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;First step gets the table and partition information for the rows which need to be migrated using a lookup activity in ADF.&lt;/LI&gt;
&lt;LI&gt;For each row that is retrieved from step1 above, there are a series of activities which are executed in parallel. Details of it are provided below.&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;img&gt;&lt;STRONG&gt;Fig 1.3 ForEach Activity Details&lt;/STRONG&gt;&lt;/img&gt;
&lt;P&gt;As there are multiple rows which come into the foreach activity, each instance of foreach represents on partition of the source table. So the activities highlighted below will be performed for each and every partition in parallel that has been marked for migration.&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;&lt;STRONG&gt; &lt;/STRONG&gt;First we update the Status table that the data copy has started for this particular partition using a script activity.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt; &lt;/STRONG&gt;Next is a copy activity, which copies the particular partition data information from source to the same partition on a cloned SQL table.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt; &lt;/STRONG&gt;If the Copy activity was successfully, the status table is updated with a status as Start Switching.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt; &lt;/STRONG&gt;If the copy activity fails, it updates the status as Copy Failed. This helps in tracking which partition data was copied and which failed. It also helps in restarting copy of failed partitions.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt; &lt;/STRONG&gt;Then there is a script activity which actually does the partition switch using an Alter table command.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt; &lt;/STRONG&gt;Again the status of the partition switch is updated as success/Failure in the status table, helping in exactly tracking which partitions were successful and which were not.&lt;/LI&gt;
&lt;/OL&gt;
&lt;H1&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc207650898"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Conclusion&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;This comprehensive Azure Data Factory solution provides a robust, scalable approach to migrating partitioned tables from IBM Db2 z/OS to Azure SQL Database. However it is not restricted to Db2 as source. The same architecture can be used for other partitioned sources with slight modifications. The two-pipeline architecture ensures:&lt;/P&gt;
&lt;H2&gt;&lt;SPAN class="lia-text-color-10"&gt;Key Benefits Achieved&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Automated Discovery: Eliminates manual partition mapping and reduces human error&lt;/LI&gt;
&lt;LI&gt;Intelligent Optimization: Applies data-driven strategies for optimal performance&lt;/LI&gt;
&lt;LI&gt;Parallel Processing: Maximizes throughput through partition-level parallelism&lt;/LI&gt;
&lt;LI&gt;Minimal Downtime: Uses partition switching for near-instantaneous data integration&lt;/LI&gt;
&lt;LI&gt;Comprehensive Monitoring: Provides real-time visibility into migration progress&lt;/LI&gt;
&lt;LI&gt;Error Resilience: Isolates failures and enables selective retry operations&lt;/LI&gt;
&lt;/OL&gt;
&lt;H2&gt;&lt;SPAN class="lia-text-color-10"&gt;Business Impact&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Reduced Migration Time: From weeks to days through automation and parallelization&lt;/LI&gt;
&lt;LI&gt;Lower Risk: Comprehensive testing and rollback capabilities&lt;/LI&gt;
&lt;LI&gt;Cost Efficiency: Optimal resource utilization and reduced manual effort&lt;/LI&gt;
&lt;LI&gt;Maintainability: Standardized approach applicable across multiple migration projects&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;&lt;SPAN class="lia-text-color-10"&gt;Future Enhancements&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;The solution foundation supports several potential enhancements:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Machine Learning Integration: Predictive optimization of batch sizes and thread counts&lt;/LI&gt;
&lt;LI&gt;Advanced Monitoring: Integration with Azure Monitor and custom dashboards&lt;/LI&gt;
&lt;LI&gt;Cross-Platform Support: Extension to other database platforms beyond Db2&lt;/LI&gt;
&lt;LI&gt;Automated Testing: Built-in data validation and integrity checking&lt;/LI&gt;
&lt;LI&gt;Cloud-Native Optimization: Leverage Azure SQL Database elastic pools and serverless compute&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;This solution represents a best-practice approach to enterprise data migration, combining the power of Azure Data Factory with intelligent partition management strategies. Organizations implementing this solution can expect significant reductions in migration time, risk, and cost while achieving reliable, repeatable results across their data migration initiatives.&lt;/P&gt;
&lt;H1&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc207650899"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Feedback and suggestions&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;If you have feedback or suggestions for improving this data migration asset, please send an email to&amp;nbsp;&lt;A class="lia-external-url" href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;Database Platform Engineering Team&lt;/A&gt;.&lt;/P&gt;</description>
      <pubDate>Fri, 05 Dec 2025 01:08:02 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/faster-data-copy-between-source-and-target-for-partitioned-table/ba-p/4456585</guid>
      <dc:creator>Ramanath_Nayak</dc:creator>
      <dc:date>2025-12-05T01:08:02Z</dc:date>
    </item>
    <item>
      <title>AI-Powered Db2 LUW to Azure Database for PostgreSQL Schema Converter</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/ai-powered-db2-luw-to-azure-database-for-postgresql-schema/ba-p/4458436</link>
      <description>&lt;P&gt;&lt;STRONG&gt;Introduction&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;This blog introduces a custom-built tool designed to migrate database objects from IBM Db2 LUW (Linux, Unix, Windows) to Azure Database for PostgreSQL. While SSMA for Db2 supports migrations to SQL Server, this tool specifically assists with migration to Azure Database for PostgreSQL, helping customers transition smoothly to the Azure ecosystem.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Db2 LUW to Azure database for PostgreSQL&lt;/STRONG&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;As organizations work to migrate Db2 systems, tools that assist with database migrations are increasingly relevant. The &lt;STRONG&gt;Db2 LUW to Azure PostgreSQL Database Converter&lt;/STRONG&gt; is designed to facilitate the migration of IBM Db2 LUW (Linux, Unix, Windows) databases to Azure database for PostgreSQL. Created by the Microsoft Azure SQL CSE/Ninja Team, this tool provides an interface intended to support migration workflows.&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;With the appropriate drivers and prerequisites, the tool currently supports the conversion of Db2 LUW to Azure database for PostgreSQL. Future versions of this may include enhancements to existing object conversion and support for other Db2 platforms like z/OS and Db2 for i (AS400).&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;System Requirements&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;▪&amp;nbsp;&amp;nbsp;&amp;nbsp; Supports Db2 on LUW version 9.8, 10.1, and later versions&lt;/P&gt;
&lt;P&gt;▪&amp;nbsp;&amp;nbsp;&amp;nbsp; Windows 10, Windows 11&lt;/P&gt;
&lt;P&gt;▪&amp;nbsp;&amp;nbsp;&amp;nbsp; .NET 8.0 Desktop Runtime&lt;/P&gt;
&lt;P&gt;▪&amp;nbsp;&amp;nbsp;&amp;nbsp; Microsoft OLEDB Provider for DB2 is required to access the IBM Db2 Databases.&lt;/P&gt;
&lt;P&gt;▪&amp;nbsp;&amp;nbsp;&amp;nbsp; Azure OpenAI models (For converting Triggers, Functions and Stored Procedures).&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Database Object Conversion&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The Db2 LUW to Azure PostgreSQL Database Converter streamlines enterprise-scale database migrations with a segmented workflow. Users are self-guided through connection setup, schema selection and object conversion via intuitive tabs and input fields. With Azure OpenAI integration Db2 LUW triggers, functions and procedures are converted seamlessly. The tool also features built-in conversion log and statistics, a robust design for reliable use by engineers and architects during database migrations.&lt;/P&gt;
&lt;img /&gt;
&lt;P class="lia-align-justify"&gt;The tool effectively extracts metadata from the source Db2 database and transforms the relevant objects into PostgreSQL formats. All Db2 metadata is retained locally, enabling efficient reuse during subsequent offline conversion process. Error and warning notifications are issued as appropriate, with detailed error log files generated to record any issues arising throughout execution and conversion. Furthermore, a comprehensive telemetry report detailing object conversions is provided upon completion of the process.&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;&lt;SPAN data-teams="true"&gt;Looking to migrate your Db2 LUW workloads to Azure Database for PostgreSQL? Contact the Azure SQL Engineering Team at &lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;datasqlninja@microsoft.com&lt;/A&gt; to get started with the tool and user guide.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;If you have feedback or suggestions for improving this data migration asset, please contact the &lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;Database Platform Engineering Team&lt;/A&gt;. Thanks for your support!&lt;EM&gt; &lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class="lia-align-justify"&gt;Note: Db2 editions on z/OS, iSeries, and Linux/UNIX/Windows (LUW) differ in their subsystems, databases, and object definitions/functions. This tool currently supports only Db2 LUW; Db2 z/OS and Db2 iSeries are not yet supported.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM class="lia-align-justify"&gt;Future Road Map: &amp;nbsp;&lt;/EM&gt;&lt;EM&gt;Multiple significant enhancements are scheduled for the current tool, including expanded support for Db2 for z/OS and Db2 for iSeries. Aimed at strengthening compatibility, increasing performance, and introducing additional features to facilitate enterprise-level Db2 database migrations to Azure PostgreSQL.&lt;/EM&gt;&lt;/P&gt;</description>
      <pubDate>Wed, 12 Nov 2025 14:56:30 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/ai-powered-db2-luw-to-azure-database-for-postgresql-schema/ba-p/4458436</guid>
      <dc:creator>naruldoss</dc:creator>
      <dc:date>2025-11-12T14:56:30Z</dc:date>
    </item>
    <item>
      <title>Enforcing SQL PaaS backup retention with Azure Policy</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/enforcing-sql-paas-backup-retention-with-azure-policy/ba-p/4443657</link>
      <description>&lt;H2&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc205816612"&gt;&lt;/A&gt;Implementation for SQL DB PITR using the portal&lt;/H2&gt;
&lt;P&gt;Azure policy covers much more than SQL; here we are using a small portion of its capabilities. The bits we are using are&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;A policy definition, on what the policy checks for, and what to do about issues&lt;/LI&gt;
&lt;LI&gt;A policy assignment, with the scope to check the definition across, and parameter values&lt;/LI&gt;
&lt;LI&gt;A remediation task, that makes the required changes&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The requirement in this example is to ensure that all Azure SQL Databases have a short-term (PITR) backup retention of at least 9 days.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Any database created without specifying the retention period will have this added&lt;/LI&gt;
&lt;LI&gt;Any update made with a shorter period will have that modified to be 9 days&lt;/LI&gt;
&lt;LI&gt;Modifications or database creation that explicitly set the retention period to more than 9 days will have that value honoured&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;All these are built under “Policy” in the portal&lt;/P&gt;
&lt;img /&gt;
&lt;H3&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc205816613"&gt;&lt;/A&gt;The definition&lt;/H3&gt;
&lt;P&gt;Open Policy | Authoring | Definitions, and on that blade, use the “+ Policy definition” to create a new one&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Definition Location:&lt;/STRONG&gt; the subscription to hold this (there’s a pull-down menu of valid items)&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Name:&lt;/STRONG&gt; for example, “Enforce SQL DB PITR”&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Category:&lt;/STRONG&gt; for example “Backup”&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Role Definitions:&lt;/STRONG&gt; “Contributor” for this example, but in general this should be the minimum needed for the updates that the definition will make&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Policy rule:&amp;nbsp;&lt;/STRONG&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="json"&gt;{
  "mode": "All",
  "policyRule": {
    "if": {
      "anyOf": [
        {
          "field": "Microsoft.Sql/servers/databases/backupShortTermRetentionPolicies/retentionDays",
          "exists": false
        },
        {
          "field": "Microsoft.Sql/servers/databases/backupShortTermRetentionPolicies/retentionDays",
          "less": "[parameters('Minimum_PITR')]"
        }
      ]
    },
    "then": {
      "effect": "modify",
      "details": {
        "roleDefinitionIds": [
          "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"
        ],
        "operations": [
          {
            "operation": "addOrReplace",
            "field": "Microsoft.Sql/servers/databases/backupShortTermRetentionPolicies/retentionDays",
            "value": "[parameters('Minimum_PITR')]"
          }
        ]
      }
    }
  },
  "parameters": {
    "Minimum_PITR": {
      "type": "Integer",
      "metadata": {
        "displayName": "Min PITR",
        "description": "Min PITR retention days"
      }
    }
  }
}&lt;/LI-CODE&gt;
&lt;P&gt;In this code&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;STRONG&gt;Field&lt;/STRONG&gt; is what we want to check and/or change; get the list of field names using PowerShell&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="powershell"&gt;$aliases = Get-AzPolicyAlias -ListAvailable -NamespaceMatch 'Microsoft.Sql' 
| where ResourceType -like 'retentionpol' 
| Select-Object -ExpandProperty 'Aliases' $aliases | select Name&lt;/LI-CODE&gt;
&lt;UL&gt;
&lt;LI&gt;For the list of fields that can be modified/updated, look at the Modifiable attribute&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="powershell"&gt;$aliases | Where-Object { $_.DefaultMetadata.Attributes -eq 'Modifiable' } 
| select Name &lt;/LI-CODE&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Minimum_PITR&lt;/STRONG&gt; is the name of the parameter the assignment (next step) will pass in. You choose the name of the parameter&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;roleDefinitionIds&lt;/STRONG&gt; are the full GUID path of the roles that the update needs. The &lt;A href="https://learn.microsoft.com/en-us/azure/governance/policy/how-to/remediate-resources?tabs=azure-portal#configure-the-policy-definition" target="_blank" rel="noopener"&gt;policy remediation docs&lt;/A&gt; talk about this, but we can get the GUID with PowerShell&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="powershell"&gt;Get-AzRoleDefinition -name 'contributor' # replace contributor with the role needed&lt;/LI-CODE&gt;
&lt;P&gt;This definition is saying that if the PITR retention isn’t set, or is less than the parameter value, then make it (via addOrReplace) the parameter value.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H3&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc205816614"&gt;&lt;/A&gt;The Assignment&lt;/H3&gt;
&lt;P&gt;Once you save the definition, use “Assign policy” on the screen that appears&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;For this, there are several tabs&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Basics:
&lt;UL&gt;
&lt;LI&gt;Scope and exclusions let you work on less than the entire subscription&lt;/LI&gt;
&lt;LI&gt;enable “policy enforcement”&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;Parameters
&lt;UL&gt;
&lt;LI&gt;Enter 9 for Min_PITR (to have policy apply 9 days as the minimum)&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;Remediation
&lt;UL&gt;
&lt;LI&gt;Tick “create remediation task”&lt;/LI&gt;
&lt;LI&gt;Default is to use a system managed identity&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Then create this assignment&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H3&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc205816615"&gt;&lt;/A&gt;&amp;nbsp;Initial Remediation&lt;/H3&gt;
&lt;P&gt;Once the assignment is created, look at the compliance blade to see it; Azure policy is asynchronous, so for a newly created assignment, it takes a little while before it begins checking resources in its scope.&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;Similarly, “remediation tasks” on the remediation blade shows the task pending to begin with.&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;Once the initial remediation scan completes, you can look at the backup retention policies (in Data Management | backups) on the logical server(s) and see that the PITR retention periods have been increased to a minimum of 9 days.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H3&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc205816616"&gt;&lt;/A&gt;Ongoing operation&lt;/H3&gt;
&lt;P&gt;With the initial remediation complete, the policy will now intercept non-compliant changes, and refactor them on the fly. For example, if we use PowerShell to set the retention to 2 days&lt;/P&gt;
&lt;LI-CODE lang="powershell"&gt;$DB_PITR = get-azsqldatabasebackupshorttermretentionpolicy -ResourceGroupName mylittlestarter-rg -ServerName mylittlesql -DatabaseName oppo
$DB_PITR | Set-AzSqlDatabaseBackupShortTermRetentionPolicy -RetentionDays 2


ResourceGroupName         : mylittlestarter-rg
ServerName                : mylittlesql
DatabaseName              : oppo
RetentionDays             : 9
DiffBackupIntervalInHours : 12&lt;/LI-CODE&gt;
&lt;P&gt;The update completes, but the summary shows that the retention stays as 9 days&lt;/P&gt;
&lt;P&gt;The experience on the portal is the same; we can change the retention to 1 day in the GUI, and the operation succeeds, but with the retention remaining at 9 days. In the activity log of either the logical server or the database, this shows up as a modify, with the JSON detail of the modify showing the policy name and the effect.&lt;/P&gt;
&lt;img /&gt;&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H3&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc205816617"&gt;&lt;/A&gt;Tricky bits&lt;/H3&gt;
&lt;P&gt;A few challenges that can cause delays:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Retention policies are separate resources – Both short-term and long-term backup retention aren’t direct attributes of the database resource. Instead, they exist as their own resources (e.g., with retentionDays) tied to the database.&lt;/LI&gt;
&lt;LI&gt;
&lt;P&gt;Keep policies simple – Focusing each policy on a single resource (like SQL DB PITR) proved more effective than trying to create one large, all-encompassing policy.&lt;/P&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;P&gt;Case sensitivity matters – The policy definition code is case-sensitive, which can easily trip you up if not handled carefully.&lt;/P&gt;
&lt;/LI&gt;
&lt;LI&gt;The definitionRoleID is just the GUID of the security role that the policy is going to need, not anything to do with the identity that’s created for the remediation task…but the GUID is potentially different for each subscription, hence the PowerShell to figure out this GUID&lt;/LI&gt;
&lt;LI&gt;Writing the definitions in PowerShell means that they are just plain-text, and don’t have any syntax helpers; syntax issues in the definition tend to appear as strange “error converting to JSON” messages.&lt;/LI&gt;
&lt;LI&gt;Waiting patiently for the initial policy remediation cycle to finish; I haven’t found any “make it so” options&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3&gt;References&lt;/H3&gt;
&lt;P&gt;The posts mentioned in the introduction are&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;A href="https://techcommunity.microsoft.com/blog/modernizationbestpracticesblog/automatically-enable-ltr-and-pitr-policy-upon-a-database-creation-on-azure-sql-m/3740040" target="_blank" rel="noopener"&gt;Automatically Enable LTR and PITR Policy upon a Database creation on Azure SQL Managed Instance | Microsoft Community Hub&lt;/A&gt; using audits and runbooks&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://techcommunity.microsoft.com/blog/azuredbsupport/azure-custom-policy-to-prevent-backup-retention-period-to-be-below-x-number---az/3967097" target="_blank" rel="noopener"&gt;Azure custom policy to prevent backup retention period to be below X number - Azure SQL | Microsoft Community Hub&lt;/A&gt; which uses ‘Deny’ to fail attempts that don’t meet the requirements.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc205816618"&gt;&lt;/A&gt;Expanding this using PowerShell&lt;/H2&gt;
&lt;P&gt;With a working example for SQL DB PITR, we now want to expand this to have policies that cover both short and long term retention for both SQL DB and SQL MI.&lt;/P&gt;
&lt;P&gt;The code below isn’t exhaustive, and being a sample, doesn’t have error checking; note that the code uses “less” for the policy test, but operators like “equals” and “greater” (&lt;A href="https://learn.microsoft.com/en-us/azure/governance/policy/concepts/definition-structure-policy-rule#conditions" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/en-us/azure/governance/policy/concepts/definition-structure-policy-rule#conditions&lt;/A&gt; ) are available to build more complex tests, depending on the policy requirements. The document &lt;A href="https://learn.microsoft.com/en-us/azure/governance/policy/how-to/programmatically-create" target="_blank" rel="noopener"&gt;Programmatically create policies - Azure Policy | Microsoft Learn&lt;/A&gt; covers using powershell with Azure policy.&lt;/P&gt;
&lt;P&gt;Other wrinkles that this sample doesn’t explicitly cater for include&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;LTR retentions are held in ISO 8601 format (eg, ‘P8D’ for 8 days), so it’s not trivial to do less than tests; in theory&amp;nbsp;&lt;A href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/template-functions" target="_blank" rel="noopener"&gt;ARM template functions&lt;/A&gt; could be used to convert these into the number of days, but this example just does an equality check, and enforces the policy, without any understanding that P4W is a longer period than P20D&lt;/LI&gt;
&lt;LI&gt;LTR&amp;nbsp; isn’t available for serverless databases with autopause enabled (&lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/serverless-tier-overview?view=azuresql&amp;amp;tabs=general-purpose#auto-pause" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/en-us/azure/azure-sql/database/serverless-tier-overview?view=azuresql&amp;amp;tabs=general-purpose#auto-pause&lt;/A&gt; ); this would need some form of scope control, potentially either using resource groups, or a more complex test in the policy definition to look at the database attributes&lt;/LI&gt;
&lt;LI&gt;A few service levels, for example the Basic database SLO, have different limits for their short term retention&lt;/LI&gt;
&lt;LI&gt;PITR for databases that could be offline (stopped managed instances, auto-paused serverless databases, etc) hasn’t been explicitly tested.&lt;/LI&gt;
&lt;LI&gt;Remediation tasks just run to completion, with no rescheduling; to ensure that all existing databases are made compliant, this could be expanded to have a loop to check the count of resources needing remediation, and start a task if the relevant existing ones are complete&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="powershell"&gt;&amp;lt;# /***This Artifact belongs to the Data SQL Ninja Engineering Team***/

Name:     Enforce_SQL_PaaS_backup_retention.ps1
Author:   Databases SQL CSE/Ninja, Microsoft Corporation
Date:     August 2025
Version:  1.0

Purpose: This is a sample to create the Azure policy defintions, assignment and remediation tasks to enfore organisational policies for minimum short-term (PITR) and weekly long-term (LTR) backup retention.
  
Prerequisities:
- connect to your azure environment using Connect-AzAccount
- Register the resource provider (may already be done in your environment) using Register-AzResourceProvider -ProviderNamespace 'Microsoft.PolicyInsights'
- if needed to modify/update this script, this can be used to find field names:
Get-AzPolicyAlias -ListAvailable -NamespaceMatch 'Microsoft.Sql' | where ResourceType -like '*retentionpol*' | Select-Object -ExpandProperty 'Aliases' | select Name

      
Warranty: This script is provided on as "AS IS" basis and there are no warranties, express or implied, including, but not limited to implied warranties of merchantability or fitness for a particular purpose. USE AT YOUR OWN RISK. 
Feedback: Please provide comments and feedback to the author at datasqlninja@microsoft.com
#&amp;gt;


# parameters to modify

$Location = 'EastUS'        # the region to create the managed identities used by the remediation tasks
$subscriptionID = (Get-AzContext).Subscription.id  # by default use the current Subscription as the scope; change if needed

# the policies to create; PITR can do a less than comparison, but LTR has dates, so uses string equalities
[array]$policies = @()
$policies += @{type = 'DB'; backups='PITR'; name = 'Enforce SQL DB PITR retention'; ParameterName = 'Minimum_PITR'; ParameterValue = 9; Role = 'contributor'; Category='Backup'}
$policies += @{type = 'MI'; backups='PITR'; name = 'Enforce SQL MI PITR retention'; ParameterName = 'Minimum_PITR'; ParameterValue = 9; Role = 'contributor'; Category='Backup'}
# LTR retention is in ISO8601 format, eg P2W = 2 weeks, P70D = 70 days; 'PT0S' = no retention
$policies += @{type = 'DB'; backups='LTR';name = 'Enforce SQL DB LTR retention'; Weekly = 'P4W'; Monthly = 'PT0S'; Yearly = 'PT0S'; WeekofYear = 1; Role = 'contributor'; Category='Backup'}
$policies += @{type = 'MI'; backups='LTR';name = 'Enforce SQL MI LTR retention'; Weekly = 'P4W'; Monthly = 'PT0S'; Yearly = 'PT0S'; WeekofYear = 1; Role = 'contributor'; Category='Backup'}


# templates for the Policy definition code; this has placeholders that are replaced in the loop
$Policy_definition_template_PITR = @'
{
  "mode": "All",
  "policyRule": {
    "if": {
      "anyOf": [
        {
          "field": "Microsoft.Sql/&amp;lt;Type&amp;gt;/databases/backupShortTermRetentionPolicies/retentionDays",
          "exists": false
        },
        {
          "field": "Microsoft.Sql/&amp;lt;Type&amp;gt;/databases/backupShortTermRetentionPolicies/retentionDays",
          "less": "[parameters('&amp;lt;ParameterName&amp;gt;')]"
        }
      ]
    },
    "then": {
      "effect": "modify",
      "details": {
        "roleDefinitionIds": [
          "/providers/Microsoft.Authorization/roleDefinitions/&amp;lt;RoleGUID&amp;gt;"
        ],
        "operations": [
          {
            "operation": "addOrReplace",
            "field": "Microsoft.Sql/&amp;lt;Type&amp;gt;/databases/backupShortTermRetentionPolicies/retentionDays",
            "value": "[parameters('&amp;lt;ParameterName&amp;gt;')]"
          }
        ]
      }
    }
  },
  "parameters": {
    "&amp;lt;ParameterName&amp;gt;": {
      "type": "Integer"
    }
  }
}
'@

# LTR, look for any of the weekly/monthly/yearly retention settings not matching
$Policy_definition_template_LTR = @'
{
  "mode": "All",
  "policyRule": {
    "if": {
      "anyOf": [
        {
          "field": "Microsoft.Sql/&amp;lt;Type&amp;gt;/databases/backupLongTermRetentionPolicies/weeklyRetention",
          "exists": false
        },
        {
          "field": "Microsoft.Sql/&amp;lt;Type&amp;gt;/databases/backupLongTermRetentionPolicies/weeklyRetention",
          "notEquals": "[parameters('Weekly_retention')]"
        },
        {
          "field": "Microsoft.Sql/&amp;lt;Type&amp;gt;/databases/backupLongTermRetentionPolicies/monthlyRetention",
          "notEquals": "[parameters('Monthly_retention')]"
        },
        {
          "field": "Microsoft.Sql/&amp;lt;Type&amp;gt;/databases/backupLongTermRetentionPolicies/yearlyRetention",
          "notEquals": "[parameters('Yearly_retention')]"
        }
      ]
    },
    "then": {
      "effect": "modify",
      "details": {
        "roleDefinitionIds": [
          "/providers/Microsoft.Authorization/roleDefinitions/&amp;lt;RoleGUID&amp;gt;"
        ],
        "operations": [
          {
            "operation": "addOrReplace",
            "field": "Microsoft.Sql/&amp;lt;Type&amp;gt;/databases/backupLongTermRetentionPolicies/weeklyRetention",
            "value": "[parameters('Weekly_retention')]"
          },
          {
            "operation": "addOrReplace",
            "field": "Microsoft.Sql/&amp;lt;Type&amp;gt;/databases/backupLongTermRetentionPolicies/monthlyRetention",
            "value": "[parameters('Monthly_retention')]"
          },
          {
            "operation": "addOrReplace",
            "field": "Microsoft.Sql/&amp;lt;Type&amp;gt;/databases/backupLongTermRetentionPolicies/yearlyRetention",
            "value": "[parameters('Yearly_retention')]"
          },
          {
            "operation": "addOrReplace",
            "field": "Microsoft.Sql/&amp;lt;Type&amp;gt;/databases/backupLongTermRetentionPolicies/weekOfYear",
            "value": "[parameters('WeekofYear')]"
          }
        ]
      }
    }
  },
  "parameters": {
    "Weekly_retention": {
      "type": "String"
    },
    "Monthly_retention": {
      "type": "String"
    },
    "Yearly_retention": {
      "type": "String"
    },
    "WeekofYear": {
      "type": "Integer"
    }
  }
}
'@

# main loop

foreach ($policy in $policies)
{
    # translate the Role name into its GUID
    $Role = Get-AzRoleDefinition -name $($policy.Role)
    $type = $policy.type -replace 'MI','managedInstances' -replace 'DB','servers'
    $template = if ($policy.backups -eq 'PITR') {$Policy_definition_template_PITR} else {$Policy_definition_template_LTR}
    # generate the definition code for this policy
    $policy_definition = $template -replace '&amp;lt;Type&amp;gt;',$type -replace '&amp;lt;RoleGUID&amp;gt;',$($Role.Id) -replace '&amp;lt;ParameterName&amp;gt;',$policy.ParameterName 

    # create the policy definition
    $PolicyDefinition = new-AzPolicyDefinition -Name $($policy.name) -Policy $policy_definition -Metadata "{'category':'$($policy.Category)'}"
    
    # create the assignment
    if ($policy.backups -eq 'PITR')
        {
            $PolicyParameters = @{$($policy.ParameterName)=($($policy.ParameterValue))}
        }
        else
        {   
            $PolicyParameters = @{"Weekly_retention"=($($policy.Weekly)); "Monthly_retention"=($($policy.Monthly)); "Yearly_retention"=($($policy.Yearly)); "WeekofYear"=($($policy.WeekofYear));}
        }
        $PolicyAssignment = New-AzPolicyAssignment -Name $($policy.name) -PolicyDefinition $PolicyDefinition -PolicyParameterObject $PolicyParameters -IdentityType 'SystemAssigned' -Location $Location

    # now follow the docs page to wait for the ID to be created, and assign the roles required to it; https://learn.microsoft.com/en-us/azure/governance/policy/how-to/remediate-resources?tabs=azure-powershell
    # include a loop to wait until the managed identity created as part of the assignment creation is available
    do
    {
      $ManagedIdentity = Get-AzADServicePrincipal -ObjectId $PolicyAssignment.IdentityPrincipalId -erroraction SilentlyContinue
      if (!($ManagedIdentity)) {start-sleep -Seconds 1} # wait for a bit...
    }
    until ($ManagedIdentity)
    $roleDefinitionIds = $PolicyDefinition.PolicyRule.then.details.roleDefinitionIds
    
    if ($roleDefinitionIds.Count -gt 0)
    {
        $roleDefinitionIds | ForEach-Object {
            $roleDefId = $_.Split("/") | Select-Object -Last 1
            $roleAssigned = New-AzRoleAssignment -ObjectId $PolicyAssignment.IdentityPrincipalId -RoleDefinitionId $roleDefId -Scope "/subscriptions/$($subscriptionID)"
        }
    }

    # lastly create the remediation task
    $RemediationTask = Start-AzPolicyRemediation -Name $($policy.name) -PolicyAssignmentId $PolicyAssignment.Id 
}

# confirm that the policies have been set up
Get-AzPolicyDefinition | where name -In $policies.name | format-table Name, PolicyType
Get-AzPolicyAssignment | where name -In $policies.name | format-table Name, Parameter&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc205816619"&gt;&lt;/A&gt;&amp;nbsp;Feedback and suggestions&lt;/H2&gt;
&lt;P&gt;If you have feedback or suggestions for improving this data migration asset, please contact the Databases SQL Customer Success Engineering (Ninja) Team (&lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;datasqlninja@microsoft.com&lt;/A&gt;). Thanks for your support!&lt;/P&gt;
&lt;P&gt;Note: For additional information about migrating various source databases to Azure, see the &lt;A href="https://datamigration.microsoft.com/" target="_blank" rel="noopener"&gt;Azure Database Migration Guide&lt;/A&gt;.&lt;/P&gt;</description>
      <pubDate>Mon, 08 Sep 2025 15:56:52 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/enforcing-sql-paas-backup-retention-with-azure-policy/ba-p/4443657</guid>
      <dc:creator>David_Lyth</dc:creator>
      <dc:date>2025-09-08T15:56:52Z</dc:date>
    </item>
    <item>
      <title>Migrating Oracle Partitioned Tables to Azure PostgreSQL Without Altering Partition Keys</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/migrating-oracle-partitioned-tables-to-azure-postgresql-without/ba-p/4441962</link>
      <description>&lt;P&gt;&lt;STRONG&gt;Introduction&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;When migrating partitioned tables from Oracle to Azure PostgreSQL Flexible Server, many customers prefer to preserve their existing Oracle table design exactly as defined in the original DDLs. Specifically, they want to avoid altering the partition key structure, especially by not adding the partition key to any primary or unique constraints, because doing so would change the table’s original design integrity.&lt;/P&gt;
&lt;P&gt;The challenge arises because PostgreSQL enforces a rule: any primary key or unique constraint on a partitioned table must include the partition key. This difference in constraint handling creates a migration roadblock for customers aiming for a like-for-like move from Oracle without schema changes.&lt;/P&gt;
&lt;P&gt;To bridge this gap and emulate Oracle’s partitioning behavior, the pg_partman extension offers a practical solution. It supports declarative partitioning in PostgreSQL while eliminating the need to modify primary or unique constraints to include the partition key. This enables successful migrations while preserving complete compatibility with Oracle’s partitioning model and eliminating the need for schema changes.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Background&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;For example, consider the following Oracle “Orders” table partitioned by the order_date column.&lt;/P&gt;
&lt;LI-CODE lang=""&gt;CREATE TABLE orders (
    order_id NUMBER PRIMARY KEY,
    customer_id NUMBER NOT NULL,
    order_date DATE NOT NULL,
    status TEXT,
    total_amount NUMERIC(10,2)
) PARTITION BY RANGE (order_date);
CREATE TABLE orders_2025_m1 PARTITION OF orders FOR VALUES FROM ('2024-12-01') TO ('2025-01-01');
CREATE TABLE orders_2025_m2 PARTITION OF orders FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');
CREATE TABLE orders_2025_m3 PARTITION OF orders FOR VALUES FROM ('2025-02-01') TO ('2025-03-01');
CREATE TABLE orders_2025_m4 PARTITION OF orders FOR VALUES FROM ('2025-03-01') TO ('2025-04-01');
&lt;/LI-CODE&gt;
&lt;P&gt;In Oracle, it’s valid to define a primary key only on order_id without including the partition key (order_date). Many customers want to preserve this design when migrating to Azure PostgreSQL Flexible Server. However, Azure PostgreSQL Flexible Server requires that any primary or unique constraint on a partitioned table must also include the partition key. Attempting to keep a primary key solely on order_id will result in an error.&lt;/P&gt;
&lt;P&gt;To replicate the Oracle’s behavior, the pg_partman extension along with the table template can be used. It allows partition management without forcing the partition key into primary or unique constraints, enabling the migration to retain the original table structure.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&amp;nbsp;&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang=""&gt;    CREATE TABLE orders (
        order_id      BIGINT PRIMARY KEY,
        customer_id   BIGINT NOT NULL,
        order_date    DATE NOT NULL,
        status        VARCHAR(20),
        total_amount  NUMERIC(10,2)
    )
    PARTITION BY RANGE (order_date);

unique constraint on partitioned table must include all partitioning columns
DETAIL: PRIMARY KEY constraint on table "orders" lacks column "order_date" which is part of the partition key.
&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Enable the Server Level Parameters for PG_PARTMAN&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;To configure the server-level parameter, go to the Azure portal, open the left-hand panel, and search for ‘Server Parameters’ under the Settings section. Then, search for azure.extensions, check the box for PG_PARTMAN in the value field, and click &lt;STRONG&gt;Save&lt;/STRONG&gt;&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Once the above is completed search for “shared_preload_libraries” and in the value section click the checkbox for PG_PARTMAN_BGW and then click SAVE.&lt;/P&gt;
&lt;P&gt;The above step would prompt the restart of the server.&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Prerequisites at Database level&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Once the server is restarted login to the database either by using PgAdmin or through psql. And set up the role and following permissions.&lt;/P&gt;
&lt;LI-CODE lang=""&gt;CREATE ROLE partman_role WITH LOGIN; 
CREATE SCHEMA partman; 
CREATE EXTENSION pg_partman SCHEMA partman;  --- Create extension if not already created
GRANT ALL ON SCHEMA partman TO partman_role; 
GRANT ALL ON ALL TABLES IN SCHEMA partman TO partman_role; 
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA partman TO partman_role; 
GRANT EXECUTE ON ALL PROCEDURES IN SCHEMA partman TO partman_role; 
GRANT ALL ON SCHEMA public TO partman_role; 
GRANT TEMPORARY ON DATABASE postgres to partman_role; 

&lt;/LI-CODE&gt;
&lt;P&gt;And if you have the partition table not part of partman_role ensure that usage on the schema granted. Example:- GRANT USAGE ON SCHEMA partman TO partman_role;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Create partition table without Primary Key&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Create the parent table including the partition key without including the Primary key.&amp;nbsp; &amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang=""&gt;CREATE TABLE orders (
        order_id      BIGINT,
        customer_id   BIGINT NOT NULL,
        order_date    DATE NOT NULL,
        status        VARCHAR(20),
        total_amount  NUMERIC(10,2)
    )
    PARTITION BY RANGE (order_date);
&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Create a Template Table&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;In order to use the Primary Key part of the table and not to include it in the partition key use the template as shown below. Notice that it’s the same structure as parent table and included primary key for the column order_id.&lt;/P&gt;
&lt;LI-CODE lang=""&gt;    CREATE TABLE orders_template (
        order_id      BIGINT ,
        customer_id   BIGINT NOT NULL,
        order_date    DATE NOT NULL,
        status        VARCHAR(20),
        total_amount  NUMERIC(10,2),
    PRIMARY KEY (order_id) 
    );
&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Create parent table using Pg_Partman&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Once the above tables are completed, the next step is to invoke the create_parent function as shown below.&lt;/P&gt;
&lt;LI-CODE lang=""&gt;SELECT partman.create_parent(
    p_parent_table := 'public.orders',
    p_control := 'order_date',
    p_type := 'native',
    p_interval := 'monthly',
    p_template_table := 'public.orders_template'
);&lt;/LI-CODE&gt;
&lt;P&gt;Notice that, the above script included orders_template table as a parameter for template table, this would ensure that partitions are created with the primary keys automatically.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Validate the partition table&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;After inserting the records validate the partitions created&lt;/P&gt;
&lt;LI-CODE lang=""&gt;SELECT tableoid::regclass AS partition, * FROM orders;&lt;/LI-CODE&gt;&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang=""&gt;EXPLAIN SELECT * FROM orders WHERE order_date &amp;gt; '2025-01-01';&lt;/LI-CODE&gt;&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang=""&gt;EXPLAIN SELECT * FROM orders WHERE order_id &amp;gt; 100;&lt;/LI-CODE&gt;&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;The query plan above shows that the partition key (order_date) is primarily used for date-range queries, independent of the primary key. In contrast, queries filtering by order_id rely on the primary key, which is defined separately from the partition key.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Feedback and Suggestions&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;If you have feedback or suggestions for improving this asset, please contact the Data SQL Ninja Team (&lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;datasqlninja@microsoft.com&lt;/A&gt;).&lt;BR /&gt;Note: For additional information about migrating various source databases to Azure, see the&amp;nbsp;&lt;A href="https://datamigration.microsoft.com/" target="_blank" rel="noopener"&gt;Azure Database Migration Guide&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;Thank you for your support!&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Thu, 21 Aug 2025 19:17:06 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/migrating-oracle-partitioned-tables-to-azure-postgresql-without/ba-p/4441962</guid>
      <dc:creator>VenkatMR</dc:creator>
      <dc:date>2025-08-21T19:17:06Z</dc:date>
    </item>
    <item>
      <title>Key Considerations to avoid Implicit Conversion issues in Oracle to Azure SQL Modernization</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/key-considerations-to-avoid-implicit-conversion-issues-in-oracle/ba-p/4442186</link>
      <description>&lt;H4 aria-level="7"&gt;&amp;nbsp;&lt;/H4&gt;
&lt;H4 aria-level="7"&gt;&lt;SPAN class="lia-text-color-15"&gt;&lt;STRONG&gt;Overview&amp;nbsp;&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/H4&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;This blog dives into the mechanics of implicit data type conversions and their impact during the post-migration performance optimization phase of heterogeneous database migrations. Drawing from our observed Engineering field patterns across diverse application architectures, this blog explores why certain platforms like ORMs, JDBC drivers, and cross-platform data models are more prone to implicit conversions than others and how they result in performance issues, break indexes, or cause query regressions. You'll gain actionable strategies to detect, mitigate, and design around these pitfalls to ensure a successful and performant data migration to platforms like Azure SQL.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H4 aria-level="7"&gt;&lt;SPAN class="lia-text-color-15"&gt;&lt;STRONG&gt;Understanding Implicit Conversions in Database Migrations&amp;nbsp;&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/H4&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;When an application communicates with a database whether through JDBC, ODBC, ADO.NET, or any other data access API it sends parameters and query values in a specific data type format. However, if the type of the value provided by the application does not exactly match the data type of the target column in the database, the database engine attempts to reconcile the mismatch automatically. This automatic adjustment is known as implicit conversion. For instance, a value passed as a string from the application may be compared against a numeric or date column in the database. This occurs because many front-end systems and APIs transmit values as strings by default, even if the underlying business logic expects numbers or dates. Unless the application explicitly parses or casts these values to match the expected types, the database engine must decide how to handle the type mismatch during query execution. In such cases, the engine applies type conversion internally, either to the parameter or the column, based on its own rules. While this feature can simplify application development by allowing flexible data handling, it often introduces engine-specific behavior that becomes more visible during cross engine database migrations, where assumptions built into one system may not hold true in another. &lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H4 aria-level="7"&gt;&lt;SPAN class="lia-text-color-15"&gt;&lt;STRONG&gt;Impact of Implicit Conversions&amp;nbsp;&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/H4&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;Implicit conversions can adversely affect database performance and functionality in several ways, some of which are discussed below:&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="" data-font="Symbol" data-listid="1" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:720,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Symbol&amp;quot;,&amp;quot;469769242&amp;quot;:[8226],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="1" data-aria-level="1"&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;Performance Degradation&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;&lt;SPAN data-contrast="auto"&gt;&lt;EM&gt;&lt;STRONG&gt;:&lt;/STRONG&gt;&lt;/EM&gt; When a database performs an implicit conversion, it may bypass indexes, resulting in slower query execution. For example, comparing a VARCHAR column to an INT value in SQL Server can trigger a table scan instead of an index&amp;nbsp;seek, significantly increasing query time.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="" data-font="Symbol" data-listid="1" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:720,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Symbol&amp;quot;,&amp;quot;469769242&amp;quot;:[8226],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="2" data-aria-level="1"&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;Migration-Specific Issues and&amp;nbsp;Data Integrity Risks&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;&lt;SPAN data-contrast="auto"&gt;&lt;EM&gt;&lt;STRONG&gt;:&lt;/STRONG&gt;&lt;/EM&gt; Implicit conversions can cause data loss or incorrect results during a few instances and one such example is, when a column defined as VARCHAR2 in Oracle, which can store Unicode characters by default is mapped to a VARCHAR column in SQL Server, non-ASCII characters such as Chinese, Russian, or Korean may be silently replaced with incorrect characters/symbols. One example of scenario when this can happen:&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="lia-align-justify lia-indent-padding-left-30px"&gt;&lt;SPAN data-contrast="auto"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Oracle VARCHAR2 stores Unicode if the database character set is UTF-8 (AL32UTF8), which is common in modern Oracle installations.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:true,&amp;quot;134233118&amp;quot;:true,&amp;quot;201341983&amp;quot;:0,&amp;quot;335559740&amp;quot;:240}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class="lia-align-justify lia-indent-padding-left-30px"&gt;&lt;SPAN data-contrast="auto"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SQL Server VARCHAR is ANSI/code-page based, so non-ASCII characters are stored differently, unless the column is explicitly declared as&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; NVARCHAR.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:true,&amp;quot;134233118&amp;quot;:true,&amp;quot;201341983&amp;quot;:0,&amp;quot;335559740&amp;quot;:240}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class="lia-align-justify lia-indent-padding-left-60px"&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:true,&amp;quot;134233118&amp;quot;:true,&amp;quot;201341983&amp;quot;:0,&amp;quot;335559740&amp;quot;:240}"&gt;-- In Real World this can happen on any other data types&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="" data-font="Symbol" data-listid="1" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:720,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Symbol&amp;quot;,&amp;quot;469769242&amp;quot;:[8226],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="3" data-aria-level="1"&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;Maintenance Challenges&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;&lt;SPAN data-contrast="auto"&gt;&lt;EM&gt;&lt;STRONG&gt;:&amp;nbsp;&lt;/STRONG&gt;&lt;/EM&gt;Queries relying on implicit conversions are harder to debug and optimize, as these conversions are not explicitly visible in the code and may only surface during performance regressions. &amp;nbsp;These queries forces the optimizer to compile an execution plan containing scans of large clustered indexes, or tables, instead of a seek resulting in degraded performance&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="" data-font="Symbol" data-listid="1" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:720,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Symbol&amp;quot;,&amp;quot;469769242&amp;quot;:[8226],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="4" data-aria-level="1"&gt;&lt;SPAN data-contrast="auto"&gt;&lt;EM&gt;&lt;STRONG&gt;Execution Overhead and Resource Consumption&lt;/STRONG&gt;&lt;/EM&gt;:&lt;/SPAN&gt;&lt;SPAN data-contrast="auto"&gt; Implicit conversions increase execution times for both queries and API calls, as the engine must perform runtime casting operations. This can lead to higher CPU usage, increased logical reads, and memory pressure.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:720}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H4 aria-level="7"&gt;&lt;SPAN class="lia-text-color-15"&gt;&lt;STRONG&gt;Detection Methods&amp;nbsp;&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/H4&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;Detecting implicit conversions is crucial for optimizing database performance post-migration. The following methods can be employed to detect:&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=""&gt;&lt;SPAN data-contrast="auto"&gt;&lt;EM&gt;&lt;STRONG&gt;Query Store (QDS):&lt;/STRONG&gt;&lt;/EM&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN data-contrast="auto"&gt;Use QDS post-migration during load testing to track expensive queries based on cost and surface performance regressions caused by type mismatches. Review execution plans captured in QDS for conversion-related patterns.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt; You can also use custom script like below to query the QDS:&lt;/SPAN&gt;&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;USE &amp;lt;[Replace_with_actual_DB_name]&amp;gt; -- Replace with the actual database name 
GO 

SELECT TOP (100) DB_NAME() AS [Database], qt.query_sql_text AS [Consulta], 
rs.last_execution_time AS [Last Execution Time], 
rs.avg_cpu_time AS [Avg Worker Time], rs.max_cpu_time AS [Max Worker Time], rs.avg_duration AS [Avg Elapsed Time], 
rs.max_duration AS [Max Elapsed Time], rs.avg_logical_io_reads AS [Avg Logical Reads], 
rs.max_logical_io_reads AS [Max Logical Reads], rs.count_executions AS [Execution Count], 
q.last_compile_start_time AS [Creation Time], CAST(p.query_plan AS XML) AS [Query Plan] 
FROM sys.query_store_query_text AS qt JOIN sys.query_store_query AS q 
ON qt.query_text_id = q.query_text_id JOIN sys.query_store_plan AS p 
ON q.query_id = p.query_id JOIN sys.query_store_runtime_stats AS rs 
ON p.plan_id = rs.plan_id 
WHERE CAST(p.query_plan AS NVARCHAR(MAX)) LIKE '%CONVERT_IMPLICIT%' 
AND qt.query_sql_text NOT LIKE '%sys.query_store%' 
ORDER BY rs.avg_cpu_time DESC;&lt;/LI-CODE&gt;
&lt;P class=""&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;Execution Plans&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;&lt;SPAN data-contrast="auto"&gt;&lt;EM&gt;&lt;STRONG&gt;:&lt;/STRONG&gt;&lt;/EM&gt; For the expensive queries, in SSMS, hover over operators like Index Scan to inspect the Predicate. If implicit conversion exists, the plan includes something like “CONVERT_IMPLICIT(&amp;lt;data_type&amp;gt;, ..”&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=""&gt;&lt;SPAN data-contrast="auto"&gt;&lt;EM&gt;&lt;STRONG&gt;XML Plan:&lt;/STRONG&gt;&lt;/EM&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN data-contrast="auto"&gt;For a confirmation of above, reviewing the underlying XML execution plan confirms whether implicit conversion is occurring and on which side of the comparison. This technique is particularly valuable when working with parameterized queries or when graphical plan warnings are insufficient. Look for elements like below in the XML plan:&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class="lia-indent-padding-left-30px"&gt;&lt;SPAN data-contrast="auto"&gt;&amp;lt;Warnings&amp;gt; &amp;lt;PlanAffectingConvert ConvertIssue="Seek Plan" Expression="CONVERT_IMPLICIT(.. &amp;lt;/Warnings&amp;gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=""&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;Plan Cache Inspection:&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;&lt;SPAN data-contrast="auto"&gt;&amp;nbsp;Custom scripts can be written to scan the Azure SQL plan cache for any instances of CONVERT_IMPLICIT operations. Below is one such script that can be used to find.&lt;/SPAN&gt;&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;SELECT TOP (100) DB_NAME(B.[dbid]) AS [Database], B.[text] AS [SQL_text], 
A.total_worker_time AS [Total Worker Time], A.total_worker_time / A.execution_count AS [Avg Worker Time], 
A.max_worker_time AS [Max Worker Time], A.total_elapsed_time / A.execution_count AS [Avg Elapsed Time], 
A.max_elapsed_time AS [Max Elapsed Time], A.total_logical_reads / A.execution_count AS [Avg Logical Reads],
 A.max_logical_reads AS [Max Logical Reads], A.execution_count AS [Execution Count], 
A.creation_time AS [Creation Time], C.query_plan AS [Query Plan] 
FROM sys.dm_exec_query_stats AS A WITH (NOLOCK) 
CROSS APPLY sys.dm_exec_sql_text(A.plan_handle) AS B CROSS APPLY sys.dm_exec_query_plan(A.plan_handle) AS C 
WHERE CAST(C.query_plan AS NVARCHAR(MAX)) LIKE '%CONVERT_IMPLICIT%' 
AND B.[dbid] = DB_ID() AND B.[text] NOT LIKE '%sys.dm_exec_sql_text%' 
ORDER BY A.total_worker_time DESC&lt;/LI-CODE&gt;
&lt;P class=""&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;XE event: &lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;Extended Events (XE) is valuable in support scenarios when Query Store or telemetry data alone can't pinpoint issues like implicit conversions, especially if plans aren't cached or historical data lacks detail. XE provides real-time capture of plan-affecting convert events, offering granular insights into query behavior that QS might miss during short-lived or dynamic workloads. However, use it sparingly due to overhead, as a targeted diagnostic tool rather than a broad solution. You can use below script to turn it off. Remember to stop and drop the event immediately when you are done collecting.&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;IF EXISTS (SELECT * FROM sys.server_event_sessions WHERE name = 'Detect_Conversion_Performance_Issues')
    DROP EVENT SESSION [Detect_Conversion_Performance_Issues] ON SERVER;
GO

CREATE EVENT SESSION [Detect_Conversion_Performance_Issues] ON SERVER 
ADD EVENT sqlserver.plan_affecting_convert(
    ACTION(sqlserver.database_name, sqlserver.sql_text)
    WHERE ([sqlserver].[database_name] = N'&amp;lt;Replace_with_your_DB_name&amp;gt;')  -- Replace your DB name
)
ADD TARGET package0.ring_buffer 
WITH (
    MAX_MEMORY = 4096 KB,
    EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,
    MAX_DISPATCH_LATENCY = 30 SECONDS,
    MEMORY_PARTITION_MODE = NONE,
    TRACK_CAUSALITY = OFF,
    STARTUP_STATE = OFF
);
GO

ALTER EVENT SESSION [Detect_Conversion_Performance_Issues] ON SERVER STATE = START;

-- View the raw Extended Events buffer
SELECT 
    s.name AS session_name,
    t.target_name,
    CAST(t.target_data AS XML) AS raw_buffer_xml
FROM sys.dm_xe_sessions s
JOIN sys.dm_xe_session_targets t ON s.address = t.event_session_address
WHERE s.name = 'Detect_Conversion_Performance_Issues';&lt;/LI-CODE&gt;
&lt;P class=""&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;Documentation Reference:&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;&lt;SPAN data-contrast="auto"&gt;&amp;nbsp;Microsoft docs on&amp;nbsp;&lt;/SPAN&gt;&lt;A href="https://learn.microsoft.com/sql/t-sql/data-types/data-type-conversion-database-engine" target="_blank" rel="noopener"&gt;&lt;SPAN data-contrast="none"&gt;&lt;SPAN data-ccp-charstyle="Hyperlink"&gt;conversion&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN data-contrast="auto"&gt;&amp;nbsp;and&amp;nbsp;&lt;/SPAN&gt;&lt;A href="https://learn.microsoft.com/sql/t-sql/data-types/data-type-precedence-transact-sql" target="_blank" rel="noopener"&gt;&lt;SPAN data-contrast="none"&gt;&lt;SPAN data-ccp-charstyle="Hyperlink"&gt;precedence&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN data-contrast="auto"&gt; help explain engine behavior and mappings around implicit conversion triggers. This close look at them along with app developers during the schema/code conversion phase can help better understanding and mitigation.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:720}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H4 aria-level="7"&gt;&lt;SPAN class="lia-text-color-15"&gt;&lt;STRONG&gt;Implicit Conversion: Real-World&amp;nbsp;Example&amp;nbsp;&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/H4&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;To evaluate the impact of implicit conversions in Azure SQL during post-migration scenarios, we created a synthetic workload example using a table named dbo.Customers. It contains one million rows and includes columns such as AccountNumber, CustomerName, PhoneNumber, and JoinDate. The AccountNumber, CustomerName, and PhoneNumber columns were initially defined as VARCHAR, and N&lt;/SPAN&gt;&lt;SPAN data-contrast="auto"&gt;onclustered&amp;nbsp;indexes&lt;/SPAN&gt;&lt;SPAN data-contrast="auto"&gt; were created on these fields to enable efficient lookups. From the application layer, parameters were passed using NVARCHAR, which mirrors typical real-world ORM behavior particularly in Java-based applications or when migrating from Oracle, where VARCHAR2 frequently stores Unicode characters. This deliberate mismatch allows us to study the real performance consequences of implicit conversions in Azure SQL’s query execution engine. Although enabling SET STATISTICS XML ON can expose implicit conversions during query execution, our approach tries to reflect how these issues are usually uncovered in real-world scenarios where customers are less aware of this issue. In this case, we used Query Store and execution plan XML inspection&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H6 aria-level="6"&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="none"&gt;&lt;SPAN data-ccp-parastyle="heading 6"&gt;Problem: Implicit Conversion Due to Type Mismatch:&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134245418&amp;quot;:true,&amp;quot;134245529&amp;quot;:true,&amp;quot;335559738&amp;quot;:40,&amp;quot;335559739&amp;quot;:0}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;&lt;/H6&gt;
&lt;P aria-level="6"&gt;&lt;SPAN data-contrast="auto"&gt;&lt;SPAN data-ccp-parastyle="heading 6"&gt;A NVARCHAR parameter from the application is compared against a VARCHAR column in the database.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN data-ccp-parastyle="heading 6"&gt;This scenario highlights a silent performance regression that can go unnoticed post-migration without detailed plan inspection&lt;/SPAN&gt;&lt;SPAN data-ccp-parastyle="heading 6"&gt;.&lt;/SPAN&gt;&lt;SPAN data-ccp-parastyle="heading 6"&gt;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134245418&amp;quot;:true,&amp;quot;134245529&amp;quot;:true,&amp;quot;335559738&amp;quot;:40,&amp;quot;335559739&amp;quot;:0}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;Query Used: DECLARE ACC NVARCHAR(20) = N’ACC000500’; 

SELECT CustomerID, AccountNumber, CustomerName, PhoneNumber 
FROM dbo.Customers 
WHERE AccountNumber = ACC;&lt;/LI-CODE&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;Execution Plan Behavior:&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:758}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:758}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:758}"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;SPAN data-contrast="auto"&gt;Fig: 1&lt;/SPAN&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="1" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="1" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;SQL Server applies an implicit CONVERT_IMPLICIT(nvarchar, AccountNumber) on the column side as we can see from Fig 1 when you hover to Index Scan and see the Predicate&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="1" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="2" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;This disables the use of the&amp;nbsp;nonclustered&amp;nbsp;index on&amp;nbsp;AccountNumber, leading to an Index Scan&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="1" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="3" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;The&amp;nbsp;XML&amp;nbsp;plan includes a&amp;nbsp;&amp;lt;Warning&amp;gt;&amp;nbsp;tag&amp;nbsp;under &amp;lt;PlanAffectingConvert&amp;gt;&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="1" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="4" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;Extended Events&amp;nbsp;monitoring&amp;nbsp;consistently shows "plan_affecting_convert" warnings&amp;nbsp;indicating&amp;nbsp;suboptimal query plans caused by these conversions&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;What's Missing:&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:720}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="15" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="1" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;Type alignment between the query parameter and the column.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="15" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="2" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;Awareness that even matching string lengths&amp;nbsp;won’t&amp;nbsp;help if encoding mismatches exist.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;Impact:&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:720}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="16" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="1" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;Index Seek is lost, and full scans are triggered.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="16" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="2" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;Higher&amp;nbsp;Exec times and Overall&amp;nbsp;costs&amp;nbsp;observed.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:720}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H6 aria-level="6"&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="none"&gt;&lt;SPAN data-ccp-parastyle="heading 6"&gt;Mitigation via Explicit CAST – Matching the Column’s Type&lt;/SPAN&gt;&lt;SPAN data-ccp-parastyle="heading 6"&gt;:&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134245418&amp;quot;:true,&amp;quot;134245529&amp;quot;:true,&amp;quot;335559738&amp;quot;:40,&amp;quot;335559739&amp;quot;:0}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;&lt;/H6&gt;
&lt;P aria-level="6"&gt;&lt;SPAN data-contrast="auto"&gt;&lt;SPAN data-ccp-parastyle="heading 6"&gt;In some cases, especially during post-migration tuning, application teams may not be able to change the database schema, but developers can update the query to explicitly align data types. This scenario simulates such a mitigation where an NVARCHAR parameter is explicitly cast to VARCHAR to match the column’s data type and avoid implicit conversions.&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134245418&amp;quot;:true,&amp;quot;134245529&amp;quot;:true,&amp;quot;335559738&amp;quot;:40,&amp;quot;335559739&amp;quot;:0}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;Query Used: DECLARE ACC NVARCHAR(20) = N'ACC000500'; SELECT CustomerID, AccountNumber, CustomerName, PhoneNumber FROM dbo.Customers WHERE AccountNumber = CAST(@acc AS VARCHAR(20)); -- Explicit use of CAST&lt;/LI-CODE&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:758}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;Execution Plan Behavior:&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:758}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:758}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:758}"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;SPAN data-contrast="auto"&gt;Fig:&amp;nbsp;2&lt;/SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="1" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="5" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;The CAST operation ensures that the parameter side matches the VARCHAR column type.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="1" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="6" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;SQL performs an Index Seek on the IX_AccountNumber index. The Seek Predicates as seen in Fig 2 confirms this showing Scalar “Operator(CONVERT..”&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="1" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="7" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;No &amp;lt;Warning&amp;gt; tag appears in the XML execution plan&amp;nbsp;indicating&amp;nbsp;the absence of implicit conversions.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:1440}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;What's&amp;nbsp;Fixed:&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:720}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="15" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="3" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;Type mismatch is resolved on the query side without altering the database schema.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="15" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="4" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;The query is now&amp;nbsp;SARGable, enabling index usage.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;Impact:&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:720}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="16" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="3" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;Index Seek is lost, and full scans are triggered.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="16" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="4" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;Higher&amp;nbsp;Exec times and Overall&amp;nbsp;costs&amp;nbsp;observed.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;What's&amp;nbsp;Still&amp;nbsp;Missing:&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:720}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="15" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="5" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;This&amp;nbsp;still&amp;nbsp;creates a long-term maintainability concern, especially when many queries or columns are affected.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="15" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="6" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;Developers must remember to manually CAST in every affected query, increasing code complexity and the chance of inconsistency.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="15" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="7" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;Missed CASTs in other queries can still cause implicit conversions, so the issue&amp;nbsp;isn’t&amp;nbsp;eliminated&amp;nbsp;just patched locally.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:1440}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H6 aria-level="6"&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="none"&gt;&lt;SPAN data-ccp-parastyle="heading 6"&gt;Fix at DB end&lt;/SPAN&gt;&lt;SPAN data-ccp-parastyle="heading 6"&gt;&amp;nbsp;–&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN data-ccp-parastyle="heading 6"&gt;Parameter Usage aligned Schema Column type&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134245418&amp;quot;:true,&amp;quot;134245529&amp;quot;:true,&amp;quot;335559738&amp;quot;:40,&amp;quot;335559739&amp;quot;:0}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;&lt;/H6&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;This fix involves altering the column type to NVARCHAR, aligning it with the NVARCHAR parameter passed from the application. It&amp;nbsp;eliminates&amp;nbsp;implicit conversions and enables index&amp;nbsp;seeks, improving performance. However,&amp;nbsp;it’s&amp;nbsp;a database-side&amp;nbsp;adjustment,&amp;nbsp; the&amp;nbsp;ideal long-term fix lies in ensuring the application sends parameters matching the original column type.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;Query Used: DECLARE ACC NVARCHAR(20) = N'ACC000500'; 

SELECT CustomerID, AccountNumber, CustomerName, PhoneNumber 
FROM dbo.Customers 
WHERE AccountNumber = CAST(@acc AS VARCHAR(20));&lt;/LI-CODE&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;Execution Plan Behavior:&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:758}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:758}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Fig:&amp;nbsp;3&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:758}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="1" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="8" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;The CAST operation ensures that the parameter side matches the VARCHAR column type.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="1" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="9" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;As seen in Fig 3 an Index Seek is performed on the updated IX_AccountNumber index. The Seek Predicates confirm this showing “Scalar Operator(..”&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="1" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="10" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;No &amp;lt;Warning&amp;gt; tag appears in the XML execution plan&amp;nbsp;indicating&amp;nbsp;the absence of implicit conversions.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:1440}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;What's&amp;nbsp;Fixed:&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:720}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="15" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="8" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;The fix is schema-driven and works universally, ensuring consistent performance across tools and interfaces.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="15" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="9" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;Encoding alignment between the parameter and column removes conversion logic entirely, making query plans stable and predictable.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;Impact:&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:720}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="16" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="5" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;Indexes&amp;nbsp;remain&amp;nbsp;fully usable without manual intervention in queries.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="16" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="6" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;Application code stays&amp;nbsp;clean,&amp;nbsp;no casts or workarounds are needed.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="16" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="7" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;This is the most sustainable fix but may require coordination with application and DB teams.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;What's&amp;nbsp;Still&amp;nbsp;Missing:&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:720}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI aria-setsize="-1" data-leveltext="o" data-font="Courier New" data-listid="15" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:1440,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Courier New&amp;quot;,&amp;quot;469769242&amp;quot;:[9675],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;o&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;multilevel&amp;quot;}" data-aria-posinset="10" data-aria-level="2"&gt;&lt;SPAN data-contrast="auto"&gt;Some of implications around data type changes&amp;nbsp;will&amp;nbsp;be&amp;nbsp;around data types consuming&amp;nbsp;additional&amp;nbsp;storage,&amp;nbsp;for example&amp;nbsp;NVARCHAR consumes 2 bytes/char, increasing storage&amp;nbsp;when compared to VARCHAR.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{&amp;quot;335559685&amp;quot;:1440}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H6&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="none"&gt;Implicit vs Explicit vs Aligned: Execution Plan Behavior Comparison&lt;/SPAN&gt;&lt;SPAN data-contrast="auto"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;&lt;/H6&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="width: 100%; border-width: 1px;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;&lt;SPAN data-ccp-charstyle="Strong"&gt;Scenario&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;335551550&amp;quot;:2,&amp;quot;335551620&amp;quot;:2}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;&lt;SPAN data-ccp-charstyle="Strong"&gt;Predicate Expression&lt;/SPAN&gt;&lt;SPAN data-ccp-charstyle="Strong"&gt;&amp;nbsp;in Exec Plan&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;335551550&amp;quot;:2,&amp;quot;335551620&amp;quot;:2}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;&lt;SPAN data-ccp-charstyle="Strong"&gt;Implicit Conversion&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;335551550&amp;quot;:2,&amp;quot;335551620&amp;quot;:2}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;&lt;SPAN data-ccp-charstyle="Strong"&gt;Index Seek Used&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;335551550&amp;quot;:2,&amp;quot;335551620&amp;quot;:2}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;&lt;SPAN data-ccp-charstyle="Strong"&gt;XML&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN data-ccp-charstyle="Strong"&gt;Plan Warning Shown&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;335551550&amp;quot;:2,&amp;quot;335551620&amp;quot;:2}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;Data Type Mismatch&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;SPAN class="lia-text-color-21"&gt;CONVERT_IMPLICIT(nvarchar,&amp;nbsp;AccountNumber) = &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3093839" data-lia-user-login="ACC" class="lia-mention lia-mention-user"&gt;ACC​&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;Yes&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;No (results in scan)&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;PlanAffectingConvert&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;Explicit Cast in Query&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;SPAN class="lia-text-color-21"&gt;AccountNumber&amp;nbsp;=&amp;nbsp;CONVERT(varchar, &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3093839" data-lia-user-login="ACC" class="lia-mention lia-mention-user"&gt;ACC​&lt;/a&gt;)&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;No&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;Yes&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;No&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;Matching Data Types (NVARCHAR)&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;SPAN class="lia-text-color-21"&gt;AccountNumber&amp;nbsp;= &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3093839" data-lia-user-login="ACC" class="lia-mention lia-mention-user"&gt;ACC​&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;No&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;Yes&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td class="lia-align-center"&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;No&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P aria-level="7"&gt;&amp;nbsp;&lt;/P&gt;
&lt;H4 aria-level="7"&gt;&lt;SPAN class="lia-text-color-15"&gt;&lt;STRONG&gt;Best Practices for Managing Implicit Conversions&amp;nbsp;&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/H4&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN data-contrast="auto"&gt;&lt;STRONG&gt;&lt;EM&gt;Refactoring Application:&lt;/EM&gt;&lt;/STRONG&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN data-contrast="auto"&gt;Legacy systems, especially those using dynamic SQL or lacking strict type enforcement, are prone to implicit conversion issues. Refactor your application code to leverage strongly typed variables and parameter declarations to ensure data type consistency at the source, minimizing implicit conversions during query execution.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{&amp;quot;134233117&amp;quot;:false,&amp;quot;134233118&amp;quot;:false,&amp;quot;335551550&amp;quot;:0,&amp;quot;335551620&amp;quot;:0,&amp;quot;335559738&amp;quot;:0,&amp;quot;335559739&amp;quot;:0}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;Explicit Data Type Casting&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;&lt;SPAN data-contrast="auto"&gt;&lt;EM&gt;&lt;STRONG&gt;:&lt;/STRONG&gt;&lt;/EM&gt;&amp;nbsp;Use CAST or CONVERT functions to explicitly define conversions, reducing reliance on implicit behavior.&amp;nbsp;In our&amp;nbsp;example&amp;nbsp;we have used CAST, but a CONVERT function would have worked equally well. Both approaches explicitly align the parameter type to the column and avoid implicit conversions, enabling index&amp;nbsp;seek.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;Data Type Alignment&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;&lt;SPAN data-contrast="auto"&gt;&lt;EM&gt;&lt;STRONG&gt;:&lt;/STRONG&gt;&lt;/EM&gt;&amp;nbsp;When you are&amp;nbsp;performing&amp;nbsp;heterogenous&amp;nbsp;migrations which involve&amp;nbsp;different&amp;nbsp;database engines,&amp;nbsp;ensure data types are consistent between&amp;nbsp;source and target DB engines.&amp;nbsp;Check&amp;nbsp;official documents&amp;nbsp;thoroughly to know and see the&amp;nbsp;nuances around your data and application&amp;nbsp;convertibility&amp;nbsp;and&amp;nbsp;know the implications&amp;nbsp;like&amp;nbsp;additional&amp;nbsp;storage, collation changes etc.&amp;nbsp;that can negatively affect your business.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;Indexing&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;&lt;SPAN data-contrast="auto"&gt;: Create indexes on columns frequently involved in WHERE filters and JOIN predicates with matching data types to avoid implicit conversions that would cause index seeks to degrade into scans and ensures optimal index utilization by the optimizer.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;Early&amp;nbsp;Testing&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;&lt;SPAN data-contrast="auto"&gt;&lt;EM&gt;&lt;STRONG&gt;:&lt;/STRONG&gt;&lt;/EM&gt;&amp;nbsp;Conduct thorough post-migration testing using&amp;nbsp;QDS&amp;nbsp;to&amp;nbsp;identify, then&amp;nbsp;drill&amp;nbsp;down on&amp;nbsp;execution plans and performance metrics to&amp;nbsp;identify&amp;nbsp;and resolve conversion-related issues.&amp;nbsp;Early&amp;nbsp;collaboration between&amp;nbsp;Developer and&amp;nbsp;DBA teams&amp;nbsp;will be&amp;nbsp;crucial.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN data-contrast="auto"&gt;Tools and Scripts&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;&lt;SPAN data-contrast="auto"&gt;&lt;EM&gt;&lt;STRONG&gt;:&lt;/STRONG&gt;&lt;/EM&gt; Utilize SQL Server Migration Assistant (SSMA) for Oracle to identify and change mappings early when you know your application needs. Additionally, use can use custom scripts or third-party tools if necessary to detect implicit conversions in the plan cache.&lt;/SPAN&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H4&gt;&lt;STRONG&gt;&lt;SPAN class="lia-text-color-15"&gt;References&amp;nbsp;&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/H4&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/sql/t-sql/data-types/data-type-conversion-database-engine" target="_blank" rel="noopener"&gt;&lt;SPAN data-contrast="none"&gt;&lt;SPAN data-ccp-charstyle="Hyperlink"&gt;https://learn.microsoft.com/sql/t-sql/data-types/data-type-conversion-database-engine&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/sql/t-sql/data-types/data-type-precedence-transact-sql" target="_blank" rel="noopener"&gt;&lt;SPAN data-contrast="none"&gt;&lt;SPAN data-ccp-charstyle="Hyperlink"&gt;https://learn.microsoft.com/sql/t-sql/data-types/data-type-preced&lt;/SPAN&gt;&lt;SPAN data-ccp-charstyle="Hyperlink"&gt;e&lt;/SPAN&gt;&lt;SPAN data-ccp-charstyle="Hyperlink"&gt;nce-transact-sql&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN data-ccp-props="{}"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H4&gt;&lt;SPAN class="lia-text-color-15"&gt;&lt;STRONG&gt;Final Thoughts&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/H4&gt;
&lt;P&gt;We hope that this post has helped&amp;nbsp;&lt;SPAN data-contrast="auto"&gt;you gain actionable strategies to detect, mitigate, and design around implicit conversions in order to ensure a successful and performant data migration to platforms such as SQL Server or Azure SQL&lt;/SPAN&gt;. If you have feedback or suggestions for improving this post, please contact the&amp;nbsp;&lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;Azure Databases SQL Customer Success Engineering Team&lt;/A&gt;. Thanks for your support!&lt;/P&gt;</description>
      <pubDate>Tue, 09 Sep 2025 05:27:35 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/key-considerations-to-avoid-implicit-conversion-issues-in-oracle/ba-p/4442186</guid>
      <dc:creator>Nitish_reddy_kotha</dc:creator>
      <dc:date>2025-09-09T05:27:35Z</dc:date>
    </item>
    <item>
      <title>Optimized Data Transfer from Sybase ASE to Azure SQL via Chunked BCP Processing</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/optimized-data-transfer-from-sybase-ase-to-azure-sql-via-chunked/ba-p/4436624</link>
      <description>&lt;H2&gt;&lt;SPAN class="lia-text-color-10"&gt;Introduction&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;Enterprises upgrading legacy databases often face challenges in migrating complex schemas and efficiently transferring large volumes of data. Transitioning from SAP ASE (Sybase ASE) to Azure SQL Database is a common strategy to take advantage of enhanced features, improved scalability, and seamless integration with Microsoft services. With business growth, the limitations of the legacy system become apparent, performance bottlenecks, high maintenance costs, and difficulty in integrating with modern cloud solutions.&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/en-us/sql/ssma/sybase/getting-started-with-ssma-for-sybase-sybasetosql?view=sql-server-ver17" target="_blank" rel="noopener"&gt;SQL Server Migration Assistant for SAP Adaptive Server Enterprise (&lt;/A&gt;SSMA) Automates migration from SAP ASE to SQL Server, Azure SQL Database and Azure SQL Managed Instance. &amp;nbsp;While SSMA provides a complete end-to-end migration solution, the custom BCP script &lt;SPAN class="lia-text-color-6"&gt;(&lt;STRONG&gt;ASEtoSQLdataloadusingbcp.sh&lt;/STRONG&gt;)&lt;/SPAN&gt; enhances this process by enabling parallel data transfers, making it especially effective for migrating large databases with minimal downtime.&lt;/P&gt;
&lt;H2&gt;&lt;SPAN class="lia-text-color-10"&gt;Script Workflow&lt;/SPAN&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;/H2&gt;
&lt;P&gt;One of the most common challenges we hear from customers migrating from Sybase ASE to SQL Server is: “How can we speed up data transfer for large tables without overwhelming the system?” When you are dealing with hundreds of tables or millions of rows, serial data loads can quickly become a bottleneck.&lt;/P&gt;
&lt;P&gt;To tackle this, we created a script called&lt;SPAN class="lia-text-color-6"&gt; &lt;STRONG&gt;ASEtoSQLdataloadusingbcp.sh&lt;/STRONG&gt; &lt;/SPAN&gt;that automates and accelerates the data migration process using parallelism. It starts by reading configuration settings from external files and retrieves a list of tables, either from the source database or from a user-provided file. For each table, the script checks if it meets criteria for chunking based on available indexes. If it does, the table is split into multiple views, and each view is processed in parallel using BCP, significantly reducing the overall transfer time. If chunking is not possible, the script performs a standard full-table transfer.&lt;/P&gt;
&lt;P&gt;Throughout the entire process, detailed logging ensures everything is traceable and easy to monitor. This approach gives users both speed and control , helping migrations finish faster without sacrificing reliability.&lt;/P&gt;
&lt;H2&gt;&lt;SPAN class="lia-text-color-10"&gt;Prerequisites&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;Before running the script, ensure the following prerequisites are met:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Database schema is converted and deployed using &lt;A href="https://learn.microsoft.com/en-us/sql/ssma/sybase/sql-server-migration-assistant-for-sybase-sybasetosql?view=sql-server-ver17" target="_blank" rel="noopener"&gt;SQL Server Migration Assistant&lt;/A&gt; (SSMA).&lt;/LI&gt;
&lt;LI&gt;Both the source (SAP ASE) and target (Azure SQL DB) databases are accessible from the host system running the script.&lt;/LI&gt;
&lt;LI&gt;Source ASE database should be hosted on Unix or Linux.&lt;/LI&gt;
&lt;LI&gt;The target SQL Server can be hosted on Windows, Linux, or as an Azure.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN class="lia-text-color-10"&gt;Configuration Files&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;The configuration aspect of the solution is designed for clarity and reuse. All operational parameters are defined in external files, this script will use following external config files during&amp;nbsp; &lt;BR /&gt;&lt;BR /&gt;&lt;SPAN class="lia-text-color-14"&gt;&lt;STRONG&gt;&lt;U&gt;bcp_config.env&lt;/U&gt;&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;BR /&gt;&lt;BR /&gt;The primary configuration file, &lt;STRONG&gt;bcp_config.env&lt;/STRONG&gt;, contains connection settings and control flags. In the screenshot below you can see the format of the file.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;U&gt;&amp;nbsp;&lt;/U&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class="lia-text-color-14"&gt;&lt;STRONG&gt;&lt;U&gt;chunking_config.txt&lt;/U&gt;&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;The &lt;STRONG&gt;chunking_config.txt&lt;/STRONG&gt; file defines the tables to be partitioned, identifies the primary key column for chunking, and specifies the number of chunks into which the data should be divided.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class="lia-text-color-14"&gt;&lt;STRONG&gt;&lt;U&gt;table_list.txt&lt;/U&gt;&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;Use table_list.txt as the input if you want a specific list of tables. &lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN class="lia-text-color-10"&gt;Steps to run the script&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN class="lia-text-color-10"&gt;Script Execution Log&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;The script log records tables copied, timestamps, and process stages.&lt;/P&gt;
&lt;img /&gt;
&lt;H2&gt;&lt;SPAN class="lia-text-color-10"&gt;Performance Baseline&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;A test was run on a 32-core system with a 10 GB table (262,1440 rows) for ASE and SQL. Migration using SSMA took &lt;STRONG&gt;about 3 minutes&lt;/STRONG&gt;.&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Using the BCP script with 10 chunks, the entire export and import finished in 1 minute 7 seconds. This demonstrates how parallelism and chunk-based processing greatly boost efficiency for large datasets. &lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;BR /&gt;&lt;SPAN class="lia-text-color-7"&gt;&lt;STRONG&gt;&lt;EM&gt;Disclaimer&lt;/EM&gt;&lt;/STRONG&gt;&lt;STRONG&gt;&lt;EM&gt;:&lt;/EM&gt;&lt;/STRONG&gt;&lt;EM&gt; These results are for illustration purposes only. Actual performance will vary depending on system hardware (CPU cores, memory, disk I/O), database configurations, network latency, and table structures. We recommend validating performance in dev/test to establish a baseline.&lt;/EM&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;BR /&gt;&lt;SPAN class="lia-text-color-15"&gt;General Recommendation&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Larger batch sizes (e.g., 10K–50K) can boost throughput if disk IOPS and memory are sufficient, as they lower commit overhead.&lt;/LI&gt;
&lt;LI&gt;More chunks increase parallelism and throughput if CPU resources are available; otherwise, they may cause contention when CPU usage is high.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN class="lia-text-color-10"&gt;Monitor system’s CPU and IOPS:&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;When the system has high idle CPU and low I/O wait, increasing both the number of chunks and the batch size is appropriate.&lt;/LI&gt;
&lt;LI&gt;If CPU load or I/O wait is high, reduce batch size or chunk count to avoid exhausting resources.&lt;/LI&gt;
&lt;LI&gt;This method aligns BCP operations with your system's existing capacity and performance characteristics. &lt;BR /&gt;&lt;BR /&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;H6&gt;&lt;U&gt;&lt;SPAN class="lia-text-color-10"&gt;&lt;STRONG&gt;Steps to Download the script&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/U&gt;&lt;BR /&gt;&lt;BR /&gt;Please send an email to the alias: &lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;datasqlninja@microsoft.com&lt;/A&gt; and we will send you the download link with instructions.&lt;BR /&gt;&lt;BR /&gt;&lt;/H6&gt;
&lt;P&gt;&lt;SPAN class="lia-text-color-10"&gt;What’s Next: Upcoming Enhancements to the Script&lt;/SPAN&gt;&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Smart Chunking for Tables Without Unique Clustered Indexes&lt;/LI&gt;
&lt;UL&gt;
&lt;LI&gt;Enable chunk-based export using any &lt;STRONG&gt;unique key column&lt;/STRONG&gt;, even if the table lacks a unique clustered index.&lt;/LI&gt;
&lt;LI&gt;This will extend chunking capabilities to a broader range of tables, ensuring better parallelization.&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI&gt;Multi-Table Parallel BCP with Intelligent Chunking&lt;/LI&gt;
&lt;UL&gt;
&lt;LI&gt;Introduce full &lt;STRONG&gt;parallel execution across multiple tables&lt;/STRONG&gt;.&lt;/LI&gt;
&lt;LI&gt;If a table qualifies for chunking, its export/import will also run in parallel internally, delivering &lt;STRONG&gt;two-tier parallelism&lt;/STRONG&gt;: across and within tables.&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI&gt;LOB Column Handling (TEXT, IMAGE, BINARY)&lt;/LI&gt;
&lt;UL&gt;
&lt;LI&gt;Add robust support for &lt;STRONG&gt;large object data types&lt;/STRONG&gt;.&lt;/LI&gt;
&lt;LI&gt;Include optimized handling strategies for exporting and importing tables with TEXT, IMAGE, or BINARY columns, ensuring data fidelity, and avoiding performance bottlenecks.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/OL&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;BR /&gt;&lt;SPAN class="lia-text-color-14"&gt;Feedback and Suggestions&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;If you have feedback or suggestions for improving this asset, please contact the Data SQL Ninja Team (&lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;datasqlninja@microsoft.com&lt;/A&gt;). &lt;BR /&gt;Note: For additional information about migrating various source databases to Azure, see the &lt;A href="https://datamigration.microsoft.com/" target="_blank" rel="noopener"&gt;Azure Database Migration Guide&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;Thank you for your support!&lt;/P&gt;</description>
      <pubDate>Tue, 07 Oct 2025 14:33:30 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/optimized-data-transfer-from-sybase-ase-to-azure-sql-via-chunked/ba-p/4436624</guid>
      <dc:creator>Manish_Kumar_Pandey</dc:creator>
      <dc:date>2025-10-07T14:33:30Z</dc:date>
    </item>
    <item>
      <title>Temporal Table Replication in SQL Server: Common Barriers and Solutions</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/temporal-table-replication-in-sql-server-common-barriers-and/ba-p/4422032</link>
      <description>&lt;H3&gt;Introduction&lt;/H3&gt;
&lt;P&gt;Transactional replication is a SQL Server feature that copies and synchronizes data and database objects across servers. It generally begins with a snapshot of the publication database objects and data. After this initial snapshot, any data changes and schema modifications made at the Publisher are delivered to the Subscriber as they occur, typically in near real time. These data changes are applied to the Subscriber in the same order and within the same transaction boundaries as at the Publisher, maintaining transactional consistency within a publication&lt;/P&gt;
&lt;P&gt;Standard transactional replication in SQL Server does not provide support for system-versioned temporal tables. This constraint presents difficulties for organizations aiming to replicate historical data maintained in temporal columns, such as ValidFrom and ValidTo. The challenge persists even when system versioning is disabled, yet there remains a requirement to retain the original values within the target database.&lt;/P&gt;
&lt;H3&gt;Understanding Temporal Tables&lt;/H3&gt;
&lt;P&gt;System-versioned temporal tables are a specialized form of user table designed to retain a comprehensive record of all data modifications. These tables facilitate point-in-time analysis by automatically recording historical changes. Each temporal table contains two datetime2 period columns that specify the validity duration for each row. In addition to the current table, an associated history table preserves previous versions of rows whenever updates or deletions take place.&lt;/P&gt;
&lt;H3&gt;Scenario &amp;amp; Challenge&lt;/H3&gt;
&lt;P&gt;In one of the migration scenarios, the customer faced an issue where system versioning was disabled, but there was still a requirement to replicate data from the ValidFrom and ValidTo columns to the target database without modification. Although temporal tables are commonly used for auditing and historical analysis, replicating them within a transactional replication setup can present specific technical challenges:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;System managed period columns complicate schema compliance.&lt;/LI&gt;
&lt;LI&gt;Mismatch in ValidFrom and ValidTo columns across environments can compromise audit reliability.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;As transactional replication currently does not support temporal columns, we devised the following solution to address this requirement.&lt;/P&gt;
&lt;H3&gt;Common Error Example&lt;/H3&gt;
&lt;UL&gt;
&lt;LI&gt;When configuring replication for an article that includes a system-versioned temporal table, the setup process may encounter failures due to SQL Server limitations related to system-generated columns.&lt;/LI&gt;
&lt;/UL&gt;
&lt;img /&gt;
&lt;UL&gt;
&lt;LI&gt;In certain situations where system versioning is disabled, it may still be necessary to replicate the initial values of the ValidFrom and ValidTo period columns on the target system. However, during the configuration of transactional replication, the snapshot application process can fail on these columns, resulting in the following error:&lt;/LI&gt;
&lt;/UL&gt;
&lt;H6 class="lia-indent-padding-left-60px"&gt;&lt;STRONG&gt;Error message:&lt;/STRONG&gt;&lt;/H6&gt;
&lt;img /&gt;
&lt;P class="lia-clear-both"&gt;&amp;nbsp;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;This issue arises because SQL Server considers these columns system-generated and restricts direct inserts, including during replication. The following workaround addresses this situation.&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;The Workaround&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;To successfully replicate temporal tables, follow these steps:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;U&gt;&lt;STRONG&gt;Note:&lt;/STRONG&gt;&lt;/U&gt;&amp;nbsp; This approach will work in case of scenarios when there is a scope of minimal downtime.&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;OL&gt;
&lt;LI&gt;&lt;STRONG&gt;Predefine Table Schema on Target&lt;/STRONG&gt;: Ensure that the source table schema exists on the target and matches with the source schema.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Disable System Versioning Temporarily&lt;/STRONG&gt;: Before configuring replication, disable system versioning on the temporal table. This allows replication to treat it like a regular table.&lt;/LI&gt;
&lt;/OL&gt;
&lt;LI-CODE lang="sql"&gt;ALTER TABLE [dbo].[Department] SET (SYSTEM_VERSIONING = OFF);&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 3. When you &lt;STRONG&gt;set SYSTEM_VERSIONING = OFF and don't drop the SYSTEM_TIME period&lt;/STRONG&gt;, the system continues to update the period columns for every insert and update operation. Use the below script to remove the period for system time.&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;ALTER TABLE dbo.Department DROP PERIOD FOR SYSTEM_TIME;&lt;/LI-CODE&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;4. After this step, we can use the below script step by step to configure replication.&lt;/P&gt;
&lt;P class="lia-align-justify lia-indent-padding-left-30px"&gt;&lt;STRONG&gt;Replication Setup Steps&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Set a replication database option for the specified database. This stored procedure is executed at the Publisher or Subscriber on any database.&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="sql"&gt;use master
GO

exec sp_replicationdboption 
@dbname = N'SourceDBNAme', 
@optname = N'publish', 
@value = N'true'
GO&lt;/LI-CODE&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&amp;nbsp;&lt;/DIV&gt;
&lt;UL&gt;
&lt;LI&gt;Create a transactional publication. This stored procedure is executed at the Publisher on the publication database.&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="sql"&gt;use [SourceDBName]
GO

exec sp_addpublication 
@publication = N'PublicationName', 
@description = N'Transactional Replication publication of database',
@sync_method = N'concurrent',
@retention = 0, 
@allow_push = N'true', 
@allow_pull = N'true', 
@allow_anonymous = N'true', 
@enabled_for_internet = N'false',
@SnapShot_in_defaultfolder = N'true', 
@compress_snapshot = N'false', 
@ftp_port = 21,
@allow_subscription_copy = N'false', 
@add_to_active_directory = N'false',
@repl_freq = N'continuous', 
@status = N'active', 
@independent_agent = N'true', 
@immediate_sync = N'true', 
@allow_sync_tran = N'false', 
@allow_queued_tran = N'false',
@allow_dts = N'false', 
@replicate_ddl = 1, 
@allow_initialize_from_backup = N'false', 
@enabled_for_p2p = N'false', 
@enabled_for_het_sub = N'false'
GO&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Create the Snapshot Agent for the specified publication. This stored procedure is executed at the Publisher on the publication database.&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="sql"&gt;use [SourceDBName]
GO

exec sp_addpublication_snapshot
@publication = N'PublicationName',
@frequency_type = 1,
@frequency_interval = 1,
@frequency_relative_interval = 1, 
@frequency_recurrence_factor = 0, 
@frequency_subday = 8, 
@frequency_subday_interval = 1, 
@active_start_time_of_day = 0,
@active_end_time_of_day = 235959,
@active_start_date = 0, 
@active_end_date = 0,
@publisher_security_mode = 0, 
@job_login = N'',
​@job_password = N'', 
@publisher_login = N'', 
@publisher_password = N''&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Create an article and add it to a publication. This stored procedure is executed at the Publisher on the publication database.&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="sql"&gt;use [SourceDBName]
GO

exec sp_addarticle 
@publication = N'PublicationName', 
@article = N'ArticleName',
@source_owner = N'Source Schema Name', 
@source_object = N'SourceTableName',
@type = N'logbased',
@description = null,
@creation_script = null,
@pre_creation_cmd = N'truncate', 
@schema_option = 0x000000000803509F, 
@identityrangemanagementoption = N'manual', 
@destination_table = N'Destination Table Name',
@destination_owner = N'Destination Schema Name',
@vertical_partition = N'false',
@ins_cmd = N'CALL sp_MSins_dboEmployee',
​@del_cmd = N'CALL sp_MSdel_dboEmployee', 
@upd_cmd = N'SCALL sp_MSupd_dboEmployee'
GO&lt;/LI-CODE&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&amp;nbsp;&lt;/DIV&gt;
&lt;UL&gt;
&lt;LI class="lia-align-justify"&gt;Add a subscription to a publication and set the Subscriber status. This stored procedure is executed at the Publisher on the publication database.&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="sql"&gt;use [SourceDBName]
GO

exec sp_addsubscription 
@publication = N'PublicationNAme', 
@subscriber = N'Azure SQL DB Server NAme', 
@destination_db = N'Target DB Name', 
@subscription_type = N'Push', 
@sync_type = N'automatic', 
@article = N'all', 
@update​_mode = N'read only', 
@subscriber_type = 0
GO&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Add a new scheduled agent job used to synchronize a push subscription to a transactional publication. This stored procedure is executed at the Publisher on the publication database.&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="sql"&gt;Use [SourceDBNAme]
GO

exec sp_addpushsubscription_agent 
@publication = N'PublicationNAme', 
@subscriber = N'Azure SQL DB Server NAme', 
@subscriber_db = N'Target DB Name', ​
@job_login = N'', ​
@job_password = null, 
@subscriber_security_mode = 0,
@subscriber_login = N'', 
@subscriber_password = null, 
@frequency_type = 64, 
@frequency_interval = 1, 
@frequency_relative_interval = 1, 
@frequency_recurrence_factor = 0, 
@frequency_subday = 4, 
@frequency_subday_interval = 5, 
@active_start_time_of_day = 0, 
@active_end_time_of_day = 235959, 
@active_start_date = 0, 
@active_end_date = 0, 
@dts_package_location = N'Distributor'
GO&lt;/LI-CODE&gt;
&lt;P class="lia-align-justify"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-justify"&gt;5. Once you have performed all the above steps and completed the data migration on target database you need to stop/delete the replication and again add period for system_time on the target table and enable system versioning.&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;ALTER TABLE dbo.Department ADD PERIOD FOR SYSTEM_TIME(&amp;lt;ValidFrom&amp;gt;,&amp;lt;ValidTo&amp;gt;);

ALTER TABLE [dbo].[Department] SET (SYSTEM_VERSIONING = ON);&lt;/LI-CODE&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;U&gt;&lt;STRONG&gt;Note&lt;/STRONG&gt;&lt;/U&gt;:&amp;nbsp;&lt;/P&gt;
&lt;P&gt;The &amp;lt;ValidFrom&amp;gt; and &amp;lt;ValidTo&amp;gt; columns are datetime2 columns defined as PERIOD FOR SYSTEM_TIME, using GENERATED ALWAYS AS ROW START and ROW END. Request to refer the period column names you have created while creating the temporal table and use the same while adding the period columns in the above script.&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H3&gt;Conclusion&lt;/H3&gt;
&lt;P&gt;Migrating temporal tables within a transactional replication environment involves managing system-versioned features appropriately. Temporarily disabling system versioning and removing the SYSTEM_TIME period allows for adherence to schema requirements and facilitates data replication. After completing replication on the target platform, re-enabling system versioning reinstates temporal table functionality while maintaining data integrity.&lt;/P&gt;
&lt;P&gt;This workaround ensures that your replication strategy remains robust while preserving the audit trail and historical insights offered by temporal tables.&lt;/P&gt;</description>
      <pubDate>Fri, 18 Jul 2025 18:06:14 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/temporal-table-replication-in-sql-server-common-barriers-and/ba-p/4422032</guid>
      <dc:creator>Sonali_Solanki</dc:creator>
      <dc:date>2025-07-18T18:06:14Z</dc:date>
    </item>
    <item>
      <title>Seamless Online Homogeneous SQL Family Migration via Azure Data Factory using SQL CDC</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/seamless-online-homogeneous-sql-family-migration-via-azure-data/ba-p/4376314</link>
      <description>&lt;P&gt;Migrating data across SQL platforms, be it SQL Server, Azure SQL Database, Managed Instance, or SQL Server on IaaS, often involves operational complexity and potential downtime. Azure Data Factory (ADF) removes those barriers by enabling seamless, logical data movement across these services in either direction. Whether using SQL Change Data Capture (CDC) for near-zero downtime or traditional batch-based strategies, ADF ensures data consistency and operational continuity throughout the process.&lt;/P&gt;
&lt;P&gt;While physical data migration strategies remain valuable in many scenarios, this blog focuses on how ADF delivers a unified, scalable approach to logical database migration, in modernizing the database environments with minimal downtime.&lt;/P&gt;
&lt;H3&gt;Prerequisites&lt;/H3&gt;
&lt;P&gt;&lt;STRONG&gt;NOTE&lt;/STRONG&gt;:&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Please make sure to go through the limitations of CDC as this blog doesn't cover those.&lt;/P&gt;
&lt;P&gt;&lt;A class="lia-external-url" href="https://docs.microsoft.com/en-us/sql/relational-databases/track-changes/about-change-data-capture-sql-server?view=sql-server-ver15#limitations" target="_blank" rel="noopener"&gt;SQL CDC Limitations&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/sql/relational-databases/track-changes/known-issues-and-errors-change-data-capture?view=sql-server-ver17" target="_blank" rel="noopener"&gt;Known Issues with CDC&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;Before proceeding, please ensure you have the following prerequisites:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;An Azure subscription.&lt;/LI&gt;
&lt;LI&gt;Access to Azure Data Factory.&lt;/LI&gt;
&lt;LI&gt;Source and target databases, such as SQL Server, Azure SQL Database, Azure SQL MI etc.&lt;/LI&gt;
&lt;LI&gt;Enable &lt;STRONG&gt;Change Data Capture (CDC)&amp;nbsp;&lt;/STRONG&gt;on the source database for online migration.
&lt;UL&gt;
&lt;LI&gt;CDC captures changes like insert, update, and delete (DML) operations in the source database, allowing near real-time replication to a target database with minimal latency. To enable CDC, run:&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="sql"&gt;-- Enable CDC on the database

EXEC sys.sp_cdc_enable_db;

-- Enable CDC on the source table 

EXEC sys.sp_cdc_enable_table

@source_schema = N'dbo',

@source_name = N'SourceTable',

 @role_name = NULL;&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Azure Data Factory Provisioning &lt;/STRONG&gt;
&lt;UL&gt;
&lt;LI&gt;ADF should be provisioned to provide a runtime environment for executing the pipeline.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Self-hosted Integration Runtime (SHIR)&lt;/STRONG&gt;
&lt;UL&gt;
&lt;LI&gt;SHIR is required to connect to the data source or destination which is not natively reachable by Azure (e.g., on-premises, private VNET, behind firewall).&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Linked Services&lt;/STRONG&gt;
&lt;UL&gt;
&lt;LI&gt;These should be created to connect to the source and target.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Datasets&amp;nbsp;&lt;/STRONG&gt;
&lt;UL&gt;
&lt;LI&gt;Datasets identify data within different data stores, such as tables, files, folders, and documents.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Performance Optimization&lt;/STRONG&gt;
&lt;UL&gt;
&lt;LI&gt;To speed up the process, primary keys, non-clustered indexes and constraints should be dropped on the target to reduce blocking/deadlocks and minimize resource contention.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Script Components&lt;/STRONG&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Adf_source.sql&lt;/STRONG&gt;
&lt;UL&gt;
&lt;LI&gt;This script should be deployed on the source SQL Server. It will populate information in the &lt;STRONG&gt;dbo.data_extraction_config_adf &lt;/STRONG&gt;table to run Change Data Capture (CDC) and the initial load pipeline.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Adf_target.sql&lt;/STRONG&gt;
&lt;UL&gt;
&lt;LI&gt;This script should be deployed on the target SQL server. It will create stored procedures to help merge CDC changes and create objects necessary for running pipelines smoothly.&lt;/LI&gt;
&lt;/UL&gt;
&amp;nbsp;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Master tables&lt;/STRONG&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;dbo.cdc__watermark__adf &lt;/STRONG&gt;contains information about the CDC tables for the last watermark.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;dbo.data__extraction__config__adf &lt;/STRONG&gt;contains information about the heap tables for initial load and CDC tables.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;dbo.sqlqueries &lt;/STRONG&gt;contains information about the clustered tables for initial load.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Let's deep dive into pipelines to handle different scenarios&amp;nbsp;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Pipeline 1: ClusteredTableMigration_Initial&lt;/STRONG&gt;
&lt;UL&gt;
&lt;LI&gt;This pipeline migrates data only from clustered tables.&lt;/LI&gt;
&lt;LI&gt;The &lt;STRONG&gt;dbo.sqlqueries&lt;/STRONG&gt; table automatically populates with clustered table info via the pipeline (Stored Procedure Activity).&lt;/LI&gt;
&lt;LI&gt;Ensure the source table schema matches the target table schema. To run the pipeline for specific tables, set the&amp;nbsp;&lt;STRONG&gt;IsActive &lt;/STRONG&gt;flag to 0 (inactive) or 1 (active) in the &lt;STRONG&gt;sqlqueries&lt;/STRONG&gt; table or add the table name in the Lookup activity.&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Pipeline 2: HeapTableMigration_Initial&lt;/STRONG&gt;
&lt;UL&gt;
&lt;LI&gt;This pipeline is designated for migrating heap tables. Prior to executing this pipeline, ensure that the heap table information has been added to the &lt;STRONG&gt;dbo.data__extraction__config__adf&lt;/STRONG&gt; table.&lt;/LI&gt;
&lt;LI&gt;The source table schema should be synchronized with the target table schema.&lt;/LI&gt;
&lt;LI&gt;To execute the pipeline for a set of tables, the &lt;STRONG&gt;IsActive&lt;/STRONG&gt; flag may be set to 0 (inactive) or 1 (active) in the dbo.&lt;STRONG&gt;data__extraction__config__adf&lt;/STRONG&gt; table.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;img&gt;&lt;STRONG&gt;Pipeline 2: Heap Table Migration&lt;/STRONG&gt;&lt;/img&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Pipeline 3: CDCTableMigration&lt;/STRONG&gt;
&lt;UL&gt;
&lt;LI&gt;This pipeline facilitates the migration of clustered tables with Change Data Capture (CDC) enabled. Prior to execution, please ensure that the relevant information for these clustered tables is entered into the &lt;STRONG&gt;dbo.data__extraction__config__adf&lt;/STRONG&gt; table.&lt;/LI&gt;
&lt;LI&gt;Ensure the table schema is synchronized with the target schema, and that all tables intended for CDC synchronization possess a primary key and matching schema definition on the target system (excluding constraints and non-clustered indexes).&lt;/LI&gt;
&lt;LI&gt;To execute the pipeline for specific tables, the&amp;nbsp;&lt;STRONG&gt;IsActive&lt;/STRONG&gt; flag can be set to 0 (inactive) or 1 (active) in the &lt;STRONG&gt;dbo.data__extraction__config__adf&amp;nbsp;&lt;/STRONG&gt;table.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;img&gt;&lt;STRONG&gt;Pipeline 3: CDC Table Migration&lt;/STRONG&gt;&lt;/img&gt;
&lt;P&gt;&lt;STRONG&gt;Schedule the Pipeline - For CDC load only&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Create a trigger&lt;/STRONG&gt;: Create a trigger to schedule the pipeline to run at regular intervals (e.g., every 5-30 minutes based on application requirements) to capture and apply changes incrementally.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Monitor the pipeline&lt;/STRONG&gt;: Monitor the pipeline runs to verify that the data is being migrated and synchronized accurately.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Cutover and cleanup&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Once the delta changes are synchronized fully on source and target database, cutover can be initiated by setting the source database to read-only and then changing the connection string of the application (or all apps, agent jobs etc. that are impacted) to use the new target database and perform cleanup by deleting the SPs in target database and stop the CDC, remove tables, and SPs in source database.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Conclusion&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Using Azure Data Factory allows for both online and offline data migration with minimal downtime, ensuring consistency between source and target databases. Change Data Capture enables near real-time data migration, suitable for environments requiring continuous data synchronization.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Note &lt;/STRONG&gt;- To get ADF Pipelines and T-SQL Queries mentioned in this blog please reach out to our team alias : &lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;datasqlninja@microsoft.com&lt;/A&gt;&lt;/P&gt;</description>
      <pubDate>Thu, 06 Nov 2025 05:20:45 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/seamless-online-homogeneous-sql-family-migration-via-azure-data/ba-p/4376314</guid>
      <dc:creator>Vinod_Kumar_MSFT</dc:creator>
      <dc:date>2025-11-06T05:20:45Z</dc:date>
    </item>
    <item>
      <title>Hidden pitfalls of Temporary Tables in Oracle to PostgreSQL Migrations</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/hidden-pitfalls-of-temporary-tables-in-oracle-to-postgresql/ba-p/4416636</link>
      <description>&lt;P&gt;If you have been relying on Oracle Database as your primary system for analytics and the generation of MIS reports, you are probably familiar with the use of temporary tables within stored procedures. These temporary tables play an important role in managing intermediate data, performing complex calculations, and streamlining the overall data processing workflow.&amp;nbsp;Temporary tables help in handling large volumes of data, break down queries into manageable steps, and produce complex analytical reports efficiently.&lt;/P&gt;
&lt;P&gt;However, when organizations migrate these systems to Azure PostgreSQL, most automated code converters simply translate Oracle temporary tables into Azure PostgreSQL temporary tables without highlighting the key difference.&lt;/P&gt;
&lt;H1&gt;Understand the misconception&lt;/H1&gt;
&lt;P&gt;In Oracle the Global Temporary Table is a persistent schema object whose structure is permanent, but data is temporary. Internally, Oracle stores all data inserted into a GTT in the temporary tablespace, isolating it per session by using temporary segments that are dynamically allocated and cleaned up at the end of the session or transaction, depending on whether the table is defined with ON COMMIT DELETE ROWS or ON COMMIT PRESERVE ROWS. While the table metadata remains in the data dictionary, the data itself is never written to the redo logs.&lt;/P&gt;
&lt;P&gt;Oracle also introduced Private Temporary table in version 18C which has an extra option of ON COMMIT DROP DEFINITION which drops the table at transaction commit.&lt;/P&gt;
&lt;P&gt;Azure PostgreSQL too has a temporary table object that supports all the three commit clauses available in Oracle i.e. ON COMMIT DELETE ROWS, ON COMMIT PRESERVE ROWS and ON COMMIT DROP but the object is private to the session that created it with both its structure and data completely invisible to other sessions and the table itself is dropped automatically when the session ends.&lt;/P&gt;
&lt;P&gt;Oracle Global Temporary Tables have a permanent table definition accessible by all sessions but store data privately per session or transaction, whereas Azure PostgreSQL temporary tables exist only for the duration of a session and are dropped automatically at session end.&lt;/P&gt;
&lt;P&gt;At first glance, this difference might seem trivial, after all, you can simply add a CREATE TABLE statement in your code to recreate the temporary table at the start of every session. But what appears to be a small tweak can quickly spiral into a performance nightmare, overloading your system in ways you wouldn’t expect if not managed carefully.&lt;/P&gt;
&lt;P&gt;Whole Azure PostgreSQL is built on an MVCC architecture, which means even its internal tables and system catalogue tables retain deleted rows of dropped objects. If you relook at the key difference between temp tables, you will understand that every time a temp table is created and drop per session, it adds few rows and deletes it from many system catalogue tables. See example below&lt;/P&gt;
&lt;P&gt;Following is the output of Pgstattuple for three of the system tables.&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;Now I run a function a few times sequentially that joins multiple tables and writes the data into a temp table and return the response. You can see that there is a slight increase but nothing to be concerned about.&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;But if the same function is called by 500 sessions concurrently, you can see that the increase is dramatic.&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;There is also a marked increase in IOPS consumption as shown below&lt;/P&gt;
&lt;img /&gt;
&lt;H1&gt;Understand the impact&lt;/H1&gt;
&lt;P&gt;As seen above, system catalogue tables like pg_class, pg_attribute, and pg_type can grow rapidly in size as each session that creates and drops temporary tables leaves behind dead tuples in these catalogues, contributing to significant bloat. This accumulation happens because Azure PostgreSQL records metadata for every temporary table in the system catalogues, and when the tables are dropped (typically at session end), their metadata is simply marked as dead rather than immediately removed.&lt;/P&gt;
&lt;P&gt;In highly transactional environments, this bloat can escalate dramatically, sometimes increasing by hundreds or even thousands of times within just a few hours. Azure PostgreSQL relies heavily on its system catalogue during parsing, planning, and execution phases of every SQL statement.&lt;/P&gt;
&lt;P&gt;Also, every temp table created will try to utilize temp buffer to store the data but if the data is large and temp buffer is small then naturally the data is stored on disk. This frequent creation and deletion of files adds a lot of disk IO. Under normal conditions, this will be taken care of by the file management. However, when the system is under heavy load, this process can become a bottleneck and slow down even normal select statements.&lt;/P&gt;
&lt;P&gt;This catalogue bloat and frequent file and buffer management under heavy or repeated use of temporary tables will lead to high CPU consumption and will slow down existing users which will intern add more CPU load and quickly the system will get inundated and possibly crash.&lt;/P&gt;
&lt;P&gt;Below example shows almost 3 times increase in planning time with bloated system table as compared to without bloat&lt;/P&gt;
&lt;img /&gt;&lt;img /&gt;
&lt;H1&gt;Conclusion&lt;/H1&gt;
&lt;P&gt;It's important to recognize that Azure PostgreSQL and Oracle implement temporary tables differently: Oracle's global temporary tables are persistent schema objects that do not add significant system load whereas Azure PostgreSQL's temporary tables are always session-specific and are dropped at session, this along with its MVCC architecture adds significant load on the system in certain situations. This fundamental difference means that if not handled properly it can cause database to crash.&lt;/P&gt;
&lt;P&gt;When migrating workloads from Oracle to Azure PostgreSQL, developers should carefully consider whether a temporary table is truly necessary, or if the requirement can be addressed more elegantly using alternatives like CTEs or views.&lt;/P&gt;
&lt;P&gt;In some scenarios, temporary tables are indispensable, for example, they provide an efficient way to store intermediate results, for simplifying complex query logic or for collecting data from ref-cursor and no workarounds fully match the flexibility of the temp tables for these use cases.&lt;/P&gt;
&lt;P&gt;If you can’t get rid of temp tables, then it’s absolutely necessary to have a robust alerting on system table bloat and having a custom job that does frequent vacuuming on these tables.&lt;/P&gt;
&lt;H4&gt;Feedback and suggestions&lt;/H4&gt;
&lt;P&gt;If you have feedback or suggestions for improving this data migration asset, please contact the Databases SQL Customer Success Engineering (Ninja) Team (&lt;A href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;datasqlninja@microsoft.com&lt;/A&gt;). Thanks for your support!&lt;/P&gt;
&lt;P&gt;Note: For additional information about migrating various source databases to Azure, see the&amp;nbsp;&lt;A href="https://datamigration.microsoft.com/" target="_blank" rel="noopener"&gt;Azure Database Migration Guide&lt;/A&gt;.&lt;/P&gt;</description>
      <pubDate>Thu, 26 Jun 2025 00:59:58 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/hidden-pitfalls-of-temporary-tables-in-oracle-to-postgresql/ba-p/4416636</guid>
      <dc:creator>KapilSamant</dc:creator>
      <dc:date>2025-06-26T00:59:58Z</dc:date>
    </item>
    <item>
      <title>Ingesting Mainframe File System Data (EBCDIC) into SQL DB on Fabric Using OSS Cobrix</title>
      <link>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/ingesting-mainframe-file-system-data-ebcdic-into-sql-db-on/ba-p/4402105</link>
      <description>&lt;H1&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc189641243"&gt;&lt;/A&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc189646718"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Introduction&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;Mainframe/Midrange data is often stored in fixed-length format, where each record has a predetermined length, or variable-length format, where each record’s length may vary. The data is stored in binary format, using &lt;A class="lia-external-url" href="https://en.wikipedia.org/wiki/EBCDIC" target="_blank" rel="noopener"&gt;Extended Binary Coded Decimal Interchange Code (EBCDIC)&lt;/A&gt; encoding and the metadata for the EBCDIC files is stored in a copybook file. These EBCDIC encoded files store data uniquely based on its data type, which is vital Mainframe file system data optimal storage and performance.&lt;/P&gt;
&lt;P&gt;However, this presents a challenge when migrating data from Mainframe or Midrange systems to distributed systems. The data, originally stored in a format specific to Mainframe or Midrange systems, is not directly readable upon transfer to distributed systems. As distributed systems only understand code pages like &lt;A class="lia-external-url" href="https://en.wikipedia.org/wiki/ASCII" target="_blank" rel="noopener"&gt;American Standard Code for Information Interchange (ASCII)&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;To make this data readable on a distributed system, we would need to do an EBCDIC to ASCII code page conversion. This conversion can be achieved in many ways. Few of them are&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Microsoft Host Integration Server, &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/host-integration-server/core/data-source-wizard-host-files-2#ma" target="_blank" rel="noopener"&gt;Host File client&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;Logic app &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/connectors/integrate-host-files-ibm-mainframe" target="_blank" rel="noopener"&gt;IBM host File connector&lt;/A&gt;.&amp;nbsp; &amp;nbsp;Our detailed blog about it is &lt;A class="lia-internal-link lia-internal-url lia-internal-url-content-type-blog" href="https://techcommunity.microsoft.com/blog/modernizationbestpracticesblog/mainframe-ebcdic-data-file-to-ascii-conversion-using-azure-logic-app/3763750" target="_blank" rel="noopener" data-lia-auto-title="here" data-lia-auto-title-active="0"&gt;here&lt;/A&gt;.&lt;/LI&gt;
&lt;LI&gt;Open Source (OSS) Libraries.&lt;/LI&gt;
&lt;LI&gt;Third-party ISV solutions.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1&gt;&lt;SPAN class="lia-text-color-10"&gt;Microsoft Host Intergration server&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;Microsoft Host Integration server (HIS) has a component named Host File Client (HFC). This particular component helps in converting Mainframe EBCDIC files to ASCII using a custom developed C# solution. &amp;nbsp;More details on this solution is provided in &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/host-integration-server/core/data-providers-for-host-files1" target="_blank" rel="noopener"&gt;HIS documentation page&lt;/A&gt;.&lt;/P&gt;
&lt;H1&gt;&lt;SPAN class="lia-text-color-10"&gt;Logic App Converter.&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;If you prefer to choose a cloud native solution, then you can try to use the Host File Connector in Azure Logic Apps. The detailed process has been documented in&amp;nbsp;&lt;A href="https://techcommunity.microsoft.com/blog/modernizationbestpracticesblog/mainframe-ebcdic-data-file-to-ascii-conversion-using-azure-logic-app/3763750" target="_blank" rel="noopener" data-lia-auto-title="this blog post" data-lia-auto-title-active="0"&gt;this blog post&lt;/A&gt;.&lt;/P&gt;
&lt;H1&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc448428265"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Fabric (with Open-Source Libraries)&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;&lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/fabric/fundamentals/microsoft-fabric-overview" target="_blank" rel="noopener"&gt;Microsoft Fabric&lt;/A&gt; is an enterprise-ready, end-to-end analytics platform. It unifies data movement, data processing, ingestion, transformation, real-time event routing, and report building. It supports these capabilities with integrated services like Data Engineering, Data Factory, Data Science, Real-Time Intelligence, Data Warehouse, and Databases.&lt;BR /&gt;&lt;BR /&gt;There are many open-source solutions which can help in achieving conversion of mainframe data to ASCII. This will help in converting files using Fabric, Databricks, Synapse on Azure.&lt;/P&gt;
&lt;H5&gt;This blog will focus on the OSS option.&lt;/H5&gt;
&lt;H1&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc189646719"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Data Ingestion Architecture&lt;/SPAN&gt;&lt;/H1&gt;
&lt;img /&gt;
&lt;H1&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc815687239"&gt;&lt;/A&gt;&lt;/H1&gt;
&lt;H1&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc189641244"&gt;&lt;/A&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc189646720"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Using OSS on Fabric&lt;BR /&gt;&lt;/SPAN&gt;&lt;/H1&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;There are multiple Open-source libraries that can be utilized for this data conversion. In this article we will dive deeper into one of these solutions -&amp;nbsp;&lt;A href="https://github.com/AbsaOSS/cobrix" target="_blank" rel="noopener"&gt;Cobrix &lt;/A&gt;&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&lt;A class="lia-external-url" href="https://github.com/AbsaOSS/cobrix" target="_blank" rel="noopener"&gt;COBRIX&lt;/A&gt; is an open-source library built using scala and leverages the multithreaded process powered framework of spark. This helps in converting the file faster than compared to other single threaded processes. As this is multithreaded, it will need a pool of compute resources to achieve the conversion. Cobrix can run on spark environments like Azure Synapse, Databricks and Microsoft Fabric. We will dive deeper into how we can set up Cobrix on Microsoft Fabric.&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc189641245"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Download required Cobrix packages&lt;/SPAN&gt;
&lt;UL&gt;
&lt;LI&gt;We will have to first download the required Cobrix packages from the right sources. As Fabric has a particular runtime dependency, please make sure your download the right build for Scala as per the fabric environment that you setup. You will have to download two jars named Cobol-Parser_xx.xx.jar and Spark-cobol_xxx.xx.jar.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;&lt;SPAN class="lia-text-color-10"&gt;Setup the Fabric Environment.&lt;/SPAN&gt;
&lt;UL&gt;
&lt;LI&gt;Login to &lt;A class="lia-external-url" href="https://fabric.microsoft.com/" target="_blank" rel="noopener"&gt;fabric.microsoft.com&lt;/A&gt;.&lt;/LI&gt;
&lt;LI&gt;Create a &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/fabric/get-started/create-workspaces" target="_blank" rel="noopener"&gt;Fabric workspace&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;Create an &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/fabric/data-engineering/create-and-use-environment" target="_blank" rel="noopener"&gt;Environment in the workspace&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;Open the Environment and click on custom Libraries.&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;/LI&gt;
&lt;LI&gt;Upload the two jars which were downloaded earlier.Once you have uploaded your custom library setup should look something like this.&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;/LI&gt;
&lt;LI&gt;Create a &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/fabric/data-engineering/tutorial-build-lakehouse" target="_blank" rel="noopener"&gt;new Lakehouse&lt;/A&gt;. Upload the cobol copybook file as well as the Mainframe Datafile in Binary to a particular location in the lakehouse. At the end of this step your lakehouse setup should look something of this kind.&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;/LI&gt;
&lt;LI&gt;For both these files, copy the &lt;STRONG&gt;Azure Blob File System Secure (&lt;/STRONG&gt;ABFSS) path by right clicking on the files. This link can be used to point to the file from the Spark notebook.&lt;img /&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;&lt;SPAN class="lia-text-color-10"&gt;Create a new Fabric pipeline.&lt;/SPAN&gt;
&lt;UL&gt;
&lt;LI&gt;This pipeline will have two components, the first component will be a notebook, which will call the Cobrix framework to convert the file from EBCDIC to ASCII. Second piece of it will be a copy activity to copy the contents of the output file created in the notebook to a SQL DB on Fabric.&lt;/LI&gt;
&lt;LI&gt;Create a &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/fabric/data-engineering/how-to-use-notebook" target="_blank" rel="noopener"&gt;new Notebook&lt;/A&gt; . Attach the environment which you had created earlier to this notebook.&lt;img /&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;In the notebook cell, use can use this piece of code.&lt;/P&gt;
&lt;LI-CODE lang="scala"&gt;//Blob access var CopyBookName = "abfss://file1.cpy" var DataFileName = "abfss://file1.dat" var outputFileName = "abfss://output.txt" //Cobrix Converter Execution val cobolDataframe = spark .read .format("za.co.absa.cobrix.spark.cobol.source") .option("copybook", CopyBookName) .load(DataFileName) //Display DataFrame to view conversion results cobolDataframe.printSchema() cobolDataframe.show()&lt;/LI-CODE&gt;
&lt;UL&gt;
&lt;LI style="list-style-type: none;"&gt;
&lt;UL&gt;
&lt;LI&gt;Once you have set the configuration properly, you are all set to run the notebook. And this will convert the file from EBCDIC to ASCII and store it to the Lakehouse.&lt;/LI&gt;
&lt;LI&gt;Add a Copy activity to the pipeline with &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/fabric/data-factory/connector-lakehouse-copy-activity#source" target="_blank" rel="noopener"&gt;File as Source&lt;/A&gt; and &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/fabric/data-factory/connector-sql-server-copy-activity#destination" target="_blank" rel="noopener"&gt;SQL server as destination&lt;/A&gt;.&lt;/LI&gt;
&lt;LI&gt;At this point in time, your pipeline should look something like this&lt;img /&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;UL&gt;
&lt;LI style="list-style-type: none;"&gt;
&lt;UL&gt;
&lt;LI&gt;Once you run this pipeline, the Mainframe EBCDIC file will be converted to ASCII and then loaded into Fabric Native SQL DB table.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc1256804094"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Third-party ISV solutions.&lt;/SPAN&gt;&lt;/H1&gt;
&lt;UL&gt;
&lt;LI style="list-style-type: none;"&gt;
&lt;UL&gt;
&lt;LI&gt;There are many third-party ISV solutions which are available for EBCDIC to ASCII conversions. Please get int touch with us to help you get the right solution for your requirements.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc189641247"&gt;&lt;/A&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc189646723"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Summary&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;EBCDIC to ASCII conversion is a critical piece of work during the data migration/modernization journey. Being able to do this with ease and accuracy will drive the success of data migration.&amp;nbsp; With this feature enabled in fabric, this opens up a new set of use cases like Mainframe report generation etc kind of use cases which are predominantly data warehouse driven.&lt;/P&gt;
&lt;H1&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc189641248"&gt;&lt;/A&gt;&lt;A class="lia-anchor" target="_blank" name="_Toc189646724"&gt;&lt;/A&gt;&lt;SPAN class="lia-text-color-10"&gt;Feedback and suggestions&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;If you have feedback or suggestions for improving this data migration asset, please send an email to&amp;nbsp;&lt;A class="lia-external-url" href="mailto:datasqlninja@microsoft.com" target="_blank" rel="noopener"&gt;Database Platform Engineering Team&lt;/A&gt;.&lt;/P&gt;</description>
      <pubDate>Fri, 06 Jun 2025 15:18:39 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/modernization-best-practices-and/ingesting-mainframe-file-system-data-ebcdic-into-sql-db-on/ba-p/4402105</guid>
      <dc:creator>Ramanath_Nayak</dc:creator>
      <dc:date>2025-06-06T15:18:39Z</dc:date>
    </item>
  </channel>
</rss>

