如何在PL / pgSQL函数中引用参数?

时间:2015-08-31 19:47:09

标签: postgresql parameter-passing plpgsql dynamic-sql

我遇到一个问题,当我尝试直接按名称引用参数时,我会在调用函数后返回文字值。任何人都可以帮我解决如何在这里使用参数值的问题吗?

CREATE OR REPLACE FUNCTION dbo.reset_sequence(
   tablename text,
   columnname text,
   sequence_name text)
RETURNS void AS
$BODY$  
  DECLARE 
  BEGIN 
    IF( (SELECT MAX( columnname ) ) < (SELECT min_value FROM dbo.tablename)  )
     THEN
        --   EXECUTE 'SELECT setval( ' || sequence_name || ', (SELECT min_value FROM dbo.' || sequence_name ||')';
    ELSE
     --  EXECUTE 'SELECT setval( ' || sequence_name || ', ' || '(SELECT MAX("' || columnname || '") FROM dbo."' || tablename || '")' || '+1)';
    END IF;
  END;
$BODY$
LANGUAGE plpgsql VOLATILE;

编辑:我遇到的问题更具体地与EXECUTE命令之外的语法有关。另一种解决方案并没有真正帮助我。

在研究了另一个主题后,我正在尝试另一种解决方案,但仍然遇到问题。

CREATE OR REPLACE FUNCTION dbo.reset_sequence(
   tablename text,
   columnname text,
   sequence_name text)
RETURNS void AS
$BODY$  
  DECLARE 
  _maxVal int;
  _minVal int;

  BEGIN   
    EXECUTE format('SELECT MAX( ''' || columnname || ''' ) FROM ' || schema_name || '."' || tablename || '"')
    INTO _maxVal;
    EXECUTE format('SELECT min_value FROM ' || schema_name || '."' || sequence_name || ''' ')
    INTO _minVal;

    IF( maxVal < _minVal)
     THEN
        --   EXECUTE 'SELECT setval( ' || sequence_name || ', (SELECT min_value FROM dbo.' || sequence_name ||')';
    ELSE
     --  EXECUTE 'SELECT setval( ' || sequence_name || ', ' || '(SELECT MAX("' || columnname || '") FROM dbo."' || tablename || '")' || '+1)';
    END IF;
  END;
$BODY$
LANGUAGE plpgsql VOLATILE;

关于它的语法有效,但是当我调用该函数时,我得到一个错误,它不能将EXECUTE语句存储为整数,它似乎返回列的名称,而不是该列中的最大值。

2 个答案:

答案 0 :(得分:1)

没有解释该函数应该做什么,代码是模糊的。据我了解,这是该功能的目的:

将给定模式中的给定序列重置为同一模式的给定表中给定列的最大值 - 或者如果它应该更大,则给定序列的最小值。

尚不清楚架构dbo是否也参与其中。将dbo保留在循环中,此工作:

CREATE OR REPLACE FUNCTION reset_sequence(
   sch text, -- schema_name
   tbl text, -- table_name
   col text, -- column_name
   seq text  -- sequence_name  -- all unquoted and case-SENSITIVE!
   ) RETURNS void AS
$func$  
DECLARE 
   _max_val int;
   _min_val int;
BEGIN
   EXECUTE format('SELECT MAX(%I) FROM %I.%I', col, sch, tbl)
   INTO _max_val;

   EXECUTE format('SELECT min_value FROM %I.%I', sch, seq)
   INTO _min_val;

   IF _max_val < _min_val THEN
      EXECUTE format($$SELECT setval('%1$I.%2$I', min_value, false) FROM dbo.%2$I;$$
                   , sch, seq);
   ELSE
      EXECUTE format($$SELECT setval('%I.%I', max(%I)) FROM dbo.%I;$$
                   , sch, seq, col, tbl);
   END IF;
END
$func$ LANGUAGE plpgsql;

可以简化为:

CREATE OR REPLACE FUNCTION pg_temp.reset_sequence(sch text, tbl text, col text, seq text)
   RETURNS void AS
$func$  
DECLARE 
   _found bool;
BEGIN
   EXECUTE format('SELECT true FROM %1$I.%2$I
                   HAVING MAX(%3$I) < (SELECT min_value FROM %1$I.%4$I)'
                 , sch, tbl, col, seq)
   INTO _found;

   IF _found THEN
      EXECUTE format($$SELECT setval('%1$I.%2$I', min_value, false) FROM dbo.%2$I;$$
                   , sch, seq);
   ELSE
      EXECUTE format($$SELECT setval('%I.%I', max(%I)) FROM dbo.%I;$$
                   , sch, seq, col, tbl);
   END IF;
END
$func$ LANGUAGE plpgsql;

如果使用 dbo 只是我怀疑的拼写错误,更简单

CREATE OR REPLACE FUNCTION pg_temp.reset_sequence(sch text, tbl text, col text, seq text)
   RETURNS void AS
$func$  
BEGIN
   EXECUTE format($$
      SELECT setval('%1$I.%4$I', GREATEST(s.min, t.max + 1), false)  -- now we need + 1
      FROM  (SELECT MAX(%3$I) FROM %1$I.%2$I) t(max)
          , (SELECT min_value FROM %1$I.%4$I) s(min)
      $$, sch, tbl, col, seq);
END
$func$ LANGUAGE plpgsql;

各种问题

  • 您正在以无意义的方式将format()与纯字符串连接混合。在继续之前,请务必read the manual on format()

  • 变量schema_name未定义。我添加了另一个函数参数来传递它。 您在 奇数 中使用了最后两个dbo调用中的架构setval()。此外,"dbo"是SQL Server的典型标识符,但不是Postgres中的标识符。可能是另一个错误还是故意的?

  • 变量maxVal未定义。可能应该是_maxVal。我在简化版中完全删除了该变量。

  • + 1您不需要setval(),因为默认情况下,返回的下一个值会增加。 Example in the manual:

    SELECT setval('foo', 42);        -- Next nextval will return 43
    
  • 另一方面,如果您想从序列的最开始处开始,请使用:

    SELECT setval('my_sequence', min_value, false)

更多解释

  • 只有运行查询才能干净:

    SELECT setval('my_sequence', min_value) FROM other_sequence;
    

    ..因为SEQUENCE的表格保证完全 1行

答案 1 :(得分:0)

如果先使用EXECUTE .. INTO将它们存储到临时值中,则可以使用EXECUTE语句中IF语句的结果。例如:

DECLARE
  max_column_value int;
BEGIN
  EXECUTE 'SELECT MAX(' || columnname || ') FROM dbo."' || tablename || '"'
    INTO max_column_value;
  IF max_column_value < 1000 THEN
    ...

如果columnname'col'tablename'tbl',则应该相当于:

IF (SELECT MAX(col) FROM dbo."tbl") < 100 THEN