我们正在使用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子句中使用该函数从不良记录中分叉良好记录。但是我似乎无法解决这个问题?有什么想法吗?
答案 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;