我正在尝试检查在PL / pgSQL函数中作为参数发送的JSON中是否存在密钥。
这是功能。
CREATE FUNCTION sp_update_user(user_info json) RETURNS json
LANGUAGE plpgsql
AS $$
BEGIN
PERFORM user_info->>'lastname';
IF FOUND
THEN
UPDATE users SET lastname = user_info->>'lastname' WHERE id = sp_update_user.user_id;
END IF;
PERFORM user_info->>'firstname';
IF FOUND
THEN
UPDATE users SET firstname = user_info->>'firstname' WHERE id = sp_update_user.user_id;
END IF;
RETURN row_to_json(row) FROM (SELECT true AS success) row;
END;$$;
我尝试使用PERFORM
- 子句,但即使json键不存在,也会执行IF FOUND
子句中的语句。
我尝试了PERFORM user_info->>'firstname' INTO myvar;
以检查变量内容,但却触发了错误ERROR: query "SELECT user_info->>'firstname' INTO myvar" is not a SELECT
。
如何检查带有PL / pgSQL的JSON中是否存在json密钥?
答案 0 :(得分:4)
您已经发现可以将表达式user_info->>'username'
测试为NULL。但是你的功能仍然非常低效。并且仍有含糊不清。
为多列重复更新行是很昂贵的。 Postgres为每次更新都写了一个新的行版本。如果可能,请使用单 UPDATE
:
CREATE OR REPLACE FUNCTION sp_update_user(_user_id int, _user_info json)
RETURNS json AS
$func$
BEGIN
UPDATE users u
SET firstname = COALESCE(_user_info->>'firstname', u.firstname)
, lastname = COALESCE(_user_info->>'lastname' , u.lastname)
WHERE id = sp_update_user._user_id
AND ((_user_info->>'firstname') IS NOT NULL OR
(_user_info->>'lastname') IS NOT NULL);
IF FOUND THEN
RETURN '{"success":true}'::json;
ELSE
RETURN '{"success":false}'::json;
END IF;
END
$func$ LANGUAGE plpgsql;
呼叫:
SELECT sp_update_user(123, '{"firstname": "jon", "lastname": "doe"}')
这对于多列来说要快得多,因为只执行了一个UPDATE
(最多)。如果WHERE
子句未评估为true
,则根本不会进行更新,结果会得到'{"success":false}'
。
如果有时表中的值已经是他们要更改的值,则可以进行另一次优化。考虑这个相关答案的最后一段:
原始版本中缺少变量/参数user_id
。
还有一个角落案例歧义。如果元素存在且设置为 JSON null
,则还会得到SQL NULL
作为结果。考虑:
SELECT ('{"b": null}'::json->>'b') IS NULL AS b_is_null
, ('{"c": 2}'::json->>'b') IS NULL AS b_missing;
不确定为什么使用数据类型json
作为返回类型,我只是保留了它。但是如果该功能没有更新,您无法确定为什么会得到false
。可能没有给定id
的行,键名'firstname'
和'lastname'
可能会丢失 - 或者是null
...
Postgres 9.4 中有一个干净和简单的解决方案 jsonb
和?
"existence" operator - 这可以甚至使用较大表的索引(与函数无关):
SELECT ('{"b": null}'::jsonb ? 'b') AS b_is_null
, ('{"c": 2}'::jsonb ? 'b') AS b_missing;
?|
and ?&
variants一次检查多个密钥。
所以我们可以实现:
CREATE OR REPLACE FUNCTION sp_update_user(_user_id int, _user_info jsonb)
RETURNS jsonb AS
$func$
BEGIN
UPDATE users u
SET firstname = CASE WHEN _user_info ? 'firstname' THEN _user_info->>'firstname' ELSE u.firstname END
, lastname = CASE WHEN _user_info ? 'lastname' THEN _user_info->>'lastname' ELSE u.lastname END
WHERE id = sp_update_user._user_id
AND _user_info ?| '{firstname,lastname}';
IF FOUND THEN
RETURN '{"success":true}'::jsonb;
ELSE
RETURN '{"success":false}'::jsonb;
END IF;
END
$func$ LANGUAGE plpgsql;
这些调用现在按预期工作:
SELECT sp_update_user(123, '{"firstname": null, "lastname": "doe1"}'::jsonb);
SELECT sp_update_user(123, '{"firstname": "doris"}'::jsonb);
答案 1 :(得分:2)
“json_object_keys”功能可以解决。
您可以将使用此函数的查询结果选择为声明的变量,然后检查您的变量是否为null。这是一个例子:
DECLARE
test character varying;
BEGIN
SELECT a INTO test FROM json_object_keys(user_info) a WHERE a = 'lastname';
IF (test IS NOT NULL) THEN
--SUCESSS
ELSE
--FAIL
END IF;
END;
答案 2 :(得分:0)
找到一个更简单的解决方案,检查json密钥是否有值。
IF (user_info->>'username') IS NOT NULL
THEN
RAISE NOTICE 'username';
UPDATE users SET username = user_info->>'username' WHERE id = sp_update_user.user_id;
END IF;
这将检查json中的密钥“username”是否具有NULL以外的卷。
答案 3 :(得分:0)
检查JSON密钥是否存在的另一种方法是使用json_extract_path()
。以下是一个使用示例:
SELECT permit_id FROM building_permits WHERE
json_extract_path(containing_boundaries, 'neighborhood') IS NULL
这将在您的JSON对象(“containing_boundaries”)中查找名为“neighborhood”的属性。如果该属性不存在,则返回NULL。