The Legacy Obituaries

Case q17 · fact

Is ROWID usable after migration to Azure SQL?

Expected: SSMA maps ROWID to uniqueidentifier but semantics differ (no physical row address); code using ROWID for dedupe/pagination must be refactored to keys.

No tools

The model alone, answering from memory.
larger model · 1 turn · 17 s · 16.4k in / 1.6k out

✓ verdict ✗ grounded ✓ cited

Grader: Correct verdict; physloc, fragmentation and conversion-error claims look unsupported

Answer

Can you still use ROWID after moving to Azure SQL?

No. Azure SQL Database and Azure SQL Managed Instance have no ROWID pseudo-column. You have to swap it for a real key or a surrogate column you add yourself.

What SSMA for Oracle does with it

  • ROWID or UROWID as a column data type: SSMA maps these to uniqueidentifier by default. It keeps the column, but the column no longer holds a physical row address. You can change this in SSMA's type mapping settings.
  • The ROWID pseudo-column (WHERE ROWID = …, SELECT ROWID …): SSMA's "Generate ROWID column" project setting can add a ROWID column of type uniqueidentifier to converted tables. It fills the column with a NEWID()-style default and indexes it, which lets converted code that references ROWID keep running. You can turn this on for all tables or only for tables that have triggers. If the setting is off, statements that use ROWID fail to convert and show up as errors in the assessment report.

Things to watch for

  • Semantics change: Oracle ROWID is a physical address, so it's fast and can change after table moves or partition operations. The SSMA column is only a logical GUID. It behaves like a key, and fetching rows by it is no faster than using any other indexed column.
  • Cost of the extra column: It adds 16 bytes per row plus an extra index, and random GUIDs cause index fragmentation. Before you migrate data, decide whether to keep this column or remove it.
  • Don't use %%physloc%%: It's an undocumented, unsupported SQL Server virtual column. Its values change when the table is rebuilt or reorganized, so application code shouldn't depend on it.
  • Stored ROWIDs are dead data: Any ROWID values saved in tables, logs or external systems point to nothing after migration.

How to fix code that uses ROWID

  • Row lookup or update by ROWID: Use the primary key, or add an IDENTITY/SEQUENCE column if the table has no key.
  • Dedup patterns like DELETE … WHERE ROWID NOT IN (SELECT MIN(ROWID) …): Rewrite with a CTE and ROW_NUMBER() OVER (PARTITION BY …), then DELETE FROM cte WHERE rn > 1.
  • WHERE CURRENT OF cursor logic: T-SQL supports this for updatable cursors, but it's better to rewrite it as set-based statements that use the key.
  • Pagination or ordering by ROWID: Use ORDER BY on a real key with OFFSET/FETCH. Rows have no physical order to rely on.

Recommendation: During assessment, list every ROWID reference: grep the PL/SQL, application SQL and ORM mappings. Use SSMA's generated ROWID column only as a temporary bridge, and aim to have every table use a proper key in the end.

Keyword search

BM25 over the same 446 documents: top 8 passages pasted in, one pass, references not followed.
larger model · 1 turn · 9 s · 19.1k in / 0.8k out

✓ verdict ✓ grounded ✗ cited

Grader: uniqueidentifier mapping, semantics differ, dedupe and pagination rewritten to keys

8 keyword-search passages
  1. mapping_rowid--azure-pg-flex score 10.161
  2. mapping_rowid--azure-sql-db score 9.678
  3. target_ssma score 9.15
  4. dispute_rowid-uniqueidentifier-vs-oid score 8.948
  5. feature_rowid score 7.734
  6. mapping_boolean-23ai--azure-pg-flex score 7.626
  7. mapping_tls-1-0-1-1--azure-sql-db score 7.467
  8. target_azure-dms score 7.383
Answer

No. Azure SQL has no ROWID. SSMA only gives you a GUID surrogate in its place, so any code that relies on ROWID behaving as a physical row address has to be rewritten.

  • Why it doesn't carry over: In Oracle, ROWID is a row's physical address (data object number, file, block and slot). SQL Server and Azure SQL don't expose any stable physical row address. [2][5]
  • What SSMA does: By default it converts the ROWID type to uniqueidentifier. It can also add a ROWID column that defaults to NEWID(), with a unique index. By default it only does this for tables that have triggers. That gives each row a surrogate identity, but not physical addressing, fast access by address, or the duplicate-removal tricks Oracle code used ROWID for. [2][3][4]
  • Code that breaks and needs a key-based rewrite:
    • removing duplicates with MIN(ROWID)
    • WHERE ROWID = lookups and re-fetching rows by ROWID in bulk loops
    • using ROWID for pagination or batching
    • matching rows inside triggers by ROWID
    • application frameworks that cache ROWIDs as row handles [4][5]
  • Recommended approach:
    1. Find every use of ROWID and UROWID in the code.
    2. Rewrite each one against the primary key. For duplicate removal, use ROW_NUMBER(). [2][4]
    3. Only add an explicit uniqueidentifier column with a unique index when a table truly has no key. Set it in SSMA's settings on purpose rather than relying on the default. [4]
    4. Drop the generated ROWID column once the application no longer references it. [2]
  • Relying on ROWID was already risky in Oracle: Oracle itself warns that a rowid can change, for example when a row moves. Storing rowids for later use gives unpredictable results even before migration. [5]

The passages don't include any documentation URLs, so I can only cite them by passage number.

Sanity Context · GROQ

The agent with the GROQ endpoint: schema, joins, Portable Text.
smaller model · 4 turns · 19 s · 32.0k in / 1.4k out

✓ verdict ✓ grounded ✓ cited

Grader: Correct mapping and caveats; primary-key rewrite; cites the docs

