Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Como selecionar JSON aninhado no SQL Server com OPENJSON


Se você estiver usando OPENJSON() , mas você está tentando lembrar como selecionar um fragmento interno do documento JSON, continue lendo.

O OPENJSON() A sintaxe permite converter documentos JSON em uma visualização tabular. Também permite selecionar um fragmento JSON aninhado do documento JSON.

A maneira de fazer isso é com caminhos .

Caminhos


Um caminho consiste no seguinte:
  • Um cifrão ($ ), que representa o item de contexto.
  • Um conjunto de etapas do caminho. As etapas do caminho podem conter os seguintes elementos e operadores:
    • Nomes de chave. Por exemplo, $.pets e $.pets.dogs . Se o nome da chave começar com um cifrão ou contiver caracteres especiais, como espaços, ele deverá estar entre aspas (por exemplo, $."my pets" ).
    • Elementos de matriz. Por exemplo, $.pets.dogs[1] . Os índices de matriz são baseados em zero, portanto, este exemplo seleciona o segundo elemento na matriz.
    • O operador ponto (. ) indica um membro de um objeto. Por exemplo, em $.pets.dogs , dogs é membro de pets .

Exemplo básico


Aqui está um exemplo simples para demonstrar.
DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, '$.pets.dogs')
WITH  (
        [id]    int,  
        [name]  varchar(60), 
        [sex]   varchar(6)
    );

Resultado:
+------+--------+--------+
| id   | name   | sex    |
|------+--------+--------|
| 1    | Fetch  | Male   |
| 2    | Fluffy | Male   |
| 3    | Wag    | Female |
+------+--------+--------+

Neste caso, o segundo argumento para OPENJSON() é '$.pets.dogs' , o que significa que estamos selecionando o valor de dogs key, que é filha de pets .

O cifrão ($ ) representa o item de contexto.

Observe que neste exemplo, eu também uso o WITH cláusula para definir o esquema. Se eu não incluísse isso, o esquema padrão seria usado.

Veja como fica usando o esquema padrão.
DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, '$.pets.dogs');

Resultado:
+-------+-------------------------------------------------+--------+
| key   | value                                           | type   |
|-------+-------------------------------------------------+--------|
| 0     | { "id" : 1, "name" : "Fetch", "sex" : "Male" }  | 5      |
| 1     | { "id" : 2, "name" : "Fluffy", "sex" : "Male" } | 5      |
| 2     | { "id" : 3, "name" : "Wag", "sex" : "Female" }  | 5      |
+-------+-------------------------------------------------+--------+

Então ainda estamos selecionando o mesmo JSON aninhado, só que estamos usando um esquema diferente.

O esquema padrão sempre retorna três colunas; chave , valor e tipo .

Selecionando elementos da matriz


Como mencionado, você pode usar a notação de colchetes para selecionar um elemento específico em uma matriz.

Aqui está um exemplo.
DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, '$.pets.dogs[0]')
WITH  (
        [id]    int,  
        [name]  varchar(60), 
        [sex]   varchar(6)
    );

Resultado:
+------+--------+-------+
| id   | name   | sex   |
|------+--------+-------|
| 1    | Fetch  | Male  |
+------+--------+-------+

Visto que os índices de matriz são baseados em zero, especificando um valor de 0 retorna o primeiro elemento na matriz.

Veja como este exemplo se parece ao usar o esquema padrão (ou seja, sem o WITH cláusula).
DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, '$.pets.dogs[0]');

Resultado:
+-------+---------+--------+
| key   | value   | type   |
|-------+---------+--------|
| id    | 1       | 2      |
| name  | Fetch   | 1      |
| sex   | Male    | 1      |
+-------+---------+--------+

Modo de caminho


Ao usar caminhos, você tem a opção de declarar o modo de caminho.

O modo de caminho determina o que acontece quando uma expressão de caminho contém um erro.

