ใน PostgreSQL ฉันจะแทรก id ล่าสุดลงในตารางได้อย่างไร
ใน MS SQL มี SCOPE_IDENTITY ()
โปรดอย่าแนะนำให้ฉันใช้สิ่งนี้:
select max(id) from table
ใน PostgreSQL ฉันจะแทรก id ล่าสุดลงในตารางได้อย่างไร
ใน MS SQL มี SCOPE_IDENTITY ()
โปรดอย่าแนะนำให้ฉันใช้สิ่งนี้:
select max(id) from table
คำตอบ:
( tl;dr
: ตัวเลือก goto 3: INSERT พร้อมการส่งคืน)
จำได้ว่าใน postgresql ไม่มีแนวคิด "id" สำหรับตารางเพียงแค่ซีเควนซ์ (ซึ่งโดยทั่วไปแล้ว แต่ไม่จำเป็นต้องใช้เป็นค่าเริ่มต้นสำหรับคีย์หลักตัวแทนพร้อมกับประเภทหลอกอนุกรม )
หากคุณสนใจรับ id ของแถวที่เพิ่งแทรกใหม่มีหลายวิธี:
ตัวเลือกที่ CURRVAL(<sequence name>);
1:
ตัวอย่างเช่น:
INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
SELECT currval('persons_id_seq');
ต้องทราบชื่อของลำดับนั้นโดยพลการจริงๆ ในตัวอย่างนี้เราสมมติว่าตารางpersons
มีid
คอลัมน์ที่สร้างขึ้นด้วยSERIAL
ประเภทหลอก เพื่อหลีกเลี่ยงการพึ่งพาสิ่งนี้และรู้สึกสะอาดมากขึ้นคุณสามารถใช้แทนpg_get_serial_sequence
:
INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
SELECT currval(pg_get_serial_sequence('persons','id'));
ข้อแม้: currval()
ทำงานเฉพาะหลังจากที่INSERT
(ซึ่งได้ดำเนินการnextval()
) ในช่วงเดียวกัน
ตัวเลือก 2: LASTVAL();
นี้คล้ายกับก่อนหน้านี้เฉพาะที่คุณไม่จำเป็นต้องระบุชื่อลำดับ: มันมองหาลำดับที่แก้ไขล่าสุด (มักจะอยู่ในเซสชั่นของคุณเสมอข้อแม้เหมือนข้างต้น)
ทั้งปลอดภัยCURRVAL
และLASTVAL
พร้อมกันทั้งหมด พฤติกรรมของลำดับใน PG ได้รับการออกแบบเพื่อให้เซสชันที่แตกต่างกันจะไม่รบกวนดังนั้นจึงไม่มีความเสี่ยงของสภาพการแข่งขัน (ถ้าเซสชันอื่นแทรกแถวอื่นระหว่าง INSERT และ SELECT ของฉันฉันยังคงได้รับค่าที่ถูกต้อง)
อย่างไรก็ตามพวกเขามีปัญหาที่อาจเกิดขึ้นได้ หากฐานข้อมูลมีTRIGGER (หรือ RULE) ที่แทรกลงในpersons
ตารางทำให้มีการแทรกพิเศษในตารางอื่น ๆ ... จากนั้นLASTVAL
อาจจะให้ค่าที่ไม่ถูกต้องกับเรา ปัญหาอาจเกิดขึ้นได้CURRVAL
หากการแทรกพิเศษเสร็จสิ้นลงในpersons
ตารางเดียวกัน(นี่เป็นเรื่องปกติน้อยกว่ามาก แต่ยังมีความเสี่ยงอยู่)
ตัวเลือก 3: INSERT
ด้วยRETURNING
INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John') RETURNING id;
นี่เป็นวิธีที่สะอาดมีประสิทธิภาพและปลอดภัยที่สุดในการรับรหัส มันไม่มีความเสี่ยงใด ๆ จากครั้งก่อน
ข้อเสีย? เกือบไม่มี: คุณอาจต้องแก้ไขวิธีที่คุณเรียกใช้คำสั่ง INSERT ของคุณ (ในกรณีที่เลวร้ายที่สุดบางทีเลเยอร์ API หรือ DB ของคุณอาจไม่คาดว่า INSERT จะส่งกลับค่า) ไม่ใช่ SQL มาตรฐาน (ใครสนใจ); มีให้ตั้งแต่ Postgresql 8.2 (ธ.ค. 2549 ... )
บทสรุป: ถ้าคุณสามารถไปได้สำหรับตัวเลือก 3 ที่อื่นชอบ 1
หมายเหตุ: วิธีการทั้งหมดนี้ไม่มีประโยชน์หากคุณต้องการรับรหัสที่แทรกล่าสุดทั่วโลก (ไม่จำเป็นต้องใช้ในเซสชันของคุณ) สำหรับสิ่งนี้คุณต้องหันไปใช้SELECT max(id) FROM table
(แน่นอนสิ่งนี้จะไม่อ่านส่วนแทรกที่ไม่ได้รับการยอมรับจากการทำธุรกรรมอื่น ๆ )
ในทางกลับกันคุณไม่ควรใช้SELECT max(id) FROM table
ตัวเลือกใดตัวเลือกหนึ่งในสามตัวเลือกข้างต้นเพื่อให้ได้รหัสที่เพิ่งสร้างขึ้นโดยINSERT
คำสั่งของคุณเพราะ (นอกเหนือจากประสิทธิภาพ) สิ่งนี้ไม่ปลอดภัยพร้อมกัน: ระหว่างINSERT
คุณSELECT
กับเซสชันอื่นของคุณ
SELECT max(id)
ขออภัยที่ไม่ได้ทำงานทันทีที่คุณเริ่มลบแถว
1,2,3,4,5
และลบแถวที่ 4 และ 5 ID ที่แทรกล่าสุดยังคงเป็น 5 แต่max()
จะส่งคืน 3
RETURNING id;
ในการแทรกสิ่งนี้ในตารางอื่นจะได้รับการต้อนรับ!
RETURNING id
ภายในสคริปต์ SQL ที่ส่งไปยังpsql
เครื่องมือบรรทัดคำสั่งได้อย่างไร
ดูส่วนคำสั่งการส่งคืนของคำสั่งINSERT โดยพื้นฐานแล้ว INSERT จะเพิ่มการสืบค้นเป็นสองเท่าและให้คุณค่าที่แทรกกลับมา
insert into names (firstname, lastname) values ('john', 'smith') returning id;
, มันจะแสดงผล id เพียงแค่คุณรันselect id from names where id=$lastid
โดยตรง หากคุณต้องการบันทึกการคืนค่าลงในตัวแปร aaดังนั้นinsert into names (firstname, lastname) values ('john', 'smith') returning id into other_variable;
หากคำสั่งที่มีการส่งคืนเป็นคำสั่งสุดท้ายในฟังก์ชั่นดังนั้น id ที่เป็นreturning
'ed จะถูกส่งกลับโดยฟังก์ชันโดยรวม
คุณสามารถใช้คำสั่ง RETURNING ในคำสั่ง INSERT เหมือนดังต่อไปนี้
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
คำตอบของ Leonbloyค่อนข้างสมบูรณ์ ฉันจะเพิ่มเฉพาะกรณีพิเศษที่หนึ่งต้องได้รับมูลค่าแทรกล่าสุดจากภายในฟังก์ชั่น PL / pgSQL ที่ OPTION 3 ไม่พอดี
ตัวอย่างเช่นถ้าเรามีตารางต่อไปนี้:
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
);
หากเราจำเป็นต้องแทรกบันทึกลูกค้าเราจะต้องอ้างถึงบันทึกบุคคล แต่สมมติว่าเราต้องการประดิษฐ์ฟังก์ชัน PL / pgSQL ที่แทรกเร็กคอร์ดใหม่ลงในไคลเอนต์ แต่ยังดูแลการแทรกเร็กคอร์ดบุคคลใหม่ ด้วยเหตุนี้เราต้องใช้รูปแบบที่แตกต่างเล็กน้อยของตัวเลือกของ leonbloy 3:
INSERT INTO person(lastname, firstname)
VALUES (lastn, firstn)
RETURNING id INTO [new_variable];
โปรดทราบว่ามีสองข้อเป็น ดังนั้นฟังก์ชั่น PL / pgSQL จะถูกกำหนดเช่น:
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;
ตอนนี้เราสามารถแทรกข้อมูลใหม่โดยใช้:
SELECT new_client('Smith', 'John');
หรือ
SELECT * FROM new_client('Smith', 'John');
และเราได้รับ ID ที่สร้างขึ้นใหม่
new_client
integer
----------
1
สำหรับผู้ที่ต้องการบันทึกข้อมูลทั้งหมดคุณสามารถเพิ่ม
returning *
ท้ายแบบสอบถามของคุณเพื่อรับวัตถุทั้งหมดรวมถึงรหัส
ดูตัวอย่างด้านล่าง
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);
จากนั้นสำหรับการแทรก id ล่าสุดใช้สำหรับตาราง "ผู้ใช้" ชื่อคอลัมน์คอลัมน์ "id"
SELECT currval(pg_get_serial_sequence('users', 'id'));
SELECT CURRVAL(pg_get_serial_sequence('my_tbl_name','id_col_name'))
คุณต้องระบุชื่อตารางและชื่อคอลัมน์ของหลักสูตร
สิ่งนี้จะใช้สำหรับเซสชัน / การเชื่อมต่อปัจจุบัน http://www.postgresql.org/docs/8.3/static/functions-sequence.html
ลองสิ่งนี้:
select nextval('my_seq_name'); // Returns next value
หากสิ่งนี้คืนค่า 1 (หรืออะไรก็ตามที่เป็น start_value สำหรับลำดับของคุณ) ให้รีเซ็ตลำดับกลับเป็นค่าดั้งเดิมผ่านค่าสถานะเท็จ:
select setval('my_seq_name', 1, false);
มิฉะนั้น,
select setval('my_seq_name', nextValue - 1, true);
สิ่งนี้จะคืนค่าลำดับไปสู่สถานะเดิมและ "setval" จะกลับมาพร้อมกับค่าลำดับที่คุณกำลังมองหา
Postgres มีกลไกแบบ inbuilt เหมือนกันซึ่งในแบบสอบถามเดียวกันส่งคืนรหัสหรือสิ่งที่คุณต้องการให้แบบสอบถามส่งคืน นี่คือตัวอย่าง พิจารณาว่าคุณมีตารางที่มีคอลัมน์ 2 คอลัมน์ 1 และคอลัมน์ 2 และคุณต้องการให้คอลัมน์ 1 ถูกส่งคืนหลังจากแทรกทุกครั้ง
# 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)
ฉันมีปัญหานี้กับ Java และ Postgres ฉันแก้ไขโดยอัปเดตเวอร์ชัน Connector-J ใหม่
PostgreSQL-9.2-1002.jdbc4.jar
https://jdbc.postgresql.org/download.html : รุ่น 42.2.12