如何创建根据另一列的值增加的列

时间:2017-01-27 20:47:22

标签: postgresql field auto-increment unique-constraint

我有一个表格,我想在其中放置有关文章的所有信息,我需要创建一个包含自动增量的列,如果字段ID tipo可以具有相同的值)具有另一个值,对于此特定ID是唯一的。例如:

ID  /  TIPO
1   /   AJE -- Is Ok
1   /   AJS -- Is Ok (because this Tipo is AJS, different from AJE)
1   /   SI    -- Is Ok
2   /   AJE  -- Is Ok (New ID)
2   /   AJE  -- Is Wrong, because ID=2, TIPO=AJE already exist.

我做了这个独特的句子:

ALTER TABLE public.art_movimientos 
ADD CONSTRAINT uk_mov UNIQUE (id,tipo) USING INDEX TABLESPACE sistema_index;

但是如何创建覆盖两列的自动增量?

我的表格代码:

CREATE TABLE public.art_movimientos
(
  id integer NOT NULL DEFAULT nextval('seq_art_movimientos'::regclass),
  tipo character(3) NOT NULL, -- Tipos de Valores:...
  documento integer,
  fecha_doc date[] NOT NULL,
  fecha_mov date[] NOT NULL,
  calmacen integer NOT NULL,
  status character(13) NOT NULL DEFAULT 'PENDIENTE'::bpchar, -- PENDIENTE...
  mes integer NOT NULL,
  "año" integer NOT NULL,
  donado integer NOT NULL DEFAULT 0
)
WITH (
  OIDS=FALSE
);

1 个答案:

答案 0 :(得分:1)

您可以使用插入触发器来管理这种情况,模仿@dhke所声明的行为:

CREATE TABLE art_movimientos
(
  id integer NOT NULL DEFAULT NULL, -- You don't want a serial, nor a default
  tipo character(3) NOT NULL, -- Tipos de Valores:...
  documento integer,
  fecha_doc date[] NOT NULL,
  fecha_mov date[] NOT NULL,
  calmacen integer NOT NULL,
  status character(13) NOT NULL DEFAULT 'PENDIENTE'::bpchar, -- PENDIENTE...
  mes integer NOT NULL,
  "año" integer NOT NULL,
  donado integer NOT NULL DEFAULT 0,

  /* You have actually a 2-column Primary Key */
  PRIMARY KEY (tipo, id)

);

-- Create a trigger function to generate 'id'
CREATE FUNCTION art_movimientos_insert_trigger()
    RETURNS trigger
AS 
$$
BEGIN
    /* Compute "id", as the following id for a certain "tipo" */
    new.id = coalesce( 
                  (SELECT max(id) + 1 
                     FROM art_movimientos a 
                    WHERE a.tipo = new.tipo), 1); 
    return new; 
END 
$$
    LANGUAGE 'plpgsql'
    VOLATILE ;

-- This trigger will be called whenever a new row is inserted, and "id" is 
-- not specified (i.e.: it defaults to null), or is specified as null
CREATE TRIGGER art_movimientos_ins_trg
    BEFORE INSERT
    ON art_movimientos
    FOR EACH ROW
    WHEN (new.id IS NULL)
    EXECUTE PROCEDURE art_movimientos_insert_trigger();

然后,您可以插入以下行(不带指定id列):

INSERT INTO art_movimientos
   (tipo, documento, fecha_doc, fecha_mov, calmacen, mes, "año")
VALUES
   ('AJE', 1, array['20170128'::date], array['20170128'::date], 1, 1, 2017), 
   ('AJS', 2, array['20170128'::date], array['20170128'::date], 1, 1, 2017), 
   ('SI',  3, array['20170128'::date], array['20170128'::date], 1, 1, 2017), 
   ('AJE', 4, array['20170128'::date], array['20170128'::date], 1, 1, 2017), 
   ('AJE', 5, array['20170128'::date], array['20170128'::date], 1, 1, 2017) ;

...并且看到你得到了你想要的东西:

SELECT 
    id, tipo 
FROM 
    art_movimientos 
ORDER BY 
    documento ;


| id | tipo |
|----|------|
|  1 |  AJE |
|  1 |  AJS |
|  1 |  SI  |
|  2 |  AJE |
|  3 |  AJE |

你可以检查一下SQLFiddle(这对PL / pgSQL函数和分号有点挑剔)。

旁注:由于死锁和/或竞争条件,可能会出现一些极端情况,此过程可能会在事务中失败,如果其他事务也同时将数据插入同一个表中。因此,您的整体代码应该能够处理中止的事务,并重试它们或向用户显示错误。