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

How create a column that increase according to the value of another column

我有一个table,我想把所有关于文章使用的信息放在这里,我需要创建一个自动增量的列,如果字段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;

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

我的table代码:

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
);

您可以通过使用 before insert 触发器来管理这种情况,模仿@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 函数和分号有点挑剔)。


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