在动态SQL中使用序列

时间:2014-06-22 14:46:37

标签: postgresql plpgsql dynamic-sql

我们的表有点奇怪,在我们的存储过程中创建新行变得非常混乱所以我试图创建另一个单独的函数来处理行的创建,以便我们的存储过程保持干净。

为此,我将where子句的一部分作为参数传递,并且我在PostgreSQL中使用动态SQL。

我有一个跟踪当前row_in_transaction(与主键不同)的序列,以及一个跟踪每个销售交易中所有行的行表。当我插入行表时,我必须使用序列来生成新的row_in_transaction值,但我很难这样做。

目前,我试图以这种方式调用序列的nextval:

SELECT 'INSERT INTO row (trans_id, row_in_trans)
             SELECT '  ||  p_trans_id || ',' ||  'nextval(' || v_row_in_trans || ')
              FROM sales 
             WHERE trans_id =' || p_trans_id || ' AND ' || p_where_clause into v_sql;

EXECUTE v_sql;

但是我得到了

  

错误:列" v_row_in_trans"不存在

我已正确定义。

我尝试使用nextval而没有引号,我认为它只是在那一刻将nextval附加到查询中,所以它本质上是作为常量传递的,我收到的错误是row_in_transaction和trans_id组合已经存在。

这是我在桌旁看到的一个高峰:

CREATE TABLE row
(
    id serial NOT NULL,
    trans_id integer,
    row_in_trans integer,
    CONSTRAINT row_pkey PRIMARY KEY (id),
    CONSTRAINT row_trans UNIQUE (trans_id , row_in_trans)
)

CREATE TABLE sales
(
    trans_id integer,
    row_id integer,
    row_in_trans

    CONSTRAINT sale_row_fk 
        FOREIGN KEY (row_id)
        REFERENCES row (id) MATCH SIMPLE
            ON UPDATE NO ACTION ON DELETE NO ACTION
}


v_row_in_trans := 'row_in_trans'  || REPLACE(EXTRACT('epoch' FROM CURRENT_TIMESTAMP) :: character varying, '.', ''); --added the timestamp to make it distinct, just in case
EXECUTE('CREATE SEQUENCE '|| v_row_in_trans   || ' START '||   max_row_trans_id + 1); --max_row_trans_id is just a select max(id) from the row table for that trans_id

我已经留下了许多不重要的细节,但将每笔销售视为交易,并在每次销售中进行多次购买(行)。每笔销售都分配了一个唯一的trans_id,并且销售中的每一行都有一行包含trans_id的行表,以及事务中的行号(row_in_trans)。我知道它的设置是愚蠢的,但我实际上无法做任何事情。因此,当我想在存储过程中动态创建一个新行时(可能是买一送一的交易),我还必须在行表中创建一个新行。在某些销售中,我创造了超过一千个额外的行。我正在使用一个序列,以便我可以自动增加row_in_trans。

我不确定我怎样才能动态地传递这个nextval,我对SQL或动态SQL不是很有经验。任何指导将不胜感激!

谢谢!

2 个答案:

答案 0 :(得分:1)

对于这项工作来说,序列绝对是错误的工具。您不仅要执行不必要的DDL,而且序列不是事务安全的或保证无间隙的。 (它们也共享命名空间,因此您的程序可能必须以令人讨厌的方式等待对方)。

不要使用序列。它对橡胶鸡的工作和锤子一样好。

猜到了我实际上想要做的事情,我现在相信你想要的只是row_number()窗口。

e.g。

EXECUTE format('
  INSERT INTO row (trans_id, row_in_trans)
  SELECT 
    p_trans_id, 
    row_number() OVER (ORDER BY p_trans_id)
  FROM sales 
  WHERE trans_id = p_trans_id AND %s', p_where_clause);

答案 1 :(得分:0)

没有深入了解原因(@CraigRinger正在做一个极好的工作)尝试:

SELECT 'INSERT INTO row (trans_id, row_in_trans)
             SELECT '  ||  p_trans_id || ',' ||  nextval('v_row_in_trans')
              FROM sales 
             WHERE trans_id =' || p_trans_id || ' AND ' || p_where_clause into v_sql;

EXECUTE v_sql;