Skip to main content

Oracle → SQL Server / Azure SQL Engine Internals

Companion to the Oracle → Azure SQL conceptual mapping. Where that page maps business outcomes, this one maps the engine internals — bytes on the page, latch classes, lock managers, recovery, encryption — as a reference for DBAs running Oracle and Azure SQL side by side, or modernizing one to the other.

🎯 Exam Focus

Content is verified against Microsoft Learn (learn.microsoft.com/sql/...?view=sql-server-ver17) and the Oracle Database Concepts Guide (12c / 19c / 21c). Behaviors flagged with ⚠️ differ enough between engines that they regularly cause migration surprises. Items flagged 🆕 require SQL Server 2025 / Azure SQL Database current generation.


1. Latches, Spinlocks, Mutexes — Synchronization Primitives

Both engines layer three levels of intra-process synchronization on top of OS sync. Vocabularies overlap; implementations differ.

ConcernOracleSQL Server / Azure SQL
Cheapest, busy-wait onlyMutex (since 10gR2 for library-cache cursors) — atomic CAS, no sleep, no post/waitSpinlock — busy-wait on a kernel-mode lock-bit; backoff after threshold
Short-term structural protectionLatch — spin then sleep; held microsecondsLatch — short-term physical protection of in-memory structures (pages, allocation maps, log buffer); modes KP / SH / UP / EX / DT
Logical, transactional, long-termEnqueue (TX, TM, UL, ST, HW, SQ…) — full lock manager with queue & modesLock — full lock manager: KEY / RID / PAGE / EXTENT / OBJECT / METADATA / DATABASE; modes IS, S, U, IX, SIX, X, BU, RangeS-S, …

⚠️ Naming trap. What Oracle calls a latch and what SQL Server calls a latch are the same concept. What SQL Server calls a spinlock maps best to Oracle's mutex. What both call lock / enqueue is the heavyweight, transaction-scoped object.

SQL Server latch types

Latch classProtectsWait stat
BUF (buffer latch)Individual page in buffer poolPAGELATCH_* (in-memory) / PAGEIOLATCH_* (page being read from disk)
Non-buffer latches (~150 classes)Internal structures (allocation bitmaps, log buffer, tempdb metadata, version store)LATCH_*
Super-latchAuto-promotion of read-mostly buffer latches to a per-CPU sub-latchReduces PAGELATCH_SH contention

Oracle event ↔ SQL Server wait crosswalk

Oracle eventSQL Server equivalentCause
cache buffers chains latchPAGELATCH_SH / PAGELATCH_EX on a hot pageMany sessions hashing to the same buffer header
latch: shared poolLATCH_EX on ACCESS_METHODS_HOBT_* / spinlock SOS_CACHESTOREPlan cache / metadata contention
library cache: mutex XSpinlock on plan cache hash bucketHot reused plan
enq: TX – row lock contentionLCK_M_X on KEY / RID, or LCK_M_S_XACT_* (🆕 SQL 2025 with Optimized Locking)Writer waiting for writer
enq: TX – allocate ITL entry(no analog)Block ran out of ITL slots
enq: TM – contentionLCK_M_Sch-S / LCK_M_Sch-M (schema lock)DDL vs DML
log file syncWRITELOGCommit waiting for log flush
db file sequential readPAGEIOLATCH_SHSingle-block read from data file
db file scattered readPAGEIOLATCH_SH (multi-page)Read-ahead / extent read
gc cr block 2-way (RAC)HADR_SYNC_COMMIT (closest in AG)Cross-node block transfer

Diagnostics

-- SQL Server / Azure SQL
SELECT * FROM sys.dm_os_spinlock_stats ORDER BY spins DESC;
SELECT * FROM sys.dm_os_latch_stats ORDER BY waiting_requests_count DESC;
SELECT * FROM sys.dm_os_wait_stats
WHERE wait_type LIKE 'PAGELATCH%' OR wait_type LIKE 'LATCH%' OR wait_type LIKE 'LCK%'
ORDER BY wait_time_ms DESC;
-- Oracle
SELECT name, gets, misses, sleeps FROM v$latch ORDER BY misses DESC;
SELECT * FROM v$mutex_sleep_history;
SELECT event, total_waits, time_waited FROM v$system_event ORDER BY time_waited DESC;

