Làm cách nào để sử dụng dòng điện () trong PostgreSQL để lấy id được chèn cuối cùng?


64

Tôi có một cái bàn:

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

Tôi muốn "id được chèn cuối cùng" từ bảng đó, mà không cần sử dụng RETURNING idkhi chèn. Dường như có một chức năng CURRVAL(), nhưng tôi không hiểu cách sử dụng nó.

Tôi đã thử với:

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

nhưng không ai trong số họ làm việc Làm thế nào tôi có thể sử dụng currval()để có được id chèn cuối cùng?


2
Người đọc về vấn đề / giải pháp này nên biết rằng việc sử dụng dòng điện () thường không được khuyến khích vì mệnh đề RETURNING cung cấp mã định danh mà không cần chi phí truy vấn bổ sung và không có khả năng trả về GIÁ TRỊ SAU (mà dòng chảy sẽ làm trong một số trường hợp sử dụng.)
chander

1
@chander: bạn có tài liệu tham khảo nào cho yêu cầu đó không? Việc sử dụng currval() là chắc chắn không nản lòng.
a_horse_with_no_name

Có lẽ vấn đề là liệu việc sử dụng dòng điện có được khuyến khích hay không, nhưng trong một số trường hợp nhất định, người dùng nên lưu ý rằng nó có thể mang lại một giá trị không như bạn mong đợi (do đó làm cho RETURNING trở thành lựa chọn tốt hơn khi được hỗ trợ.) Giả sử bạn có bảng A sử dụng chuỗi a_seq và bảng B cũng sử dụng a_seq (gọi nextval ('a_seq') cho cột PK.) Giả sử bạn cũng có một trình kích hoạt (a_trg) chèn vào bảng B ON INSERT cho bảng A. Trong trong trường hợp đó, hàm currentval () (sau khi chèn vào bảng A) sẽ trả về số được tạo cho phần chèn trên bảng B, chứ không phải bảng A.
chander

Câu trả lời:


51

Nếu bạn tạo một cột như serialPostgreSQL sẽ tự động tạo một chuỗi cho điều đó.

Tên của chuỗi được tự động tạo và luôn là tablename_columnname_seq, trong trường hợp của bạn, chuỗi sẽ là tên names_id_seq.

Sau khi chèn vào bảng, bạn có thể gọi currval()với tên trình tự đó:

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=>

Thay vì mã hóa tên chuỗi, bạn cũng có thể sử dụng pg_get_serial_sequence()thay thế:

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

Bằng cách đó, bạn không cần phải dựa vào chiến lược đặt tên mà Postgres sử dụng.

Hoặc nếu bạn không muốn sử dụng tên trình tự, hãy sử dụng lastval()


Tôi đoán nó không tốt để sử dụng currval()trong thiết lập nhiều người dùng. Ví dụ: trên một máy chủ web.
Jonas

9
Không có bạn nhầm. currval()là "cục bộ" cho kết nối hiện tại của bạn. Vì vậy, không có vấn đề sử dụng nó trong một môi trường nhiều người dùng. Đó là toàn bộ mục đích của một chuỗi.
a_horse_with_no_name

1
Điều này có thể phá vỡ trong trường hợp (khá hiếm) trong đó phần chèn của bạn kích hoạt nhiều phần chèn hơn trong cùng một bảng, phải không?
leonbloy

1
@a_horse_with_no_name: Tôi đã nghĩ rằng không phải trên nhiều phần chèn (điều đó dễ phát hiện) mà là các trình kích hoạt (có lẽ chưa biết) được xác định trên bảng. Hãy thử ví dụ này ở trên: gist.github.com/anonymous/9784814
leonbloy

1
Đó không phải là LUÔN tên bảng và cột. Nếu đã có một chuỗi có tên đó thì nó sẽ tạo ra một chuỗi mới bằng cách nối hoặc tăng một số ở cuối và có thể cần phải rút ngắn tên nếu vượt quá giới hạn (dường như là 62 ký tự).
PhilHibbs 21/07/2016

