ฉันจะใช้ currval () ใน PostgreSQL เพื่อรับรหัสที่แทรกล่าสุดได้อย่างไร


64

ฉันมีโต๊ะ:

CREATE TABLE names (id serial, name varchar(20))

ฉันต้องการ "id ที่แทรกล่าสุด" จากตารางนั้นโดยไม่ต้องใช้การRETURNING idแทรก ดูเหมือนจะมีฟังก์ชั่นCURRVAL()แต่ฉันไม่เข้าใจวิธีการใช้งาน

ฉันได้ลองกับ:

SELECT CURRVAL() AS id FROM names_id_seq
SELECT CURRVAL('names_id_seq')
SELECT CURRVAL('names_id_seq'::regclass)

แต่ไม่มีใครทำงาน ฉันcurrval()จะใช้เพื่อรับ id ที่แทรกล่าสุดได้อย่างไร


2
ผู้อ่านของปัญหา / วิธีแก้ปัญหานี้ควรทราบว่าการใช้งานของกระแส () เป็นกำลังใจโดยทั่วไปเป็นประโยคส่งกลับให้ตัวบ่งชี้โดยไม่ต้องมีค่าใช้จ่ายของแบบสอบถามเพิ่มเติมและไม่มีความเป็นไปได้ของการคืนค่าผิด (กระแสที่จะทำ บางกรณีใช้งาน.)
chander

1
@chander: คุณมีการอ้างอิงสำหรับการเรียกร้องใด ๆ การใช้งานcurrval() นั้นไม่ทำให้หมดกำลังใจอย่างแน่นอน
a_horse_with_no_name

อาจเป็นเรื่องของความเห็นที่ว่าการใช้กระแสข้อมูลนั้นท้อแท้หรือไม่ แต่ในบางกรณีผู้ใช้ควรตระหนักว่ามันอาจส่งมอบคุณค่าที่ไม่ใช่สิ่งที่คุณคาดหวัง (ทำให้การเลือกทางเลือกที่ดีกว่าที่สนับสนุน) สมมติว่าคุณ มีตาราง A ที่ใช้ลำดับ a_seq และตาราง B ซึ่งใช้ a_seq (การเรียก nextval ('a_seq') สำหรับคอลัมน์ PK) สมมติว่าคุณมีทริกเกอร์ (a_trg) ที่แทรกลงในตาราง B บน INSERT กับตาราง A ใน ในกรณีนั้นฟังก์ชั่น currval () (หลังจากใส่บนโต๊ะ A) จะคืนค่าจำนวนที่สร้างขึ้นสำหรับการแทรกบนตาราง B ไม่ใช่ตาราง A
chander

คำตอบ:


51

หากคุณสร้างคอลัมน์ตามserialPostgreSQL จะสร้างลำดับให้โดยอัตโนมัติ

ชื่อของลำดับคือสร้างโดยอัตโนมัติและมักจะเป็น tablename_columnname_seq names_id_seqในกรณีของคุณลำดับจะเป็นชื่อ

หลังจากแทรกลงในตารางคุณสามารถโทรcurrval()ด้วยชื่อลำดับที่:

postgres=> CREATE TABLE names in schema_name (id serial, name varchar(20));
CREATE TABLE
postgres=> insert into names (name) values ('Arthur Dent');
INSERT 0 1
postgres=> select currval('names_id_seq');
 currval
---------
       1
(1 row)
postgres=>

แทน hardcoding ชื่อลำดับคุณสามารถใช้pg_get_serial_sequence()แทน:

select currval(pg_get_serial_sequence('names', 'id'));

ด้วยวิธีนี้คุณไม่จำเป็นต้องพึ่งพากลยุทธ์การตั้งชื่อที่ Postgres ใช้

หรือถ้าคุณไม่ต้องการใช้ชื่อลำดับเลยให้ใช้ lastval()


ฉันเดาว่ามันไม่ดีที่จะใช้currval()ในการตั้งค่าผู้ใช้หลายคน เช่นบนเว็บเซิร์ฟเวอร์
Jonas

