Accessing and Changing Relational Data

Managing ntext, text, and image Data

The Microsoft® SQL Server™ ntext, text, and image data types are capable of holding extremely large amounts of data (up to 2 GB) in a single value. A single data value is typically larger than can be retrieved by an application in one step; some values may be larger than the virtual memory available on the client. Therefore, special steps are usually needed to retrieve these values.

If an ntext, text, and image data value is no longer than a Unicode, character, or binary string (4,000 characters, 8,000 characters, 8,000 bytes respectively), the value can be referenced in SELECT, UPDATE, and INSERT statements much the same way as the smaller data types. For example, an ntext column with a short value can be referenced in a SELECT statement select list the same way an nvarchar column is referenced. Some restrictions that must be observed, such as not being able to directly reference an ntext, text, or image column in a WHERE clause. These columns can be included in a WHERE clause as parameters of a function that returns another data type (such as ISNULL, SUBSTRING or PATINDEX) or in an IS NULL, IS NOT NULL, or LIKE expression.

Handling Larger Data Values

When the ntext, text, and image data values get larger, however, they must be handled on a block-by-block basis. Both Transact-SQL and the database APIs contain functions that allow applications to work with ntext, text, and image data block by block.

The database APIs follow a common pattern in the ways they handle long ntext, text, and image columns:

Using text in row

In Microsoft SQL Server 2000, users can enable a text in row option on a table so it could store text, ntext, or image data in its data row.

To enable the option, execute the sp_tableoption stored procedure, specifying text in row as the option name and on as the option value. The default maximum size that can be stored in a row for a BLOB (binary large object: text, ntext, or image data) is 256 bytes, but values may range from 24 through 7000. To specify a maximum size that is not the default, specify an integer within the range as the option value.

text, ntext, or image strings are stored in the data row if the following conditions apply:

When BLOB strings are stored in the data row, reading and writing the text, ntext, or image strings can be as fast as reading or writing character and binary strings. SQL Server does not have to access separate pages to read or write the BLOB string.

If a text, ntext, or image string is larger than the specified limit or the available space in the row, pointers are stored in the row instead. The conditions for storing the BLOB strings in the row still apply though: There must be enough space in the data row to hold the pointers.

For more information, see sp_tableoption.

Using text pointers

Unless the text in row option is specified, text, ntext, or image strings are stored outside a data row; only the text pointers to these strings reside in the data rows. Text pointers point to the root node of a tree built of internal pointers that map to the pages in which string fragments (of text, ntext, and image data) are actually stored.

In row text pointers in SQL Server 2000 are different from the text pointers in earlier versions of SQL Server. In row text pointers behave like file handles for BLOB data; earlier text pointers function like addresses to the BLOB data. Thus, when using in row text pointers, keep in mind the following characteristics:

Important  Although an in row text is allowed in a cursor, an in row text pointer is not. SQL Server will return the error message (8654, 16, 1, 'A cursor plan could not be generated for the given statement because it contains textptr(inrow lob).', 1033) if you attempt to declare a cursor that contains an in row text pointer.

  1. Number

    A maximum of 1024 active in row text pointers are allowed per transaction per database.

  2. Locking

    When a user obtains an active text pointer, SQL Server 2000 locks the data row and ensures no other user modifies or deletes the row while the first user has the text pointer. The lock is released when the text pointer becomes invalid. To invalidate a text pointer, use sp_invalidate_textptr.

    A text pointer cannot be used to update BLOB values when the isolation level of the transaction is read uncommitted, or the database is in read-only mode.

    SQL Server 2000 does not lock the data row if the database is in single-user mode.

    To illustrate, given the following table:

    CREATE TABLE t1 (c1 int, c2 text)
    EXEC sp_tableoption 't1', 'text in row', 'on'
    INSERT t1 VALUES ('1', 'a')
    

    The following transaction will succeed:

    INSERT t1 VALUES ('1','This is text.')
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
    GO
    BEGIN TRAN
    DECLARE @ptr varbinary(16)
    SELECT @ptr = textptr(c2)
    FROM t1
    WHERE c1 = 1
    READTEXT t1.c2 @ptr 0 5
    COMMIT TRAN
    GO
    

    The following transaction will fail:

    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
    GO
    BEGIN TRAN
    DECLARE @ptr varbinary(16)
    SELECT @ptr = textptr(c2)
    FROM t1
    WHERE c1 = 1
    WRITETEXT t1.c2 @ptr 'xx'
    COMMIT TRAN
    GO
    
  3. Duration

    In row text pointers are valid only within a transaction. When a transaction is committed, the text pointer becomes invalid.

    Within a transaction, in row text pointers can be invalidated when any of the following actions take place:

    • The session ends.

    • The data row is deleted in the same transaction. (Other transactions cannot delete a data row because of the lock obtained on it.)

    • The schema of a table in which the text pointer resides is changed. Schema-changing actions that invalidate text pointers include: creating or dropping clustered index, altering or dropping the table, truncating the table, changing the text in row option through sp_tableoption, and executing sp_indexoption.

    Using the earlier example, the following script would work in earlier versions of SQL Server, but will generate an error in SQL Server 2000.

    DECLARE @ptrval varbinary(16)
    PRINT 'get error here'
    SELECT @ptrval = TEXTPTR(c2)
    FROM t1
    WHERE c1 = 1
    READTEXT t1.c2 @ptrval 0 1
    

    In SQL Server 2000, the in row text pointer must be used inside a transaction:

    BEGIN TRAN
    DECLARE @ptrval varbinary(16)
    SELECT @ptrval = TEXTPTR(c2)
    FROM t1
    WHERE c1 = 1
    READTEXT t1.c2 @ptrval 0 1
    COMMIT
    
  4. NULL text

    You can get an in row text pointer on NULL text that is generated by INSERT. Previously, you can get text pointers only after updating a BLOB to NULL.

    For example, the following code does not work in SQL Server 7.0, but works in SQL Server 2000.

    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    GO
    INSERT INTO t1 VALUES (4, NULL)
    BEGIN TRAN
    DECLARE @ptrval VARBINARY(16)
    SELECT @ptrval = TEXTPTR(c2)
    FROM t1
    WHERE c1 = 4
    WRITETEXT t1.c2 @ptrval 'x4'
    COMMIT
    

    In SQL Server 7.0, you must do the following:

    INSERT INTO t1 VALUES (4, NULL)
    UPDATE t1 
        SET c2 = NULL 
        WHERE c1 = 4
    DECLARE @ptrval VARBINARY(16)
    SELECT @ptrval = TEXTPTR(c2)
    FROM t1
    WHERE c1 = 4
    WRITETEXT t1.c2 @ptrval 'x4'
    

This table summarizes the differences.

Difference in row text pointer Non in row text pointer
Number Maximum of 1024 active per transaction per database. Unlimited.
Locking Data row is S locked until the pointer becomes invalid.

Locks are not obtained when the transaction is 'read uncommitted', or the database is 'single-user' or 'read-only' mode.

Data row is not locked.
Duration Becomes invalid at the end of transaction or session, when a row is deleted or the schema of the table is changed. Becomes invalid when the row is deleted.
NULL text Obtainable right after the insert of NULL text. Obtainable only after update.

Using ntext, text, and image Data with Database APIs

This is a summary of the ways the database APIs handle ntext, text, and image data:

For more information, see Text and Image Functions.