Подтвердить что ты не робот

Последовательность PostgreSQL на основе другого столбца

Скажем, у меня есть таблица как таковая:

Column   |     Type    |                        Notes
---------+------------ +----------------------------------------------------------
 id      | integer     | An ID that FK to some other table
 seq     | integer     | Each ID gets its own seq number
 data    | text        | Just some text, totally irrelevant.

id + seq - это комбинированный ключ.

Что я хотел бы увидеть:

ID  | SEQ   |                        DATA
----+------ +----------------------------------------------
 1  | 1     | Quick brown fox, lorem ipsum, lazy dog, etc etc.
 1  | 2     | Quick brown fox, lorem ipsum, lazy dog, etc etc.
 1  | 3     | Quick brown fox, lorem ipsum, lazy dog, etc etc.
 1  | 4     | Quick brown fox, lorem ipsum, lazy dog, etc etc.
 2  | 1     | Quick brown fox, lorem ipsum, lazy dog, etc etc.
 3  | 1     | Quick brown fox, lorem ipsum, lazy dog, etc etc.
 3  | 2     | Quick brown fox, lorem ipsum, lazy dog, etc etc.
 3  | 3     | Quick brown fox, lorem ipsum, lazy dog, etc etc.
 3  | 4     | Quick brown fox, lorem ipsum, lazy dog, etc etc.

Как вы можете видеть, комбинация id и seq уникальна.

Я не уверен, как настроить мою таблицу (или вставить инструкцию?), чтобы сделать это. Я хотел бы вставить id и data, что привело к тому, что seq была подпоследовательностью, зависящей от id.

4b9b3361

Ответ 1

Вы можете использовать функцию окна, чтобы присвоить свои значения SEQ, например:

INSERT INTO YourTable
    (ID, SEQ, DATA)
    SELECT ID, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY DATA), DATA
        FROM YourSource

Ответ 2

Нет проблем! Мы собираемся сделать две таблицы: things и stuff. stuff будет таблицей, которую вы описываете в своем вопросе, а things - это тот, на который он ссылается:

CREATE TABLE things (
    id serial primary key,
    name text
);

CREATE TABLE stuff (
    id integer references things,
    seq integer NOT NULL,
    notes text,
    primary key (id, seq)
);

Затем мы установим things с помощью триггера, который будет создавать новую последовательность каждый раз, когда будет создана строка:

CREATE FUNCTION make_thing_seq() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
begin
  execute format('create sequence thing_seq_%s', NEW.id);
  return NEW;
end
$$;

CREATE TRIGGER make_thing_seq AFTER INSERT ON things FOR EACH ROW EXECUTE PROCEDURE make_thing_seq();

Теперь мы закончим с thing_seq_1, thing_seq_2 и т.д. и т.д.

Теперь еще один триггер на stuff, чтобы он каждый раз использовал правильную последовательность:

CREATE FUNCTION fill_in_stuff_seq() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
begin
  NEW.seq := nextval('thing_seq_' || NEW.id);
  RETURN NEW;
end
$$;

CREATE TRIGGER fill_in_stuff_seq BEFORE INSERT ON stuff FOR EACH ROW EXECUTE PROCEDURE fill_in_stuff_seq();

Это гарантирует, что, когда строки войдут в stuff, столбец id используется для поиска правильной последовательности для вызова nextval on.

Здесь демонстрация:

test=# insert into things (name) values ('Joe');
INSERT 0 1
test=# insert into things (name) values ('Bob');
INSERT 0 1
test=# select * from things;
 id | name
----+------
  1 | Joe
  2 | Bob
(2 rows)

test=# \d
              List of relations
 Schema |     Name      |   Type   |  Owner
--------+---------------+----------+----------
 public | stuff         | table    | jkominek
 public | thing_seq_1   | sequence | jkominek
 public | thing_seq_2   | sequence | jkominek
 public | things        | table    | jkominek
 public | things_id_seq | sequence | jkominek
(5 rows)

test=# insert into stuff (id, notes) values (1, 'Keychain');
INSERT 0 1
test=# insert into stuff (id, notes) values (1, 'Pet goat');
INSERT 0 1
test=# insert into stuff (id, notes) values (2, 'Family photo');
INSERT 0 1
test=# insert into stuff (id, notes) values (1, 'Redundant lawnmower');
INSERT 0 1
test=# select * from stuff;
 id | seq |        notes
----+-----+---------------------
  1 |   1 | Keychain
  1 |   2 | Pet goat
  2 |   1 | Family photo
  1 |   3 | Redundant lawnmower
(4 rows)

test=#

Ответ 3

Если seq отражает (или должен отражать) порядок, в который вставлены строки, я предпочитаю использовать timestamp, который автоматически заполняется и генерирует порядковый номер "на лету" при выборе строк с помощью row_number():

create table some_table
( 
  id          integer   not null,
  inserted_at timestamp not null default current_timestamp,
  data text
);

Чтобы получить столбец seq, вы можете сделать:

select id,  
       row_number() over (partition by id order by inserted_at) as seq,
       data
from some_table
order by id, seq;

Выбор будет, однако, немного медленнее по сравнению с использованием сохраненного столбца seq (особенно с индексом id, seq).

Если это становится проблемой, вы можете либо изучить использование материализованного представления, либо добавить столбец seq, а затем обновить его на регулярной основе (я бы не сделал этого в триггере по причинам производительности).

Пример SQLFiddle: http://sqlfiddle.com/#!15/db69b/1

Ответ 4

Вы можете использовать Последовательности либо Это также отличный вариант

CREATE SEQUENCE SEQ_NUM START 1;

INSERT INTO TABLE_NAME(ID, SEQ, DATA) VALUES(**SOME_ID**, SELECT nextval('SEQ_NUM') AS SEQ_NUM, **SOME DATA**);

Теперь в этом случае значение Sequence "SEQ_NUM" будет полностью управляться PostgreSQL, теперь вам не нужно управлять никаким счетчиком или чем-либо еще.

Ответ 5

Просто догадаться.

INSERT INTO TABLE (ID, SEQ, DATA)
VALUES
(
 IDVALUE,
 (SELECT max(SEQ) +1 FROM TABLE WHERE ID = IDVALUU),
 DATAVALUE
);

Ответ 6

Вот простой способ использования стандартного SQL:

INSERT INTO mytable (id, seq, data)
SELECT << your desired ID >>,
       COUNT(*) + 1,
       'Quick brown fox, lorem ipsum, lazy dog, etc etc.'
FROM mytable
WHERE id = << your desired ID (same as above) >>;

Смотрите SQL Fiddle Demo.

(Если вы хотите быть немного умнее, вы можете подумать о создании trigger, чтобы обновить строку, используя тот же метод сразу после вставить.)

Ответ 7

PostgreSQL поддерживает сгруппированные уникальные столбцы:

CREATE TABLE example (
    a integer,
    b integer,
    c integer,
    UNIQUE (a, c)
);

См. Документация PostgreSQL - Раздел 5.3.3

Легко: -)

Ответ 8

У меня нет никакого опыта postgresql, но можете ли вы использовать подзапрос в своем заявлении insert? Что-то вроде, в Mysqlish,

INSERT INTO MYTABLE SET 
   ID=4, 
   SEQ=(  SELECT MAX(SEQ)+1 FROM MYTABLE WHERE ID=4  ),
   DATA="Quick brown fox, lorem ipsum, lazy dog, etc etc."