尝试解析PipelineDB中的数据类型并将流错误传输到失败的文本表?

时间:2019-05-15 18:08:09

标签: postgresql pipelinedb

我们正在使用Pipeline DB将数据接收到流表中,并且在两个流视图中,在一个视图中,筛选出将失败的类型转换validataion错误的记录,在另一个视图中,筛选在失败的类型转换中的记录。 。理想情况下,我们正在尝试将不良记录与不良记录分开,并使其变为两个最终表。

例如,系统配置为从第三方接收数据,格式为YYYY / MM / DD HH24:MI:SS,但由于某些原因,显示了日期和月份翻转的值。在PipelineDB中,由于使用“ PostGres SQL”“ to_timestamp(mycolumn,'YYYY / MM / DD HH24:MI:SS')”,如果“ mycolumn”中的文本类似于“ 2019/15/05 13”,则将引发硬错误: 10:24'。并在该事务中输入流中的所有记录都会回滚。 (因此,如果使用了PG复制,一条记录在实现流式传输视图时失败,将导致零记录被全部插入。这在数据自动化中不是理想的情况,在这种情况下,第三方自动化系统可能不太在乎我们要处理的问题它的数据。)

从我所看到的: -PostGres没有执行“ try-parse”的“本机SQL”方法 -PipelineDB不支持用户定义的函数(如果我们编写的函数具有两个输出,一个输出用于解析值,另一个输出返回布尔值“ is_valid”列)。 (我的假设是该函数驻留在服务器上,pipelinedb作为外部服务器执行,这是完全不同的事情。)

理想情况下,函数返回类型转换值和布尔标志(如果有效),并且可以在流视图的WHERE子句中使用该函数从不良记录中分叉良好记录。但是我似乎无法解决这个问题?有什么想法吗?

1 个答案:

答案 0 :(得分:0)

经过很多时间,我找到了解决该问题的方法。我不喜欢它,但是它可以工作。

在意识到整个问题后,我的曙光就来了:

http://docs.pipelinedb.com/continuous-transforms.html “您可以将连续转换视为传入流数据之上的触发器,其中连续转换输出的每个新行均执行触发功能。内部该功能被执行为每行的AFTER INSERT行和新行包含通过连续变换输出的行。”

我花了几个小时试图弄清楚:“为什么我的自定义函数不能为传入的数据流编写为“ try-parse”数据类型,所以什么都不会在实现视图或输出表中显示? PipelineDB引发了严重的错误吗?然后几个小时后,我意识到问题与以下事实有关:PipelineDB无法处理用户定义的函数,而是在连续转换中发生了以SQL表示的转换因此,从根本上讲,在实现流中更改数据字段的类型转换在开始之前是失败的。

解决方案(不是很优雅)是: 1-将类型转换逻辑或任何可能导致错误的SQL逻辑移到触发函数中 2-在触发函数内创建一个“当其他时候会例外” 3-确保RETURN NEW;成功转换和失败转换都会发生。 4-进行连续转换就像不使用逻辑的传递一样,仅调用触发器。 (在这种情况下,它确实在某种程度上抵消了使用PipelineDB解决此初始数据分段问题的全部要点。但是,我离题了。)

这样,我创建了一个表来捕获错误,并通过确保上面列出的所有3个步骤都得以实现,然后确保交易将成功进行。

这很重要,因为如果不这样做,并且“您在异常中得到异常”,或者您没有优雅地处理异常,那么将不会加载任何记录。

这支持以下策略:我们只是试图进行“河流中的叉子”数据处理,以路由以一种方式成功转换为一个表(或流式表)的记录,以及将未能转换为错误表的记录进行路由

下面我显示了一个POC,在该POC中,我们将记录作为流处理并将其具体化为物理表。 (它也可能是另一个流)。关键在于实现:

错误表使用了文本列 触发功能捕获尝试进行的转换中的错误,并将错误的基本描述从系统中回写到错误表中。

我提到我不喜欢该解决方案,但这是我在几个小时内能找到的最好的解决方案,可以克服PipelineDB作为触发器在插入后做事的局限性,因此插入失败不会被捕获,并且pipelinedb没有内置的内在功能来处理: -失败时继续处理事务中的流 -在行级正常失败,并提供了一种更简单的机制来将失败的转换路由到错误表

DROP SCHEMA IF EXISTS pdb CASCADE;
CREATE SCHEMA IF NOT EXISTS pdb;


