web-dev-qa-db-fra.com

Comment détecter si une clé est présente dans un objet JSON à l'aide de TSQL?

Celui-ci est intéressant.

Imaginez que j'ai un objet JSON contenant quatre paires clé-valeur où les clés sont a, b, c et d.

Imaginez que cet objet est passé à une procédure stockée et que chaque clé est facultative (tant qu'il y a au moins 1 clé dans l'objet). Autrement dit, il est valide de passer '{"a": "1"}' même si b, c et d ont été exclus.

La question est:

Comment puis-je effectuer une vérification, dans la procédure stockée, pour savoir si une clé particulière a été transmise?

Si j'essaie d'accéder à une clé à l'aide de json_value, et la clé est absente, puis en mode lax, je reçois une valeur null; en mode strict, je reçois une erreur. Je suppose que c'est une façon de faire la différence: référencer la clé en mode strict et intercepter les erreurs - mais je veux éviter d'utiliser la logique try-catch.

Voici quelques exemples d'appels:

declare @data nvarchar(max); set @data = N'{"a":"1","b":"","c":null}';
select [value] from openjson(@data);
select 'strict a', json_value(@data, 'strict $.a') union
select 'strict b', json_value(@data, 'strict $.b') union
select 'strict c', json_value(@data, 'strict $.c') union
-- select 'strict d', json_value(@data, 'strict $.d') union
select 'lax a', json_value(@data, 'lax $.a') union
select 'lax b', json_value(@data, 'lax $.b') union
select 'lax c', json_value(@data, 'lax $.c') union
select 'lax d', json_value(@data, 'lax $.d');

Ceci montre:

  • Une valeur de chaîne non nulle est toujours renvoyée "en l'état" (clé 'a')
  • Une chaîne vide est toujours retournée "en l'état" (clé 'b')
  • Un null est toujours retourné "tel quel" (clé 'c')
  • Une clé absente est retournée comme nulle en mode laxiste. Si vous décommentez la ligne appropriée, la tentative de référence à une clé absente en mode strict renvoie une erreur.

En d'autres termes, je ne peux pas distinguer entre une clé absente et une clé qui a été fournie avec une valeur nulle.

Pourquoi est-ce que je veux distinguer la différence entre null étant passé et une clé absente? Si la clé est présente, sa valeur doit être persistante (qu'elle soit nulle ou non). Je ne veux pas persister null en cas d'absence de la clé. Si la clé est absente, je veux simplement ne rien faire concernant cette clé (ou la colonne où sa valeur serait conservée).

Mon approche suivante était de me demander si je pouvais compter les lignes correspondantes - peut-être plutôt qu'une erreur, la fonction de comptage retournerait "0" comme le nombre de lignes correspondant à la clé "d".

C'est là que ça devient intéressant.

declare @data nvarchar(max); set @data = N'{"a":"1","b":"","c":null}';

with CTE as (select json_value(@data, 'strict $.a') as strictA)
select count(1) from CTE;

with CTE2 as (select json_value(@data, 'strict $.d') as strictD)
select count(1) from CTE2;

J'espérais voir les valeurs 1 et 0. Je pensais que ce serait ça, ou que ça jetterait une erreur. Le résultat réel est que les deux requêtes renvoient un nombre de 1.

Pourquoi la deuxième requête renvoie-t-elle "1"?

Et dois-je recourir à la logique try-catch pour détecter si une clé donnée existe dans un fragment JSON?

2

Essayez d'utiliser OPENJSON à la place. Cela renvoie une colonne Type qui indique une valeur NULL pour une clé. Vous pouvez LEFT JOIN à une liste source de clés possibles et vérifier une valeur de type NULL ou une valeur de retour NULL pour déterminer si la clé est présente ou non. Exemple ci-dessous:

declare @data nvarchar(max); set @data = N'{"a":"1","b":"","c":null}';

SELECT [Value],
    CASE 
        WHEN [type] IS NULL THEN 'Not Present'
        WHEN [type] = 0 THEN 'Null Value'
        ELSE 'Non-Null Value'
    END AS [KeyPresent]
FROM
(
    SELECT 'a' AS [key]
    UNION
    SELECT 'b' AS [key]
    UNION
    SELECT 'c' AS [key]
    UNION
    SELECT 'd' AS [key]
) keys
LEFT JOIN OPENJSON(@data) jdata ON keys.[key] = jdata.[key]
2
HandyD

... En fait, juste quelques minutes de plus de travail et j'ai trouvé une réponse, à savoir:

declare @data nvarchar(max); set @data = N'{"a":"1","b":"","c":null}';

select 'checking a ', case when 'a' in (select [key] from openjson(@data)) then 'present' else 'absent' end union
select 'checking d', case when 'd' in (select [key] from openjson(@data)) then 'present' else 'absent' end;

Sélectionnez simplement toutes les clés et voyez si votre clé souhaitée se trouve dans l'ensemble de résultats.

1