Monday, December 8, 2025

Value LOBs in Oracle 26ai: The End of Chunked LOB Overhead

 
LOBs in Oracle have historically been stored as chunks, with an associated LOB index segment to allow navigation through the LOB structure. Because of this architecture, when a query returns a LOB column, the client historically receives a pointer to the LOB, and only when the client actually requests the LOB value does the database send the data piece-by-piece across the network.
 
This design still makes sense for very large LOBs, but today many modern JSON or text payloads fall into an awkward middle area: 
  • too large for VARCHAR datatypes
  • too small to justify chunking and LOB indexing overhead 
Let’s walk through a simple demonstration.

 
demo@ADB26AI> create table base_table
  2  as
  3  with base_data as (
  4     select rownum id
  5             , 'Text' || rownum as data
  6     from all_objects
  7     where rownum <= 80 )
  8  , one_clob as (
  9             select json_arrayagg(json{*} returning clob) as jdoc
 10             from base_data )
 11  select rownum as pk
 12      , one_clob.jdoc
 13  from one_clob , all_objects
 14  where rownum <= 100;
 
Table created.
 
And we will create a table with clob data type in a standard way and populate it with some source data
 
demo@ADB26AI> create table t_reference_clob( x number, y clob ) ;
 
Table created.
 
demo@ADB26AI>
demo@ADB26AI> insert into t_reference_clob select * from base_table;
 
100 rows created.
 
demo@ADB26AI> commit;
 
Commit complete.
 
Now query the table from a client session far from the database (in my case, Autonomous Database in a remote region):
 
demo@ADB26AI> select * from t_reference_clob;
 
100 rows selected.
 
Elapsed: 00:11:04.73
 
Statistics
----------------------------------------------------------
       1070  recursive calls
         17  db block gets
       5713  consistent gets
       5215  physical reads
       2504  redo size
     943880  bytes sent via SQL*Net to client
     631599  bytes received via SQL*Net from client
       2602  SQL*Net roundtrips to/from client
         59  sorts (memory)
          0  sorts (disk)
        100  rows processed
 
 
This happens because the client presumes the LOB might be arbitrarily large, so it retrieves each row’s LOB in chunks, issuing repeated network requests.
 
From 23ai onward, Oracle lets us tell the database that certain LOBs should be treated like values rather than chunked LOB objects.
 
demo@ADB26AI> create table t_value_clob( x number, y clob )
  2  lob(y) query as value;
 
Table created.
 
demo@ADB26AI> insert into t_value_clob select * from base_table;
 
100 rows created.
 
Now if I rerun the query, notice the improvements in the round trips.
 
demo@ADB26AI> select * from t_value_clob;
 
100 rows selected.
 
Elapsed: 00:00:03.12
 
Statistics
----------------------------------------------------------
         23  recursive calls
         16  db block gets
        137  consistent gets
          0  physical reads
       2796  redo size
     420380  bytes sent via SQL*Net to client
        404  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
        100  rows processed
 
 
This is all good If the Lobs are small, what if there is a mix ? we can also decide on query by query basis on how would the lob data to be retrieved.  Here is our original table query with LOB_VALUE function.
 
demo@ADB26AI> select x,lob_value(y) from t_reference_clob;
 
100 rows selected.
 
Elapsed: 00:00:03.14
 
Statistics
----------------------------------------------------------
         11  recursive calls
          9  db block gets
         16  consistent gets
        112  physical reads
       1552  redo size
     420391  bytes sent via SQL*Net to client
        404  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
        100  rows processed
 
If the data is JSON, then we can also provide the instruction to the database as part of the JSON_SERIALIZE function.
 
demo@ADB26AI> select x,json_serialize(y returning clob value) from t_reference_clob;
 
100 rows selected.
 
Elapsed: 00:00:03.12
 
Statistics
----------------------------------------------------------
         12  recursive calls
         11  db block gets
         16  consistent gets
        100  physical reads
       1700  redo size
     420414  bytes sent via SQL*Net to client
        404  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
        100  rows processed
 
 
Why this matters today. 
  • For most modern JSON use cases:
  • ORDS APIs
  • APEX pages
  • Microservices
  • Autonomous clients
  • Cloud apps 
…the payloads are typically kilobytes, not gigabytes. The default LOB architecture is overkill and causes unnecessarily high network latency.
 
With the new approach, we can treat JSON CLOB as a value, not a chunked object, dramatically reducing network round trips and improving performance—without changing the data model.
 
If your applications plan to leverage Oracle’s modern data types such as JSON or Vector, Oracle Database 26ai will store these values as VALUE LOBs by default. This means Oracle uses the VALUE LOB format internally for optimized retrieval and processing.

 
demo-user@ADB26ai> create table t ( x json ,y vector );
 
Table T created.
 
demo-user@ADB26ai> select column_name, value_based
  2  from user_lobs
  3* where table_name ='T' ;
 
COLUMN_NAME    VALUE_BASED
______________ ______________
X              YES
Y              YES

No comments:

Post a Comment