2. Block / Page Format — Bytes on the Floor

The single highest-value internal to learn for cross-engine debugging.

Layout side by side

RegionOracle block (default 8 KB)SQL Server page (8 KB, fixed)
TopCache layer (~20 B): block address, block type, format, SCNPage header (96 B): m_pageId, m_type, m_objId, m_indexId, m_freeCnt, m_lsn, m_slotCnt, m_freeData, m_xactReserved, m_ghostRecCnt, m_tornBits
ThenTransaction layer = ITL slots (24 B each, INITRANS preallocated, growable to MAXTRANS or until PCTFREE blocks growth)(no equivalent — locks live in the lock manager, not on the page; until 🆕 2025 TID stamps)
ThenTable directory (clustered tables only)n/a
ThenRow directory (one entry per row, 2 B each, points down to row data)(no equivalent here)
BodyFree space, then row data growing downward from endRow data growing forward from byte 96
Bottom(free space meets row data)Slot array: 2 bytes per row, each = byte offset to row start, grows backward

Row format

FieldOracle rowSQL Server row
Row header3 B: flag byte (cluster key, deleted, head/chain), lock byte = ITL slot index, column count4 B status bits (TagA + TagB) + 2 B Length of fixed portion
BodyPer-column 1 B length (or 3 B if ≥ 250) + bytes; trailing-NULL columns omittedFixed-length cols; then 2 B Total column count + null bitmap; then 2 B Variable-column count + 2-B-per-column variable offset array, then variable column data

Row size & overflow

LimitOracleSQL Server
Max row in blockblock size − overhead (~7.9 KB for 8 KB block)8060 B in-row, hard limit
Long row strategyRow chaining when row > block; row migration when UPDATE no longer fitsROW_OVERFLOW_DATA allocation unit — variable cols (varchar, nvarchar, varbinary, sql_variant, CLR UDT) move out; 24-B in-row pointer to overflow page
LOB out-of-rowLOB locator (~20 B in-row) → LOB segmentLOB_DATA allocation unit — 16-B pointer in-row → LOB tree (varchar(max), nvarchar(max), varbinary(max), xml, json)

What changes with 🆕 SQL Server 2025 Optimized Locking

ChangeEffect
Each row gets a TID stamp (transaction ID of last modifier), persisted with the row when ADR is onPlays the role of Oracle's lock byte + ITL entry — but with no fixed slot count and no per-block contention
Row & page locks are still acquired during the modify, then released immediately (before commit)Only the single XACT/TID lock survives until commit
With RCSI: predicates evaluated on the versioned image before locking (Lock-After-Qualification)Closes Oracle's "consistent-read for predicate eval" gap
Skip Index Locks (SIL) for INSERT on heaps and UPDATE on clustered/nonclustered indexes & heapsCloser to Hekaton's lock-free behavior than to traditional row-locking

⚠️ Oracle's per-block ITL exhaustion (enq: TX – allocate ITL entry) has no SQL Server analog — neither classic nor 2025. SQL Server lock memory is global; TID stamping is per-row, not per-block.

📖 Deep-dive: SQL Server 2025 Optimized Locking — prerequisites, T-SQL to verify, limitations, and the full Oracle DBA translation table.

Inspecting a real page / block

-- SQL Server / Azure SQL
SELECT * FROM sys.dm_db_page_info(DB_ID(), 1, <page_id>, 'DETAILED');
SELECT * FROM sys.dm_db_database_page_allocations(DB_ID(), OBJECT_ID('dbo.Orders'), NULL, NULL, 'DETAILED');
DBCC PAGE('ContosoDB', 1, 100, 3) WITH TABLERESULTS; -- requires TF 3604
-- Oracle
SELECT dbms_rowid.rowid_block_number(rowid), dbms_rowid.rowid_relative_fno(rowid)
FROM orders WHERE rownum = 1;
ALTER SYSTEM DUMP DATAFILE 4 BLOCK 100;
SELECT * FROM v$transaction;

