通过


矢量数据类型 (ODBC)

本文介绍从版本 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_VECTORSQL_C_BINARY返回。 SQL_C_SS_VECTOR 以紧凑、类型化的形式返回向量。 SQL_C_BINARY 返回 varbinary 负载。

    • 对于输入或参数绑定,Microsoft 驱动程序 (18.6.1.1,包含vectorTypeSupport=v1) 同时支持 SQL_C_SS_VECTORSQL_C_BINARYSQL_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_VECTORSQL_C_BINARY

  • TargetValuePtr:当SQL_SS_VECTOR_STRUCT时,指向具有浮点数组的TargetType的指针;否则,指向一个缓冲区,大小为SQL_C_SS_VECTOR中的值。

  • BufferLengthsizeof(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_VECTORSQL_C_BINARY

  • TargetValuePtr:当SQL_SS_VECTOR_STRUCT时,指向具有浮点数组的TargetType的指针;否则,指向一个缓冲区,大小为SQL_C_SS_VECTOR中的值。

  • BufferLengthsizeof(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_Indsizeof(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向量列时:

  • DataTypeSQL_SS_VECTOR
  • ColumnSizeSQL_DESC_PRECISION
  • DecimalDigits0 (基础类型指示符,而不是数值刻度)
  • NullableSQL_NULLABLE

报告列大小表示本机矢量有效负载大小: sizeof(SQL_SS_VECTOR_STRUCT) + (维度 * sizeof(float)

SQLDescribeParam

当您使用SQLDescribeParam作为向量参数时:

  • DataTypeSQL_SS_VECTOR
  • ColumnSize 等于本地矢量有效负载大小
  • DecimalDigits0 (基础类型指示符,而不是数值刻度)
  • NullableSQL_NULLABLE

此信息允许应用程序在绑定之前正确分配参数缓冲区。

SQLColAttribute

  • 使用 SQLColAttribute(hstmt, ColumnNumber, SQL_DESC_OCTET_LENGTH, ...) 获取矢量有效负载的确切字节长度。 SQL_DESC_LENGTHSQL_DESC_PRECISION 可能携带特定于驱动程序的值。 对于字节计数,首选 SQL_DESC_OCTET_LENGTH

    • 处理:当列为 时, 或与 一起使用的 将返回 。 在使用返回的长度或缓冲区之前进行 SQL_NULL_DATA 检查。

批量复制(BCP)

可以通过 BCP 文件和 bcp_bind API 批量导入和导出矢量列,就像其他数据类型一样。 目前,矢量导入和导出仅支持本机格式(SQLVECTOR)或 varbinarySQLBINARY),但不支持字符格式。 不支持向量类型和字符类型之间的转换。

有关 矢量的类型标记、默认前缀长度和默认字段长度的详细信息,请参阅 文件存储类型前缀长度字段长度

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) * 维度)完全相等的值。 如果没有,则会发生错误。
  • eDataTypeSQLVECTORSQLBINARY

将数据导入到向量列bcp_bind时,请将eDataType设置为SQLVECTORSQLBINARY。 在这两种情况下,您必须以SQL_SS_VECTOR_STRUCT提供数据。

故障排除和提示

  • 如果 SQLGetTypeInfo 未列出 VECTOR,请退回将向量存储为 varchar