我尝试在redshift中运行查询,我使用json_extract_path_text
进行选择。不幸的是,此数据库列中的某些JSON条目无效。
会发生什么: 当查询遇到无效的JSON值时,它会以" JSON解析错误停止"。
我想要的:忽略该列中包含无效JSON的所有行,但返回可以解析JSON的所有行。
为什么我无法做到我想做的事情:我不认为我理解Redshift / Postgres中的错误处理。应该可以简单地跳过任何产生错误的行,但我尝试输入EXEC SQL WHENEVER SQLERROR CONTINUE
(基于the Postgres docs)并在SQLERROR
附近得到"语法错误&# 34。
答案 0 :(得分:10)
创建一个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
答案 1 :(得分:4)
修改:好像是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
答案 2 :(得分:2)
我假设JSON数据实际上存储在TEXT列而不是JSON列中(否则您将无法在那里存储非JSON)。
如果数据存在一些模式,允许您创建一个检测有效行的正则表达式,或者无效的行,那么您可以使用CASE语句。例如:
SELECT CASE
WHEN mycol !~ 'not_json' THEN json_extract_path_text(mycol, ....)
ELSE NULL
END AS mystuff
...
将not_json替换为检测非JSON格式值的正则表达式。
根据您的数据格式,这可能也可能不实用。
根据this question上的答案,显然可以使用一些正则表达式实现来完全验证任意JSON数据,但不是postgresql使用的那个。
答案 3 :(得分:2)
更新: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.
]
}
因此,拥有一个顶级&#34;将会很方便。包含其他字段的元素,如下所示:(因此,$['data']
是您记录中的所有内容)
{
"data": {
"id": 1
...
}
}
{
"data": {
"id": 2
...
}
}
如果您无法更改源格式,Redshift的UNLOAD
将有所帮助:
UNLOAD ('select_statement')
TO 's3://object_path_prefix'
使用select_statement
进行连接很容易:{ "data" :
+旧字符串+ }
...
然后,Redshift再次摇滚!
答案 4 :(得分:1)
Redshift缺少许多Postgres功能,例如错误处理。
我处理这个的方式是:
使用CREATE TABLE AS
创建一个带有JSON字段的'fixup'表,以及您尝试查询的主表上的密钥。确保将DISTKEY和SORTKEY设置为JSON字段。
在我的修正表中添加两列:valid_json(BOOLEAN)和extract_test(VARCHAR)
尝试使用JSON_EXTRACT_PATH_TEXT
使用JSON字段中的某些文本更新extract_test。
使用其中的错误来发现破坏JSON的常见字符。如果我从网络日志数据导入,我可能会发现????或类似的东西
对具有该值的JSON字段使用UPDATE表SET valid_json = false
最后,使用UPDATE c SET json_field = NULL FROM fixup_table f WHERE original_table.id = f.id AND f.valid_json = FALSE
它仍然是手动的,但比在大桌子上逐行修复快得多,并且通过在修正表上使用正确的DISTKEY / SORTKEY,您可以快速运行查询。
答案 5 :(得分:0)
您可以使用以下功能:
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
答案 6 :(得分:0)
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