3. Columnstore vs Oracle In-Memory Column Store

Both stores are columnar with compression and zone elimination — but SQL Server columnstore IS the on-disk format, while Oracle In-Memory is a memory-only mirror of the row store.

ConcernOracle In-Memory Column Store (12c+)SQL Server Columnstore (2014+)
Where columnar data livesIn-Memory Area of the SGA (RAM only)On disk + memory; clustered CS replaces the heap, nonclustered CS sits beside a rowstore
Authoritative copyRow store on diskColumnstore on disk is the authoritative copy (clustered CS)
Unit of compressionIMCU = In-Memory Compression Unit, ~ 0.5–1 M rowsRowgroup = up to 2²⁰ (≈ 1 048 576) rows; one column segment per column per rowgroup
EncodingsDictionary, RLE, bit-packing, common-prefixDictionary (global + local) + RLE + bit-packing + value encoding
Zone eliminationStorage Index (auto, in-memory min/max + bitmap per IMCU)Min/max per segment in sys.column_store_segments
Concurrent DMLTX modifies row store + an IM journal; IMCU repopulated periodicallyInserts < 102 400 rows go to the delta store (a regular B-tree); tuple mover compresses them later
UpdatesUPDATE → row store, IM journal marks rows staleUPDATE = mark old row deleted in rowgroup (deleted bitmap) + insert new in delta store
Bulk insertDirect-path insert, populates IM in backgroundBulk insert ≥ 102 400 rows compresses directly to a closed rowgroup
LicenseDatabase In-Memory option (extra cost, EE)Free in all editions since SQL Server 2016 SP1
-- SQL Server / Azure SQL
SELECT * FROM sys.column_store_segments; -- min/max per segment
SELECT * FROM sys.column_store_row_groups; -- state, deleted_rows, total_rows
SELECT * FROM sys.column_store_dictionaries;
-- Oracle
SELECT * FROM v$im_segments;
SELECT * FROM v$inmemory_area;
SELECT segment_name, populate_status, bytes, bytes_not_populated FROM v$im_user_segments;

⚠️ Don't treat them as the same thing. Oracle In-Memory is an acceleration layer — drop it and the database still works. SQL Server columnstore is the table (clustered) — you can't "drop columnstore and keep the data" without rebuilding the table.


4. Hekaton (Memory-Optimized Tables) vs Oracle Database In-Memory

Both are "in memory" but solve different problems: Hekaton is OLTP (single-row, lock-free writes); Oracle In-Memory is OLAP (scan-heavy queries on transactional data).

ConcernOracle In-Memory (12c+)SQL Server / Azure SQL Hekaton (2014+)
Workload targetOLAP scan accelerationOLTP write throughput, hot tables
Format in memoryColumnar (in IM Area)Row (memory-optimized data structure, multi-version)
On-disk persistenceRow store remains the source of truthCheckpoint files (data + delta) + tail of transaction log
ConcurrencySame as row store (locks/MVCC via UNDO)Optimistic MVCC, no locks, no latches, no spinlocks; rows have multiple versions chained in the same data structure
Conflict detectionUNDO + SCNValidation phase at commit; on conflict → ROLLBACK with error 41302 / 41305 / 41325
IndexesB-tree (heap), bitmap, IMCU min/maxHash (point lookup, fixed bucket count) + range / Bw-tree (ordered scan)
Stored procsPL/SQL (interpreted or native compiled)Natively compiled procs → C → DLL loaded into sqlservr.exe; WITH NATIVE_COMPILATION, SCHEMABINDING
DurabilityAlways durableDURABILITY = SCHEMA_AND_DATA (default) or SCHEMA_ONLY (data lost on restart, useful for staging)
LoggingSame redo / UNDOLess log volume — single log record per transaction (vs per-row)
tempdb impactMinimalZero — version store is in the same data structure, not in tempdb

In Hekaton each row carries BeginTs and EndTs timestamps. A logical row can have many physical versions chained in the same hash/range index buckets. Garbage collection is cooperative (worker threads sweep dead versions whose EndTs < oldest_active_tx_ts).

