Obter dados Long

Os DBMSs definem dados longos como qualquer caractere ou dados binários acima de um determinado tamanho, como 255 caracteres. Esses dados podem ser pequenos o suficiente para serem armazenados em um só buffer, como uma descrição de parte de vários milhares de caracteres. No entanto, eles podem ser longos demais para serem armazenados na memória, como documentos de texto longos ou bitmaps. Como esses dados não podem ser armazenados em um só buffer, eles são recuperados do driver em partes com SQLGetData depois que foi efetuado o fetch de outros dados na linha.

Nota

Um aplicativo realmente pode recuperar qualquer tipo de dados com SQLGetData, não apenas dados longos, embora apenas dados de caracteres e binários possam ser recuperados em partes. Porém, se os dados forem pequenos o suficiente para caber em um só buffer, geralmente não haverá razão para usar SQLGetData. É muito mais fácil associar um buffer à coluna e permitir que o driver retorne os dados no buffer.

Para recuperar dados longos de uma coluna, um aplicativo primeiro chama SQLFetchScroll ou SQLFetch para mover para uma linha e efetuar fetch dos dados para colunas acopladas. O aplicativo então chama SQLGetData. SQLGetData tem os mesmos argumentos que SQLBindCol: identificador de instrução, número de coluna, tipo de dados C, endereço e comprimento de byte de uma variável de aplicativo e endereço de um buffer de comprimento/indicador. Ambas as funções têm os mesmos argumentos porque executam essencialmente a mesma tarefa: as duas descrevem uma variável de aplicativo para o driver e especificam que os dados de uma coluna específica devem ser retornados nessa variável. As principais diferenças são que SQLGetData é chamado depois de efetuar o fetch de uma linha (e às vezes é chamado de associação tardia por esse motivo) e que a associação especificada por SQLGetData se mantém apenas durante a chamada.

Em relação a uma única coluna, SQLGetData se comporta como SQLFetch: recupera os dados para a coluna, converte-os para o tipo da variável de aplicativo e os retorna nessa variável. Ele também retorna o comprimento de byte dos dados no buffer de comprimento/indicador. Para obter mais informações sobre como o SQLFetch retorna dados, confira Efetuar fetch de uma linha de dados.

O SQLGetData difere do SQLFetch em um aspecto importante. Se for chamada mais de uma vez seguida para a mesma coluna, cada chamada retornará uma parte sucessiva dos dados. Cada chamada, exceto a última, retorna SQL_SUCCESS_WITH_INFO e SQLSTATE 01004 (dados de cadeia de caracteres, truncados à direita); a última chamada retorna SQL_SUCCESS. É assim que SQLGetData é usado para recuperar dados longos em partes. Quando não há mais dados para retornar, SQLGetData retorna SQL_NO_DATA. O aplicativo é responsável por reunir dados longos, o que pode significar concatenar as partes dos dados. Cada parte é terminada em nulidade. O aplicativo deverá remover o caractere de terminação nula se concatenar as partes. A recuperação de dados em partes pode ser feita para indicadores de comprimento variável, bem como para outros dados longos. O valor retornado no buffer de comprimento/indicador diminui a cada chamada pelo número de bytes retornados na chamada anterior, embora seja comum que o driver não consiga descobrir a quantidade de dados disponíveis e retornar um comprimento de byte de SQL_NO_TOTAL. Por exemplo:

// 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);  

Há diversas restrições no uso de SQLGetData. Em geral, colunas acessadas com SQLGetData:

  • Devem ser acessadas em ordem crescente de número de coluna (devido à maneira como as colunas de um conjunto de resultados são lidas da fonte de dados). Por exemplo, é um erro chamar SQLGetData para a coluna 5 e então chamá-lo para a coluna 4.

  • Não pode ser associado.

  • Deve ter um número de coluna maior do que a última coluna associada. Por exemplo, se a última coluna associada for a 3, será um erro chamar SQLGetData para a coluna 2. Por isso, os aplicativos devem colocar colunas de dados longas no final da lista de seleção.

  • Não pode ser usado se SQLFetch ou SQLFetchScroll foi chamado para recuperar mais de uma linha. Para obter mais informações, confira Como usar cursores em bloco.

Alguns drivers não impõem essas restrições. Os aplicativos interoperáveis devem presumir que elas existem ou determinar quais restrições não são impostas chamando SQLGetInfo com a opção SQL_GETDATA_EXTENSIONS.

Se o aplicativo não precisar de todos os dados em uma coluna de dados binários ou de caracteres, poderá reduzir o tráfego de rede em drivers baseados em DBMS definindo o atributo de instrução SQL_ATTR_MAX_LENGTH antes de executar a instrução. Isso restringe o número de bytes de dados que serão retornados para qualquer caractere ou coluna binária. Por exemplo, suponha que uma coluna contenha documentos de texto longos. Um aplicativo que navega na tabela que contém essa coluna pode precisar exibir apenas a primeira página de cada documento. Embora esse atributo de instrução possa ser simulado no driver, não há razão para fazer isso. Em particular, se um aplicativo quiser truncar caracteres ou dados binários, ele deverá vincular um pequeno buffer à coluna com SQLBindCol e permitir que o driver trunque os dados.