PL / pgSQL函数没有返回预期值

时间:2012-08-21 10:00:33

标签: sql postgresql triggers plpgsql postgresql-9.1

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

触发器函数调用另外两个函数,第一个函数不返回正确的值。我在这方面已经结束了,搜索没有找到任何答案。

Cam有人对这个问题有所了解吗?

以下触发功能:

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'

我已经尝试过我可以在互联网上找到的所有内容以及之前的问题但无济于事。非常感谢任何帮助。

1 个答案:

答案 0 :(得分: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;

重点

  • 优良做法是引用函数体以避免引用问题。

  • 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之外,这些函数返回a   没有选择行时为空值。

  • count()返回bigint,因此在此方案中转为integer

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

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