TN045: Compatibilidad MFC/base de datos con Long Varchar/Varbinary
Nota:
La nota técnica siguiente no se ha actualizado desde que se incluyó por primera vez en la documentación en línea. Como resultado, algunos procedimientos y temas podrían estar obsoletos o ser incorrectos. Para obtener información más reciente, se recomienda buscar el tema de interés en el índice de la documentación en línea.
En esta nota se describe cómo recuperar y enviar los tipos de datos SQL_LONGVARCHAR ODBC y SQL_LONGVARBINARY mediante las clases de base de datos de MFC.
Información general sobre la compatibilidad con Long Varchar/Varbinary
Los tipos de datos SQL_LONG_VARCHAR y SQL_LONGBINARY de ODBC (a los que se hace referencia aquí como columnas de datos largas) pueden contener grandes cantidades de datos. Hay 3 formas en que puede controlar estos datos:
Enlazarlos a
CString
/CByteArray
.Enlazarlos a
CLongBinary
.No enlazarlos en absoluto y recuperar y enviar manualmente el valor de datos largos, independientemente de las clases de la base de datos.
Cada uno de los tres métodos tiene ventajas y desventajas.
Las columnas de datos largas no se admiten para parámetros en una consulta. Solo se admiten para outputColumns.
Enlace de una columna de datos larga a CString/CByteArray
Ventajas:
Este enfoque es sencillo de entender y se trabaja con clases conocidas. El marco proporciona compatibilidad de CFormView
para CString
con DDX_Text
. Tiene una gran cantidad de funcionalidades generales de cadena o colección con las clases CString
y CByteArray
, y puede controlar la cantidad de memoria asignada localmente para contener el valor de datos. El marco mantiene una copia antigua de los datos de campo durante las llamadas a la función Edit
o AddNew
y el marco puede detectar automáticamente los cambios en los datos por usted.
Nota:
Dado que CString
está diseñado para trabajar en datos de caracteres y CByteArray
para trabajar en datos binarios, se recomienda colocar los datos de caracteres (SQL_LONGVARCHAR) en CString
y los datos binarios (SQL_LONGVARBINARY) en CByteArray
.
Las funciones RFX para CString
y CByteArray
tienen un argumento adicional que permite invalidar el tamaño predeterminado de la memoria asignada para contener el valor recuperado de la columna de datos. Observe el argumento nMaxLength en las siguientes declaraciones de función:
void AFXAPI RFX_Text(CFieldExchange* pFX,
const char *szName,
CString& value,
int nMaxLength = 255,
int nColumnType =
SQL_VARCHAR);
void AFXAPI RFX_Binary(CFieldExchange* pFX,
const char *szName,
CByteArray& value,
int nMaxLength = 255);
Si recupera una columna de datos larga en CString
o CByteArray
, la cantidad máxima de datos devuelta es, de forma predeterminada, de 255 bytes. Cualquier valor que supere este límite se omite. En este caso, el marco iniciará la excepción AFX_SQL_ERROR_DATA_TRUNCATED. Afortunadamente, puede aumentar explícitamente nMaxLength a valores mayores, hasta MAXINT.
Nota:
MFC usa el valor de nMaxLength para establecer el búfer local de la función SQLBindColumn
. Este es el búfer local para el almacenamiento de los datos y no afecta realmente la cantidad de datos devueltos por el controlador ODBC. RFX_Text
y RFX_Binary
solo realizan una llamada mediante SQLFetch
para recuperar los datos de la base de datos de back-end. Cada controlador ODBC tiene una limitación diferente en la cantidad de datos que puede devolver en una sola captura. Este límite puede ser mucho menor que el valor establecido en nMaxLength, en cuyo caso se producirá la excepción AFX_SQL_ERROR_DATA_TRUNCATED. En estas circunstancias, cambie al uso de RFX_LongBinary
en lugar de RFX_Text
o RFX_Binary
para que se puedan recuperar todos los datos.
ClassWizard enlazará un SQL_LONGVARCHAR a CString
o un SQL_LONGVARBINARY a un CByteArray
por usted. Si desea asignar más de 255 bytes para recuperar valores de su columna de datos larga, puede proporcionar un valor explícito de nMaxLength.
Cuando una columna de datos larga está enlazada a CString
o CByteArray
, la actualización del campo funciona igual que cuando está enlazada a un SQL_VARCHAR o SQL_VARBINARY. Durante Edit
, el valor de datos se almacena en caché y posteriormente se compara cuando se llama a Update
para detectar cambios en el valor de datos y establecer los valores Dirty y Null de la columna correctamente.
Enlace de una columna de datos larga a un CLongBinary
Si la columna de datos larga puede contener más bytes de datos MAXINT, es probable que considere la posibilidad de recuperarla en un CLongBinary
.
Ventajas:
Esto recupera una columna de datos larga completa, hasta el límite de memoria disponible.
Desventajas:
Los datos se retienen en la memoria. Este enfoque también es prohibitivamente costoso para grandes cantidades de datos. Debe llamar a SetFieldDirty
para que el miembro de datos enlazado se asegure de que el campo se incluye en una operación Update
.
Si recupera columnas de datos largas en un CLongBinary
, las clases de la base de datos comprobarán el tamaño total de la columna de datos larga y, a continuación, asignarán un segmento de memoria HGLOBAL
lo suficientemente grande como para contener todo el valor de datos. A continuación, las clases de base de datos recuperan el valor de datos completo en el HGLOBAL
asignado.
Si el origen de datos no puede devolver el tamaño esperado de la columna de datos larga, el marco producirá la excepción AFX_SQL_ERROR_SQL_NO_TOTAL. Si se produce un error en el intento de asignar HGLOBAL
, se produce una excepción de memoria estándar.
ClassWizard enlazará un SQL_LONGVARCHAR o SQL_LONGVARBINARY a un CLongBinary
por usted. Seleccione CLongBinary
como tipo de variable en el cuadro de diálogo Agregar variable miembro. ClassWizard agregará una llamada RFX_LongBinary
a la llamadaDoFieldExchange
e incrementará el número total de campos enlazados.
Para actualizar los valores de la columna de datos largos, primero asegúrese de que la asignación HGLOBAL
es lo suficientemente grande como para contener los nuevos datos mediante una llamada a ::GlobalSize en el miembro m_hData de CLongBinary
. Si es demasiado pequeño, suelte el HGLOBAL
y asigne uno del tamaño adecuado. A continuación, establezca m_dwDataLength para reflejar el nuevo tamaño.
De lo contrario, si m_dwDataLength es mayor que el tamaño de los datos que va a reemplazar, puede liberar y reasignar el HGLOBAL
, o dejarlo asignado. Asegúrese de indicar el número de bytes que se usan realmente en m_dwDataLength.
Cómo funciona la actualización de un CLongBinary
No es necesario comprender cómo funciona la actualización de un CLongBinary
, pero puede ser útil como ejemplo sobre cómo enviar valores de datos largos a un origen de datos, si elige este tercer método, que se describe a continuación.
Nota:
Para que un campo CLongBinary
se incluya en una actualización, debe llamar explícitamente a SetFieldDirty
para el campo. Si realiza algún cambio en un campo, lo que incluye establecerlo en Null, debe llamar a SetFieldDirty
. También debe llamar a SetFieldNull
, con el segundo parámetro comoFALSE, para marcar que el campo tiene un valor.
Al actualizar un campo CLongBinary
, las clases de la base de datos usan el mecanismo DATA_AT_EXEC de ODBC (consulte la documentación de ODBC sobre el argumento rgbValue de SQLSetPos
). Cuando el marco prepara la instrucción insert o update, en lugar de apuntar a la HGLOBAL
que contiene datos, la dirección de CLongBinary
se establece como el valor de la columna en su lugar y el indicador de longitud se establece en SQL_DATA_AT_EXEC. Más adelante, cuando se envíe la instrucción update al origen de datos, SQLExecDirect
devolverá SQL_NEED_DATA. Esto alerta al marco de trabajo que el valor del parámetro de esta columna es realmente la dirección de un CLongBinary
. El marco llama a SQLGetData
una vez con un búfer pequeño, a la espera de que el controlador devuelva la longitud real de los datos. Si el controlador devuelve la longitud real del objeto binario grande (el BLOB), MFC reasigna tanto espacio como sea necesario para capturar el BLOB. Si el origen de datos devuelve SQL_NO_TOTAL, lo que indica que no puede determinar el tamaño del BLOB, MFC creará bloques más pequeños. El tamaño inicial predeterminado es de 64 000 y los bloques posteriores serán el doble del tamaño; por ejemplo, el segundo será de 128 000, el tercero de 256 000, etc. El tamaño inicial es configurable.
Sin enlace: recuperar o enviar datos directamente desde ODBC con SQLGetData
Con este método se omiten completamente las clases de base de datos y usted lidia con la columna de datos larga usted mismo.
Ventajas:
Puede almacenar en caché los datos en el disco si es necesario o decidir dinámicamente cuánto datos se recuperarán.
Desventajas:
No obtiene el Edit
del marco ni la compatibilidad con AddNew
, y debe escribir código usted mismo para realizar la funcionalidad básica (Delete
no funciona, ya que no es una operación de nivel de columna).
En este caso, la columna de datos larga debe estar en la lista de selección del conjunto de registros, pero no debe estar enlazada a por el marco. Una manera de hacerlo es proporcionar su propia instrucción SQL a través de GetDefaultSQL
o como el argumento lpszSQL a la función Open
de CRecordset
, y no enlazar la columna adicional con una llamada de función RFX_. ODBC requiere que los campos no enlazados aparezcan a la derecha de los campos enlazados, por lo que debe agregar la columna o columnas sin enlazar al final de la lista de selección.
Nota:
Dado que la columna de datos larga no está enlazada por el marco, los cambios en ella no se controlarán con las llamadas a CRecordset::Update
. Debe crear y enviar las instrucciones SQL INSERT y UPDATE necesarias usted mismo.