Redshift / Postgres:如何忽略生成错误的行? (json_extract_path_text中的JSON无效)

问题描述 投票:8回答:7

我正在尝试使用json_extract_path_text在redshift中运行查询。不幸的是,此数据库列中的某些JSON条目无效。

会发生什么:当查询遇到无效的JSON值时,它会因“JSON解析错误”而停止。

我想要的:忽略该列中包含无效JSON的所有行,但返回可以解析JSON的任何行。

为什么我不能让它做我想做的事情:我认为我不理解Redshift / Postgres中的错误处理。应该可以简单地跳过产生错误的任何行,但我尝试输入EXEC SQL WHENEVER SQLERROR CONTINUE(基于the Postgres docs)并在SQLERROR或附近得到“语法错误”。

postgresql amazon-redshift
7个回答
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 

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
投票

我假设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使用的那个。


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.
    ]
}

因此,拥有包含其他字段的“顶级”元素会很方便,例如:(因此,$['data']就是您记录中的所有内容)

{
    "data": {
        "id": 1
        ...
    }
}
{
    "data": {
        "id": 2
        ...
    }
}

如果您无法更改源格式,Redshift的UNLOAD将有助于:

UNLOAD ('select_statement')
TO 's3://object_path_prefix'

很容易使用select_statement连接:{ "data" : +旧字符串+ } ...

然后,Redshift再次摇滚!


1
投票

Redshift缺少许多Postgres功能,例如错误处理。

我处理这个的方式是:

  1. 使用CREATE TABLE AS创建一个带有JSON字段的“fixup”表,以及您尝试查询的主表上的密钥。确保将DISTKEY和SORTKEY设置为JSON字段。
  2. 在我的fixup表中添加两列:valid_json(BOOLEAN)和extract_test(VARCHAR)
  3. 尝试使用JSON_EXTRACT_PATH_TEXT使用JSON字段中的一些文本更新extract_test。
  4. 使用其中的错误来发现搞砸JSON的常见字符。如果我从网络日志数据导入,我可能会发现????或类似的东西
  5. 对于具有该值的JSON字段,请使用UPDATE表SET valid_json = false
  6. 最后,使用UPDATE c SET json_field = NULL FROM fixup_table f WHERE original_table.id = f.id AND f.valid_json = FALSE更改原始表中的json字段

它仍然是手动的,但比在大桌子上逐行修复快得多,并且通过在修正表上使用正确的DISTKEY / SORTKEY,您可以快速运行查询。


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


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

© www.soinside.com 2019 - 2024. All rights reserved.