67、SQL Server JSON支持:JSON_VALUE、JSON_QUERY、OPENJSON、C语言处理JSON

说实话,JSON 这东西在数据库里越来越常见了。早些年我们做嵌入式数据库开发,数据格式基本都是定长字段、结构体硬编码,谁要是说用 JSON 存数据,大家会觉得你疯了。但现在不一样了,物联网设备上报的数据、配置文件的动态字段、甚至日志系统,JSON 几乎成了标配。

SQL Server 从 2016 版本开始就原生支持 JSON 了,提供了 JSON_VALUEJSON_QUERYOPENJSON 这几个核心函数。我在一个工业数据采集项目里,就靠它们把设备上报的 JSON 数据直接解析入库,省掉了中间层 C 代码的 JSON 解析步骤,性能提升很明显。

今天我们就来聊聊,怎么在 C 语言里调用 SQL Server 的 JSON 函数,以及我自己踩过的那些坑。

JSON_VALUE:提取标量值

JSON_VALUE 用来从 JSON 字符串中提取一个标量值(字符串、数字、布尔值)。说白了,就是你要拿 JSON 里某个字段的值,用这个函数就对了。

语法很简单:

JSON_VALUE ( expression , path )

其中 expression 是 JSON 字符串,path 是类似 '$.key.subkey' 的路径表达式。

举个例子,假设设备上报了这样的 JSON:

{
  "deviceId": "SN-001",
  "temperature": 36.5,
  "status": "online"
}

在 SQL 里这样提取:

SELECT 
  JSON_VALUE(@json, '$.deviceId') AS DeviceId,
  JSON_VALUE(@json, '$.temperature') AS Temperature,
  JSON_VALUE(@json, '$.status') AS Status;
我的经验:JSON_VALUE 只能返回标量值。如果你试图用它提取一个对象或数组,它会返回 NULL。我曾经在这个问题上浪费了半小时,后来才发现应该用 JSON_QUERY。

JSON_QUERY:提取对象或数组

当你要从 JSON 里取出一整个子对象或数组时,就得用 JSON_QUERY。它返回的是 JSON 片段,不是标量。

比如上面的 JSON,如果我想取出整个设备信息作为一个 JSON 对象:

SELECT JSON_QUERY(@json, '$') AS FullObject;

或者提取一个数组:

-- 假设 JSON 里有 "sensors": [{"type":"temp","value":25}, {"type":"humidity","value":60}]
SELECT JSON_QUERY(@json, '$.sensors') AS SensorArray;
注意:JSON_QUERY 的路径必须以 $ 开头,而且路径中不能包含通配符。如果你需要更复杂的查询,可以考虑用 OPENJSON。

OPENJSON:行集函数,把 JSON 变成表

OPENJSON 是我用得最多的 JSON 函数。它能把 JSON 数组或对象展开成一张表,每一行对应一个元素。这在 C 语言里处理起来特别方便,因为你可以直接用 SQL 查询结果集,逐行读取。

基本用法:

SELECT * FROM OPENJSON(@json, '$.sensors')
WITH (
  type VARCHAR(50) '$.type',
  value FLOAT '$.value'
);

这段代码会把 sensors 数组里的每个元素解析成一行,typevalue 作为列。你想想看,这比在 C 代码里手动遍历 JSON 数组要省多少事?

C 语言中调用 SQL Server JSON 函数

好了,理论说完了,咱们来点实战。在 C 语言里操作 SQL Server,通常用 ODBC 或者 SQL Server Native Client。我个人习惯用 ODBC,因为跨平台兼容性好一些。

下面是一个完整的示例,演示如何用 C 语言调用 OPENJSON 解析设备上报的 JSON 数据:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sql.h>
#include <sqlext.h>