9
ไม่คุณเข้าใจผิด currval()คือ "ท้องถิ่น" สำหรับการเชื่อมต่อปัจจุบันของคุณ ดังนั้นจึงไม่มีปัญหาในการใช้งานในสภาพแวดล้อมที่มีผู้ใช้หลายคน นั่นคือจุดประสงค์ทั้งหมดของลำดับ
a_horse_with_no_name

1
สิ่งนี้อาจแตกในกรณี (ค่อนข้างหายาก) ซึ่งการแทรกของคุณจะทำให้แทรกเพิ่มขึ้นในตารางเดียวกันได้ไหม
leonbloy

1
@a_horse_with_no_name: ฉันกำลังคิดที่จะไม่แทรกหลาย ๆ อัน (เป็นเรื่องง่ายที่จะมองเห็น) แต่เป็นทริกเกอร์ (อาจไม่ทราบ) ที่กำหนดไว้บนโต๊ะ ลองตัวอย่างนี้จากด้านบน: gist.github.com/anonymous/9784814
leonbloy

1
ไม่ใช่ชื่อตารางและคอลัมน์เสมอ หากมีลำดับที่มีชื่อนั้นอยู่แล้วมันจะสร้างลำดับใหม่โดยการต่อหรือเพิ่มหมายเลขในตอนท้ายและอาจต้องทำให้ชื่อสั้นลงถ้าเกินขีด จำกัด (ซึ่งปรากฏเป็นอักขระ 62 ตัว)
PhilHibbs

47

ตรงนี้มาจาก Stack Overflow

@a_horse_with_no_name และ @Jack Douglas ชี้ให้เห็นว่ากระแสข้อมูลจะทำงานเฉพาะกับเซสชันปัจจุบันเท่านั้น ดังนั้นหากคุณตกลงกับความจริงที่ว่าผลลัพธ์อาจได้รับผลกระทบจากการทำธุรกรรมที่ไม่มีข้อผูกมัดของเซสชันอื่นและคุณยังต้องการบางสิ่งที่จะทำงานข้ามเซสชันคุณสามารถใช้สิ่งนี้:

SELECT last_value FROM your_sequence_name;

ใช้ลิงก์ไปที่ SO เพื่อรับข้อมูลเพิ่มเติม

จากเอกสารของ Postgresมีการระบุไว้อย่างชัดเจนว่า

เป็นข้อผิดพลาดในการเรียกใช้ lastval หาก nextval ยังไม่ได้ถูกเรียกใช้ในเซสชันปัจจุบัน

ดังนั้นฉันคิดว่าการพูดอย่างเคร่งครัดเพื่อที่จะใช้ currval หรือ last_value สำหรับลำดับในเซสชั่นคุณจะต้องทำอะไรแบบนั้นเหรอ?

SELECT setval('serial_id_seq',nextval('serial_id_seq')-1);

แน่นอนว่าคุณจะไม่มีส่วนแทรกหรือวิธีอื่นใดในการใช้เขตข้อมูลอนุกรมในเซสชันปัจจุบัน


3
ฉันไม่สามารถคิดถึงสถานการณ์ที่จะเป็นประโยชน์
ypercubeᵀᴹ

ฉันแค่สงสัยว่านี่เป็นวิธีที่จะได้รับกระแสถ้า nextval ไม่ได้ถูกเรียกในเซสชั่นปัจจุบัน ข้อเสนอแนะใด ๆ
Slak

ฉันต้องทำสิ่งนี้เมื่อฉันเป็นคีย์หลักที่ยากสำหรับการสร้างข้อมูลการติดตั้งที่ฉันต้องการค่า PK ที่โดยปกติจะถูกสร้างขึ้นทีละน้อยเพื่อกำหนดล่วงหน้าก่อนเวลาเพื่อให้ลูกค้าทดสอบง่ายขึ้น เพื่อสนับสนุนการแทรกเมื่อทำเช่นนั้นในคอลัมน์ที่ควบคุมโดยค่าเริ่มต้นจากnextval()คุณต้องกำหนดลำดับให้ตรงกับจำนวนของบันทึกการติดตั้งที่คุณแทรกด้วย hardcoded ID นอกจากนี้วิธีการแก้ปัญหาของ currval () / lastval () ที่ไม่สามารถใช้งานได้ pre-nextval ก็คือSELECTการเรียงลำดับโดยตรง
Peter M. Elias

