我有一个 H2 数据库(版本 2.2.220),数据位于
JSON
列中;我需要通过 SQL 脚本迁移列,以便数据处于传统的关系数据库结构中。主要的障碍是在提取 JSONValue 字符串值(用 JSON 格式的双引号括起来)时,我正在努力将其转换为不包含周围双引号的 VARCHAR
值。
我希望下面的 SQL 语句能够以最基本的方式说明这个问题:
create table my_table (
id int generated by default as identity,
my_json JSON null default null,
);
insert into my_table (my_json) values (JSON '"abcdef"');
字符串常量内的双引号是必需的,否则它不会被识别为有效的 JSON。select my_json from my_table;
这将返回 "abcdef"
,字符串中包含引号。不过,我希望返回原始字符串值 abcdef
,而不带周围的引号。select CAST(my_json as VARCHAR(255)) from my_table;
这仍然返回 "abcdef"
,字符串中包含引号。select BTRIM(my_json, '"') from my_table;
这达到了预期的结果abcdef
,但是有问题。导致错误的一种方式是字符串值应故意在开头和/或结尾处包含双引号,例如xyz"
(JSON '"xyz\""'
) - 这将被修剪以返回 xyz\
。我想知道是否有一种“推荐”/更干净的方法来进行这种转换,并且不易出现错误。
恐怕在 H2 数据库中实现此目的最安全的方法是使用用户定义函数(UDF)。不幸的是,没有直接的方法可以使用 H2 的内置函数删除这些引号,因为 H2 数据库不允许直接查询单个 JSON 值。 H2 将 JSON 数据存储为纯字符串。因此,当您将
"abcdef"
保存到表中时(无论您使用 JSON '"abcdef"'
还是 'abcdef'::JSON
),查询 select my_json from my_table;
始终返回整个 JSON 对象(即 "abcdef"
),而不仅仅是值 () abcdef
),就像您查询 JSON 对象(例如 select JSON '{"foo":"bar"}';
)一样,没有本地方法可以从 bar
字段中提取 foo
值。
因此,如果您需要单独的值,我建议创建这样的 UDF:
DROP ALIAS IF EXISTS STRIP_JSON_QUOTES;
CREATE ALIAS STRIP_JSON_QUOTES AS $$
String strip_json_quotes(String jsonString) {
if (jsonString != null && !jsonString.isEmpty()) {
if (jsonString.startsWith("\"") && jsonString.endsWith("\"") && jsonString.length() > 1) {
return jsonString.substring(1, jsonString.length() - 1);
}
}
return jsonString;
}
$$;
然后你可以这样称呼它:
select strip_json_quotes(my_json) from my_table;
我不建议使用
BTRIM
,因为它会删除所有前导和尾随字符,而不仅仅是最外面的字符。例如,在极少数情况下,您想要存储带引号的值,BTRIM 会删除太多内容:
sql> select BTRIM('"abcdef"'::JSON, '"') as trimmed_json_value;
TRIMMED_JSON_VALUE
\"abcdef\
(1 row, 29 ms)