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

时间:2014-08-14 21:14:22

标签: postgresql amazon-redshift

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

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

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

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

7 个答案:

答案 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的COPYMAXERROR参数。

所以,你可以改用这样的东西:

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功能,例如错误处理。

我处理这个的方式是:

  1. 使用CREATE TABLE AS创建一个带有JSON字段的'fixup'表,以及您尝试查询的主表上的密钥。确保将DISTKEY和SORTKEY设置为JSON字段。

  2. 在我的修正表中添加两列: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

  7. 更改原始表格中的json字段

    它仍然是手动的,但比在大桌子上逐行修复快得多,并且通过在修正表上使用正确的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