DROP TABLE IF EXISTS pdb.lis_final;
CREATE TABLE pdb.lis_final(
    edm___row_id bigint,
    edm___created_dtz timestamp with time zone DEFAULT current_timestamp,
    edm___updatedat_dtz timestamp with time zone DEFAULT current_timestamp,
    patient_id text,
    encounter_id text,
    order_id text,
    sample_id text,
    container_id text,
    result_id text,
    orderrequestcode text,
    orderrequestname text,
    testresultcode text,
    testresultname text,
    testresultcost text,
    testordered_dt timestamp,
    samplereceived_dt timestamp,
    testperformed_dt timestamp,
    testresultsreleased_dt timestamp,
    extractedfromsourceat_dt timestamp,
    birthdate_d date
);

DROP TABLE IF EXISTS pdb.lis_errors;
CREATE TABLE pdb.lis_errors(
    edm___row_id bigint,
    edm___errorat_dtz timestamp with time zone default current_timestamp,
    edm___errormsg text,
    patient_id text,
    encounter_id text,
    order_id text,
    sample_id text,
    container_id text,
    result_id text,
    orderrequestcode text,
    orderrequestname text,
    testresultcode text,
    testresultname text,
    testresultcost text,
    testordered_dt text,
    samplereceived_dt text,
    testperformed_dt text,
    testresultsreleased_dt text,
    extractedfromsourceat_dt text,
    birthdate_d text
);


DROP FOREIGN TABLE IF EXISTS pdb.lis_streaming_table CASCADE;
CREATE FOREIGN TABLE pdb.lis_streaming_table (
    edm___row_id serial,
    patient_id text,
    encounter_id text,
    order_id text,
    sample_id text,
    container_id text,
    result_id text,
    orderrequestcode text,
    orderrequestname text,
    testresultcode text,
    testresultname text,
    testresultcost text,
    testordered_dt text,
    samplereceived_dt text,
    testperformed_dt text,
    testresultsreleased_dt text,
    extractedfromsourceat_dt text,
    birthdate_d text
)
SERVER pipelinedb;


CREATE OR REPLACE FUNCTION insert_into_t()
  RETURNS trigger AS
  $$
  BEGIN

    INSERT INTO pdb.lis_final
    SELECT
        NEW.edm___row_id,
        current_timestamp as edm___created_dtz,
        current_timestamp as edm___updatedat_dtz,
        NEW.patient_id,
        NEW.encounter_id,
        NEW.order_id,
        NEW.sample_id,
        NEW.container_id,
        NEW.result_id,
        NEW.orderrequestcode,
        NEW.orderrequestname,
        NEW.testresultcode,
        NEW.testresultname,
        NEW.testresultcost,
        to_timestamp(NEW.testordered_dt,'YYYY/MM/DD HH24:MI:SS') as testordered_dt,
        to_timestamp(NEW.samplereceived_dt,'YYYY/MM/DD HH24:MI:SS') as samplereceived_dt,
        to_timestamp(NEW.testperformed_dt,'YYYY/MM/DD HH24:MI:SS') as testperformed_dt,
        to_timestamp(NEW.testresultsreleased_dt,'YYYY/MM/DD HH24:MI:SS') as testresultsreleased_dt,
        to_timestamp(NEW.extractedfromsourceat_dt,'YYYY/MM/DD HH24:MI:SS') as extractedfromsourceat_dt,
        to_date(NEW.birthdate_d,'YYYY/MM/DD') as birthdate_d;

    -- Return new as nothing happens
    RETURN NEW;

    EXCEPTION WHEN others THEN

        INSERT INTO pdb.lis_errors
        SELECT
            NEW.edm___row_id,
            current_timestamp as edm___errorat_dtz,
            SQLERRM as edm___errormsg,
            NEW.patient_id,
            NEW.encounter_id,
            NEW.order_id,
            NEW.sample_id,
            NEW.container_id,
            NEW.result_id,
            NEW.orderrequestcode,
            NEW.orderrequestname,
            NEW.testresultcode,
            NEW.testresultname,
            NEW.testresultcost,
            NEW.testordered_dt,
            NEW.samplereceived_dt,
            NEW.testperformed_dt,
            NEW.testresultsreleased_dt,
            NEW.extractedfromsourceat_dt,
            NEW.birthdate_d;

        -- Return new back to the streaming view as we don't want that process to error.  We already routed the record above to the errors table as text.
        RETURN NEW;

  END;
  $$
  LANGUAGE plpgsql;


DROP VIEW IF EXISTS pdb.lis_tryparse CASCADE;
CREATE VIEW pdb.lis_tryparse WITH (action=transform, outputfunc=insert_into_t) AS
SELECT
    edm___row_id,
    patient_id,
    encounter_id,
    order_id,
    sample_id,
    container_id,
    result_id,
    orderrequestcode,
    orderrequestname,
    testresultcode,
    testresultname,
    testresultcost,
    testordered_dt,
    samplereceived_dt,
    testperformed_dt,
    testresultsreleased_dt,
    extractedfromsourceat_dt,
    birthdate_d
FROM pdb.lis_streaming_table as st;