获取 Long 数据

DBMS 将长数据定义为特定大小的任意字符或二进制数据,例如 255 个字符。 此数据可能足够小,可以存储在单个缓冲区中,例如包含几千个字符的一部分说明。 但是,它在内存中存储时间可能太长,例如长文本文档或位图。 由于此类数据不能存储在单个缓冲区中,因此在提取行中的其他数据后,将使用 SQLGetData 分部分从驱动程序中提取此数据。

注意

应用程序实际上可以使用 SQLGetData 检索任何类型的数据,而不仅仅是长数据,尽管只能分部分检索字符和二进制数据。 但是,如果数据足够小,无法容纳在单个缓冲区中,则通常没有理由使用 SQLGetData。 将缓冲区绑定到列并让驱动程序返回缓冲区中的数据要容易得多。

要从列检索长数据,应用程序首先会调用 SQLFetchScrollSQLFetch 移动到行并提取绑定列的数据。 然后,应用程序调用 SQLGetDataSQLGetData 的参数与 SQLBindCol 相同:语句句柄;列编号;应用程序变量的 C 数据类型、地址和字节长度;长度/指示器缓冲区的地址。 这两个函数具有相同的参数,因为它们基本上执行相同的任务:它们都向驱动程序描述应用程序变量,并指定应在该变量中返回特定列的数据。 主要区别在于,提取行后调用 SQLGetData(有时称为晚期绑定),SQLGetData 指定的绑定仅在调用期间持续。

对于单个列,SQLGetData 的行为类似于 SQLFetch:它检索该列的数据,将其转换为应用程序变量的类型,并在该变量中予以返回。 它还返回长度/指示器缓冲区中的数据的字节长度。 有关 SQLFetch 如何返回数据的详细信息,请参阅提取数据行

SQLGetData 在一个重要方面不同于 SQLFetch。 如果针对同一列连续调用多次,则每个调用都会返回数据的连续部分。 最后一次调用以外的每个调用返回 SQL_SUCCESS_WITH_INFO 和 SQLSTATE 01004(字符串数据,右截断):最后一个调用返回 SQL_SUCCESS。 这是使用 SQLGetData 分部分检索长数据的方式。 如果没有更多要返回的数据,SQLGetData 将返回 SQL_NO_DATA。 应用程序负责将长数据放在一起,这可能意味着拼接数据的各个部分。 每个部分都以 null 结尾;如果拼接各部分,则应用程序必须删除 null 终止字符。 对于可变长度书签以及其他长数据,可以分部分检索数据。 每次调用中的长度/指示器缓冲区中返回的值随着上一次调用中返回的字节数而减少,尽管驱动程序通常无法发现可用数据量并返回 SQL_NO_TOTAL 字节长度。 例如:

// Declare a binary buffer to retrieve 5000 bytes of data at a time.  
SQLCHAR       BinaryPtr[5000];  
SQLUINTEGER   PartID;  
SQLINTEGER    PartIDInd, BinaryLenOrInd, NumBytes;  
SQLRETURN     rc;   
SQLHSTMT      hstmt;  
  
// Create a result set containing the ID and picture of each part.  
SQLExecDirect(hstmt, "SELECT PartID, Picture FROM Pictures", SQL_NTS);  
  
// Bind PartID to the PartID column.  
SQLBindCol(hstmt, 1, SQL_C_ULONG, &PartID, 0, &PartIDInd);  
  
// Retrieve and display each row of data.  
while ((rc = SQLFetch(hstmt)) != SQL_NO_DATA) {  
   // Display the part ID and initialize the picture.  
   DisplayID(PartID, PartIDInd);  
   InitPicture();  
  
   // Retrieve the picture data in parts. Send each part and the number   
   // of bytes in each part to a function that displays it. The number   
   // of bytes is always 5000 if there were more than 5000 bytes   
   // available to return (cbBinaryBuffer > 5000). Code to check if   
   // rc equals SQL_ERROR or SQL_SUCCESS_WITH_INFO not shown.  
   while ((rc = SQLGetData(hstmt, 2, SQL_C_BINARY, BinaryPtr, sizeof(BinaryPtr),  
                           &BinaryLenOrInd)) != SQL_NO_DATA) {  
      NumBytes = (BinaryLenOrInd > 5000) || (BinaryLenOrInd == SQL_NO_TOTAL) ?  
                  5000 : BinaryLenOrInd;  
      DisplayNextPictPart(BinaryPtr, NumBytes);  
   }  
}  
  
// Close the cursor.  
SQLCloseCursor(hstmt);  

使用 SQLGetData 有多个限制。 通常为使用 SQLGetData 访问的列:

  • 必须按列数增加的顺序访问(因为从数据源中读取结果集的列的方式)。 例如,为第 5 列调用 SQLGetData,然后再为第 4 列调用它是错误的。

  • 无法绑定。

  • 必须有一个高于最后一个绑定列的列编号。 例如,如果最后一个绑定列是第 3 列,则为第 2 列调用 SQLGetData 是错误的。 因此,应用程序应确保将长数据列放在 select 列表的末尾。

  • 如果调用 SQLFetchSQLFetchScroll 来检索多行,则无法使用。 有关详细信息,请参阅使用块游标

某些驱动程序不强制实施这些限制。 可互操作的应用程序应假定它们存在,或者通过使用 SQL_GETDATA_EXTENSIONS 选项调用 SQLGetInfo 来确定不强制实施哪些限制。

如果应用程序不需要字符或二进制数据列中的所有数据,则它可以通过在执行语句之前设置 SQL_ATTR_MAX_LENGTH 语句属性来减少基于 DBMS 的驱动程序中的网络流量。 这会限制将为任何字符或二进制列返回的数据字节数。 例如,假设列包含长文本文档。 浏览包含此列的表的应用程序可能只显示每个文档的第一页。 尽管可以在驱动程序中模拟此语句属性,但没有理由执行此操作。 具体而言,如果应用程序想要截断字符或二进制数据,则应使用 SQLBindCol 将小缓冲区绑定到列,并允许驱动程序截断数据。