有人可以帮我理解 Postgres 中的歧义吗?

Could someone help me understand the ambiguity here in Postgres?

所以我已经试用了几天 PG,特别是通过 dotnet 核心中的 NpgSQL,但我认为这与我的问题无关。我一直在写几个更新函数。第一个很简单:

    CREATE OR REPLACE FUNCTION "Api"."UpdateExpenseReceipt" ( "vReceiptID" UUID  , "vTotal" DOUBLE PRECISION , "vTaxPercent" DOUBLE PRECISION , "vShippingCost" DOUBLE PRECISION , "vReceiptDate" TIMESTAMP , "vReference" VARCHAR , "vCurrentToken" UUID )
        RETURNS TABLE ( ReceiptID UUID , Total DOUBLE PRECISION , TaxPercent DOUBLE PRECISION , ShippingCost DOUBLE PRECISION , Reference VARCHAR(96) , ReceiptDate TIMESTAMP )
        LANGUAGE PLPGSQL
        AS $$
        DECLARE "iValidReceipt" INTEGER;
        DECLARE "iValidUser" INTEGER;
        BEGIN
        
            "iValidReceipt" := ( SELECT COUNT("ReceiptID") FROM "Users"."ExpenseReceipt" WHERE "ReceiptID" = "vReceiptID" );
            "iValidUser"  := ( SELECT COUNT("AccountID") FROM "Users"."Account" WHERE "CurrentToken" = "vCurrentToken" LIMIT 1 );
        
            IF "iValidUser" = 0 THEN
                RAISE 'Error' USING ERRCODE = '10001';
            END IF;
        
            IF "iValidReceipt" > 0 THEN
                UPDATE "Users"."ExpenseReceipt" SET
                    "Total" = COALESCE( "vTotal" , "Total" )
                    , "TaxPercent" = COALESCE( "vTaxPercent" , "TaxPercent" )
                    , "ShippingCost" = COALESCE( "vShippingCost" , "ShippingCost" )
                    , "Reference" = COALESCE( CAST( "vReference" AS VARCHAR ) , "Reference" )
                    , "ReceiptDate" = COALESCE( "vReceiptDate" , "ReceiptDate" )
                    , "EditDate" = current_timestamp at time zone 'utc'
                WHERE "ReceiptID" = "vReceiptID";
                
                RETURN QUERY
                SELECT
                    "ReceiptID"
                    , "Total"
                    , "TaxPercent"
                    , "ShippingCost"
                    , "Reference"
                    , "ReceiptDate"
                FROM "Users"."ExpenseReceipt"
                WHERE "ReceiptID" = "vReceiptID";
            ELSE
                RAISE 'Error' USING ERRCODE = '10101';
            END IF;
        
        
        END; $$

--I'll include the table itself in case its relevant
CREATE TABLE "Users"."ExpenseReceipt"
(
    "ReceiptID" UUID NOT NULL DEFAULT uuid_generate_v4(),
    "AccountID" UUID NOT NULL ,
    "Total" DOUBLE PRECISION NOT NULL ,
    "TaxPercent" DOUBLE PRECISION DEFAULT 0.0 ,
    "ShippingCost" DOUBLE PRECISION DEFAULT 0.0 ,
    "Reference" VARCHAR(96) ,
    "ReceiptDate" TIMESTAMP ,
    "EditDate" TIMESTAMP DEFAULT ( current_timestamp at time zone 'utc' )
);

简易更新功能,如果 API 调用未设置它们,则使用合并不更新值。一切正常(在处理了我 运行 对 postgres 的众多命名问题之后,我认为 NpgSQL 是相关的)。我知道如何做对。我做了第二个,基本一模一样:

