Son eklenen kimlik için PostgreSQL işlevi


321

PostgreSQL'de son kimliği tabloya nasıl ekleyebilirim?

MS SQL'de SCOPE_IDENTITY () vardır.

Lütfen böyle bir şey kullanmamı tavsiye etmeyin:

select max(id) from table

max fonksiyonundan neden nefret ediyorsunuz? Bence çok basit. Güvenlik gibi bir sorun var mı?
jeongmin.cha

9
@ jeongmin.cha arasında başka işlemler ve daha fazla ekleme (eşzamanlı işlemler) varsa, açıkça bir kilit almadıkça ve serbest bırakmadığınız sürece maksimum kimlik değiştiği anlamına gelir
rohanagarwal

Kullanım amacı, son eklenen kimliği sonraki bir eklemenin değerinin bir parçası olarak kullanmaksa, bu soruya bakın .
Flux

Yanıtlar:


606

( tl;dr: seçenek 3'e dön: RETURNING ile ekle)

Postgresql'de tablolar için "id" kavramı bulunmadığını hatırlayın, sadece sekanslar (bunlar tipik ancak zorunlu birincil anahtarlar için SERIAL sözde türüyle varsayılan değerler olarak kullanılmaz ).

Yeni eklenen bir satırın kimliğini almakla ilgileniyorsanız, birkaç yol vardır:


1. Seçenek: CURRVAL(<sequence name>);.

Örneğin:

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval('persons_id_seq');

Dizinin adı bilinmelidir, gerçekten keyfi; bu örnekte, tablonun sözde türle oluşturulmuş personsbir idsütunu olduğunu varsayıyoruz SERIAL. Buna güvenmekten kaçınmak ve daha temiz hissetmek için bunun yerine şunları kullanabilirsiniz pg_get_serial_sequence:

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval(pg_get_serial_sequence('persons','id'));

Uyarı: currval()yalnızca aynı oturumdaINSERT (yürütülmüş nextval()) sonra çalışır .


Seçenek 2: LASTVAL();

Bu öncekine benzer, sadece dizi adını belirtmenize gerek yoktur: en son değiştirilen diziyi arar (her zaman oturumunuzun içinde, yukarıdakiyle aynı uyarı).


Her ikisi de CURRVALve LASTVALtamamen eşzamanlı güvenlidir. PG'deki dizinin davranışı, farklı oturumun müdahale etmeyeceği şekilde tasarlanmıştır, bu nedenle yarış koşulları riski yoktur (başka bir oturum INSERT ile SELECT'im arasına başka bir satır eklerse, yine de doğru değerimi alırım).

Ancak ince bir potansiyel problemleri var. Veritabanının, tabloya eklenirken, diğer tablolara bazı ek eklemeler yapan bazı TRIGGER (veya KURAL) varsa persons... LASTVALmuhtemelen bize yanlış değer verecektir. CURRVALEk personstablolar aynı tabloya yapıldıysa bile sorun ortaya çıkabilir (bu çok daha az olağandır, ancak risk hala mevcuttur).


Seçenek 3: INSERTileRETURNING

INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John') RETURNING id;

Bu, kimliği almanın en temiz, verimli ve güvenli yoludur. Bir öncekinin riskleri yok.

