PostgreSQL Autoincrement


578

Я переходив з MySQL на PostgreSQL і мені було цікаво, як я можу робити значення самовдосконалення. Я бачив у документах PostgreSQL тип даних "серійний", але я отримую синтаксичні помилки при його використанні (в v8.0).


9
якщо ви надасте запит і помилку, яку ви отримуєте - можливо, хтось може сказати вам, що не так із запитом.

2
Мій перший удар теж Mich ', і як це питання, яке отримує достатньо переглядів, щоб бути актуальними, чому б не проголосувати його. PS це не банально, якщо ви не знаєте, як це зробити.
baash05

1
SERIAL - це кращий вибір, якщо вашим клієнтським драйвером є Npgsql. Постачальник внутрішньо вибирає нові значення після INSERT, використовуючи курсор SELECT (pg_get_serial_sequence ('table', 'column')). Це не вдасться, якщо нижній стовпець не буде типів серійного типу (наприклад, числовий тип + явна послідовність)
Олів'є МАТРОТ

Тільки для цікавості ... Чому комусь доводиться переходити з MySQL, що дуже добре, на PostgreSql?
віламеджія

17
... що ще краще.
Ромер

Відповіді:


701

Так, SERIAL - це еквівалентна функція.

CREATE TABLE foo (
id SERIAL,
bar varchar);

INSERT INTO foo (bar) values ('blah');
INSERT INTO foo (bar) values ('blah');

SELECT * FROM foo;

1,blah
2,blah

SERIAL - це лише макрос створення таблиці часу навколо послідовностей. Ви не можете змінити SERIAL на існуючий стовпець.


19
цитування назви таблиці - це дійсно погана практика
Еван Керролл

71
Цитувати назви таблиць - звичка, оскільки я успадкував БД, який мав змішані назви регістрів, а цитування імен таблиць - це вимога використання.
Трей

26
@Evan Carroll - Чому це шкідлива звичка (просто питати)?
Крістіан

27
тому що якщо у вас немає столу, "Table"а "table"потім просто залиште його без котирування і канонізуйте його table. Конвенція просто ніколи не використовує цитати в Pg. Ви можете, якщо хочете, використовувати змішані назви регістрів для появи, просто не вимагайте цього: CREATE TABLE fooBar ( .. ); SELECT * FROM fooBar;буде працювати, як буде SELECT * FROM foobar.
Еван Керролл

26
За документами postgres або постійно
цитуйте

226

Ви можете використовувати будь-який інший цілий тип даних , наприклад smallint.

Приклад:

CREATE SEQUENCE user_id_seq;
CREATE TABLE user (
    user_id smallint NOT NULL DEFAULT nextval('user_id_seq')
);
ALTER SEQUENCE user_id_seq OWNED BY user.user_id;

Краще використовувати власний тип даних, а не серійний тип даних користувача .


11
Я б сказав, що це насправді краща відповідь, оскільки це дозволило мені змінити таблицю, яку я тільки що створив у PostgreSQL, встановивши стовпці за замовчуванням (після читання на CREATE SEQUENCE postgresql.org/docs/8.1/interactive/sql-createsequence.html ) . ЯКЩО, я не зовсім впевнений, чому ви змінили власника.
JayC

12
@JayC: З документації : Нарешті, послідовність позначена як "належить" стовпцю, так що вона буде відкинута, якщо стовпець або таблиця буде випав.
користувач272735

8
чому спільнота postgres просто не винаходить ключове слово для автоматичного підвищення?
Д-р Део

2
@Dr Deo: вони використовують серійне замість ключового слова для автоматичного підвищення, я не знаю, чому :)
Ахмад

4
Існує також невеликий серіал, якщо ви просто хочете менший тип даних.
beldaz

110

Якщо ви хочете додати послідовність до ідентифікатора в таблиці, яка вже існує, ви можете використовувати:

CREATE SEQUENCE user_id_seq;
ALTER TABLE user ALTER user_id SET DEFAULT NEXTVAL('user_id_seq');

Що таке послідовність? Де AUTO_INCREMENT?
Зелений

