0

我在 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'

我已经尝试了我在互联网上可以找到的所有内容,并且在你们之前的问题中也无济于事。任何帮助将不胜感激。

4

1 回答 1

2

扳机

简化和重写:

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;

要点

  • 对函数体进行美元引用以避免引用问题是一种很好的做法。

  • plpgsql是关键字,LANGUAGE plpgsql不必引用。

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

  • 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$

要点

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

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

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

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

  • count()永不返回NULL,您不需要提供这种情况。我在这里引用手册

需要注意的是,除了count之外,这些函数在没有选择行时返回一个空值。

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

  • 对于这种简单的情况,LANGUAGE sql函数可能会做得更好。

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

于 2012-08-22T19:22:37.803 回答