我有一个以奇怪的方式失败的删除插入 CTE
I have a delete-insert CTE that fails in a strange manner
这是一个成功的例子:
with x as (
delete from common.companies where id = '0f8ed160-370a-47bb-b4bf-2dcf79100a52'
returning row_to_json(companies) as old_data, null as new_data, 'common.companies' as model, id, 'delete' as action)
insert into edit_history (old_data, new_data, model, model_pk, action, submitter)
select old_data, null, model, id, action, '0b392013-f680-45a6-b19a-34f3d42d0120' from x;
INSERT 0 1
请注意,insert-select 中的第二列显式为空。
这是一个失败的例子:
with x as (
delete from common.companies where id = '160d7ef2-807c-4fe0-bfed-7d282c031610'
returning row_to_json(companies) as old_data, null as new_data, 'common.companies' as model, id, 'delete' as action)
insert into edit_history (old_data, new_data, model, model_pk, action, submitter)
select old_data, new_data, model, id, action, '0b392013-f680-45a6-b19a-34f3d42d0120' from x;
ERROR: failed to find conversion function from unknown to json
请注意,在此示例中,我得到的是 new_data
,而不是第二列中的显式 null,它从 delete 语句返回为 null。
如果两个值都为空,为什么第二个示例会给我带来这个错误?我已经仔细研究过了,这是唯一的功能差异。
诀窍似乎是将 null 转换为任何列类型(在我的例子中是json):
with x as (
delete from common.companies where id = '160d7ef2-807c-4fe0-bfed-7d282c031610'
returning row_to_json(companies) as old_data, null::json as new_data, 'common.companies' as model, id, 'delete' as action
)
insert into edit_history (old_data, new_data, model, model_pk, action, submitter)
select old_data, new_data, model, id, action, '0b392013-f680-45a6-b19a-34f3d42d0120' from x;
这需要在 returning 子句中完成,因为这会创建一个 temp/pseudo table ,谁知道如何定义(没有强制转换)... Postgres 无法从中推断类型价值。因此,当您尝试将该值插入不同类型时,您会收到转换错误。
在第一个示例中,您向 INSERT
语句提供了一个 尚未类型化的 NULL。
在第二个示例中,您早一步(在 CTE 中)提供了 NULL,必须键入表达式并分配类型 unknown
。对于其他 constants(如数字常量:123
),Postgres 可以导出更合适的默认数据类型,但 NULL(或字符串文字 'foo'
)可能是 anything 。 unknown
和 json
.
之间没有定义类型转换
在 CTE 中将 NULL 转换为正确的数据类型以避免出现问题(正如您现在发现的那样)。
或者使用 text
作为铸造链中的垫脚石,如果为时已晚。一切都可以投射到/从text
.
您可以将演示简化为以下内容:
作品:
SELECT NULL::json;
失败:
SELECT new_data::json
FROM (SELECT NULL AS new_data) t;
再次工作:
SELECT new_data
FROM (SELECT NULL::json AS new_data) t;
或:
SELECT new_data::text::json
FROM (SELECT NULL AS new_data) t;
这是一个成功的例子:
with x as (
delete from common.companies where id = '0f8ed160-370a-47bb-b4bf-2dcf79100a52'
returning row_to_json(companies) as old_data, null as new_data, 'common.companies' as model, id, 'delete' as action)
insert into edit_history (old_data, new_data, model, model_pk, action, submitter)
select old_data, null, model, id, action, '0b392013-f680-45a6-b19a-34f3d42d0120' from x;
INSERT 0 1
请注意,insert-select 中的第二列显式为空。
这是一个失败的例子:
with x as (
delete from common.companies where id = '160d7ef2-807c-4fe0-bfed-7d282c031610'
returning row_to_json(companies) as old_data, null as new_data, 'common.companies' as model, id, 'delete' as action)
insert into edit_history (old_data, new_data, model, model_pk, action, submitter)
select old_data, new_data, model, id, action, '0b392013-f680-45a6-b19a-34f3d42d0120' from x;
ERROR: failed to find conversion function from unknown to json
请注意,在此示例中,我得到的是 new_data
,而不是第二列中的显式 null,它从 delete 语句返回为 null。
如果两个值都为空,为什么第二个示例会给我带来这个错误?我已经仔细研究过了,这是唯一的功能差异。
诀窍似乎是将 null 转换为任何列类型(在我的例子中是json):
with x as (
delete from common.companies where id = '160d7ef2-807c-4fe0-bfed-7d282c031610'
returning row_to_json(companies) as old_data, null::json as new_data, 'common.companies' as model, id, 'delete' as action
)
insert into edit_history (old_data, new_data, model, model_pk, action, submitter)
select old_data, new_data, model, id, action, '0b392013-f680-45a6-b19a-34f3d42d0120' from x;
这需要在 returning 子句中完成,因为这会创建一个 temp/pseudo table ,谁知道如何定义(没有强制转换)... Postgres 无法从中推断类型价值。因此,当您尝试将该值插入不同类型时,您会收到转换错误。
在第一个示例中,您向 INSERT
语句提供了一个 尚未类型化的 NULL。
在第二个示例中,您早一步(在 CTE 中)提供了 NULL,必须键入表达式并分配类型 unknown
。对于其他 constants(如数字常量:123
),Postgres 可以导出更合适的默认数据类型,但 NULL(或字符串文字 'foo'
)可能是 anything 。 unknown
和 json
.
在 CTE 中将 NULL 转换为正确的数据类型以避免出现问题(正如您现在发现的那样)。
或者使用 text
作为铸造链中的垫脚石,如果为时已晚。一切都可以投射到/从text
.
您可以将演示简化为以下内容:
作品:
SELECT NULL::json;
失败:
SELECT new_data::json
FROM (SELECT NULL AS new_data) t;
再次工作:
SELECT new_data
FROM (SELECT NULL::json AS new_data) t;
或:
SELECT new_data::text::json
FROM (SELECT NULL AS new_data) t;