{"id":47235,"date":"2026-10-07T08:20:08","date_gmt":"2026-10-07T06:20:08","guid":{"rendered":"https:\/\/www.dbi-services.com\/blog\/?p=47235"},"modified":"2026-10-07T08:20:09","modified_gmt":"2026-10-07T06:20:09","slug":"archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2","status":"publish","type":"post","link":"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/","title":{"rendered":"Archiving 5 TB from Oracle into SQL Server: views, indexes and compression (part 2)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">At the end of&nbsp;<a href=\"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-a-migration-logbook-part-1\/\">part 1<\/a>&nbsp;the data was across, at 42,170 rows per second into a database that could not answer a single question. Every nonclustered index disabled, the largest tables loaded as heaps with no primary key, every foreign key switched off, and 4,784 views that mostly did not compile.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This part is about putting it back: <strong>translating the views<\/strong>, <strong>rebuilding the keys and indexes under a Standard Edition licence<\/strong> that makes every build offline, <strong>and compressing 3.2 TB of heaps<\/strong> that turned out to be mostly empty space.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The logbook resumes at day 5.<\/p>\n\n\n\n<h2 id=\"h-day-5-four-thousand-views-to-convert\" class=\"wp-block-heading\">Day 5. Four thousand views to convert<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">4,658 of the 4,784 views live in a single schema, and nobody needed all of them. We went through the list with the client first and kept the ones that actually matter to the business, which brought the scope down to about 710. SSMA converted around 600 of those on its own.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">SSMA fails mostly on <code>CONNECT BY<\/code>, <code>START WITH<\/code> and <code>SYS_CONNECT_BY_PATH<\/code>. For some easy cases, <code>DECODE<\/code> to <code>CASE<\/code> and <code>NVL<\/code> handles it well, for the record. For more complex use case, we needed to find an automatic solution process. So first, we discussed with the client to know which view were critical and needed to be migrated. Then, with this list, we leveraged the power of AI to help us converting the remaining views based on the following workflow:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"1024\" src=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-45-786x1024.png\" alt=\"\" class=\"wp-image-47241\" srcset=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-45-786x1024.png 786w, https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-45-230x300.png 230w, https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-45-768x1001.png 768w, https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-45.png 1120w\" sizes=\"auto, (max-width: 786px) 100vw, 786px\" \/><\/figure>\n\n\n\n<h3 id=\"h-the-workflow\" class=\"wp-block-heading\">The workflow<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">SSMA converts what it can. The failures get extracted from <code>dba_views <\/code>into files. Then, and this is the step that changes the economics, they get<strong> classified by pattern in the Oracle source<\/strong> rather than one at a time by error message.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Translation itself was done in batches of one family at a time, outside production, on extracted files, <strong>with an AI assistant<\/strong> and a context file describing the type mapping decisions, the column types of the referenced tables and the target version. Then human review, tests against Oracle and finally, deployment.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The catalogue of families is the real deliverable. Eight of them closed the great majority of the cases:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Family<\/th><th>Verdict<\/th><\/tr><\/thead><tbody><tr><td><code>+<\/code> concatenation on numerics (<code>col + '_' + col<\/code>)<\/td><td><code>CONCAT<\/code><\/td><\/tr><tr><td>Untyped NULL in a UNION, cast to <code>numeric(38,10)<\/code> against a <code>datetime2<\/code> sibling<\/td><td>retype the cast<\/td><\/tr><tr><td>Call to a package function that was never migrated<\/td><td>inline it if possible<\/td><\/tr><tr><td><code>ssma_oracle.*<\/code> helper functions<\/td><td>install the extension pack<\/td><\/tr><tr><td>XML extraction (<code>.extract().getStringVal()<\/code>)<\/td><td>rewrite part of the query<\/td><\/tr><tr><td><code>TO_TIMESTAMP_TZ<\/code><\/td><td>rewrite part of the query<\/td><\/tr><tr><td>Date arithmetic landing in float<\/td><td>redefine type mapping<\/td><\/tr><tr><td><code>CONNECT BY<\/code> used as a generator<\/td><td>materialise the level connectors<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><span style=\"text-decoration: underline\">And a word on the AI<\/span><\/strong>, since that is the part people ask about. <strong>No data ever left the environment<\/strong>: only the view definitions were processed, and every test against the live database was run by hand so we kept control of the process. We could have added MCP connectors and let a local model run the tests itself, but with the number of days allocated to the project and the small number of views left to fix, doing it manually was simpler. Around 85 percent of the views <strong>wanted by the client<\/strong> had already come through SSMA.<\/p>\n\n\n\n<h2 id=\"h-day-6-putting-the-indexes-back-and-making-the-database-smaller\" class=\"wp-block-heading\">Day 6. Putting the indexes back and making the database smaller<\/h2>\n\n\n\n<h3 id=\"h-recovering-what-i-had-thrown-away\" class=\"wp-block-heading\">Recovering what I had thrown away<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The primary keys I dropped to load into heaps had left no trace on the SQL Server side. Nothing in <code>sys.indexes<\/code>, no disabled definition, nothing.<\/p>\n\n\n\n<h3 id=\"h-what-standard-edition-costs\" class=\"wp-block-heading\">What Standard Edition costs<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Index builds are offline and single-threaded. The table is locked for the duration, metadata reads queue behind it, but we managed to run 4 builds in parallel. <code>SORT_IN_TEMPDB = OFF<\/code> for the clustered builds. The sort covers the entire table, and 36 GB of tempdb was never going to hold a 900 GB sort.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Meanwhile <code>percent_complete<\/code> sits at 0 for the whole build, with the serene confidence of something that has never been asked to account for itself. It works for <code>BACKUP DATABASE<\/code>. It does not work here. Real progress lives in <code><a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/relational-databases\/system-dynamic-management-objects\/sys-dm-exec-query-profiles-transact-sql?view=sql-server-ver17\">sys.dm_exec_query_profile<\/a>s<\/code>, operator by operator :<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;\nSELECT physical_operator_name, row_count, estimate_row_count,\n       CAST(row_count*100.0\/NULLIF(estimate_row_count,0) AS decimal(5,1)) AS pct\nFROM sys.dm_exec_query_profiles WITH (NOLOCK)\nWHERE session_id = &lt;spid of the build&gt; \nORDER BY node_id;\n\n<\/pre><\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"399\" height=\"87\" src=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-46.png\" alt=\"\" class=\"wp-image-47252\" srcset=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-46.png 399w, https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-46-300x65.png 300w\" sizes=\"auto, (max-width: 399px) 100vw, 399px\" \/><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">Those are not three parallel counters. They are three sequential phases of one pipeline, and only one of them moves at a time. The scan reads the source table, and while it runs its percentage is the only honest progress figure available. The sort is a blocking operator: it cannot emit a single row until it has consumed all 31.9 million, so it stays at zero through the whole scan and then through the whole sort. That is the blind spot. Once the scan reaches 100 percent you get a long stretch where all three counters report nothing while the server works flat out, and if the sort spills to disk that stretch can outlast the other two phases combined. Index Insert starts moving only when the sort begins handing rows over, and it is the first number that means the end is actually in sight.<\/p>\n\n\n\n<h3 id=\"h-why-compress-and-what-it-returned\" class=\"wp-block-heading\">Why compress, and what it returned<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">On a read-only database, PAGE compression has no recurring write cost. You keep the read benefit and pay nothing back. The argument is rarely this clean.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Compression trades CPU for I\/O, and on an archive that trade is one-sided. These pages are read rarely, and when they are, nothing heavy is competing for the processor, so the decompression cost lands on a machine with nothing better to do. What you get back is density: more rows per page, which means fewer reads to find the one row somebody asked for and more of the table sitting in memory at once.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The data was unusually well suited to it, for a reason worth spelling out. These tables carry roughly 200 numeric columns, mostly null, plus five <code>nvarchar(200)<\/code> aggregation labels that repeat endlessly. Here is the part that surprises people: <strong>a fixed-length numeric column that is NULL still occupies its full width<\/strong> in SQL Server. \u201cMostly null\u201d saves nothing before compression. It is precisely why the heap was that large, and precisely what the page dictionary destroys.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The allocated space per row tells the story:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Table<\/th><th>Rows<\/th><th>GB (heap)<\/th><th>bytes allocated per row<\/th><\/tr><\/thead><tbody><tr><td>TABLE1<\/td><td>251,777,518<\/td><td>960.5<\/td><td>~4,100<\/td><\/tr><tr><td>TABLE2<\/td><td>229,881,717<\/td><td>876.9<\/td><td>~4,100<\/td><\/tr><tr><td>TABLE3<\/td><td>343,997,550<\/td><td>874.8<\/td><td>~2,700<\/td><\/tr><tr><td>TABLE4<\/td><td>447,972,849<\/td><td>227.9<\/td><td>~550<\/td><\/tr><tr><td>TABLE5<\/td><td>519,727,931<\/td><td>120.2<\/td><td>~250<\/td><\/tr><tr><td>TABLE6<\/td><td>505,819,544<\/td><td>117.0<\/td><td>~250<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Three tables hold 2.7 TB of the 3.2 TB total<\/strong>. At around 4 KB of allocated space per row against an 8 KB page, the top two fit around two rows per page and waste most of what is left.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then the first build, on 505,819,544 rows with a five-column primary key:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nALTER TABLE &lt;TABLE6&gt;\nADD CONSTRAINT PK_TABLE6 PRIMARY KEY CLUSTERED\n    (&lt;COLUMNS_NAME&gt;)\nWITH (DATA_COMPRESSION = PAGE, SORT_IN_TEMPDB = OFF);\n\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\"><strong>117 GB to 30.7 GB. A ratio of 3.81.<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"721\" height=\"121\" src=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-47.png\" alt=\"\" class=\"wp-image-47258\" srcset=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-47.png 721w, https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-47-300x50.png 300w\" sizes=\"auto, (max-width: 721px) 100vw, 721px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, as you can see above, more than 2.5 T have been reclaimed (datafiles were full with the heaps). Compression has made huge benefits and that means less data on disks, which means fewer pages to take in the buffer (less IO).<\/p>\n\n\n\n<h3 id=\"h-the-script-that-puts-everything-back\" class=\"wp-block-heading\">The script that puts everything back<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Across every schema that is two cursors, one rebuilding the disabled indexes with compression and one re-enabling the foreign keys. It is driven by the same&nbsp;<code>#Schemas<\/code>&nbsp;table as the disable script in part 1:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSET NOCOUNT ON;\nDECLARE @Execute bit = 0;   -- 0 = preview ; 1 = execute\n  \nDECLARE @cmd nvarchar(max);\nDECLARE @n   int = 0;\n  \nDECLARE reb_cur CURSOR LOCAL FAST_FORWARD FOR\n    SELECT N&#039;ALTER INDEX &#039; + QUOTENAME(i.name)\n         + N&#039; ON &#039; + QUOTENAME(s.name) + N&#039;.&#039; + QUOTENAME(t.name)\n         + N&#039; REBUILD WITH (DATA_COMPRESSION = PAGE);&#039;\n    FROM   sys.indexes  i\n    JOIN   sys.tables   t ON i.object_id = t.object_id\n    JOIN   sys.schemas  s ON t.schema_id = s.schema_id\n    WHERE  s.name IN (SELECT schema_name FROM #Schemas)\n      AND  i.is_disabled = 1          \n    ORDER BY s.name, t.name, i.name;\n  \nOPEN reb_cur;\nFETCH NEXT FROM reb_cur INTO @cmd;\nWHILE @@FETCH_STATUS = 0\nBEGIN\n    SET @n += 1;\n    PRINT @cmd;\n    IF @Execute = 1\n    BEGIN\n        BEGIN TRY EXEC sys.sp_executesql @cmd; END TRY\n        BEGIN CATCH PRINT N&#039;   -- FAILED: &#039; + ERROR_MESSAGE(); END CATCH\n    END\n    FETCH NEXT FROM reb_cur INTO @cmd;\nEND\nCLOSE reb_cur;\nDEALLOCATE reb_cur;\n  \nPRINT N&#039;---&#039;;\nPRINT CONVERT(nvarchar(10), @n) + N&#039; index &#039;\n    + CASE WHEN @Execute = 1 THEN N&#039;rebuilt.&#039; ELSE N&#039;to be rebuilt (preview).&#039; END;\nGO\n  \n  \nSET NOCOUNT ON;\nDECLARE @Execute bit = 1;   -- 0 = preview ; 1 = executer\n  \nDECLARE @cmd nvarchar(max);\nDECLARE @n   int = 0;\n  \nDECLARE fkc_cur CURSOR LOCAL FAST_FORWARD FOR\n    SELECT N&#039;ALTER TABLE &#039; + QUOTENAME(s.name) + N&#039;.&#039; + QUOTENAME(t.name)\n         + N&#039; WITH NOCHECK CHECK CONSTRAINT &#039; + QUOTENAME(fk.name) + N&#039;;&#039;\n    FROM   sys.foreign_keys fk\n    JOIN   sys.tables  t ON fk.parent_object_id = t.object_id\n    JOIN   sys.schemas s ON t.schema_id = s.schema_id\n    WHERE  s.name IN (SELECT schema_name FROM #Schemas);\n  \nOPEN fkc_cur;\nFETCH NEXT FROM fkc_cur INTO @cmd;\nWHILE @@FETCH_STATUS = 0\nBEGIN\n    SET @n += 1;\n    PRINT @cmd;\n    IF @Execute = 1\n    BEGIN\n        BEGIN TRY EXEC sys.sp_executesql @cmd; END TRY\n        BEGIN CATCH PRINT N&#039;   -- FAILED : &#039; + ERROR_MESSAGE(); END CATCH\n    END\n    FETCH NEXT FROM fkc_cur INTO @cmd;\nEND\nCLOSE fkc_cur;\nDEALLOCATE fkc_cur;\n  \nPRINT N&#039;---&#039;;\nPRINT CONVERT(nvarchar(10), @n) + N&#039; FK &#039;\n    + CASE WHEN @Execute = 1 THEN N&#039;activated and validated.&#039; ELSE N&#039;to be activated (preview).&#039; END;\nGO\n\n<\/pre><\/div>\n\n\n<h3 id=\"h-the-foreign-keys-which-turned-out-to-be-free\" class=\"wp-block-heading\">The foreign keys, which turned out to be free<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">All 6,072 foreign keys were already back, active and marked not trusted, because the loading script re-enabled them with <code>NOCHECK<\/code>. Revalidating them with <code>WITH CHECK<\/code> would mean full joins against the largest tables in the database.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On a read-only archive whose source integrity <strong>was enforced by Oracle<\/strong> for fifteen years, that buys one thing: the optimizer regains the right to eliminate redundant joins. Worth having on a schema of deeply nested views, not worth days of scanning here. They stayed not trusted.<\/p>\n\n\n\n<h2 id=\"h-day-7-testing-600-views\" class=\"wp-block-heading\">Day 7. Testing 600 views<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The principle is simple: run every view, record what happened, and never lose the record. Each one gets a <code>SELECT TOP (100) *<\/code> inside a <code>TRY\/CATCH<\/code>, so a view that fails is logged rather than fatal, and the outcome lands in a table with its duration in milliseconds. The cursor only picks up views that are not already recorded, which means an interrupted run resumes where it stopped instead of starting over, and the duration column is what you sort on once it is done:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSET NOCOUNT ON;\n \nIF OBJECT_ID(&#039;dbo.ViewTestResults&#039;) IS NULL\n    CREATE TABLE dbo.ViewTestResults (\n        schema_name  sysname,\n        view_name    sysname,\n        statut       varchar(20),\n        duree_ms     int,\n        message      nvarchar(2048),\n        tested_at    datetime2 DEFAULT SYSDATETIME(),\n        CONSTRAINT PK_ViewTestResults PRIMARY KEY (schema_name, view_name)\n    );\n \nDECLARE @sch sysname, @vue sysname, @sql nvarchar(max), @t0 datetime2, @ms int, @i int = 0, @n int;\n \nSELECT @n = COUNT(*)\nFROM   sys.views v JOIN sys.schemas s ON v.schema_id = s.schema_id\nWHERE  s.name = &#039;SCDAT&#039;\n  AND  NOT EXISTS (SELECT 1 FROM dbo.ViewTestResults r\n                   WHERE r.schema_name = s.name AND r.view_name = v.name);\n \nRAISERROR(&#039;=== %d view remaining ===&#039;, 0, 1, @n) WITH NOWAIT;\n \nDECLARE c CURSOR FAST_FORWARD FOR\n  SELECT s.name, v.name\n  FROM   sys.views v JOIN sys.schemas s ON v.schema_id = s.schema_id\n  WHERE  s.name = &#039;SCDAT&#039;\n    AND  NOT EXISTS (SELECT 1 FROM dbo.ViewTestResults r\n                     WHERE r.schema_name = s.name AND r.view_name = v.name)\n  ORDER  BY v.name;\n \nOPEN c;\nFETCH NEXT FROM c INTO @sch, @vue;\nWHILE @@FETCH_STATUS = 0\nBEGIN\n    SET @i += 1;\n    RAISERROR(&#039;&#x5B;%d\/%d] %s.%s&#039;, 0, 1, @i, @n, @sch, @vue) WITH NOWAIT;\n \n    SET @t0 = SYSDATETIME();\n    BEGIN TRY\n        SET @sql = N&#039;SELECT TOP (100) * FROM &#039; + QUOTENAME(@sch) + N&#039;.&#039; + QUOTENAME(@vue) + N&#039;;&#039;;\n        EXEC sp_executesql @sql;\n        SET @ms = DATEDIFF(MILLISECOND, @t0, SYSDATETIME());\n        INSERT dbo.ViewTestResults (schema_name, view_name, statut, duree_ms, message)\n        VALUES (@sch, @vue, &#039;OK&#039;, @ms, NULL);\n    END TRY\n    BEGIN CATCH\n        SET @ms = DATEDIFF(MILLISECOND, @t0, SYSDATETIME());\n        INSERT dbo.ViewTestResults (schema_name, view_name, statut, duree_ms, message)\n        VALUES (@sch, @vue, &#039;FAILED&#039;, @ms, ERROR_MESSAGE());\n        RAISERROR(&#039;    -&gt; FAILED: %s&#039;, 0, 1, @vue) WITH NOWAIT;\n    END CATCH\n \n    FETCH NEXT FROM c INTO @sch, @vue;\nEND\nCLOSE c; DEALLOCATE c;\n \nRAISERROR(&#039;=== Done ===&#039;, 0, 1) WITH NOWAIT;\n \n-- summary\nSELECT statut, COUNT(*) AS nb, SUM(duree_ms) AS duree_totale_ms\nFROM dbo.ViewTestResults GROUP BY statut;\n \n-- failed\nSELECT schema_name, view_name, duree_ms, message\nFROM dbo.ViewTestResults WHERE statut=&#039;FAILED&#039; ORDER BY view_name;\n \n-- all\nSELECT schema_name, view_name, statut, duree_ms\nFROM dbo.ViewTestResults ORDER BY duree_ms DESC;\n\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">The result looked excellent: <strong>604 views OK, zero errors<\/strong>, against real data. Six skipped because they are not implemented to be run without an explicit filter added. <code>SELECT TOP 100 *<\/code> without a filter <strong>is not a representative test<\/strong> but still confirms that definition and joins are working fine. So: test with the filter the users will actually write. A view that only misbehaves when queried in a way nobody queries it is not a broken view, but you cannot know that from a green tick.<\/p>\n\n\n\n<h2 id=\"h-day-8-closing-the-hatch\" class=\"wp-block-heading\">Day 8. Closing the hatch<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Database set to read-only, final backup taken and user&#8217;s specific queries tested with success, the story now comes to a happy end.<\/p>\n\n\n\n<h2 id=\"h-what-i-would-tell-the-next-person\" class=\"wp-block-heading\">What I would tell the next person<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">An archive migration is not a cheap application migration. It is a different decision set, and read-only is the input that drives most of it. 25,000 triggers are pointless, PAGE compression free, and untrusted foreign keys perfectly acceptable. It even tells you how to index: broadly and in advance, because nobody can tell you which row an auditor will want in three years. Before anything else, ask what the target will be used for. The answer rewrites the plan before the first byte moves.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Measure, and distrust anything that presents itself as progress. The SSMA counter refreshes per batch and sits still for minutes at a time. <code>percent_complete <\/code>stays at zero for an entire index build. A green tick on<code> SELECT TOP 100<\/code> confirms that the joins resolve and nothing beyond that. In every one of those cases the real number was sitting in a DMV.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On views, the conversion rate is the least useful number in the report. What made four thousand views a finite piece of work was not the translation but the two steps around it: agreeing the real scope with the client first, then classifying what remained by pattern in the Oracle source rather than one error message at a time. <strong>An AI assistant<\/strong> translates a family of twenty views very well. It validates nothing, and the test against the source stays yours.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Compression on an archive is the rare decision with nothing to weigh against it. 117 GB down to 30.7 GB on the first table, <strong>more than 2.5 TB<\/strong> reclaimed across the set, and not a single write to pay it back with. If your heap is mostly null numeric columns, it is bigger than you think, and it will compress better than you expect.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Translating 4,000 Oracle views, rebuilding indexes offline on Standard Edition, and compressing clustered index to save 1T.<\/p>\n","protected":false},"author":157,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":"","_members_access_role":[],"_members_access_error":""},"categories":[198,59,99],"tags":[2562,96,51],"type_dbi":[],"class_list":["post-47235","post","type-post","status-publish","format-standard","hentry","category-database-management","category-oracle","category-sql-server","tag-migration-2","tag-oracle","tag-sql-server"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v28.5 (Yoast SEO v28.6) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Archiving 5 TB from Oracle into SQL Server: views, indexes and compression (part 2) - dbi Blog<\/title>\n<meta name=\"description\" content=\"Translating 4,000 Oracle views, rebuilding indexes offline on Standard Edition, and compressing clustered index to save 1T.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Archiving 5 TB from Oracle into SQL Server: views, indexes and compression (part 2)\" \/>\n<meta property=\"og:description\" content=\"Translating 4,000 Oracle views, rebuilding indexes offline on Standard Edition, and compressing clustered index to save 1T.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/\" \/>\n<meta property=\"og:site_name\" content=\"dbi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-07T06:20:08+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-07T06:20:09+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-45.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1120\" \/>\n\t<meta property=\"og:image:height\" content=\"1460\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Louis Tochon\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Louis Tochon\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"9 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\\\/\"},\"author\":{\"name\":\"Louis Tochon\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/e4195b0cb120295b3407a502c23e75b6\"},\"headline\":\"Archiving 5 TB from Oracle into SQL Server: views, indexes and compression (part 2)\",\"datePublished\":\"2026-10-07T06:20:08+00:00\",\"dateModified\":\"2026-10-07T06:20:09+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\\\/\"},\"wordCount\":1680,\"commentCount\":0,\"image\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/2\\\/2026\\\/09\\\/image-45-786x1024.png\",\"keywords\":[\"migration\",\"Oracle\",\"SQL Server\"],\"articleSection\":[\"Database management\",\"Oracle\",\"SQL Server\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\\\/\",\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\\\/\",\"name\":\"Archiving 5 TB from Oracle into SQL Server: views, indexes and compression (part 2) - dbi Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/2\\\/2026\\\/09\\\/image-45-786x1024.png\",\"datePublished\":\"2026-10-07T06:20:08+00:00\",\"dateModified\":\"2026-10-07T06:20:09+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/e4195b0cb120295b3407a502c23e75b6\"},\"description\":\"Translating 4,000 Oracle views, rebuilding indexes offline on Standard Edition, and compressing clustered index to save 1T.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/2\\\/2026\\\/09\\\/image-45.png\",\"contentUrl\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/2\\\/2026\\\/09\\\/image-45.png\",\"width\":1120,\"height\":1460},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Archiving 5 TB from Oracle into SQL Server: views, indexes and compression (part 2)\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/\",\"name\":\"dbi Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/e4195b0cb120295b3407a502c23e75b6\",\"name\":\"Louis Tochon\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ce0ee48c64e763e6c4076e21c80729d15bc4493288aeb8695125c69082100e10?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ce0ee48c64e763e6c4076e21c80729d15bc4493288aeb8695125c69082100e10?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ce0ee48c64e763e6c4076e21c80729d15bc4493288aeb8695125c69082100e10?s=96&d=mm&r=g\",\"caption\":\"Louis Tochon\"},\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/author\\\/louistochon\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Archiving 5 TB from Oracle into SQL Server: views, indexes and compression (part 2) - dbi Blog","description":"Translating 4,000 Oracle views, rebuilding indexes offline on Standard Edition, and compressing clustered index to save 1T.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/","og_locale":"en_US","og_type":"article","og_title":"Archiving 5 TB from Oracle into SQL Server: views, indexes and compression (part 2)","og_description":"Translating 4,000 Oracle views, rebuilding indexes offline on Standard Edition, and compressing clustered index to save 1T.","og_url":"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/","og_site_name":"dbi Blog","article_published_time":"2026-10-07T06:20:08+00:00","article_modified_time":"2026-10-07T06:20:09+00:00","og_image":[{"width":1120,"height":1460,"url":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-45.png","type":"image\/png"}],"author":"Louis Tochon","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Louis Tochon","Est. reading time":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/#article","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/"},"author":{"name":"Louis Tochon","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/e4195b0cb120295b3407a502c23e75b6"},"headline":"Archiving 5 TB from Oracle into SQL Server: views, indexes and compression (part 2)","datePublished":"2026-10-07T06:20:08+00:00","dateModified":"2026-10-07T06:20:09+00:00","mainEntityOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/"},"wordCount":1680,"commentCount":0,"image":{"@id":"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/#primaryimage"},"thumbnailUrl":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-45-786x1024.png","keywords":["migration","Oracle","SQL Server"],"articleSection":["Database management","Oracle","SQL Server"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/","url":"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/","name":"Archiving 5 TB from Oracle into SQL Server: views, indexes and compression (part 2) - dbi Blog","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/#primaryimage"},"image":{"@id":"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/#primaryimage"},"thumbnailUrl":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-45-786x1024.png","datePublished":"2026-10-07T06:20:08+00:00","dateModified":"2026-10-07T06:20:09+00:00","author":{"@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/e4195b0cb120295b3407a502c23e75b6"},"description":"Translating 4,000 Oracle views, rebuilding indexes offline on Standard Edition, and compressing clustered index to save 1T.","breadcrumb":{"@id":"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/#primaryimage","url":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-45.png","contentUrl":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-45.png","width":1120,"height":1460},{"@type":"BreadcrumbList","@id":"https:\/\/www.dbi-services.com\/blog\/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/www.dbi-services.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Archiving 5 TB from Oracle into SQL Server: views, indexes and compression (part 2)"}]},{"@type":"WebSite","@id":"https:\/\/www.dbi-services.com\/blog\/#website","url":"https:\/\/www.dbi-services.com\/blog\/","name":"dbi Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/e4195b0cb120295b3407a502c23e75b6","name":"Louis Tochon","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/ce0ee48c64e763e6c4076e21c80729d15bc4493288aeb8695125c69082100e10?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/ce0ee48c64e763e6c4076e21c80729d15bc4493288aeb8695125c69082100e10?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/ce0ee48c64e763e6c4076e21c80729d15bc4493288aeb8695125c69082100e10?s=96&d=mm&r=g","caption":"Louis Tochon"},"url":"https:\/\/www.dbi-services.com\/blog\/author\/louistochon\/"}]}},"_links":{"self":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/47235","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/users\/157"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/comments?post=47235"}],"version-history":[{"count":34,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/47235\/revisions"}],"predecessor-version":[{"id":47804,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/47235\/revisions\/47804"}],"wp:attachment":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/media?parent=47235"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/categories?post=47235"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/tags?post=47235"},{"taxonomy":"type","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/type_dbi?post=47235"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}