【问题标题】:PL/pgSQL function not returning expected valuePL/pgSQL 函数未返回预期值
【发布时间】:2012-08-16 15:47:21
【问题描述】:

我在 plpgsql 中编写了一个触发函数,该函数在表上执行AFTER INSERT

触发器函数调用了另外两个函数,第一个函数没有返回正确的值。我对此束手无策,搜索没有找到任何答案。

请大家帮忙解释一下这个问题?

下面的触发函数:

CREATE TRIGGER timetotaketrigger
  AFTER INSERT
  ON "Prescription Schema"."TimeToTake"
  FOR EACH ROW
  EXECUTE PROCEDURE failtimetotakeinsert();

上述触发器按预期工作。

failtimetotakeinsert 被调用,源为:

CREATE OR REPLACE FUNCTION failtimetotakeinsert()
RETURNS trigger AS '
DECLARE

    -- declare variables to hold the information from
    -- the row being inserted
    -- and save the data from the new row
    drug_name character(32);
    drug_strength character(8);
    drug_strength_unit character(16);
    drug_dosage integer;

    row_counts integer = 0;
    frequency integer = 0;
    difference integer = 0;

 BEGIN
RAISE NOTICE ''Frequency at the start is %'',frequency;
    -- save the data from the new row
    drug_name = NEW."DrugName";
    drug_strength = NEW."DrugStrength";
    drug_strength_unit = NEW."DrugStrengthUnit";
    drug_dosage = NEW."DrugDosage";

    -- get the frequency 
    SELECT INTO frequency GetPrescriptionFrequency(drug_name,
                                    drug_strength,
                                    drug_strength_unit,
                                    drug_dosage);

RAISE NOTICE ''frequency is %'',frequency;        
    -- count the rows from the current table
    SELECT INTO row_counts CountTimeToTake(drug_name,drug_strength,
                                           drug_strength_unit,drug_dosage);
RAISE NOTICE ''row counts are %'',row_counts;

    -- work out the difference
    difference = row_counts - frequency;
RAISE NOTICE ''Difference is %'',difference;
    -- now check the two figures
    IF difference > 0 THEN
        RAISE EXCEPTION ''More rows than frequency requires'';
    END IF;

    RETURN NULL;
 END;
'  LANGUAGE 'plpgsql'

我遇到的问题是在这个函数中调用的第一个函数,因为当从 PgAdmin sql environemtn 调用时它没有返回值,结果与我预期的一样。

CREATE OR REPLACE FUNCTION GetPrescriptionFrequency
    (character, character, character, integer)
RETURNS integer AS '
#variable_conflict error
DECLARE
   -- Declare drug_name,
   --         drug_strength,
   --         drug_strength_unit and
   --         drug_dosage as an alias for the argument variables
   -- normally referenced with the $1,$2,$3 and $4 identifiers

   drug_name ALIAS FOR $1;
   drug_strength ALIAS FOR $2;
   drug_strength_unit ALIAS FOR $3;
   drug_dosage ALIAS FOR $4;

   -- declare a variable to hold the count

   freq integer := 0;

BEGIN

  SELECT INTO freq COUNT(*) 
      FROM "Prescription Schema"."PrescriptionItem" 
      WHERE "PrescriptionItem"."DrugName" = drug_name AND
            "PrescriptionItem"."DrugStrength" = drug_strength AND
            "PrescriptionItem"."DrugStrengthUnit" = drug_strength_unit AND
            "PrescriptionItem"."DrugDosage" = drug_dosage;
--       IF NOT FOUND THEN
--          RAISE EXCEPTION ''prescription item not found %'', drug_strength;
--       END IF;
   IF freq IS NULL THEN 
     RETURN 0;
   ELSE
     RETURN freq;
   END IF;
END;
' LANGUAGE 'plpgsql'

另一个函数运行正常,但我也包含了源代码:

CREATE OR REPLACE FUNCTION CountTimeToTake(character,character,character,integer)
    RETURNS integer AS '
DECLARE
   -- Declare drug_name,
   --         drug_strength,
   --         drug_strength_unit and
   --         drug_dosage as an alias for the argument variables
   -- normally referenced with the $1,$2,$3 and $4 identifiers

   drug_name ALIAS FOR $1;
   drug_strength ALIAS FOR $2;
   drug_strength_unit ALIAS FOR $3;
   drug_dosage ALIAS FOR $4;
   -- declare a variable to hold the count
   row_count integer := 0;
BEGIN