CREATE OR REPLACE FUNCTION "Api"."UpdateSupplyItem" ( "vItemID" UUID , "vDescription" VARCHAR , "vSize" VARCHAR , "vNetCost" DOUBLE PRECISION , "vPackageQuantity" DOUBLE PRECISION , "vNetWeight" DOUBLE PRECISION , "vCurrentToken" UUID )
RETURNS TABLE ( "ItemID" UUID , "Description" VARCHAR , "Size" VARCHAR , "NetCost" DOUBLE PRECISION , "PackageQuantity" DOUBLE PRECISION , "NetWeight" DOUBLE PRECISION )
LANGUAGE PLPGSQL
AS $$
DECLARE "iValidItem" INTEGER;
DECLARE "iValidUser" INTEGER;
BEGIN

    "iValidItem" := ( SELECT COUNT(usi."ItemID") FROM "Users"."SupplyItem" usi WHERE usi."ItemID" = "vItemID" );
    "iValidUser" := ( SELECT COUNT("AccountID") FROM "Users"."Account" WHERE "CurrentToken" = "vCurrentToken" LIMIT 1 );

    IF "iValidUser" = 0 THEN
        RAISE 'Error' USING ERRCODE = '10001';
    END IF;

    IF "iValidItem" > 0 THEN

        UPDATE "Users"."SupplyItem"
        SET 
             "Description" = COALESCE( "vDescription" , "Users"."SupplyItem"."Description" )
            , "Size" = COALESCE( "vSize" , "Users"."SupplyItem"."Size" ) 
            , "NetCost" = COALESCE( "vNetCost" ,  "Users"."SupplyItem"."NetCost" ) 
            , "PackageQuantity" = COALESCE( "vPackageQuantity" ,  "Users"."SupplyItem"."PackageQuantity") 
            , "NetWeight" = COALESCE( "vNetWeight" ,  "Users"."SupplyItem"."NetWeight" )
        WHERE  "Users"."SupplyItem"."ItemID" = "vItemID";

        RETURN QUERY SELECT usi."ItemID" , usi."Description" , usi."Size" , usi."NetCost" , usi."PackageQuantity" , usi."NetWeight" FROM "Users"."SupplyItem" usi WHERE usi."ItemID" = "vItemID" LIMIT 1;

    ELSE
        RAISE 'Error' USING ERRCODE = '10401';
    END IF;

END; $$


--Again, the table in case it helps

CREATE TABLE "Users"."SupplyItem"
(
    "ItemID" UUID NOT NULL DEFAULT uuid_generate_v4() ,
    "AccountID" UUID NOT NULL ,
    "Description" VARCHAR , 
    "Size" VARCHAR ,
    "NetCost" DOUBLE PRECISION , 
    "PackageQuantity" DOUBLE PRECISION ,
    "NetWeight" DOUBLE PRECISION
);

但这完全不同。您可以清楚地看到,我必须完全限定每个 equals 的右侧(早期函数不需要的地方)。我一直在更新声明中含糊不清。它从 ItemID 开始,然后是描述,然后是大小……我认为的每个属性。我做的第一件事是为 table

添加别名
UPDATE usi [...] FROM "Users"."SupplyItem" usi

但那失败了,因为你不能在 PG 的更新中做短别名(关系 usi。“[...]”不存在)这实际上有点糟糕。我只是发现它需要当有人问了类似的问题并且答案是“这一定是 RETURNS TABLE 的怪癖时,才能完全合格。”

那么,为什么我的第二次更新是“怪癖”,而我的第一次更新却运行良好?我在 PG 上度过了一段艰难的时光(而且我不是一个无精打采的人),但是有两个看起来相同的功能却有完全不同的结果(在 运行 时间不少于 ) 让我感到不舒服table。我在这里发帖是因为我知道这两个函数肯定有明显的不同; 99.9% 的时间,不存在所谓的“怪癖”。我需要了解一些事情,以便将来避免。我在第二个 UPDATE 函数中遗漏的“陷阱”是什么?

问题是您有一个函数变量 "Size"(在 RETURNS TABLE 子句中定义的 OUT 参数)和 [=15= 中的列 "Size" ].所以你必须限定引用以表明你的意思。

为简单起见,我建议使用别名:

UPDATE "Users"."SupplyItem" AS si
   SET "Size" = COALESCE("vSize" , si."Size")
...

在你的第一个例子中没有这样的歧义,因为你没有双引号参数 TaxPercent,所以它被折叠成 taxpercent 并且与列 [=18] 不同=].