@ ypercubeᵀᴹตรงกันข้ามฉันคิดได้ว่าไม่มีเหตุผลที่ดีที่จะใช้คำตอบ "ถูกต้อง" ที่เลือกไว้ คำตอบนี้ไม่จำเป็นต้องแทรกบันทึกลงในตาราง นี่เป็นคำตอบของคำถามเช่นกัน อีกครั้งฉันคิดว่าไม่มีเหตุผลที่ดีที่จะไม่ใช้คำตอบนี้กับคำตอบที่เลือก
Henley Chiu

1
คำตอบนี้ไม่เกี่ยวข้องกับการปรับเปลี่ยนตารางเลย สิ่งที่เลือกทำ เป็นการดีที่คุณเพียงแค่ต้องการทราบรหัสล่าสุดโดยไม่ทำการเปลี่ยนแปลงใด ๆ เลย เกิดอะไรขึ้นถ้านี่คือฐานข้อมูลการผลิต คุณไม่สามารถแทรกแถวสุ่มโดยไม่มีการกระทบได้ ดังนั้นคำตอบนี้ปลอดภัยกว่าและถูกต้อง
Henley Chiu

14

คุณต้องโทรnextvalหาลำดับนี้ในเซสชันนี้ก่อนcurrval :

create sequence serial;
select nextval('serial');
 nextval
---------
       1
(1 row)

select currval('serial');
 currval
---------
       1
(1 row)

ดังนั้นคุณไม่สามารถหา 'id ที่แทรกล่าสุด' จากลำดับเว้นแต่ว่าinsertจะทำในเซสชันเดียวกัน (ธุรกรรมอาจย้อนกลับ แต่ลำดับจะไม่)

ตามที่ระบุไว้ในคำตอบของ a_horse create tableด้วยคอลัมน์ประเภทserialจะสร้างลำดับโดยอัตโนมัติและใช้มันเพื่อสร้างค่าเริ่มต้นสำหรับคอลัมน์ดังนั้นการinsertเข้าถึงnextvalโดยปริยาย:

create table my_table(id serial);
NOTICE:  CREATE TABLE will create implicit sequence "my_table_id_seq" for 
         serial column "my_table.id"

\d my_table
                          Table "stack.my_table"
 Column |  Type   |                       Modifiers
--------+---------+-------------------------------------------------------
 id     | integer | not null default nextval('my_table_id_seq'::regclass)

insert into my_table default values;
select currval('my_table_id_seq');
 currval
---------
       1
(1 row)

3

ดังนั้นจึงมีปัญหาบางอย่างกับวิธีการต่าง ๆ เหล่านี้:

Currval จะรับเฉพาะค่าสุดท้ายที่สร้างขึ้นในเซสชันปัจจุบันซึ่งดีมากหากคุณไม่มีสิ่งใดที่สร้างคุณค่า แต่ในกรณีที่คุณอาจเรียกทริกเกอร์และ / หรือมีลำดับขั้นสูงมากกว่าหนึ่งครั้งในธุรกรรมปัจจุบัน จะไม่คืนค่าที่ถูกต้อง นั่นไม่ใช่ปัญหาสำหรับ 99% ของคนที่อยู่ที่นั่น - แต่มันเป็นสิ่งที่เราควรคำนึงถึง

วิธีที่ดีที่สุดในการรับตัวระบุที่ไม่ซ้ำกันที่กำหนดหลังจากการดำเนินการแทรกกำลังใช้ส่วนคำสั่ง RETURNING ตัวอย่างด้านล่างถือว่าคอลัมน์ที่เชื่อมโยงกับลำดับเรียกว่า "id":

insert into table A (cola,colb,colc) values ('val1','val2','val3') returning id;