O modo de caminho pode ser lax ou strict .
  • Em lax mode, a função retorna valores vazios se o caminho não puder ser encontrado. Por exemplo, se você solicitar o valor $.pets.cows , mas o JSON não contém essa chave, a função retorna null, mas não gera um erro.
  • Em strict modo, a função gera um erro se o caminho não puder ser encontrado.

O modo de caminho padrão é lax , então se você não declarar, lax é usado.

Exemplo


Aqui está um exemplo para demonstrar como cada modo de caminho lida com caminhos ausentes.

Modo relaxado

DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, 'lax $.pets.cows');

Resultado:
(0 rows affected)

Portanto, o modo lax não gerou nenhum erro. Simplesmente resultou em zero linhas sendo afetadas.

Se especificarmos nosso próprio esquema e selecionarmos o subobjeto correto, mas usarmos um caminho ausente para mapear para um nome de coluna, ele retornará NULL nessa coluna.
DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}'

SELECT *
FROM OPENJSON(@json, 'lax $.pets.dogs')
WITH  (
        [id]    int         'lax $.id',  
        [name]  varchar(60) 'lax $.name', 
        [color]   varchar(6)  'lax $.color'
    );

Resultado:
+------+--------+---------+
| id   | name   | color   |
|------+--------+---------|
| 1    | Fetch  | NULL    |
| 2    | Fluffy | NULL    |
| 3    | Wag    | NULL    |
+------+--------+---------+

Modo Estrito


Aqui está o que acontece quando usamos o modo estrito.
DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, 'strict $.pets.cows');

Resultado:
Msg 13608, Level 16, State 3, Line 16
Property cannot be found on the specified JSON path.

Como esperado, resultou em um erro.

O mesmo erro ocorre quando selecionamos a chave JSON correta, mas mapeamos uma coluna para uma chave inexistente.
DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, 'strict $.pets.dogs')
WITH  (
        [id]    int         'strict $.id',  
        [name]  varchar(60) 'strict $.name', 
        [color]   varchar(6)  'strict $.color'
    );

Resultado:
Msg 13608, Level 16, State 6, Line 16
Property cannot be found on the specified JSON path.

Caminhos duplicados


Se seu documento JSON contiver caminhos duplicados no mesmo nível de aninhamento, OPENJSON() pode devolver todos.

Isso contrasta com JSON_VALUE() e JSON_QUERY() , ambos retornando apenas o primeiro valor que corresponde ao caminho.

Aqui está um exemplo de uso de OPENJSON() para retornar caminhos duplicados.
DECLARE @json NVARCHAR(4000) = N'{
    "dog": {
            "names": {
                "name": "Fetch", 
                "name": "Good Dog"
            }
        }
    }';
SELECT * FROM OPENJSON(@json, '$.dog.names');

Resultado:
+-------+----------+--------+
| key   | value    | type   |
|-------+----------+--------|
| name  | Fetch    | 1      |
| name  | Good Dog | 1      |
+-------+----------+--------+

Subobjetos aninhados


Ao definir seu próprio esquema, você pode usar o AS JSON opção para retornar um subobjeto inteiro como seu próprio documento JSON.
DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, '$.pets')
WITH  (
        [dogs]  nvarchar(max) '$.dogs' AS JSON
    );

Resultado:
+--------+
| dogs   |
|--------|
| [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]        |
+--------+

Se não tivéssemos usado o AS JSON opção, teríamos recebido um erro ou NULL, dependendo se tivéssemos especificado lax ou strict modo.

Aqui está em cada modo ao omitir o AS JSON opção.

Modo relaxado

DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, '$.pets')
WITH  (
        [dogs]  nvarchar(max) 'lax $.dogs'
    );

Resultado:
+--------+
| dogs   |
|--------|
| NULL   |
+--------+

Modo estrito

DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, '$.pets')
WITH  (
        [dogs]  nvarchar(max) 'strict $.dogs'
    );

Resultado:
Msg 13624, Level 16, State 1, Line 16
Object or array cannot be found in the specified JSON path.