SQL 服务器:JSON 可以格式化分组吗?

SQL Server : JSON formatting grouping possible?

我有一个 SQL 服务器 table 的形式:

192.168.1.1, 80 , tcp
192.168.1.1, 443, tcp
...

我正在将其导出到 json,如下所示:

SELECT 
    hostIp AS 'host.ip',
    'ports' = (SELECT port AS 'port', protocol AS 'protocol'
               FOR JSON PATH)
FROM 
    test
FOR JSON PATH

此查询结果:

[
    {
        "host": {
            "ip": "192.168.1.1"
        },
        "ports": [
            {
                "port": 80,
                "protocol": "tcp"
            }
        ]
    },
    {
        "host": {
            "ip": "192.168.1.1"
        },
        "ports": [
            {
                "port": 443,
                "protocol": "tcp"
            }
        ]
    },
....

但是我希望来自单个 IP 的所有数据都这样分组:

[
    {
        "host": {
            "ip": "192.168.1.1"
        },
        "ports": [
            {
                "port": 80,
                "protocol": "tcp"
            },
            {
                "port": 443,
                "protocol": "tcp"
            }
        ]
    },
...

我发现似乎有聚合函数,但它们要么对我不起作用,要么对 Postgresql 不起作用,我的示例在 SQL Server.

知道如何让它工作吗?

唯一简单的方法是 self-join

SELECT 
    tOuter.hostIp AS [host.ip]
    ,ports = (
            SELECT
                tInner.port
               ,tInner.protocol
            FROM test tInner
            FOR JSON PATH
         )
FROM test tOuter
GROUP BY
  tOuter.hostIp
FOR JSON PATH;

显然self-join效率低下,但是SQL服务器不支持JSON_AGG。你可以用 STRING_AGG 模拟它,不用 self-join

SELECT 
    t.hostIp AS [host.ip]
    ,ports = JSON_QUERY('[' + STRING_AGG(tInner.json, ',') + ']')
FROM test t
CROSS APPLY (
    SELECT
        t.port
       ,t.protocol
    FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
) tInner(json)
GROUP BY
  t.hostIp
FOR JSON PATH;

db<>fiddle

正如您想象的那样,随着涉及的嵌套级别越来越多,它会变得越来越复杂。

Note:

  • Do not use ' for quoting column names, only []
  • There is no need to alias a column for JSON if it is already that name