我正在尝试使用json_extract_path_text
在redshift中运行查询。不幸的是,此数据库列中的某些JSON条目无效。
会发生什么:当查询遇到无效的JSON值时,它会因“JSON解析错误”而停止。
我想要的:忽略该列中包含无效JSON的所有行,但返回可以解析JSON的任何行。
为什么我不能让它做我想做的事情:我认为我不理解Redshift / Postgres中的错误处理。应该可以简单地跳过产生错误的任何行,但我尝试输入EXEC SQL WHENEVER SQLERROR CONTINUE
(基于the Postgres docs)并在SQLERROR
或附近得到“语法错误”。
创建一个python UDF:
create or replace function f_json_ok(js varchar(65535))
returns boolean
immutable
as $$
if js is None:
return None
import json
try:
json.loads(js)
return True
except:
return False
$$ language plpythonu
像这样使用它:
select *
from schema.table
where 'DesiredValue' =
case
when f_json_ok(json_column) then json_extract_path_text(json_column, 'Key')
else 'nope'
end
编辑:看起来像Redshift only supports Python UDFs所以这个答案是行不通的。我将把这个答案留给后人(如果有人发现这个人没有使用Redshift)。
可能相关:这是一个plpgsql函数,它将尝试解码JSON并在失败时返回默认值:
CREATE OR REPLACE FUNCTION safe_json(i text, fallback json) RETURNS json AS $$
BEGIN
RETURN i::json;
EXCEPTION
WHEN others THEN
RETURN fallback;
END;
$$ LANGUAGE plpgsql IMMUTABLE RETURNS NULL ON NULL INPUT;
然后你可以像这样使用它:
SELECT
…
FROM (
SELECT safe_json(my_text, '{"error": "invalid JSON"}'::json) AS my_json
FROM my_table
) as x
保证您始终拥有有效的JSON
我假设JSON数据实际上存储在TEXT列而不是JSON列中(否则你将无法在那里存储非JSON)。
如果数据的某些模式允许您创建检测有效行的正则表达式或无效行,那么您可以使用CASE语句。例如:
SELECT CASE
WHEN mycol !~ 'not_json' THEN json_extract_path_text(mycol, ....)
ELSE NULL
END AS mystuff
...
用一个检测非JSON格式值的正则表达式替换not_json。
根据您的数据格式,这可能是也可能不实用。
根据this question上的答案,显然可以使用一些正则表达式实现完全验证任意JSON数据,但不是postgresql使用的那个。
更新:UDF解决方案似乎很完美。在我写这篇文章时,那个答案并不存在。这只是一些围绕方法的工作。
虽然json_extract_path_text
不能忽视错误,但Redshift的COPY
有一个MAXERROR
参数。
所以,你可以使用这样的东西:
COPY raw_json FROM 's3://data-source'
CREDENTIALS 'aws_access_key_id;aws_secret_access_key'
JSON 's3://json_path.json'
MAXERROR 1000;
下一个陷阱是在json_path.json
文件中:你不能使用$
来指定根元素:
{
"jsonpaths": [
"$['_id']",
"$['type']",
"$" <--------------- this will fail.
]
}
因此,拥有包含其他字段的“顶级”元素会很方便,例如:(因此,$['data']
就是您记录中的所有内容)
{
"data": {
"id": 1
...
}
}
{
"data": {
"id": 2
...
}
}
如果您无法更改源格式,Redshift的UNLOAD
将有助于:
UNLOAD ('select_statement')
TO 's3://object_path_prefix'
很容易使用select_statement
连接:{ "data" :
+旧字符串+ }
...
然后,Redshift再次摇滚!
Redshift缺少许多Postgres功能,例如错误处理。
我处理这个的方式是:
CREATE TABLE AS
创建一个带有JSON字段的“fixup”表,以及您尝试查询的主表上的密钥。确保将DISTKEY和SORTKEY设置为JSON字段。JSON_EXTRACT_PATH_TEXT
使用JSON字段中的一些文本更新extract_test。UPDATE c SET json_field = NULL FROM fixup_table f WHERE original_table.id = f.id AND f.valid_json = FALSE
更改原始表中的json字段它仍然是手动的,但比在大桌子上逐行修复快得多,并且通过在修正表上使用正确的DISTKEY / SORTKEY,您可以快速运行查询。
您可以使用以下功能:
CREATE OR REPLACE FUNCTION isValidJSONv2(i varchar(MAX)) RETURNS int stable AS $CODE$
import json
import sys
try:
if i is None:
return 0
json_object = json.loads(i)
return 1
except:
return 0
$CODE$ language plpythonu;
问题仍然是如果你仍然在select中使用json解析函数,仍然会抛出错误。您必须从不同表中的无效jsons中过滤有效值。我在这里发布了这个问题:https://forums.aws.amazon.com/thread.jspa?threadID=232468
Redshift现在支持传递一个布尔参数,允许您将无效的JSON视为null
select json_extract_path_text('invalid', 'path', true)
返回null
https://docs.aws.amazon.com/redshift/latest/dg/JSON_EXTRACT_PATH_TEXT.html