⚠️ Always Encrypted with secure enclaves is not supported on memory-optimized columns; AE is also disallowed inside a natively compiled module that references such a table.


5. Query Store ↔ AWR / ASH / SPM

ConcernOracle (AWR + ASH + SPM)SQL Server / Azure SQL Query Store (2016+)
CapturesSystem-wide stats snapshots + sampled session activityPer-database queries, plans, runtime stats, wait stats, query hints
SamplingMMNL samples v$session every 1 s into v$active_session_history (in-memory ring)Continuous capture (mode: ALL, AUTO, NONE, CUSTOM)
PersistenceMMON snapshots every 60 min (default) into AWR; ~10 % of ASH samples flushed to dba_hist_active_sess_historyAggregated runtime + wait stats stored in the user DB (asynchronously flushed)
RetentionAWR: 8 days defaultConfigurable: STALE_QUERY_THRESHOLD_DAYS + MAX_STORAGE_SIZE_MB, sliding window
Plan stabilitySPM Baselines — engine prefers accepted plansForced plans (sp_query_store_force_plan) — engine forces the chosen plan
Auto-tuningSQL Tuning Advisor + auto SPM evolution (12c)Automatic Tuning (AUTOMATIC_TUNING.FORCE_LAST_GOOD_PLAN = ON) — auto-forces last good plan on regression
Wait analysisdba_hist_system_event, v$active_session_history.eventsys.query_store_wait_statsper-query wait categories
LicenseDiagnostics + Tuning Pack (extra cost, EE)Free in all editions; on by default in new DBs since 2022 and in Azure SQL DB
SELECT * FROM sys.query_store_query;
SELECT * FROM sys.query_store_plan;
SELECT * FROM sys.query_store_runtime_stats;
SELECT * FROM sys.query_store_wait_stats; -- per-plan wait category breakdown
SELECT * FROM sys.query_store_plan_feedback; -- LAQ / CE / DOP feedback (2022+)
EXEC sp_query_store_force_plan @query_id = ..., @plan_id = ...;

⚠️ Licensing trap during migration: AWR / ASH / SPM require Diagnostics + Tuning Packs on Oracle EE. Query Store is free even on Express and is on by default in Azure SQL Database.


6. Resource Governor ↔ Resource Manager

ConcernOracle Database Resource ManagerSQL Server Resource Governor (2008+, EE only on box product)
HierarchyResource PlanPlan DirectivesConsumer GroupsWorkload Group (per-request limits) → Resource Pool (CPU / memory / IO at pool level)
Classifierdbms_resource_manager.set_consumer_group_mapping (by user, service, module, program, OS user, …)Classifier UDF in master; runs at session login, returns workload group name
CPU controlmgmt_p1..p8 percentages, max_utilization_limit, parallel_target_percentageMIN_CPU_PERCENT, MAX_CPU_PERCENT, CAP_CPU_PERCENT (hard cap, 2012+)
Memoryparallel_server_limit, undo_poolMIN_MEMORY_PERCENT, MAX_MEMORY_PERCENT, REQUEST_MAX_MEMORY_GRANT_PERCENT
Parallelparallel_degree_limit_p1..8, parallel_target_percentageMAX_DOP (per workload group)
I/OExadata IORM, mgmt_p1..p8 for IOMIN_IOPS_PER_VOLUME / MAX_IOPS_PER_VOLUME (2014+)
Runawayswitch_io_megabytes, switch_estimated_execution_time, switch_io_logicalREQUEST_MAX_CPU_TIME_SEC, REQUEST_MEMORY_GRANT_TIMEOUT_SEC
Multi-tenantCDB-aware (PDB-level plans)Not container-aware on box product — Azure SQL elastic pools provide the equivalent for Azure SQL Database
CREATE RESOURCE POOL HighPriPool
WITH (MIN_CPU_PERCENT=20, MAX_CPU_PERCENT=80, CAP_CPU_PERCENT=80, MAX_DOP=4);
CREATE WORKLOAD GROUP HighPriGroup
USING HighPriPool WITH (IMPORTANCE = HIGH, REQUEST_MAX_MEMORY_GRANT_PERCENT=50);

