本文介绍从版本 18.6.1.1 开始由 Microsoft ODBC Driver for SQL Server 实现的 向量 SQL 数据类型。 本文档概述了Microsoft 驱动程序针对矢量的行为,并提供使用指南、特定于 API 的说明和代码片段。 有关矢量数据类型的概述,请参阅 Vector 数据类型。
概述
Microsoft ODBC Driver for SQL Server 原生支持 向量 数据类型。 应用程序可以有效地存储、检索和处理通常用于机器学习和 AI 工作负载的固定维度数字嵌入。 驱动程序通过标准 ODBC API 和 C 数据类型公开矢量支持。 应用程序可以与 SQL Server 矢量列互作,而无需更改现有的 ODBC 工作流。
适用于:Microsoft ODBC Driver for SQL Server 18.6.1.1 及更高版本。
对于Microsoft驱动程序(18.6.1.1),矢量支持默认处于禁用状态,必须显式启用。
原生 C 表示形式
启用向量支持后,将使用名为 的类型化 C 结构交换SQL_SS_VECTOR_STRUCT列。
typedef struct tagSQL_SS_VECTOR_STRUCT {
SQLSMALLINT dimension; /* Number of elements */
SQLSMALLINT type; /* Element type indicator (0 = float32) */
union {
float *f32; /* Pointer to float32 data */
} data;
} SQL_SS_VECTOR_STRUCT;
-
dimension:描述向量中的元素数 -
type:标识基元素类型(当前float32) -
data.f32:指向包含矢量值的应用程序缓冲区
启用矢量支持
Microsoft驱动程序公开特定于驱动程序的 C 绑定
SQL_C_SS_VECTOR,并在使用SQL_C_BINARY值配置连接或驱动程序选项vectorTypeSupport时支持v1矢量输出。 在实践中:启用
vectorTypeSupport=v1时,检索 API(例如SQLGetData,和SQLBindCol)可以将 向量 列作为任一SQL_C_SS_VECTOR或SQL_C_BINARY返回。SQL_C_SS_VECTOR以紧凑、类型化的形式返回向量。SQL_C_BINARY返回 varbinary 负载。对于输入或参数绑定,Microsoft 驱动程序 (18.6.1.1,包含
vectorTypeSupport=v1) 同时支持SQL_C_SS_VECTOR和SQL_C_BINARY。SQL_C_SS_VECTOR提供类型化、压缩的输入绑定。SQL_C_BINARY等效且可移植。 当希望驱动程序将有效负载视为本机SQL_C_SS_VECTOR类型时使用。在
vectorTypeSupport=off后,矢量列会显示为包含 JSON 数组的 varchar(max)。
在使用特定于矢量的类型之前,应用程序还必须将 ODBC 版本设置为 ODBC 3.8:
SQLSetEnvAttr(
hEnv,
SQL_ATTR_ODBC_VERSION,
(SQLPOINTER)SQL_OV_ODBC3_80,
0);
支持的绑定格式
本机矢量绑定
C 类型: SQL_C_SS_VECTOR
建议将此格式用于性能关键型应用程序。
二进制绑定
C 类型: SQL_C_BINARY
向量的返回布局与SQL_C_SS_VECTOR相同。 应用程序可以将此格式用于低级别的互作性方案。 数据指针缓冲区可以是连续的,也可以与结构内存地址不连续。
ODBC API 指南
本部分介绍 ODBC API 如何与 SQL Server 矢量数据交互,包括缓冲区布局要求、 NULL 处理和支持的数据表示形式。 启用 vectorTypeSupport=v1 时,在为 ODBC 3.8 配置环境的情况下,所有行为均适用。
SQLBindCol
使用 SQLBindCol 将结果集中的 矢量 列绑定到应用程序缓冲区。
- 典型调用:
SQLRETURN SQLBindCol(
SQLHSTMT StatementHandle,
SQLUSMALLINT ColumnNumber,
SQLSMALLINT TargetType,
SQLPOINTER TargetValuePtr,
SQLLEN BufferLength,
SQLLEN * StrLen_or_IndPtr);
TargetType:使用SQL_C_SS_VECTOR或SQL_C_BINARY。TargetValuePtr:当SQL_SS_VECTOR_STRUCT为时,指向具有浮点数组的TargetType的指针;否则,指向一个缓冲区,大小为SQL_C_SS_VECTOR中的值。BufferLength:sizeof(SQL_SS_VECTOR_STRUCT)+ 为列缓冲区分配的字节数(维度 * 4)。StrLen_or_IndPtr:接收返回向量 (SQL_DESC_OCTET_LENGTH) 的字节长度的指针。 其值为sizeof(SQL_SS_VECTOR_STRUCT)+ 浮点 数组大小(维度 * 4)。
缓冲区布局预期
非连续缓冲区(建议)
- 应用程序为每个行分配一个
SQL_SS_VECTOR_STRUCT。 - 应用程序为
data.f32分配内存。 - 驱动程序将填充矢量元数据并将元素值写入提供的 浮 点缓冲区。
SQLLEN numberOfRow = 1000; // set to SQL_ATTR_ROW_ARRAY_SIZE
SQLULEN columnSize = x; // use SQLDescribeCol or SQLColAttributeW or sizeof(SQL_SS_VECTOR_STRUCT) + (dimension * 4)
SQL_SS_VECTOR_STRUCT vecBuffer[numberOfRow]
std::vector<SQLLEN> indicator(numberOfRow, 0);
for (int i = 0; i < numberOfRow; i++)
{
vecBuffer[i].dimension = static_cast<SQLUSMALLINT>((columnSizes[col - 1] - sizeof(SQL_SS_VECTOR_STRUCT)) / 4);
vecBuffer[i].type = SQL_VECTOR_TYPE_FLOAT32;
vecBuffer[i].data.f32 = (float*)malloc(vecBuffer[i].dimension * sizeof(float));
if (!vecBuffer[i].data.f32) {
std::cerr << "Memory allocation failed for vector data." << std::endl;
return SQL_ERROR;
}
}
SQLBindCol(hStmt, col, SQL_C_SS_VECTOR, vecBuffer, columnSize, indicators.data());
连续缓冲区
- 应用程序分配单个缓冲区,ODBC 相应地填充它。
- 浮点数组必须在结构后立即开始。
- 驱动程序将
data.f32设置为指向此连续区域。
SQLLEN numberOfRow = 1000; // set to SQL_ATTR_ROW_ARRAY_SIZE
SQLULEN columnSize = x; // use SQLDescribeCol or SQLColAttributeW or sizeof(SQL_SS_VECTOR_STRUCT) + (dimension * 4)
std::vector<BYTE> vecBuffer;
std::vector<SQLLEN> indicator(numberOfRow, 0);
vecBuffer.resize(numberOfRow * columnSize);
SQLBindCol(hStmt, col, SQL_C_BINARY, vecBuffer.data(), columnSize, indicators.data());
SQLBindCol 的 NULL 处理
当列值为NULL时,驱动程序会将*StrLen_or_IndPtr设置为SQL_NULL_DATA,并且不会填充缓冲区。 在访问矢量内容之前,请始终检查指示器。
注释
详细描述了SQLFetchSQLFetchScroll列的读取和行提取行为(请参阅这些 API 引用以获取语义和数组提取示例)。
SQLGetData
使用 SQLGetData 从未绑定列中检索矢量数据。
- 典型调用:
SQLRETURN SQLGetData(
SQLHSTMT StatementHandle,
SQLUSMALLINT Col_or_Param_Num,
SQLSMALLINT TargetType,
SQLPOINTER TargetValuePtr,
SQLLEN BufferLength,
SQLLEN * StrLen_or_IndPtr);
TargetType:使用SQL_C_SS_VECTOR或SQL_C_BINARY。TargetValuePtr:当SQL_SS_VECTOR_STRUCT为时,指向具有浮点数组的TargetType的指针;否则,指向一个缓冲区,大小为SQL_C_SS_VECTOR中的值。BufferLength:sizeof(SQL_SS_VECTOR_STRUCT)+ 为列缓冲区分配的字节数(维度 * 4)。StrLen_or_IndPtr:接收返回向量 (SQL_DESC_OCTET_LENGTH) 的字节长度的指针。 其值为sizeof(SQL_SS_VECTOR_STRUCT)+ 浮点数组大小(维度 * 4)。
不支持分块检索。 必须在一次调用中检索整个向量。
示例(本机 向量):
SQLLEN dataLen = 0;
SQL_SS_VECTOR_STRUCT vec = {};
vec.data.f32 = (float*)malloc(x * sizeof(float)); // x is the dimension
if (!vec.data.f32) {
std::cerr << "Memory allocation failed for vector data." << std::endl;
return SQL_ERROR;
}
SQLGetData(hStmt, col, SQL_C_SS_VECTOR, &vec, sizeof(vec) + sizeof(float) * vec.dimension, &dataLen);
SQLGetData 的 NULL 处理
当列值为NULL时,驱动程序会将*StrLen_or_IndPtr设置为SQL_NULL_DATA,并且不会填充缓冲区。 在访问矢量内容之前,请始终检查指示器。
注释
详细描述了SQLFetchSQLFetchScroll列的读取和行提取行为(请参阅这些 API 引用以获取语义和数组提取示例)。
SQLFetch
使用 SQLFetch 来提取下一行,并正确地实现所有绑定的 SqlVector 列。
- 典型调用:
SQLRETURN SQLFetch(
SQLHSTMT StatementHandle);
在标准循环中调用 SQLFetch,其中通过 SqlVector 已绑定 SQLBindCol 列。 或者,使用 SQLGetData 将数据检索到应用程序缓冲区。
示例(本机 向量):
while ((ret = SQLFetch(hStmt)) == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
// if SQLBindCol was used directly jump to read buffer
// else use SQLGetData and than read buffer.
std::cout << " data: [";
if (dataLen != SQL_NULL_DATA)
{
for (SQLUSMALLINT d = 0; d < vec.dimension; ++d) {
std::cout << vec.data.f32[d];
if (d + 1 < vec.dimension) std::cout << ", ";
}
}
std::cout << "]" << std::endl;
}
SQLFetchScroll
使用 SQLFetchScroll 根据指定的方向提取数据集(下一步、上一步、绝对位置、相对位置、书签位置)。 驱动程序正确地将矢量数据复制到应用程序缓冲区。
- 典型调用:
SQLRETURN SQLFetchScroll(
SQLHSTMT StatementHandle,
SQLSMALLINT FetchOrientation,
SQLLEN FetchOffset);
示例(本机 向量):
// Fetch rows in batches
while ((ret = SQLFetchScroll(hStmt, SQL_FETCH_NEXT, 0)) != SQL_NO_DATA) {
for (SQLULEN i = 0; i < numRowsFetched; i++) {
// In case of Contiguous Buffer
SQL_SS_VECTOR_STRUCT* vecptr = reinterpret_cast<SQL_SS_VECTOR_STRUCT*>(
vecBuffer.data() + (i * columnSizes[col - 1]));
float* floats = reinterpret_cast<float*>(
reinterpret_cast<char*>(vecptr) + sizeof(SQL_SS_VECTOR_STRUCT)
);
// in Case of non-contiguous Buffer
SQL_SS_VECTOR_STRUCT* vecptr = &vecBuffer[i];
float* floats = vecptr->data.f32;
if (indicators[i] != SQL_NULL_DATA)
{
for (SQLUSMALLINT d = 0; d < vecptr->dimension; ++d) {
std::cout << floats[d];
if (d + 1 < vecptr->dimension) std::cout << ", ";
}
}
std::cout << "]" << std::endl;
}
}
SQLBindParameter
使用 SQLBindParameter 将向量值发送到 SQL Server。
- 典型调用:
SQLRETURN SQLBindParameter(
SQLHSTMT StatementHandle,
SQLUSMALLINT ParameterNumber,
SQLSMALLINT InputOutputType,
SQLSMALLINT ValueType,
SQLSMALLINT ParameterType,
SQLULEN ColumnSize,
SQLSMALLINT DecimalDigits,
SQLPOINTER ParameterValuePtr,
SQLLEN BufferLength,
SQLLEN * StrLen_or_IndPtr);
-
ParameterValuePtr:指向已填充的SQL_SS_VECTOR_STRUCT -
ColumnSize:会忽略输入参数。 对于输出参数和数据执行方案,请指定总矢量大小:sizeof(SQL_SS_VECTOR_STRUCT)+ (浮点 数组大小) -
DecimalDigits:对于输入参数忽略。 对于输出参数和执行时的数据方案,请指定向量基类型 -
BufferLength:≥sizeof(SQL_SS_VECTOR_STRUCT)+ (维度 *sizeof(float)) -
*StrLen_or_IndPtr:必须具有相同的总大小
示例(本机 向量):
float values[3] = {1.0f, 2.0f, 3.0f};
SQL_SS_VECTOR_STRUCT vec;
SQLLEN cb;
vec.dimension = 3;
vec.type = 0; /* float32 */
vec.data.f32 = values;
cb = sizeof(vec) + sizeof(values);
SQLBindParameter(
hStmt, 1, SQL_PARAM_INPUT, SQL_C_SS_VECTOR, SQL_SS_VECTOR, 0, 0, &vec, cb, &cb);
SQLBindParameter 中的 NULL 处理
应用程序可以使用任一 NULL 受支持的方法指示向量:
将
*StrLen_or_IndPtr设置为SQL_NULL_DATA,并将NULL作为ParameterValuePtr传递提供一个
SQL_SS_VECTOR_STRUCT:维度集类型 =
float32data.f32=NULL
SQLPutData
驱动程序支持满足以下约束条件的SQLPutData向量参数。
必须在第一次调用中提供整个向量。
驱动程序不支持分块或增量矢量传输。
NULL处理遵循与SQLBindParameter相同的规则。典型调用:
SQLRETURN SQLPutData(
SQLHSTMT StatementHandle,
SQLPOINTER DataPtr,
SQLLEN StrLen_or_Ind);
-
DataPtr:指向已填充的SQL_SS_VECTOR_STRUCT -
StrLen_or_Ind:sizeof(SQL_SS_VECTOR_STRUCT)+ (维度 *sizeof(float))
示例(本机 向量):
std::vector<float> floatArray = { 1.0f, 2.0f, 3.0f };
SQL_SS_VECTOR_STRUCT vectorValue = {0};
vectorValue.type = SQL_VECTOR_TYPE_FLOAT32;
vectorValue.dimension = (SQLUSMALLINT)floatArray.size();
vectorValue.data.f32 = floatArray.data();
// Tell ODBC this parameter will be supplied at execution time
SQLLEN cbVectorLen = SQL_LEN_DATA_AT_EXEC(
(SQLLEN)(sizeof(SQL_SS_VECTOR_STRUCT) + floatArray.size() * sizeof(float))
);
// Optional token to identify which parameter needs data
SQLPOINTER token = (SQLPOINTER)1;
// Bind as DATA_AT_EXEC: BufferLength is 0, value pointer can be a token
SQLRETURN rc = SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_SS_VECTOR, SQL_SS_VECTOR, 0, 0, token, 0, &cbVectorLen);
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
// handle error
}
rc = SQLExecute(hStmt);
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO && rc != SQL_NEED_DATA) {
// handle error
}
SQLPOINTER pParamToken = NULL;
rc = SQLParamData(hStmt, &pParamToken);
if (rc == SQL_NEED_DATA) {
// Provide entire vector in first SQLPutData call (no chunking)
rc = SQLPutData(
hStmt,
(SQLPOINTER)&vectorValue,
(SQLLEN)(sizeof(SQL_SS_VECTOR_STRUCT) + floatArray.size() * sizeof(float))
);
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
// handle error
}
// Finalize parameter transfer
rc = SQLParamData(hStmt, &pParamToken);
}
描述符元数据
启用向量支持时,Microsoft ODBC Driver for SQL Server 通过标准 ODBC 描述符 API 公开矢量元数据。 应用程序可以使用描述符信息来发现矢量架构详细信息、计算缓冲区大小,以及为参数和结果集正确配置绑定。
矢量的描述符字段值
下表汇总了 SQL Server 矢量 列和参数的描述符字段值。
| 描述符字段 | 价值 | Description |
|---|---|---|
SQL_DESC_TYPE |
SQL_SS_VECTOR(-156) |
基本 SQL 数据类型标识符 |
SQL_DESC_CONCISE_TYPE |
SQL_SS_VECTOR |
简洁的 SQL 数据类型 |
SQL_DESC_TYPE_NAME |
vector |
SQL 类型名称 |
SQL_DESC_LOCAL_TYPE_NAME |
vector |
驱动程序本地类型名称 |
SQL_DESC_LENGTH |
sizeof(SQL_SS_VECTOR_STRUCT) + (dimension * sizeof(float)) |
矢量值的逻辑大小 |
SQL_DESC_OCTET_LENGTH |
与 SQL_DESC_LENGTH 相同 |
物理大小(以字节为单位) |
SQL_DESC_PRECISION |
与 SQL_DESC_LENGTH 相同 |
用于报告矢量大小 |
SQL_DESC_SCALE |
SQL_VECTOR_TYPE_FLOAT32 |
矢量基元素类型 |
SQL_DESC_DISPLAY_SIZE |
dimension * VECTOR_FLOAT32_TO_CHAR_JSON_MAX_SIZE |
最大 JSON 显示长度 |
SQL_DESC_FIXED_PREC_SCALE |
SQL_FALSE |
矢量没有固定精度/刻度 |
SQL_DESC_NULLABLE |
SQL_NULLABLE |
矢量列允许 NULL 值 |
SQL_DESC_NUM_PREC_RADIX |
0 |
非数值类型 |
SQL_DESC_SEARCHABLE |
SQL_PRED_NONE |
在谓词中不可用 |
SQL_DESC_UNSIGNED |
SQL_TRUE |
元素类型为无符号 |
SQL_DESC_AUTO_UNIQUE_VALUE |
SQL_FALSE |
非自动唯一 |
SQL_DESC_CASE_SENSITIVE |
SQL_FALSE |
不区分大小写 |
SQL_DESC_UPDATABLE |
SQL_ATTR_READWRITE_UNKNOWN |
可更新性未知 |
SQLDescribeCol
调用SQLDescribeCol向量列时:
-
DataType是SQL_SS_VECTOR -
ColumnSize与SQL_DESC_PRECISION -
DecimalDigits是0(基础类型指示符,而不是数值刻度) -
Nullable是SQL_NULLABLE
报告列大小表示本机矢量有效负载大小: sizeof(SQL_SS_VECTOR_STRUCT) + (维度 * sizeof(float))
SQLDescribeParam
当您使用SQLDescribeParam作为向量参数时:
-
DataType是SQL_SS_VECTOR -
ColumnSize等于本地矢量有效负载大小 -
DecimalDigits是0(基础类型指示符,而不是数值刻度) -
Nullable是SQL_NULLABLE
此信息允许应用程序在绑定之前正确分配参数缓冲区。
SQLColAttribute
使用
SQLColAttribute(hstmt, ColumnNumber, SQL_DESC_OCTET_LENGTH, ...)获取矢量有效负载的确切字节长度。SQL_DESC_LENGTH并SQL_DESC_PRECISION可能携带特定于驱动程序的值。 对于字节计数,首选SQL_DESC_OCTET_LENGTH。处理:当列为 时, 或与 一起使用的 将返回 。 在使用返回的长度或缓冲区之前进行 SQL_NULL_DATA检查。
批量复制(BCP)
可以通过 BCP 文件和 bcp_bind API 批量导入和导出矢量列,就像其他数据类型一样。 目前,矢量导入和导出仅支持本机格式(SQLVECTOR)或 varbinary (SQLBINARY),但不支持字符格式。 不支持向量类型和字符类型之间的转换。
有关 矢量的类型标记、默认前缀长度和默认字段长度的详细信息,请参阅 文件存储类型、 前缀长度和 字段长度。
bcp_gettypename
当使用 bcp_gettypename 获取 矢量的 SQL 类型名称时,它将返回 BCP 类型标记(SQLVECTOR)和 "vector"。
bcp_bind
使用 bcp_bind 将程序变量批量插入到 向量 列中。
典型调用:
RETCODE bcp_bind (
HDBC hdbc,
LPCBYTE pData,
INT cbIndicator,
DBINT cbData,
LPCBYTE pTerm,
INT cbTerm,
INT eDataType,
INT idxServerCol);
-
pData:如果cbIndicator为零,则包含指向SQL_SS_VECTOR_STRUCT数据的指针,其中 具有浮点 数组数据(vectorStruct.data.f32字段)。 如果cbIndicator为非零,指示器将直接出现在数据之前的内存中。 因此pData指向一个缓冲区,该缓冲区首先包含一个长度为cbIndicator字节的指示器,后接向量结构。 -
cbData:如果提供,则必须具有与 -sizeof(SQL_SS_VECTOR_STRUCT)+ (sizeof(float32)* 维度)完全相等的值。 如果没有,则会发生错误。 -
eDataType:SQLVECTOR或SQLBINARY
将数据导入到向量列bcp_bind时,请将eDataType设置为SQLVECTOR或SQLBINARY。 在这两种情况下,您必须以SQL_SS_VECTOR_STRUCT提供数据。
故障排除和提示
- 如果
SQLGetTypeInfo未列出VECTOR,请退回将向量存储为 varchar。