Dezavantajları? Neredeyse hiçbiri: INSERT ifadenizi arama şeklinizi değiştirmeniz gerekebilir (en kötü durumda, belki de API veya DB katmanınız bir INSERT'in bir değer döndürmesini beklemez); standart SQL değil (kimin umurunda); Postgresql 8.2'den beri mevcut (Aralık 2006 ...)


Sonuç: Yapabiliyorsanız, seçenek 3'e gidin. Başka bir yerde, 1'i tercih edin.

Not: Son eklenen kimliği global olarak almayı düşünüyorsanız (oturumunuz tarafından zorunlu değildir) tüm bu yöntemler işe yaramaz . Bunun için başvurmalısınız SELECT max(id) FROM table(elbette, bu diğer işlemlerden taahhüt edilmemiş kesici uçları okumaz).

Buna karşılık, ifadeniz tarafından yeni oluşturulan kimliği elde etmek için yukarıdaki 3 seçenekten biri yerine asla kullanmamalısınız , çünkü (performans dışında) bu aynı anda güvenli değildir: sizin ve başka bir oturumunuz arasında başka bir kayıt eklenmiş olabilir.SELECT max(id) FROM tableINSERTINSERTSELECT


25
LASTVAL (), başka bir tabloya kendi üzerine satır ekleyen bir tetikleyici / kural eklerseniz çok kötü olabilir.
Kouber Saparev

3
SELECT max(id)maalesef satırları silmeye başlar başlamaz işi yapmaz.
Simon A. Eugster

2
@leonbloy Bir şeyi kaçırmadıkça, kimlikleri 1,2,3,4,5olan satırlarınız varsa ve 4 ve 5 satırlarını silerseniz, son eklenen kimlik hala max()
5'tir

9
Bunu RETURNING id;başka bir tabloya eklemek için nasıl kullanılacağına dair bir örnek memnuniyetle karşılanacaktır!
Olivier Pons

8
Komut satırı aracına RETURNING idbeslenen bir SQL komut dosyasında nasıl kullanabilirim psql?
amoe

82

INSERT deyiminin RETURNING yantümcesine bakın . Temel olarak, INSERT bir sorgu olarak iki katına çıkar ve size eklenen değeri geri verir.


4
Sürüm 8.2 itibariyle çalışır ve en iyi ve en hızlı çözümdür.
Frank Heikens

9
belki adı geçen iade id nasıl hızlı bir açıklama?
Andrew

@Andrew soruyu anladığımdan emin değilim. Bir sorgudan nasıl sonuç alınacağını bilmiyor musunuz? Bu dile / kütüphaneye bağımlıdır ve bir seçim veya geri dönen bir ekleme yapmanıza bakılmaksızın aynı şekilde çalışmalıdır. Gelebileceğim diğer tek yorum, kimliğini aramadan başarıyla aldınız ve bunun ne için olduğunu bilmiyorsunuz ... bu durumda, neden alıyorsunuz?
kwatford

8
@Andrew, psql komut satırından şu çalıştırırsanız :, insert into names (firstname, lastname) values ('john', 'smith') returning id;o zaman select id from names where id=$lastiddoğrudan doğrudan koştuğunuz gibi id çıktısı verir. Eğer isterseniz aa değişkeninin içine dönüşünü kaydetmek , daha sonra insert into names (firstname, lastname) values ('john', 'smith') returning id into other_variable;dönen içeren deyim ise bir fonksiyonu son açıklamada , daha sonra kimliği olma returning'ed bir bütün olarak işlev tarafından döndürülür.
Alexander Bird

32

aşağıdaki gibi INSERT deyiminde RETURNING deyimini kullanabilirsiniz

wgzhao=# create table foo(id int,name text);
CREATE TABLE
wgzhao=# insert into foo values(1,'wgzhao') returning id;
 id 
----
  1
(1 row)

INSERT 0 1
wgzhao=# insert into foo values(3,'wgzhao') returning id;
 id 
----
  3
(1 row)

INSERT 0 1

wgzhao=# create table bar(id serial,name text);
CREATE TABLE
wgzhao=# insert into bar(name) values('wgzhao') returning id;
 id 
----
  1
(1 row)

INSERT 0 1
wgzhao=# insert into bar(name) values('wgzhao') returning id;
 id 
----
  2
(1 row)

INSERT 0 

28

Leonbloy'un cevabı oldukça eksiksiz. Ben sadece bir OPTION 3 tam olarak uymuyor bir PL / pgSQL fonksiyonu içinden son eklenen değeri almak için gereken özel bir durum eklemek istiyorum.

Örneğin, aşağıdaki tablolarımız varsa:

CREATE TABLE person(
   id serial,
   lastname character varying (50),
   firstname character varying (50),
   CONSTRAINT person_pk PRIMARY KEY (id)
);

CREATE TABLE client (
    id integer,
   CONSTRAINT client_pk PRIMARY KEY (id),
   CONSTRAINT fk_client_person FOREIGN KEY (id)
       REFERENCES person (id) MATCH SIMPLE
);

Bir müşteri kaydı eklememiz gerekirse, bir kişi kaydına başvurmalıyız. Ancak diyelim ki istemciye yeni bir kayıt ekleyen ama aynı zamanda yeni kişi kaydını ekleyen bir PL / pgSQL fonksiyonu tasarlamak istiyoruz. Bunun için leonbloy'un SEÇENEK 3'ün küçük bir varyasyonunu kullanmalıyız:

INSERT INTO person(lastname, firstname) 
VALUES (lastn, firstn) 
RETURNING id INTO [new_variable];

İki INTO yan tümcesi olduğunu unutmayın. Bu nedenle, PL / pgSQL işlevi şöyle tanımlanır:

CREATE OR REPLACE FUNCTION new_client(lastn character varying, firstn character varying)
  RETURNS integer AS
$BODY$
DECLARE
   v_id integer;
BEGIN
   -- Inserts the new person record and retrieves the last inserted id
   INSERT INTO person(lastname, firstname)
   VALUES (lastn, firstn)
   RETURNING id INTO v_id;

   -- Inserts the new client and references the inserted person
   INSERT INTO client(id) VALUES (v_id);

   -- Return the new id so we can use it in a select clause or return the new id into the user application
    RETURN v_id;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE;

Şimdi yeni verileri aşağıdakileri kullanarak ekleyebiliriz:

SELECT new_client('Smith', 'John');

veya

SELECT * FROM new_client('Smith', 'John');

Ve yeni oluşturulan kimliği alıyoruz.

new_client
integer
----------
         1

9

Tüm veri kaydını alması gerekenler için,

returning *

id de dahil olmak üzere tüm nesneyi almak için sorgunuzun sonuna getirin.


8

Aşağıdaki örneğe bakın

CREATE TABLE users (
    -- make the "id" column a primary key; this also creates
    -- a UNIQUE constraint and a b+-tree index on the column
    id    SERIAL PRIMARY KEY,
    name  TEXT,
    age   INT4
);

INSERT INTO users (name, age) VALUES ('Mozart', 20);

Son eklenen kimliği almak için bunu tablo "kullanıcı" seq sütun adı "id" için kullanın

SELECT currval(pg_get_serial_sequence('users', 'id'));


1

Bunu dene:

select nextval('my_seq_name');  // Returns next value

Bu 1 döndürürse (veya diziniz için başlangıç_sayısı ne olursa olsun), yanlış bayrağı ileterek diziyi orijinal değerine sıfırlayın:

select setval('my_seq_name', 1, false);

Aksi takdirde,

select setval('my_seq_name', nextValue - 1, true);

Bu işlem dizi değerini orijinal durumuna geri yükler ve "setval" aradığınız dizi değeriyle geri döner.


1

Postgres, aynı sorguda kimliği veya sorgunun geri dönmesini istediğiniz her şeyi döndüren dahili bir mekanizmaya sahiptir. işte bir örnek. Sütun1 ve sütun2 içeren 2 tablonuzun oluşturulduğunu ve sütun1'in her eklemeden sonra döndürülmesini istediğinizi düşünün.

# create table users_table(id serial not null primary key, name character varying);
CREATE TABLE
#insert into users_table(name) VALUES ('Jon Snow') RETURNING id;
 id 
----
  1
(1 row)

# insert into users_table(name) VALUES ('Arya Stark') RETURNING id;
 id 
----
  2
(1 row)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.