23
@Green: AUTO_INCREMENT не входить до стандарту SQL, він специфічний для MySQL. Послідовності - це те, що робить аналогічну роботу в PostgreSQL.
beldaz

5
якщо ви використовуєте "id SERIAL", він автоматично створює послідовність у PostgreSQL. Назва цієї послідовності буде <ім'я таблиці> _ <ім'я стовпця> _seq
Джуд Нірошан

Вам не доведеться користуватися ALTER COLUMN user_id?
Алек

Я спробував цей метод, але я отримав помилку: ERROR: syntax error at or near "DEFAULT"Будь-які пропозиції?
Елі Фіалкофф

44

Хоча схожі послідовності є еквівалентом MySQL auto_increment, є деякі тонкі, але важливі відмінності:

1. Збільшити кількість запитів Послідовність / Послідовність

Серійний стовпчик збільшується при невдалих запитах. Це призводить до фрагментації від невдалих запитів, а не лише до видалення рядків. Наприклад, запустіть такі запити у вашій базі даних PostgreSQL:

CREATE TABLE table1 (
  uid serial NOT NULL PRIMARY KEY,
  col_b integer NOT NULL,
  CHECK (col_b>=0)
);

INSERT INTO table1 (col_b) VALUES(1);
INSERT INTO table1 (col_b) VALUES(-1);
INSERT INTO table1 (col_b) VALUES(2);

SELECT * FROM table1;

Ви повинні отримати такий вихід:

 uid | col_b 
-----+-------
   1 |     1
   3 |     2
(2 rows)

Зауважте, як uid переходить від 1 до 3 замість 1 до 2.

Це все-таки виникає, якщо ви вручну створили власну послідовність за допомогою:

CREATE SEQUENCE table1_seq;
CREATE TABLE table1 (
    col_a smallint NOT NULL DEFAULT nextval('table1_seq'),
    col_b integer NOT NULL,
    CHECK (col_b>=0)
);
ALTER SEQUENCE table1_seq OWNED BY table1.col_a;

Якщо ви хочете перевірити, чим відрізняється MySQL, запустіть наступне в базі даних MySQL:

CREATE TABLE table1 (
  uid int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
  col_b int unsigned NOT NULL
);

INSERT INTO table1 (col_b) VALUES(1);
INSERT INTO table1 (col_b) VALUES(-1);
INSERT INTO table1 (col_b) VALUES(2);

Ви повинні отримати наступне, без фрагментації :

+-----+-------+
| uid | col_b |
+-----+-------+
|   1 |     1 |
|   2 |     2 |
+-----+-------+
2 rows in set (0.00 sec)

2. Вручне встановлення значення послідовного стовпця може спричинити збій майбутніх запитів.

На це вказував @trev у попередній відповіді.

Для імітації цього вручну встановіть uid на 4, який згодом "зіткнеться".

INSERT INTO table1 (uid, col_b) VALUES(5, 5);

Дані таблиці:

 uid | col_b 
-----+-------
   1 |     1
   3 |     2
   5 |     5
(3 rows)

Виконати іншу вставку:

INSERT INTO table1 (col_b) VALUES(6);

Дані таблиці:

 uid | col_b 
-----+-------
   1 |     1
   3 |     2
   5 |     5
   4 |     6

Тепер якщо ви запустите іншу вставку:

INSERT INTO table1 (col_b) VALUES(7);

Не вдасться отримати таке повідомлення про помилку:

ПОМИЛКА: значення дублюючого ключа порушує унікальне обмеження "table1_pkey" ДЕТАЛІ: Key (uid) = (5) вже існує.

На відміну від цього, MySQL буде вирішувати це витончено, як показано нижче:

INSERT INTO table1 (uid, col_b) VALUES(4, 4);

Тепер вставити інший рядок без встановлення uid

INSERT INTO table1 (col_b) VALUES(3);

Запит не провалюється, uid просто переходить до 5:

+-----+-------+
| uid | col_b |
+-----+-------+
|   1 |     1 |
|   2 |     2 |
|   4 |     4 |
|   5 |     3 |
+-----+-------+