โปรดทราบว่าประโยชน์ของประโยค RETURNING นั้นทำได้ดีกว่าการรับลำดับเนื่องจากมันจะ:

  • ส่งคืนค่าที่ใช้สำหรับ "การแทรกครั้งสุดท้าย" (หลังจากตัวอย่างเช่นทริกเกอร์ก่อนอาจเปลี่ยนแปลงข้อมูลที่ถูกแทรก)
  • ส่งคืนค่าที่ถูกลบ:

    ลบออกจากตาราง A โดยที่ id> 100 ส่งคืน *

  • ส่งคืนแถวที่ถูกดัดแปลงหลังจาก UPDATE:

    update table A set X = 'y' โดยที่ blah = 'blech' กำลังกลับมา *

  • ใช้ผลลัพธ์ของการลบสำหรับการอัปเดต:

    ด้วย A as (ลบ * จากตาราง A เป็น id ที่ส่งคืน) อัปเดตชุด B ที่ถูกลบ = true โดยที่ id ใน (เลือก id จาก A);


แน่นอนว่า OP บอกอย่างชัดเจนว่าพวกเขาไม่ต้องการใช้RETURNINGประโยค - แต่ไม่มีอะไรผิดปกติกับการใช้ประโยชน์ให้ชัดเจนกับผู้อื่น
RDFozz

ฉันแค่พยายามชี้ให้เห็นถึงข้อผิดพลาดของวิธีการอื่น ๆ (ในกรณีที่ไม่มีใครระมัดระวังก็สามารถคืนค่าอื่นนอกเหนือจากค่าที่คาดไว้) - และชี้แจงวิธีปฏิบัติที่ดีที่สุด
สวดมนต์

1

ฉันต้องดำเนินการสืบค้นแม้จะใช้ SQLALchemy เพราะฉันไม่ประสบความสำเร็จในการใช้กระแสข้อมูล

nextId = db.session.execute("select last_value from <table>_seq").fetchone()[0] + 1

นี่เป็นโครงการ python flask + postgresql



1

คุณต้องGRANTใช้สคีมาเช่นนี้:

GRANT USAGE ON SCHEMA schema_name to user;

และ

GRANT ALL PRIVILEGES ON schema_name.sequence_name TO user;

1
ยินดีต้อนรับสู่ dba.se! ส่งเสียงเชียร์ในโพสต์แรกของคุณ! ดูเหมือนว่าโพสต์ต้นฉบับจะเกี่ยวกับสิทธิ์โดยเฉพาะ อาจลองขยายคำตอบของคุณเพื่อรวมเฉพาะบางอย่างเกี่ยวกับการอนุญาตล้มเหลวเมื่อเรียกใช้currval()ฟังก์ชันเพื่อให้เกี่ยวข้องกับเธรดนี้อีกเล็กน้อย
Peter Vandivier

0

ใน PostgreSQL 11.2 คุณสามารถปฏิบัติตามลำดับเหมือนตาราง:

ตัวอย่างถ้าคุณมีลำดับชื่อ: 'names_id_seq'

select * from names_id_seq;
 last_value | log_cnt | is_called
------------+---------+-----------
          4 |      32 | t
(1 row)

ที่ควรให้ ID ที่แทรกล่าสุด (4 ในกรณีนี้) ซึ่งหมายความว่าค่าปัจจุบัน (หรือค่าที่ควรใช้สำหรับรหัสถัดไป) ควรเป็น 5


-2

PostgreSQL รุ่นต่างๆอาจมีฟังก์ชั่นต่างกันเพื่อรับ id ลำดับปัจจุบันหรือถัดไป

ก่อนอื่นคุณต้องรู้จักรุ่น Postgres ของคุณ ใช้รุ่นเลือก (); เพื่อรับรุ่น

ใน PostgreSQL 8.2.15 select last_value from schemaName.sequence_nameคุณจะได้รับรหัสลำดับปัจจุบันโดยใช้

หากข้อความข้างต้นไม่ทำงานคุณสามารถใช้ select currval('schemaName.sequence_name');


2
มีข้อพิสูจน์อะไรสำหรับเวอร์ชั่นที่แตกต่างกัน?
dezso
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.