void process_json_data(SQLHSTMT hstmt) {
    const char* json_input = 
        "[{\"deviceId\":\"SN-001\",\"temp\":36.5,\"ts\":\"2024-01-15T10:30:00\"},"
        "{\"deviceId\":\"SN-002\",\"temp\":37.2,\"ts\":\"2024-01-15T10:30:05\"}]";
    
    // 构建 SQL 语句,使用 OPENJSON 解析
    char sql[1024];
    snprintf(sql, sizeof(sql),
        "SELECT deviceId, temp, ts "
        "FROM OPENJSON(?) "
        "WITH ("
        "  deviceId VARCHAR(50) '$.deviceId',"
        "  temp FLOAT '$.temp',"
        "  ts DATETIME2 '$.ts'"
        ");"
    );
    
    // 绑定参数
    SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, 
                     SQL_C_CHAR, SQL_VARCHAR, 
                     strlen(json_input), 0, 
                     (SQLPOINTER)json_input, 0, NULL);
    
    // 执行查询
    SQLExecDirect(hstmt, (SQLCHAR*)sql, SQL_NTS);
    
    // 读取结果
    char deviceId[50];
    double temp;
    char ts[30];
    
    while (SQLFetch(hstmt) == SQL_SUCCESS) {
        SQLGetData(hstmt, 1, SQL_C_CHAR, deviceId, sizeof(deviceId), NULL);
        SQLGetData(hstmt, 2, SQL_C_DOUBLE, &temp, 0, NULL);
        SQLGetData(hstmt, 3, SQL_C_CHAR, ts, sizeof(ts), NULL);
        
        printf("设备: %s, 温度: %.1f, 时间: %s\n", deviceId, temp, ts);
    }
}

int main() {
    // 这里省略了 ODBC 连接和句柄初始化的代码
    // 实际项目中需要先建立数据库连接
    printf("JSON 数据处理完成\n");
    return 0;
}

核心思路:把 JSON 解析工作交给数据库,C 代码只负责传参和读结果。这样既利用了数据库的索引和优化能力,又减少了 C 代码的复杂度。

避坑指南

我曾经在一个项目中,用 JSON_VALUE 去提取一个嵌套对象,结果一直返回 NULL。查了半天才发现,那个字段其实是个 JSON 对象,不是标量。换成 JSON_QUERY 就好了。

还有一次,我用 OPENJSON 解析一个很大的 JSON 数组(几万条记录),结果查询超时了。后来加了 WITH 子句里的类型声明,性能提升了不少。嗯,这里要注意:OPENJSONWITH 子句里一定要显式指定数据类型,否则 SQL Server 会默认用 NVARCHAR(MAX),性能会差很多。

知识体系总览

下面这张图总结了 SQL Server JSON 支持的三个核心函数及其在 C 语言中的调用关系:

SQL Server JSON 函数与 C 语言调用关系 JSON 数据源 JSON_VALUE 提取标量值 JSON_QUERY 提取对象/数组 OPENJSON JSON → 行集 SQL 查询结果集(表格形式) C 语言(ODBC 读取结果集) 注:箭头表示数据流向,从 JSON 数据源到 C 语言处理

性能对比与选择建议

函数 适用场景 返回类型 性能特点
JSON_VALUE 提取单个字段值(字符串、数字) 标量(NVARCHAR、INT、FLOAT 等) 快,适合简单查询
JSON_QUERY 提取子对象或数组片段 NVARCHAR(MAX)(JSON 格式) 中等,适合嵌套结构
OPENJSON 将 JSON 数组展开为多行记录 行集(可配合 WITH 定义列) 适合批量处理,注意类型声明
我的建议:如果只是取一两个字段,用 JSON_VALUE 就够了。如果需要把整个 JSON 数组导入临时表做进一步处理,OPENJSON 是首选。JSON_QUERY 用得相对少一些,但在处理嵌套 JSON 时不可或缺。

好了,关于 SQL Server 的 JSON 支持就聊到这里。这几个函数配合 C 语言的 ODBC 接口,能让你在处理 JSON 数据时游刃有余。记住一点:能用数据库做的事,尽量别在 C 代码里做。数据库的优化器比你手写的解析代码要聪明得多。


公众号:蓝海资料掘金营,微信deep3321