Тестування проводилося на MySQL 5.6.33, для Linux (x86_64) та PostgreSQL 9.4.9


10
Ви даєте порівняння, але я не бачу тут жодного рішення! Це відповідь?
Анвар

4
@Anwar він просто розширює різні відповіді, які стверджують, що відповідь полягає у використанні послідовного / послідовного. Це забезпечує деякий важливий контекст для врахування.
Programster

38

Починаючи з Postgres 10, також підтримуються стовпці ідентичності, визначені стандартом SQL:

create table foo 
(
  id integer generated always as identity
);

створює стовпчик ідентичності, який неможливо змінити, якщо прямо не вимагати. Наступна вставка не вдасться зі стовпцем, визначеним як generated always:

insert into foo (id) 
values (1);

Однак це може бути відмінено:

insert into foo (id) overriding system value 
values (1);

При використанні опції generated by defaultце по суті та сама поведінка, що і існуюча serialреалізація:

create table foo 
(
  id integer generated by default as identity
);

Коли значення подається вручну, базову послідовність потрібно також відрегулювати вручну - так само, як і у serialстовпці.


Стовпець ідентичності за замовчуванням не є первинним ключем (як і serialстовпець). Якщо це має бути одне, обмеження первинного ключа потрібно визначити вручну.


26

Вибачте, щоб повторно змінити старе запитання, але це було перше запитання / відповідь про переповнення стека, що з’явилося в Google.

Ця публікація (яка з’явилася першою в Google) розповідає про використання більш оновленого синтаксису для PostgreSQL 10: https://blog.2ndquadrant.com/postgresql-10-identity-column/

що трапляється:

CREATE TABLE test_new (
    id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
);

Сподіваюся, що це допомагає :)


1
Це дійсно шлях у PostgreSQL 10, і це той самий синтаксис, що і інше програмне забезпечення баз даних, як DB2 або Oracle.
Адріан

1
@adriaan Насправді GENERATED … AS IDENTITYкоманди є стандартними SQL. Спочатку додано в SQL: 2003 , потім уточнено в SQL: 2008 . Дивіться функції # T174 & F386 та T178
Василь Бурк

16

Ви повинні бути обережними, щоб не вставити безпосередньо у своє СЕРІЙНЕ або поле послідовності, інакше ваше запитання не вдасться, коли послідовність досягне введеного значення:

-- Table: "test"

-- DROP TABLE test;

CREATE TABLE test
(
  "ID" SERIAL,
  "Rank" integer NOT NULL,
  "GermanHeadword" "text" [] NOT NULL,
  "PartOfSpeech" "text" NOT NULL,
  "ExampleSentence" "text" NOT NULL,
  "EnglishGloss" "text"[] NOT NULL,
  CONSTRAINT "PKey" PRIMARY KEY ("ID", "Rank")
)
WITH (
  OIDS=FALSE
);
-- ALTER TABLE test OWNER TO postgres;
 INSERT INTO test("Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
           VALUES (1, '{"der", "die", "das", "den", "dem", "des"}', 'art', 'Der Mann küsst die Frau und das Kind schaut zu', '{"the", "of the" }');


 INSERT INTO test("ID", "Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
           VALUES (2, 1, '{"der", "die", "das"}', 'pron', 'Das ist mein Fahrrad', '{"that", "those"}');

 INSERT INTO test("Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
           VALUES (1, '{"der", "die", "das"}', 'pron', 'Die Frau, die nebenen wohnt, heißt Renate', '{"that", "who"}');

SELECT * from test; 

15

У контексті заданого питання та у відповідь на коментар @ sereja1c створюючи SERIALнеявно створює послідовності, тому для наведеного вище прикладу-

CREATE TABLE foo (id SERIAL,bar varchar);

CREATE TABLEнеявно створить послідовність foo_id_seqдля послідовного стовпця foo.id. Отже, SERIAL[4 байти] добре для простоти використання, якщо вам не потрібен певний тип даних для вашого ідентифікатора.



Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.