47

Đây là trực tiếp từ Stack Overflow

Như được chỉ ra bởi @a_horse_with_no_name và @Jack Douglas, dòng điện chỉ hoạt động với phiên hiện tại. Vì vậy, nếu bạn ổn với thực tế là kết quả có thể bị ảnh hưởng bởi giao dịch không được cam kết của một phiên khác và bạn vẫn muốn một cái gì đó sẽ hoạt động trong các phiên, bạn có thể sử dụng điều này:

SELECT last_value FROM your_sequence_name;

Sử dụng liên kết đến SO để biết thêm thông tin.

Từ tài liệu của Postgres , rõ ràng là

Sẽ là một lỗi khi gọi Lastval nếu nextval chưa được gọi trong phiên hiện tại.

Vì vậy, tôi đoán nói đúng để sử dụng đúng cách dòng hoặc giá trị cuối cho một chuỗi trong các phiên, bạn sẽ cần phải làm gì đó như thế?

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

Tất nhiên, giả sử rằng bạn sẽ không có chèn hoặc bất kỳ cách sử dụng trường nối tiếp nào khác trong phiên hiện tại.


3
Tôi không thể nghĩ về một tình huống khi điều này sẽ hữu ích.
ypercubeᵀᴹ

Tôi chỉ tự hỏi liệu đây có phải là một cách để có được dòng điện hay không, nếu nextval không được gọi trong phiên hiện tại. Có gợi ý nào không?
Slak

Tôi đã phải làm điều này khi tôi mã hóa khóa chính cho dữ liệu vật cố được tạo trong đó tôi muốn các giá trị PK thường được tạo tăng dần để được xác định trước để giúp kiểm tra máy khách dễ dàng hơn. Để hỗ trợ chèn khi thực hiện điều đó trên một cột thường được điều chỉnh bởi giá trị mặc định từ nextval()bạn, sau đó phải đặt chuỗi theo cách thủ công để khớp với số lượng bản ghi lịch thi đấu mà bạn đã chèn với ID được mã hóa cứng. Ngoài ra, cách giải quyết vấn đề về dòng điện () / lastval () không có sẵn trước giá trị tiếp theo là chỉ SELECTtrực tiếp trên chuỗi.
Peter M. Elias

@ ypercubeᵀᴹ Ngược lại, tôi không thể nghĩ ra lý do chính đáng nào để sử dụng câu trả lời "đúng" đã được chọn. Câu trả lời này không yêu cầu chèn một bản ghi vào bảng. Điều này trả lời câu hỏi là tốt. Một lần nữa, tôi có thể nghĩ rằng không có lý do chính đáng KHÔNG sử dụng câu trả lời này trên câu đã chọn.
Henley Chiu

1
Câu trả lời này không liên quan đến việc sửa đổi bảng nào cả. Người kiểm tra nào. Lý tưởng nhất là bạn chỉ muốn biết ID cuối cùng mà không thực hiện bất kỳ thay đổi nào. Nếu đây là DB sản xuất thì sao? Bạn không thể chỉ cần chèn một hàng ngẫu nhiên mà không có bộ gõ. Do đó, câu trả lời này là an toàn hơn cũng như chính xác.
Henley Chiu

14

Bạn cần gọi nextvalcho chuỗi này trong phiên này trướccurrval :

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

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

vì vậy bạn không thể tìm thấy 'id được chèn cuối cùng' từ chuỗi trừ khi insertđược thực hiện trong cùng một phiên (giao dịch có thể quay lại nhưng chuỗi sẽ không)

như được chỉ ra trong câu trả lời của a_horse, create tablevới một cột loại serialsẽ tự động tạo một chuỗi và sử dụng nó để tạo giá trị mặc định cho cột, do đó, insertthông thường truy cập nextvalngầm:

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

Vì vậy, có một số vấn đề với các phương pháp khác nhau:

