我有一个 postgresql 函数,它接受多个参数,最后一个是 jsonb。 使用以下参数值调用它(由于 postgres 诊断输出而使用双引号):
SELECT vspm.rfd_add_f
(
$1::bigint
, $2::bigint
, $3::bigint
, $4::timestamptz
, $5::timestamptz
, $6::bigint
, $7::bigint
, $8::text
, $9::text
, $10::timestamptz
, $11::text
, $12::jsonb
)
","parameters: $1 = NULL, $2 = '1', $3 = '18014398509482060', $4 = NULL, $5 = NULL, $6 = '18014398509484942', $7 = '18014398509482082', $8 = NULL, $9 = NULL, $10 = NULL, $11 = NULL, $12 = '[{""id"":null,""modUserId"":null,""validFrom"":null,""validTo"":null,""clientId"":null,""client"":null,""rfdId"":null,""rfd"":null,""rfdNumber"":null,""rfdDate"":null,""managerId"":null,""manager"":null,""estimateId"":null,""estimate"":null,""productionNeedId"":""18014398509489518"",""quantity"":""111000000"",""sortOrder"":null,""wantedDeliveryDate"":""2023-12-11T18:00:00Z"",""objectId"":null,""object"":null,""section"":null,""nomenclatureId"":null,""nomenclature"":null,""muId"":null,""mu"":null,""remarks"":null},{""id"":null,""modUserId"":null,""validFrom"":null,""validTo"":null,""clientId"":null,""client"":null,""rfdId"":null,""rfd"":null,""rfdNumber"":null,""rfdDate"":null,""managerId"":null,""manager"":null,""estimateId"":null,""estimate"":null,""productionNeedId"":""18014398509489524"",""quantity"":""222000000"",""sortOrder"":null,""wantedDeliveryDate"":""2023-12-12T18:00:00Z"",""objectId"":null,""object"":null,""section"":null,""nomenclatureId"":null,""nomenclature"":null,""muId"":null,""mu"":null,""remarks"":null},{""id"":null,""modUserId"":null,""validFrom"":null,""validTo"":null,""clientId"":null,""client"":null,""rfdId"":null,""rfd"":null,""rfdNumber"":null,""rfdDate"":null,""managerId"":null,""manager"":null,""estimateId"":null,""estimate"":null,""productionNeedId"":""18014398509489530"",""quantity"":""333000000"",""sortOrder"":null,""wantedDeliveryDate"":""2023-12-13T18:00:00Z"",""objectId"":null,""object"":null,""section"":null,""nomenclatureId"":null,""nomenclature"":null,""muId"":null,""mu"":null,""remarks"":null}]'",,,,,,,,"PostgreSQL JDBC Driver","client backend",,0
在这种情况下,以下字段很重要:
[
{
"productionNeedId": "18014398509489518"
, "quantity": "111000000"
, "wantedDeliveryDate": "2023-12-11T21:00:00+03:00"
}
, {
"productionNeedId": "18014398509489524"
, "quantity": "222000000"
, "wantedDeliveryDate": "2023-12-12T21:00:00+03:00"
}
, {
"productionNeedId": "18014398509489530"
, "quantity": "333000000"
, "wantedDeliveryDate": "2023-12-13T21:00:00+03:00"
}
]
在调试函数的开头有以下语句:
RAISE LOG '==> rfditem_add_many_f: json quantity = %, productionNeedId = %, wantedDeliveryDate = %'
, (pitems::jsonb->0)::jsonb->>'quantity'
, (pitems::jsonb->0)::jsonb->>'productionNeedId'
, (pitems::jsonb->0)::jsonb->>'wantedDeliveryDate'
;
RAISE LOG '==> rfditem_add_many_f: json quantity = %, productionNeedId = %, wantedDeliveryDate = %'''
, (pitems::jsonb->1)::jsonb->>'quantity'
, (pitems::jsonb->1)::jsonb->>'productionNeedId'
, (pitems::jsonb->1)::jsonb->>'wantedDeliveryDate'
;
RAISE LOG '==> rfditem_add_many_f: json quantity = %, productionNeedId = %, wantedDeliveryDate = %'''
, (pitems::jsonb->2)::jsonb->>'quantity'
, (pitems::jsonb->2)::jsonb->>'productionNeedId'
, (pitems::jsonb->2)::jsonb->>'wantedDeliveryDate'
;
我得到了正确的输出:
"==> rfditem_add_many_f: json quantity = 111000000, productionNeedId = 18014398509489518, wantedDeliveryDate = 2023-12-11T18:00:00Z",,,,,"PL/pgSQL function rfditem_add_many_f(bigint,bigint,timestamp with time zone,timestamp with time zone,bigint,timestamp with time zone,text,jsonb) line 20 at RAISE
"==> rfditem_add_many_f: json quantity = 222000000, productionNeedId = 18014398509489524, wantedDeliveryDate = 2023-12-12T18:00:00Z'",,,,,"PL/pgSQL function rfditem_add_many_f(bigint,bigint,timestamp with time zone,timestamp with time zone,bigint,timestamp with time zone,text,jsonb) line 25 at RAISE
"==> rfditem_add_many_f: json quantity = 333000000, productionNeedId = 18014398509489530, wantedDeliveryDate = 2023-12-13T18:00:00Z'",,,,,"PL/pgSQL function rfditem_add_many_f(bigint,bigint,timestamp with time zone,timestamp with time zone,bigint,timestamp with time zone,text,jsonb) line 30 at RAISE
但是当我进一步尝试使用 jsonb_to_recordset 函数处理这个 jsonb 时,找不到 ProductionNeedId 和 WantDeliveryDate 字段的值。
FOR lrecord IN
SELECT
id, moduserId, validFrom, validTo, clientId, rfdId, productionNeedId, quantity, sortOrder, wantedDeliveryDate, remarks
FROM jsonb_to_recordset(pitems) AS (id bigint, moduserId bigint, validFrom timestamptz, validTo timestamptz, clientId bigint, rfdId bigint, productionNeedId bigint, quantity bigint, sortOrder int, wantedDeliveryDate timestamptz, remarks text)
LOOP
RAISE LOG '==> rfditem_add_many_f read : validFrom = %, validTo = %, clientid = %, rfdId = %, productionNeedId = %, quantity = %, sortOrder = %, wantedDeliveryDate = %, remarks = %'
, coalesce(lrecord.validFrom, '1900-01-01'::timestamptz)
, coalesce(lrecord.validTo, '1900-01-01'::timestamptz)
, coalesce(lrecord.clientId, -1)
, coalesce(lrecord.rfdId, -1)
, coalesce(lrecord.productionNeedId, -1)
, coalesce(lrecord.quantity, -1)
, coalesce(lrecord.sortOrder, -1)
, coalesce(lrecord.wantedDeliveryDate, '1900-01-01'::timestamptz)
, coalesce(lrecord.remarks, '')
;
"==> rfditem_add_many_f read : validFrom = 1900-01-01 00:00:00+02:30:17, validTo = 1900-01-01 00:00:00+02:30:17, clientid = -1, rfdId = -1, productionNeedId = -1, quantity = 111000000, sortOrder = -1, wantedDeliveryDate = 1900-01-01 00:00:00+02:30:17, remarks = "
json 数组的剩余两个元素不会被处理,因为稍后在函数中,当尝试向数据库表添加记录时,发生了完整性约束冲突。
我还尝试使用以下函数调用:
FOR lrecord IN
SELECT
validFrom, validTo, clientId, rfdId, productionNeedId, quantity, sortOrder, wantedDeliveryDate, remarks
FROM jsonb_populate_recordset(null::rfditem_type, pitems)
LOOP
rfditem_type 声明为:
CREATE TYPE rfditem_type AS
(
id bigint
, moduserId bigint
, validFrom timestamptz
, validTo timestamptz
, clientId bigint
, rfdId bigint
, productionNeedId bigint
, quantity bigint
, sortOrder integer
, wantedDeliveryDate timestamptz
, remarks text
);
但结果更糟糕(调试输出是一样的):
"==> rfditem_add_many_f read : validFrom = 1900-01-01 00:00:00+02:30:17, validTo = 1900-01-01 00:00:00+02:30:17, clientid = -1, rfdId = 111000000, productionNeedId = -1, quantity = -1, sortOrder = -1, wantedDeliveryDate = 1900-01-01 00:00:00+02:30:17, remarks = "
...
"==> rfditem_add_many_f read : validFrom = 1900-01-01 00:00:00+02:30:17, validTo = 1900-01-01 00:00:00+02:30:17, clientid = -1, rfdId = 222000000, productionNeedId = -1, quantity = -1, sortOrder = -1, wantedDeliveryDate = 1900-01-01 00:00:00+02:30:17, remarks = "
...
"==> rfditem_add_many_f read : validFrom = 1900-01-01 00:00:00+02:30:17, validTo = 1900-01-01 00:00:00+02:30:17, clientid = -1, rfdId = 333000000, productionNeedId = -1, quantity = -1, sortOrder = -1, wantedDeliveryDate = 1900-01-01 00:00:00+02:30:17, remarks = "
数量的值已分配给 rfdId 字段,其他字段未分配
和
FOR lrecord IN
SELECT * from jsonb_populate_recordset(null::rfditem_type, pitems)
LOOP
在这种情况下,结果略有不同:
"==> rfditem_add_many_f read : validFrom = 1900-01-01 00:00:00+02:30:17, validTo = 1900-01-01 00:00:00+02:30:17, clientid = -1, rfdId = -1, productionNeedId = -1, quantity = 111000000, sortOrder = -1, wantedDeliveryDate = 1900-01-01 00:00:00+02:30:17, remarks = "
也就是说,数量字段的值已正确分配,但未找到 ProductionNeedId 和 WantedDeliveryDate 字段
我有一个想法,这可能是由于字段名称中字母的大小写不同造成的,但是为什么诊断输出中一切都正常工作呢? (我想在编辑后端之前确定是这种情况)。
是的,这几乎肯定是由于外壳不同造成的。当您不引用标识符时,Postgres 会将它们视为小写字母,这将导致在 JSON 对象中找不到它们。使用
->>
并将属性名称作为字符串文字不会出现此问题。
所以使用
FOR lrecord IN
SELECT *
FROM jsonb_to_recordset(pitems) AS ("id" bigint, "moduserId" bigint, "validFrom" timestamptz, "validTo" timestamptz, "clientId" bigint, "rfdId" bigint, "productionNeedId" bigint, "quantity" bigint, "sortOrder" int, "wantedDeliveryDate" timestamptz, "remarks" text)
LOOP
RAISE LOG '==> rfditem_add_many_f read : validFrom = %, validTo = %, clientid = %, rfdId = %, productionNeedId = %, quantity = %, sortOrder = %, wantedDeliveryDate = %, remarks = %'
, coalesce(lrecord."validFrom", '1900-01-01'::timestamptz)
, coalesce(lrecord."validTo", '1900-01-01'::timestamptz)
, coalesce(lrecord."clientId", -1)
, coalesce(lrecord."rfdId", -1)
, coalesce(lrecord."productionNeedId", -1)
, coalesce(lrecord."quantity", -1)
, coalesce(lrecord."sortOrder", -1)
, coalesce(lrecord."wantedDeliveryDate", '1900-01-01'::timestamptz)
, coalesce(lrecord."remarks", '')
;
是的,问题肯定出在 json 属性名称的大小写上。重新设计背景并开始仅以小写形式传输这些名称后,一切正常。
我希望 Postgresql 团队在处理 json 的函数和运算符上有更大的一致性。如果第一个诊断输出中的属性
((pitems::jsonb->0)::jsonb->>'productionNeedId')
与随后在函数 jsonb_to_recordset 和 jsonb_populate_recordset 中的定位方式不同,问题会更早地被识别和解决