-- count the number of TimeToTake rows for the given
-- parameter values

   SELECT INTO row_count COUNT(*) FROM "Prescription Schema"."TimeToTake"
          WHERE "TimeToTake"."DrugName" = drug_name AND
                "TimeToTake"."DrugStrength" = drug_strength AND
                "TimeToTake"."DrugStrengthUnit" = drug_strength_unit AND
                "TimeToTake"."DrugDosage" = drug_dosage;

   return row_count;
END;
' LANGUAGE 'plpgsql'

我已经尝试了所有可以在 Internet 上找到的方法以及您之前提出的问题,但均无济于事。任何帮助将不胜感激。

【问题讨论】:

    标签: sql postgresql triggers plpgsql postgresql-9.1


    【解决方案1】:

    触发器

    简化和重写:

    CREATE OR REPLACE FUNCTION failtimetotakeinsert()
      RETURNS trigger AS
    $func$
    BEGIN
    
    IF GetPrescriptionFrequency(NEW."DrugName", NEW."DrugStrength"
                               ,NEW."DrugStrengthUnit", NEW."DrugDosage") 
              > CountTimeToTake(NEW."DrugName",NEW."DrugStrength"
                               ,NEW."DrugStrengthUnit",NEW."DrugDosage") THEN
       RAISE EXCEPTION 'More rows than frequency requires';
    END IF;
    
    RETURN NULL;
    
    END
    $func$   LANGUAGE plpgsql;
    

    要点

    • 最好对函数体进行美元引用以避免引用问题。

    • plpgsqlLANGUAGE plpgsql 中的关键字,不必加引号。

    • 不要永远使用愚蠢的古老类型character(n),除非你确实必须这样做。它使用空白填充的字符串并截断字符串,并且很少做任何有用的事情。它只是出于历史原因和标准合规性。 只需使用text(实际上与varchar 相同),或者如果您确实需要在类型级别强制执行最大长度,请使用varchar(n)。我只使用text 99%。请务必阅读the manual about character types。 `

    • plpgsql 中的赋值运算符是:=。 SQL 风格的= 也适用于 ATM,但没有记录,可能会在没有警告的情况下消失。

    • 删除了毫无意义的RAISE NOTICE 'Frequency at start is %', frequency; - 始终是0

    • 彻底简化。

    • 如果函数CountTimeToTake() 应使用驼峰式大小写定义,则需要用双引号将其括起来。但据我所见,并非如此。

    功能

    修复、简化和重写:

    CREATE OR REPLACE FUNCTION GetPrescriptionFrequency
        (_drug_name text, _drug_strength text, _drug_strength_unit text
                                             , _drug_dosage integer)
      RETURNS integer LANGUAGE sql AS 
    $func$
    SELECT count(*)::int
    FROM  "Prescription Schema"."PrescriptionItem" p
    WHERE  p."DrugName"         = _drug_name
    AND    p."DrugStrength"     = _drug_strength
    AND    p."DrugStrengthUnit" = _drug_strength_unit
    AND    p."DrugDosage"       = _drug_dosage;
    $func$
    

    要点

    • 在 PostgreSQL 9.1 中使用参数名称而不是 ALIAS

    • !这里使用数据类型character是完全错误的,可能是您的关键问题。 charactercharacter(1) 的同义词,将字符串截断为第一个字符。

    • 在 plpgsql 函数内部的查询中,变量和参数是可见的,并且优先于列名。这可能会导致意想不到的结果。在这种情况下,您必须对列名进行表限定以使其明确。
      使用与列名不冲突的参数和变量名是一种很好的做法。我养成了使用 _ 前缀的习惯,我从不将它用于列名,但只要避免命名冲突,任何方法都可以。

    • 我建议不要对 PostgreSQL 标识符使用驼峰式大小写。只使用小写字母,避免大量双引号和混淆。

    • count() 永远不会返回NULL,您不需要提供这种情况。我引用the manual here

    需要注意的是,除了count,这些函数都返回一个 未选择任何行时为空值。

    • count() 返回bigint,因此在这种情况下转换为integer

    • 对于这个简单的案例,LANGUAGE sql function 可能会做得更好。

    • 相应地修复您的其他功能CountTimeToTake()

    【讨论】:

    • @Erwin_Brandstetter - 感谢您的建议。我以前不知道sql函数。我在哪里可以了解这些以及我尝试使用的语法版本对 plpgsql 的更改。
    • @user1613784:您可以从 PostgreSQL 手册 here 开始。我在this related answer at dba.SE 中写了一些关于plpgsqlsql 函数的更多内容(并添加了链接)。
    猜你喜欢
    • 2012-08-05
    • 1970-01-01
    • 2022-01-19
    • 2013-06-19
    • 1970-01-01
    • 2014-07-18
    • 2013-02-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多