3 tool calls
  1. initial_context on groq · 4,126 chars back
  2. groq_query on groq · 720 chars back
    *[_type=="oracleFeature" && (name match "ROWID" || slug.current match "rowid")]{_id, name, slug, category, introducedIn, deprecatedIn, desupportedIn, oracleReplacement, reviewStatus}
  3. groq_query on groq · 4,825 chars back
    *[_type=="mapping" && oracleFeature._ref=="feature_rowid"]{_id, fidelity, effort, rationale, appliesToOracleVersions, "azureTarget": azureTarget->{name, service}, "sources": sources[]->{title,url}, "caveats": *[_type=="caveat" && references(^._id)]{severity, statement, appliesTo, firsthand, "evidence": evidence->{title,url}}}
Answer

Verdict: No — not as a physical row identifier. ROWID has only a "workaround" fidelity mapping to Azure SQL Database, via a surrogate GUID column that behaves differently from Oracle's ROWID.

Scope: Oracle ROWID/UROWID (introduced pre-11.2, no deprecation recorded) → Azure SQL Database, applies across all tracked versions (11.2–26ai). [ROWID / UROWID feature record]

Facts:

  • Fidelity is workaround, effort M. SQL Server has no equivalent to a physical row address, so Microsoft's SSMA for Oracle maps ROWID/UROWID to uniqueidentifier (a 16-byte GUID from NEWID()) — this is not a physical row address and cannot be used to locate or order rows by storage position. — SSMA for Oracle: Project Settings (Type Mapping)
  • SSMA's "Generate ROWID column" setting (severity: minor) adds a ROWID uniqueidentifier surrogate column with a unique index — by default only to tables with triggers (Default/Optimistic modes), or to all tables in Full mode; the SSMA Tester requires it on all tables. — SSMA for Oracle: Project Settings (Conversion)
  • Rationale recorded: this surrogate gives applications a row identity but not the physical addressing, fast-access, or duplicate-elimination behavior Oracle code relied on ROWID for. Recommendation: rewrite queries that used ROWID to locate the current row to use the primary key instead.

Related note (different target, in case it's relevant to you): If migrating instead to Azure Database for PostgreSQL Flexible Server, the mapping is also "workaround" (effort M) — Postgres's ctid is physically similar to ROWID but changes on every UPDATE and after VACUUM FULL, so PostgreSQL's own docs say it must not be used as a row identifier. Ora2Pg by default maps ROWID/UROWID to oid, which its own documentation warns causes an import error since there's no true equivalent type; it recommends bigserial, text, or uuid instead. — PostgreSQL documentation: System columns (ctid), Ora2Pg documentation (configuration directives)

Gaps/disputes: No dispute record exists for ROWID in the dataset, and no caveat is scoped to a specific Oracle edition or Azure SQL tier (e.g., Managed Instance) — the caveats above apply generically across all listed Oracle versions.

GROQ + Knowledge Base

The agent with both endpoints.
smaller model · 4 turns · 24 s · 32.1k in / 1.6k out

✓ verdict ✓ grounded ✓ cited

Grader: Correct uniqueidentifier mapping, semantics differ, primary-key rewrite; cited

3 tool calls
  1. initial_context on groq · 4,126 chars back
  2. initial_context on kb · 7,679 chars back
  3. groq_query on groq · 5,413 chars back
    *[_type=="oracleFeature" && (name match "ROWID*" || slug.current match "*rowid*")]{_id, name, slug, category, introducedIn, deprecatedIn, desupportedIn, oracleReplacement, "mappings": *[_type=="mapping" && oracleFeature._ref==^._id]{_id, fidelity, effort, rationale, appliesToOracleVersions, azureTarget->{name, service}, sources[]->{title,url}, "caveats": *[_type=="caveat" && references(^._id)]{severity, statement, appliesTo, firsthand, evidence->{title,url}}}}
Answer

Verdict: No — Oracle's ROWID/UROWID has no equivalent in Azure SQL Database; it's only a workaround mapping, not usable as a physical row locator.

Scope: Oracle ROWID/UROWID feature (introduced pre-11.2, applies to all versions 11.2–26ai) → Azure SQL Database (mapping fidelity: workaround, effort: M).

Facts:

  • SQL Server has no physical row-address type. Microsoft's own migration tool, SSMA for Oracle, maps ROWID/UROWID to uniqueidentifier — a 16-byte GUID generated by newid(), which is not a physical row address and cannot be used to locate or order rows by storage position. — SSMA for Oracle: Project Settings (Type Mapping)
  • SSMA's "Generate ROWID column" setting (Default/Optimistic modes) adds a ROWID uniqueidentifier surrogate column only to tables with triggers (or all tables in Full mode), with a unique index — this gives a surrogate identity, not physical addressing. The SSMA Tester requires this column on all tables. — SSMA for Oracle: Project Settings (Conversion)
  • Dataset rationale: "Queries that used ROWID to locate the current row should use the primary key instead." This mapping/rationale is sourced from the two documents above.

Note on Azure SQL Managed Instance: The dataset contains no separate mapping entry for ROWID → Azure SQL Managed Instance specifically — only Azure SQL Database and Azure Database for PostgreSQL Flexible Server are mapped for this feature. The dataset has no evidence on this for Managed Instance specifically.

Related target (not "Azure SQL" but adjacent): For Azure Database for PostgreSQL Flexible Server, the picture is similar — PostgreSQL's ctid is physically analogous but changes on every UPDATE/VACUUM FULL and its own docs say it "must not be used as a row identifier." Ora2Pg defaults to mapping ROWID/UROWID to oid, which its docs warn causes an import error, recommending bigserial, text, or uuid instead. — PostgreSQL documentation: System columns (ctid), Ora2Pg documentation

No disputes are recorded in the dataset for this feature.

← q16All casesq18 →