Currval chỉ nhận được giá trị cuối cùng được tạo trong phiên hiện tại - thật tuyệt nếu bạn không có bất kỳ thứ gì khác tạo ra giá trị, nhưng trong trường hợp bạn có thể gọi trình kích hoạt và / hoặc chuỗi được nâng cao hơn một lần trong giao dịch hiện tại, nó sẽ không trả lại giá trị chính xác. Đó không phải là vấn đề đối với 99% những người ngoài kia - nhưng đó là điều mà người ta nên xem xét.

Cách tốt nhất để có được định danh duy nhất được gán sau một thao tác chèn là sử dụng mệnh đề RETURNING. Ví dụ dưới đây giả định rằng cột được gắn với chuỗi được gọi là "id":

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

Lưu ý rằng tính hữu ích của mệnh đề RETURNING vượt xa cả việc nhận chuỗi, vì nó cũng sẽ:

  • Trả về các giá trị được sử dụng cho "lần chèn cuối cùng" (ví dụ, sau đó, trình kích hoạt TRƯỚC có thể đã thay đổi dữ liệu được chèn.)
  • Trả về các giá trị đã bị xóa:

    xóa khỏi bảng A nơi id> 100 trở lại *

  • Trả về các hàng đã sửa đổi sau khi CẬP NHẬT:

    bảng cập nhật Một bộ X = 'y' trong đó blah = 'blech' trở lại *

  • Sử dụng kết quả xóa để cập nhật:

    VỚI A dưới dạng (xóa * khỏi bảng A dưới dạng id trả về) Cập nhật B được xóa = true trong đó id trong (chọn id từ A);


Tất nhiên, OP nói rằng họ không muốn sử dụng một RETURNINGđiều khoản - nhưng, không có gì sai khi làm cho lợi ích của việc sử dụng nó rõ ràng hơn với người khác.
RDFozz

Tôi thực sự chỉ đang cố gắng chỉ ra những cạm bẫy của các phương pháp khác nhau (trong đó trừ khi cẩn thận, nó có thể trả về một giá trị khác với giá trị mong đợi) - và làm rõ cách thực hành tốt nhất.
trò chuyện

1

Tôi đã phải thực hiện một truy vấn mặc dù sử dụng SQLALchemy vì tôi không thành công khi sử dụng dòng điện.

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

Đây là một dự án python bình + postgresql.



1

Bạn cần GRANTsử dụng trên lược đồ, như thế này:

GRANT USAGE ON SCHEMA schema_name to user;

GRANT ALL PRIVILEGES ON schema_name.sequence_name TO user;

1
Chào mừng đến với dba.se! Chúc mừng bài viết đầu tiên của bạn! Có vẻ như Bài đăng gốc đặc biệt liên quan đến quyền. Có lẽ xem xét mở rộng câu trả lời của bạn để bao gồm một số chi tiết cụ thể xung quanh các lỗi về quyền khi gọi currval()hàm để làm cho nó phù hợp hơn một chút với chủ đề này?
Peter Vandivier

0

Trong PostgreSQL 11.2, bạn có thể coi chuỗi như một bảng có vẻ như:

Ví dụ nếu bạn có một chuỗi có tên: 'name_id_seq'

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

Điều đó sẽ cung cấp cho bạn id được chèn cuối cùng (4 trong trường hợp này), có nghĩa là giá trị hiện tại (hoặc giá trị nên được sử dụng cho id tiếp theo) phải là 5.


-2

Các phiên bản khác nhau của PostgreSQL có thể có các chức năng khác nhau để có được id chuỗi hiện tại hoặc tiếp theo.

Đầu tiên, bạn phải biết phiên bản Postgres của bạn. Sử dụng phiên bản chọn (); để có được phiên bản.

Trong PostgreSQL 8.2.15, bạn có được id chuỗi hiện tại bằng cách sử dụng select last_value from schemaName.sequence_name.

Nếu tuyên bố trên không hoạt động, bạn có thể sử dụng select currval('schemaName.sequence_name');


2
Bất kỳ bằng chứng cho các phiên bản khác nhau làm điều đó khác nhau?
dezso
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.