有人可以帮我理解 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] 不同=].
所以我已经试用了几天 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] 不同=].