CREATE FUNCTION dbo.ClassifyMe() RETURNS sysname WITH SCHEMABINDING AS
BEGIN
RETURN CASE WHEN SUSER_SNAME() LIKE 'app_%' THEN N'HighPriGroup' ELSE N'default' END;
END;
GO
ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = dbo.ClassifyMe);
ALTER RESOURCE GOVERNOR RECONFIGURE;

⚠️ Resource Governor is not available in Azure SQL Database. Use service tier sizing + elastic pools + read-scale-out replicas instead.


7. Service Broker ↔ Oracle Advanced Queuing (AQ)

Both ship transactional, queued messaging inside the database engine — there is no MQ-broker process; the queue is just a table.

ConcernOracle AQ (since 8i)SQL Server Service Broker (since 2005)
StorageQueue table (dbms_aqadm.create_queue_table); queue is a logical view over itHidden queue table created by CREATE QUEUE; accessible via RECEIVE only
Message typesObject type / RAW / XMLType / JMS payloadsCREATE MESSAGE TYPE (XML, well-formed XML, binary, empty) bound to schemas
Conversation patternSingle-consumer or multi-consumer queue; dbms_aqadm.add_subscriberDialog conversation (full duplex, ordered, exactly-once, exactly-in-order) bound by contracts
Send / receivedbms_aq.enqueue, dbms_aq.dequeue (browse / locked / remove modes)BEGIN DIALOG CONVERSATION, SEND ON CONVERSATION, RECEIVE TOP(N) FROM Queue
Cross-DB / cross-instancePropagation scheduler + database linksRoutesCREATE ROUTE … WITH ADDRESS = 'TCP://host:4022'; dedicated TCP endpoint
ActivationNotification + callback procedureInternal activation — queue can auto-start a stored proc when messages arrive (MAX_QUEUE_READERS)
Poison messageRetry count + exception queueAfter 5 rollbacks → queue is automatically DISABLED
StatusProduction, used by Streams (legacy), OGGProduction, used internally by Database Mail, Query Notifications, Event Notifications

⚠️ Service Broker has no native JMS interop and is not available in Azure SQL Database (it remains in SQL MI and SQL Server). For cross-platform messaging from Azure SQL DB, prefer Azure Service Bus.


8. Partitioning — Why You Often Need to Bridge

StrategyOracleSQL Server / Azure SQL
RANGEYesYes (the only native option) — CREATE PARTITION FUNCTION pf (int) AS RANGE RIGHT FOR VALUES (...)
LISTYes❌ — workaround: filtered indexes + partitioned views, or computed-column → range
HASHYes❌ — workaround: persisted computed column HASHBYTES('SHA1', key) % N, then RANGE on it
Composite (RANGE-HASH, RANGE-LIST, INTERVAL-HASH)Yes❌ — only single-column RANGE; multi-key requires a composite computed column
Reference partitioning (child inherits parent's partition key)Yes
Interval partitioning (auto-create partitions on insert)Yes❌ — workaround: SQL Agent / Elastic Job runs ALTER PARTITION FUNCTION … SPLIT RANGE (...) ahead of time
Partition switching (metadata-only data move)Yes (exchange partition)Yes — ALTER TABLE … SWITCH PARTITION
Partition-wise joinYes (engine-aware)Yes for aligned partitioned tables
Truncate partitionALTER TABLE … TRUNCATE PARTITIONTRUNCATE TABLE … WITH (PARTITIONS (n,m,o)) (2016+)
Per-partition statisticsYesIncremental statistics (2014+)
Max partitions1 048 575 (12.2+)15 000 (2008 SP2+) / 1 000 000 (2017+ via trace flag)

Sliding-window pattern (Azure SQL / SQL Server)

-- Pre-create the next partition before data arrives
ALTER PARTITION SCHEME ps_monthly NEXT USED [PRIMARY];
ALTER PARTITION FUNCTION pf_monthly() SPLIT RANGE ('2026-06-01');

-- Switch out the oldest partition (instant, metadata only)
ALTER TABLE Sales SWITCH PARTITION 1 TO Sales_Archive PARTITION 1;
ALTER PARTITION FUNCTION pf_monthly() MERGE RANGE ('2025-06-01');

⚠️ Watch for partition alignment: in SQL Server, the clustered + nonclustered indexes must use the same partition scheme to enable partition-wise operations. Oracle does this automatically with local indexes.


9. Backup & Restore — RMAN ↔ T-SQL BACKUP / Azure-Managed

ConcernOracle RMANSQL Server / SQL on Azure VMAzure SQL Database / MI
CatalogRecovery catalog DB (preferred) or controlfilemsdb.dbo.backupset / backupfile / backupmediafamily / backupmediasetPlatform-managed; visible via PITR and LTR
GranularityDatabase, tablespace, datafile, archivelog, controlfile, SPFILEDatabase, filegroup, file, partial, logDatabase only
FullBACKUP DATABASEBACKUP DATABASE … TO DISK = '…'Automatic weekly
IncrementalBACKUP INCREMENTAL LEVEL 0/1, differential vs cumulativeDifferential (single level only)Automatic 12–24 h
Block-change trackingALTER DATABASE ENABLE BLOCK CHANGE TRACKING (file bct.dbf)Built in — DCM page tracks changed extents (always on, no config)Same
Log backup / archivelogBACKUP ARCHIVELOG ALL DELETE INPUTBACKUP LOG db TO DISK='…' (FULL / BULK_LOGGED only)Automatic 5–10 min
CompressionBACKUP … AS COMPRESSED BACKUPSETWITH COMPRESSION (since 2008 R2 Std)Automatic
EncryptionCONFIGURE ENCRYPTION FOR DATABASE ON (TDE wallet or password)WITH ENCRYPTION (ALGORITHM=…, SERVER CERTIFICATE=…) (2014+)Automatic; CMK via Key Vault
VerifyRESTORE VALIDATE, BACKUP … VALIDATE, VALIDATE DATABASEBACKUP … WITH CHECKSUM, RESTORE VERIFYONLY, DBCC CHECKDBPlatform-validated; run DBCC CHECKDB on demand
Point-in-timeRESTORE … UNTIL TIME 'sysdate-1/24'RESTORE LOG … WITH STOPAT = 'yyyy-mm-dd hh:mm:ss'PITR to any second within retention (7–35 d), via portal or Restore-AzSqlDatabase
Tail-of-logImplicit in RECOVER flowTail-log backup required: BACKUP LOG db TO DISK='…' WITH NO_TRUNCATE, CONTINUE_AFTER_ERRORn/a — managed
Direct to cloudRMAN to OSB / S3 / OCI Object StorageBACKUP TO URL (Azure Blob Storage), since 2012 SP1 CU2Backups are already in geo-redundant storage
Long-term archiveRMAN to tape / cloudCustom + cold-tier blobLTR up to 10 years

Recovery model — the SQL Server gotcha

Recovery modelLog behaviorPoint-in-time?
SIMPLELog auto-truncated at each checkpoint❌ — equivalent to Oracle NOARCHIVELOG
BULK_LOGGEDBulk ops minimally logged⚠️ Yes between log backups, not to a moment inside a bulk op
FULLEvery change logged, log truncated only after log backup✅ — equivalent to Oracle ARCHIVELOG

⚠️ Switching from FULLSIMPLE breaks the log chain. After switching back, take a fresh full backup before relying on log restores. Azure SQL Database is always in FULL and you cannot change it.


10. Always Encrypted vs Oracle TDE Column Encryption

This is where SQL Server has a stronger out-of-the-box threat model than Oracle.

Threat model

ThreatOracle TDE columnSQL Server TDE (full DB)SQL Server / Azure SQL Always Encrypted
Disk theft✅ (ciphertext only on disk)
Backup theft
Memory dump on server❌ Plaintext in SGA❌ Plaintext in buffer poolPlaintext never on server
Malicious DBA❌ Can SELECT plaintext❌ Can SELECT plaintext✅ DBA sees ciphertext only
Compromised app server❌ (driver decrypts)

Key hierarchy

ComponentOracle TDESQL Server TDESQL Server / Azure SQL Always Encrypted
Master keyWallet (file or HSM)Service Master KeyDatabase Master KeyServer Certificate (or Azure Key Vault for CMK)Column Master Key (CMK) — cert in Windows store / Azure Key Vault / HSM, lives on the client
Data keyColumn encryption key in DB metadata, encrypted by masterDatabase Encryption Key (DEK) in DB boot record, encrypted by Server Cert / Key Vault keyColumn Encryption Key (CEK) in DB metadata, encrypted by CMK; server cannot decrypt
Decryption locationServer side, on readServer side, on page read into buffer poolClient driver (.NET, JDBC, ODBC, Python pyodbc, OLE DB)

Encryption modes (Always Encrypted)

ModeBehaviorTrade-off
DeterministicSame plaintext → same ciphertextEquality predicates, joins, GROUP BY work; vulnerable to inference attacks on low-cardinality columns
RandomizedSame plaintext → different ciphertextMaximum security; no server-side ops beyond IS NULL / equality on the same encrypted value within a single query (driver substitutes)

🆕 Always Encrypted with Secure Enclaves (SQL Server 2019+, Azure SQL DB)

CapabilityWithout enclaveWith enclave (VBS / Intel SGX)
EqualityDeterministic onlyDeterministic + Randomized (in enclave)
Range / LIKE✅ — predicate pushed into enclave, evaluated on plaintext inside attested memory
In-place encryption❌ — required client-side encrypt + reload✅ — enclave does it server-side
Indexes on randomized cols✅ (via enclave)

Wire format

EngineProtocol
Oracle TDETDE columns sent decrypted over the network (use Native Network Encryption / TLS for transit)
SQL Server TDESame — TDE = at-rest only
Always EncryptedCiphertext stays ciphertext on the wire; driver decrypts in app process memory

⚠️ Always Encrypted is not TDE. TDE protects files at rest; Always Encrypted protects data from the DBA and the server. They compose — use both.


Where SQL Server 2025 / Azure SQL Closes Historical Oracle Gaps

Historical Oracle advantageSQL Server 2025 / Azure SQL closing it with
Non-blocking readers, non-blocking writers (UNDO + ITL)🆕 Optimized Locking (TID stamping + LAQ + SIL); requires ADR + RCSI
Persistent versioned readsPersistent Version Store (PVS) inside ADR — versions in user DB, not tempdb
O(1) long-tx rollbackAccelerated Database Recovery (ADR) (sLog + PVS + logical revert)
Mature plan baseline (SPM)Query Store + forced plans + Automatic Tuning
Partitioning richness (LIST / HASH / composite)Still a gap — only RANGE native (workarounds in §8)
RAC scale-outStill a gap — Always On AG / Business Critical is HA, not active-active scale-out
In-Memory column store optionColumnstore is free in all editions since SQL 2016 SP1 and in all Azure SQL service tiers

Source documents

  • Microsoft Learn — Pages and Extents Architecture Guide
  • Microsoft Learn — SQL Server Transaction Log Architecture and Management Guide
  • Microsoft Learn — Optimized Locking
  • Microsoft Learn — In-Memory OLTP — Introduction to Memory-Optimized Tables
  • Microsoft Learn — Accelerated Database Recovery concepts
  • Microsoft Learn — Persistent Version Store
  • Microsoft Learn — Transaction Locking and Row Versioning Guide
  • Microsoft Learn — Always Encrypted (incl. with Secure Enclaves)
  • Microsoft Learn — Query Store, Resource Governor, Service Broker, Partitioned Tables and Indexes
  • Microsoft Learn — Backup and Restore (SQL Server) and Automated Backups (Azure SQL Database)
  • Oracle Database Concepts Guide (12c / 19c / 21c) — "Data Concurrency and Consistency", "Data Blocks, Extents, and Segments", "Database In-Memory", "Backup and Recovery Concepts", "Advanced Queuing"
🎯 Exam Focus

The full local working copy of this content (with extra T-SQL snippets and the V$↔DMV cheat sheet) is kept at Work/Learning/Oracle-to-SQLServer-Engine-Map.md for personal reference.