Làm cách nào để đặt lại chuỗi khóa chính của postgres khi nó không đồng bộ?


523

Tôi gặp vấn đề là chuỗi khóa chính của tôi không đồng bộ với các hàng trong bảng.

Đó là, khi tôi chèn một hàng mới, tôi gặp lỗi khóa trùng lặp vì chuỗi được ngụ ý trong kiểu dữ liệu nối tiếp trả về một số đã tồn tại.

Nó dường như được gây ra bởi nhập / khôi phục không duy trì trình tự đúng.


Tôi tò mò .. bạn có bỏ db trước khi khôi phục không? Tôi có một hồi ức mờ nhạt về điều này xảy ra, nhưng tôi có thể sai: P
Arthur Thomas

25
Wiki PostgreSQL có một trang về Sửa lỗi trình tự .
Brad Koch

14
Chỉ để hỗ trợ khả năng hoạt động, thông báo lỗi được ném ở đây là: "giá trị khóa trùng lặp vi phạm ràng buộc duy nhất ..."
siêu sáng

4
Đây là cách sql resultencereset trong Django thực hiện: CHỌN setval (pg_get_serial_ resultence ("<table_name>", 'id'), coalesce (max ("id"), 1), max ("id") KHÔNG null) TỪ "< tên_bảng> ";
người dùng

Ví dụ đầu tiên của <tên bảng> cần được gói trong các dấu ngoặc đơn cho hàm pg_get_serioal_fterence để hoạt động: CHỌN setval (pg_get_serial_fterence ('<table_name>', 'id'), coalesce (max ("id"), 1) , max ("id") KHÔNG null) TỪ "<tên_bảng>"
nclu

Câu trả lời:


715
-- Login to psql and run the following

-- What is the result?
SELECT MAX(id) FROM your_table;

-- Then run...
-- This should be higher than the last result.
SELECT nextval('your_table_id_seq');

-- If it's not higher... run this set the sequence last to your highest id. 
-- (wise to run a quick pg_dump first...)

BEGIN;
-- protect against concurrent inserts while you update the counter
LOCK TABLE your_table IN EXCLUSIVE MODE;
-- Update the sequence
SELECT setval('your_table_id_seq', COALESCE((SELECT MAX(id)+1 FROM your_table), 1), false);
COMMIT;

Nguồn - Diễn đàn Ruby


12
Ở bất kỳ giá nào, việc thêm 1 đến MAX (id) sẽ để lại một khoảng cách số duy nhất trong ID của bạn, vì tập hợp giá trị nào là giá trị cuối cùng của chuỗi, không phải là giá trị tiếp theo.
mikl

6
Ví dụ của bạn sẽ không hoạt động nếu không có hàng trong bảng. Vì vậy, SQL được cung cấp dưới đây an toàn hơn: SELECT setval ('your_table_id_seq', coalesce ((chọn max (id) +1 từ your_table), 1), true);
Valery Viktorovsky

10
@Valery: Nhưng để tránh những khoảng trống được đề cập bởi @mikl hai bình luận ở trên, bạn cầnSELECT setval('your_table_id_seq', coalesce((select max(id)+1 from your_table), 1), false);
Antony Hatchkins

20
Tất cả các vấn đề được giải quyết và kết hợp thành một truy vấn duy nhất:SELECT setval('your_seq',(SELECT GREATEST(MAX(your_id)+1,nextval('your_seq'))-1 FROM your_table))
Frunsi

15
Nếu ứng dụng của bạn quan tâm đến các khoảng trống theo trình tự, ứng dụng của bạn bị hỏng. Các lỗ hổng trong chuỗi là bình thường và có thể xảy ra do tắt cơ sở dữ liệu ngoài dự kiến, phục hồi giao dịch sau lỗi, v.v.
Craig Ringer

202

pg_get_serial_sequencecó thể được sử dụng để tránh mọi giả định không chính xác về tên trình tự. Điều này đặt lại chuỗi trong một lần chụp:

SELECT pg_catalog.setval(pg_get_serial_sequence('table_name', 'id'), (SELECT MAX(id) FROM table_name)+1);

Hay chính xác hơn:

SELECT pg_catalog.setval(pg_get_serial_sequence('table_name', 'id'), MAX(id)) FROM table_name;

Tuy nhiên, biểu mẫu này không thể xử lý các bảng trống một cách chính xác, vì max (id) là null và bạn cũng không thể đặt giá trị 0 vì nó sẽ nằm ngoài phạm vi của chuỗi. Một cách giải quyết cho vấn đề này là sử dụng ALTER SEQUENCEcú pháp tức là

ALTER SEQUENCE table_name_id_seq RESTART WITH 1;
ALTER SEQUENCE table_name_id_seq RESTART; -- 8.4 or higher

Nhưng ALTER SEQUENCEđược sử dụng hạn chế vì tên trình tự và giá trị khởi động lại không thể là biểu thức.

Có vẻ như giải pháp đa năng tốt nhất là gọi setvalvới false là tham số thứ 3, cho phép chúng tôi chỉ định "giá trị tiếp theo sẽ sử dụng":

SELECT setval(pg_get_serial_sequence('t1', 'id'), coalesce(max(id),0) + 1, false) FROM t1;

Điều này đánh dấu vào tất cả các hộp của tôi:

  1. Tránh mã hóa cứng tên trình tự thực tế
  2. xử lý các bảng trống chính xác
  3. xử lý các bảng với dữ liệu hiện có và không để lại lỗ hổng trong chuỗi

Cuối cùng, lưu ý rằng pg_get_serial_sequencechỉ hoạt động nếu chuỗi được sở hữu bởi cột. Đây sẽ là trường hợp nếu cột tăng được xác định là một serialloại, tuy nhiên nếu trình tự được thêm bằng tay thì cần phải đảm bảo ALTER SEQUENCE .. OWNED BYcũng được thực hiện.

tức là nếu serialloại được sử dụng để tạo bảng, thì tất cả sẽ hoạt động:

CREATE TABLE t1 (
  id serial,
  name varchar(20)
);

SELECT pg_get_serial_sequence('t1', 'id'); -- returns 't1_id_seq'

-- reset the sequence, regardless whether table has rows or not:
SELECT setval(pg_get_serial_sequence('t1', 'id'), coalesce(max(id),0) + 1, false) FROM t1;

Nhưng nếu trình tự được thêm bằng tay:

CREATE TABLE t2 (
  id integer NOT NULL,
  name varchar(20)
);

CREATE SEQUENCE t2_custom_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;

ALTER TABLE t2 ALTER COLUMN id SET DEFAULT nextval('t2_custom_id_seq'::regclass);

ALTER SEQUENCE t2_custom_id_seq OWNED BY t2.id; -- required for pg_get_serial_sequence

SELECT pg_get_serial_sequence('t2', 'id'); -- returns 't2_custom_id_seq'

-- reset the sequence, regardless whether table has rows or not:
SELECT setval(pg_get_serial_sequence('t2', 'id'), coalesce(max(id),0) + 1, false) FROM t1;

11
Không cần '+1' trong truy vấn, setval()đặt giá trị hiện tại và nextval()sẽ trả về giá trị hiện tại +1.
Antony Hatchkins

1
Hàm bao bọc phương thức này có một tham số - tên_bảng - nằm trong câu trả lời của tôi dưới đây: stackoverflow.com/a/13308052/237105
Antony Hatchkins

@AntonyHatchkins chúc mừng. Tôi chỉ thấy một lần lặp lại của lỗi +1, vì vậy cuối cùng tôi đã thay đổi điều đó vì hy vọng
trì hoãn

99

Cách ngắn nhất và nhanh nhất :

SELECT setval('tbl_tbl_id_seq', max(tbl_id)) FROM tbl;

tbl_idserialcột của bảng tbl, vẽ từ chuỗi tbl_tbl_id_seq(là tên tự động mặc định).

Nếu bạn không biết tên của chuỗi được đính kèm (không phải ở dạng mặc định), hãy sử dụng pg_get_serial_sequence():

SELECT setval(pg_get_serial_sequence('tbl', 'tbl_id'), max(tbl_id)) FROM tbl;

Không có lỗi off-by-one ở đây. Mỗi tài liệu:

Biểu mẫu hai tham số đặt trường của chuỗi last_valuethành giá trị được chỉ định và đặt is_calledtrường của nó thành đúng, nghĩa là tiếp theo nextvalsẽ tiến trình tự trước khi trả về giá trị.

Nhấn mạnh đậm của tôi.

Nếu bảng có thể trống thực sự bắt đầu từ 1 trong trường hợp này:

SELECT setval(pg_get_serial_sequence('tbl', 'tbl_id')
            , COALESCE(max(tbl_id) + 1, 1)
            , false)
FROM tbl;

Chúng ta không thể chỉ sử dụng mẫu 2 paremater và bắt đầu với 0vì giới hạn dưới của chuỗi là 1 theo mặc định (trừ khi được tùy chỉnh).

Đồng thời

Không có biện pháp bảo vệ chống lại hoạt động chuỗi đồng thời hoặc ghi vào bảng trong các truy vấn trên. Nếu điều đó có liên quan, bạn có thể khóa bảng ở chế độ độc quyền. Nó giữ cho các giao dịch đồng thời không viết số cao hơn trong khi bạn đang cố gắng để có được đồng bộ hóa. (Nó cũng tạm thời chặn các văn bản vô hại không gây rối với số lượng tối đa.)

Nhưng nó không đưa khách hàng vào tài khoản có thể đã tìm nạp số thứ tự trước mà không có bất kỳ khóa nào trên bảng chính (điều này có thể xảy ra). Để cho phép điều đó cũng vậy, chỉ tăng giá trị hiện tại của chuỗi, không bao giờ giảm nó. Nó có vẻ hoang tưởng, nhưng điều đó phù hợp với bản chất của trình tự và bảo vệ chống lại các vấn đề tương tranh.

BEGIN;

LOCK TABLE tbl IN EXCLUSIVE MODE;

SELECT setval('tbl_tbl_id_seq', max(tbl_id))
FROM   tbl
HAVING max(tbl_id) > (SELECT last_value FROM tbl_tbl_id_seq);

COMMIT;

Trường hợp "TIÊU CHUẨN cộng đồng - thư viện các chức năng cơ bản"? Mệnh đề chọn thứ hai của câu trả lời này trong một EXECUTE format()(như @ EB.'s) là một hàm essencial! Làm cách nào để khắc phục sự thiếu thư viện chuẩn này trong PostgreSQL ????
Peter Krauss

Không có vấn đề gì nếu có một người khác. Khoảng trống trong chuỗi là bình thường. Nếu ứng dụng của bạn không thể đối phó, ứng dụng của bạn bị hỏng, bởi vì các lỗ hổng cũng có thể phát sinh do các giao dịch bị trục trặc, tắt máy chủ không có kế hoạch, v.v.
Craig Ringer

1
@Craig: Lỗi do một người tôi đã xử lý (và không có ở đó) sẽ có vấn đề vì chúng tôi có nguy cơ xảy ra lỗi khóa trùng lặp. Hướng ngược lại của sự cân nhắc của bạn; có vẻ như một sự hiểu lầm
Erwin Brandstetter

ah, có ý nghĩa
Craig Ringer

Điều này làm việc cho tôi
ha

54

Điều này sẽ đặt lại tất cả các chuỗi từ công khai không đưa ra giả định về tên bảng hoặc cột. Đã thử nghiệm trên phiên bản 8.4

CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text, columnname text, sequence_name text) RETURNS "pg_catalog"."void" AS 

    $body$  
      DECLARE 
      BEGIN 

      EXECUTE 'SELECT setval( ''' || sequence_name  || ''', ' || '(SELECT MAX(' || columnname || ') FROM ' || tablename || ')' || '+1)';



      END;  

    $body$  LANGUAGE 'plpgsql';


    select table_name || '_' || column_name || '_seq', reset_sequence(table_name, column_name, table_name || '_' || column_name || '_seq') from information_schema.columns where column_default like 'nextval%';

1
+1 chức năng rất hữu ích! Tên trình tự của chúng tôi không khớp chính xác với tên bảng, vì vậy tôi đã sử dụng substring(column_default, '''(.*)''')thay vì table_name || '_' || column_name || '_seq'. Hoạt động hoàn hảo.
Chris Lercher

4
Lưu ý rằng điều này sẽ thất bại với tên chuỗi chứa dấu ngoặc đơn hoặc tên bảng có chữ hoa, dấu cách, v.v. trong tên của chúng. Các chức năng quote_literalquote_ident, hay tốt nhất là formatchức năng, thực sự nên được sử dụng ở đây.
Craig Ringer

2
Ước gì tôi có thể cho điều này nhiều hơn một phiếu ... được thực hiện tốt đẹp thưa ngài. Hoạt động tuyệt vời trên Postgres 9.1 là tốt, đối với tôi ít nhất.
Peelman

1
Điều đó thật tuyệt. Tôi đã sử dụng substring(column_default from 'nextval\(''(.+)''::regclass\)')để lấy một cách rõ ràng tên trình tự. Làm việc như người ở.
Matthew MacDonald

Tôi đã tìm kiếm giải pháp này hơn một ngày nay. Cảm ơn rất nhiều, thậm chí tôi đã sử dụng phương pháp được đề xuất bởi @ChrisLercher, để thay thế văn bảnsubstring(column_default, '''(.*)''') instead of table_name || '_' || column_name || '_seq'
Sushin Pv

43

THAY ĐỔI SEQUENCE Sequ_name RESTART VỚI (CHỌN max (id) TỪ tên_bảng); Không hoạt động.

Sao chép từ câu trả lời @tardate:

SELECT setval(pg_get_serial_sequence('table_name', 'id'), MAX(id)) FROM table_name;

8
đó là lỗi cú pháp đối với tôi trong 8.4 (tại ^ (CHỌN ...). RESTART VỚI dường như chỉ chấp nhận một giá trị thứ tự. Điều này hoạt động mặc dù: CHỌN setval (pg_get_serial_fterence ('table_name', 'id'), (CHỌN MAX ( id) TỪ tên_bảng) +1);
trì hoãn

1
Giải pháp của Muruges cũng không hoạt động trong 9,4. Không hiểu tại sao rất nhiều người ủng hộ câu trả lời này. ALTER SEQUENCE không cho phép truy vấn con. Giải pháp của @tardate hoạt động hoàn hảo. Chỉnh sửa câu trả lời để loại bỏ dữ liệu không chính xác.
Vladislav Rastrusny

ALTER SEQUENCE đã làm việc hoàn hảo cho tôi. Tôi đã sử dụng COPY để mang lại một số dữ liệu và có những khoảng trống trong các khóa chính và INSERT đã đưa ra các ngoại lệ khóa trùng lặp. Thiết lập trình tự đã thực hiện các mẹo. 9,4
dùng542319

22

Lệnh này chỉ thay đổi giá trị chuỗi khóa được tạo tự động trong postgresql

ALTER SEQUENCE "your_sequence_name" RESTART WITH 0;

Thay cho số 0, bạn có thể đặt bất kỳ số nào mà bạn muốn khởi động lại chuỗi.

tên trình tự mặc định sẽ "TableName_FieldName_seq". Ví dụ: nếu tên bảng của bạn là "MyTable"và tên trường của bạn "MyID", thì tên chuỗi của bạn sẽ là "MyTable_MyID_seq".

Đây là câu trả lời giống như câu trả lời của @ murugesanponappan, nhưng có một lỗi cú pháp trong giải pháp của anh ấy. bạn không thể sử dụng truy vấn phụ (select max()...)trong alterlệnh. Vì vậy, hoặc bạn phải sử dụng giá trị số cố định hoặc bạn cần sử dụng một biến thay cho truy vấn phụ.


Đây là giải pháp hoàn hảo cảm ơn bạn rất nhiều. Nhưng trong trường hợp của tôi, tôi đã gặp lỗi, vì vậy tôi đã phải thay đổi nó thành ALTER SEQUENCE "your_ resultence_name" RESTART VỚI 1;
Deunz

18

Đặt lại tất cả các chuỗi, không có giả định về tên ngoại trừ khóa chính của mỗi bảng là "id":

CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text, columnname text)
RETURNS "pg_catalog"."void" AS
$body$
DECLARE
BEGIN
    EXECUTE 'SELECT setval( pg_get_serial_sequence(''' || tablename || ''', ''' || columnname || '''),
    (SELECT COALESCE(MAX(id)+1,1) FROM ' || tablename || '), false)';
END;
$body$  LANGUAGE 'plpgsql';

select table_name || '_' || column_name || '_seq', reset_sequence(table_name, column_name) from information_schema.columns where column_default like 'nextval%';

Hoạt động hoàn hảo trên phiên bản 9.1 của tôi
Valentin Vasilyev

Bạn cần thêm trích dẫn nếu bảng chứa chữ hoa:pg_get_serial_sequence(''"' || tablename || '"''
Manuel Darveau

Đây là chức năng tốt nhất! Bạn có thể tránh các vấn đề trích dẫn (và nâng cao sự thanh lịch) với định dạng, đại loại như EXECUTE format( 'SELECT setval(pg_get_serial_sequence(%L, %L), coalesce(max(id),0) + 1, false) FROM %I;', $1,$2,$1 );
Peter Krauss

13

Các hàm này đầy rủi ro khi tên chuỗi, tên cột, tên bảng hoặc tên lược đồ có các ký tự ngộ nghĩnh như dấu cách, dấu chấm câu và tương tự. Tôi đã viết điều này:

CREATE OR REPLACE FUNCTION sequence_max_value(oid) RETURNS bigint
VOLATILE STRICT LANGUAGE plpgsql AS  $$
DECLARE
 tabrelid oid;
 colname name;
 r record;
 newmax bigint;
BEGIN
 FOR tabrelid, colname IN SELECT attrelid, attname
               FROM pg_attribute
              WHERE (attrelid, attnum) IN (
                      SELECT adrelid::regclass,adnum
                        FROM pg_attrdef
                       WHERE oid IN (SELECT objid
                                       FROM pg_depend
                                      WHERE refobjid = $1
                                            AND classid = 'pg_attrdef'::regclass
                                    )
          ) LOOP
      FOR r IN EXECUTE 'SELECT max(' || quote_ident(colname) || ') FROM ' || tabrelid::regclass LOOP
          IF newmax IS NULL OR r.max > newmax THEN
              newmax := r.max;
          END IF;
      END LOOP;
  END LOOP;
  RETURN newmax;
END; $$ ;

Bạn có thể gọi nó cho một chuỗi bằng cách chuyển nó thành OID và nó sẽ trả về số cao nhất được sử dụng bởi bất kỳ bảng nào có trình tự như mặc định; hoặc bạn có thể chạy nó với một truy vấn như thế này, để đặt lại tất cả các chuỗi trong cơ sở dữ liệu của bạn:

 select relname, setval(oid, sequence_max_value(oid))
   from pg_class
  where relkind = 'S';

Sử dụng một chất lượng khác nhau, bạn chỉ có thể đặt lại chuỗi trong một lược đồ nhất định, v.v. Ví dụ: nếu bạn muốn điều chỉnh các chuỗi trong lược đồ "công khai":

select relname, setval(pg_class.oid, sequence_max_value(pg_class.oid))
  from pg_class, pg_namespace
 where pg_class.relnamespace = pg_namespace.oid and
       nspname = 'public' and
       relkind = 'S';

Lưu ý rằng do cách setval () hoạt động, bạn không cần thêm 1 vào kết quả.

Như một lưu ý cuối cùng, tôi phải cảnh báo rằng một số cơ sở dữ liệu dường như có mặc định liên kết đến các chuỗi theo cách không để danh mục hệ thống có thông tin đầy đủ về chúng. Điều này xảy ra khi bạn thấy những thứ như thế này trong \ d:

alvherre=# \d baz
                     Tabla «public.baz»
 Columna |  Tipo   |                 Modificadores                  
---------+---------+------------------------------------------------
 a       | integer | default nextval(('foo_a_seq'::text)::regclass)

Lưu ý rằng lệnh gọi nextval () trong mệnh đề mặc định đó có ký tự :: text ngoài biểu tượng :: reggroup. Tôi nghĩ rằng điều này là do cơ sở dữ liệu được pg_dump'ed từ các phiên bản PostgreQuery cũ. Điều gì sẽ xảy ra là hàm Sequ_max_value () ở trên sẽ bỏ qua một bảng như vậy. Để khắc phục sự cố, bạn có thể xác định lại mệnh đề DEFAULT để tham chiếu trực tiếp chuỗi mà không cần truyền:

alvherre=# alter table baz alter a set default nextval('foo_a_seq');
ALTER TABLE

Sau đó, psql hiển thị đúng:

alvherre=# \d baz
                     Tabla «public.baz»
 Columna |  Tipo   |             Modificadores              
---------+---------+----------------------------------------
 a       | integer | default nextval('foo_a_seq'::regclass)

Ngay sau khi bạn sửa lỗi đó, hàm sẽ hoạt động chính xác cho bảng này cũng như tất cả các bảng khác có thể sử dụng cùng một chuỗi.


Đây là thanx tuyệt vời! Cần lưu ý rằng tôi cần thêm một diễn viên tại bài tập (dòng 21 trong mã chức năng) như thế này: newmax := r.max::bigint;để làm cho nó hoạt động chính xác với tôi.
Tommy Bravo

Cũng phải thay đổi điều này: 'SELECT max(' || quote_ident(colname) || ') FROM ' => 'SELECT max(' || quote_ident(colname) || '::bigint) FROM ' chú ý các ::bigintdiễn viên được thêm vào trong truy vấn xây dựng động.
Tommy Bravo

9

Một plpgsql khác - chỉ đặt lại nếu max(att) > then lastval

do --check seq not in sync
$$
declare
 _r record;
 _i bigint;
 _m bigint;
begin
  for _r in (
    SELECT relname,nspname,d.refobjid::regclass, a.attname, refobjid
    FROM   pg_depend    d
    JOIN   pg_attribute a ON a.attrelid = d.refobjid AND a.attnum = d.refobjsubid
    JOIN pg_class r on r.oid = objid
    JOIN pg_namespace n on n.oid = relnamespace
    WHERE  d.refobjsubid > 0 and  relkind = 'S'
   ) loop
    execute format('select last_value from %I.%I',_r.nspname,_r.relname) into _i;
    execute format('select max(%I) from %s',_r.attname,_r.refobjid) into _m;
    if coalesce(_m,0) > _i then
      raise info '%',concat('changed: ',_r.nspname,'.',_r.relname,' from:',_i,' to:',_m);
      execute format('alter sequence %I.%I restart with %s',_r.nspname,_r.relname,_m+1);
    end if;
  end loop;

end;
$$
;

cũng bình luận dòng --execute format('alter sequencesẽ đưa ra danh sách, không thực sự đặt lại giá trị


8

Đặt lại tất cả các chuỗi từ công khai

CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text) RETURNS "pg_catalog"."void" AS 
$body$  
  DECLARE 
  BEGIN 
  EXECUTE 'SELECT setval( ''' 
  || tablename  
  || '_id_seq'', ' 
  || '(SELECT id + 1 FROM "' 
  || tablename  
  || '" ORDER BY id DESC LIMIT 1), false)';  
  END;  
$body$  LANGUAGE 'plpgsql';

select sequence_name, reset_sequence(split_part(sequence_name, '_id_seq',1)) from information_schema.sequences
        where sequence_schema='public';

Có vẻ như cách tiếp cận này đưa ra các giả định về tên cột và bảng để nó không hiệu quả với tôi
djsnowsill

Điều đó có làm hỏng dữ liệu trong cơ sở dữ liệu không?
zennin

8

Tôi đề nghị giải pháp này được tìm thấy trên wiki postgres. Nó cập nhật tất cả các chuỗi của bảng của bạn.

SELECT 'SELECT SETVAL(' ||
       quote_literal(quote_ident(PGT.schemaname) || '.' || quote_ident(S.relname)) ||
       ', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' ||
       quote_ident(PGT.schemaname)|| '.'||quote_ident(T.relname)|| ';'
FROM pg_class AS S,
     pg_depend AS D,
     pg_class AS T,
     pg_attribute AS C,
     pg_tables AS PGT
WHERE S.relkind = 'S'
    AND S.oid = D.objid
    AND D.refobjid = T.oid
    AND D.refobjid = C.attrelid
    AND D.refobjsubid = C.attnum
    AND T.relname = PGT.tablename
ORDER BY S.relname;

Cách sử dụng (từ wiki postgres):

  • Lưu cái này vào một tập tin, nói 'reset.sql'
  • Chạy tệp và lưu đầu ra của nó theo cách không bao gồm các tiêu đề thông thường, sau đó chạy đầu ra đó. Thí dụ:

Thí dụ:

psql -Atq -f reset.sql -o temp
psql -f temp
rm temp

Bài viết gốc (cũng có sửa lỗi cho quyền sở hữu trình tự) tại đây


7

Một số câu trả lời thực sự khó hiểu ở đây, tôi cho rằng nó đã từng rất tệ vào khoảng thời gian khi điều này được hỏi, vì rất nhiều câu trả lời từ đây không hoạt động cho phiên bản 9.3. Các tài liệu kể từ phiên bản 8.0 cung cấp một câu trả lời cho câu hỏi này rất:

SELECT setval('serial', max(id)) FROM distributors;

Ngoài ra, nếu bạn cần quan tâm đến các tên trình tự phân biệt chữ hoa chữ thường, đó là cách bạn thực hiện:

SELECT setval('"Serial"', max(id)) FROM distributors;

7

Vấn đề này xảy ra với tôi khi sử dụng khung thực thể để tạo cơ sở dữ liệu và sau đó chọn cơ sở dữ liệu với dữ liệu ban đầu, điều này làm cho chuỗi không khớp.

Tôi đã giải quyết nó bằng cách tạo một tập lệnh để chạy sau khi gieo cơ sở dữ liệu:

DO
$do$
DECLARE tablename text;
BEGIN
    -- change the where statments to include or exclude whatever tables you need
    FOR tablename IN SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE' AND table_name != '__EFMigrationsHistory'
        LOOP
            EXECUTE format('SELECT setval(pg_get_serial_sequence(''"%s"'', ''Id''), (SELECT MAX("Id") + 1 from "%s"))', tablename, tablename);
    END LOOP;
END
$do$

1
tại sao MAX("Id") + 1nó hoạt động tốt nhất với tôi khi chuỗi là = đến mức tối đa.
Lastlink

6

Phiên bản của tôi sử dụng phiên bản đầu tiên, với một số kiểm tra lỗi ...

BEGIN;
CREATE OR REPLACE FUNCTION reset_sequence(_table_schema text, _tablename text, _columnname text, _sequence_name text)
RETURNS pg_catalog.void AS
$BODY$
DECLARE
BEGIN
 PERFORM 1
 FROM information_schema.sequences
 WHERE
  sequence_schema = _table_schema AND
  sequence_name = _sequence_name;
 IF FOUND THEN
  EXECUTE 'SELECT setval( ''' || _table_schema || '.' || _sequence_name  || ''', ' || '(SELECT MAX(' || _columnname || ') FROM ' || _table_schema || '.' || _tablename || ')' || '+1)';
 ELSE
  RAISE WARNING 'SEQUENCE NOT UPDATED ON %.%', _tablename, _columnname;
 END IF;
END; 
$BODY$
 LANGUAGE 'plpgsql';

SELECT reset_sequence(table_schema, table_name, column_name, table_name || '_' || column_name || '_seq')
FROM information_schema.columns
WHERE column_default LIKE 'nextval%';

DROP FUNCTION reset_sequence(_table_schema text, _tablename text, _columnname text, _sequence_name text) ;
COMMIT;

Cảm ơn bạn đã kiểm tra lỗi! Rất nhiều đánh giá cao khi tên bảng / cột bị cắt ngắn nếu chúng quá dài, mà bạn RAISE WARNINGxác định cho tôi.
Nicholas Riley

5

Để tất cả chúng cùng nhau

CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text) 
RETURNS "pg_catalog"."void" AS
$body$
DECLARE
BEGIN
  EXECUTE 'SELECT setval( pg_get_serial_sequence(''' || tablename || ''', ''id''),
  (SELECT COALESCE(MAX(id)+1,1) FROM ' || tablename || '), false)';
END;
$body$  LANGUAGE 'plpgsql';

sẽ sửa ' id'trình tự của bảng đã cho (ví dụ như thường là cần thiết với django).


4

trước khi tôi chưa thử mã: trong phần sau tôi đã đăng phiên bản cho mã sql cho cả hai giải pháp Klaus và user456226 hoạt động trên máy tính của tôi [Postgres 8.3], chỉ với một số điều chỉnh nhỏ cho phiên bản Klaus và phiên bản của tôi cho người dùng456226 một.

Giải pháp Klaus:

drop function IF EXISTS rebuilt_sequences() RESTRICT;
CREATE OR REPLACE FUNCTION  rebuilt_sequences() RETURNS integer as
$body$
  DECLARE sequencedefs RECORD; c integer ;
  BEGIN
    FOR sequencedefs IN Select
      constraint_column_usage.table_name as tablename,
      constraint_column_usage.table_name as tablename, 
      constraint_column_usage.column_name as columnname,
      replace(replace(columns.column_default,'''::regclass)',''),'nextval(''','') as sequencename
      from information_schema.constraint_column_usage, information_schema.columns
      where constraint_column_usage.table_schema ='public' AND 
      columns.table_schema = 'public' AND columns.table_name=constraint_column_usage.table_name
      AND constraint_column_usage.column_name = columns.column_name
      AND columns.column_default is not null
   LOOP    
      EXECUTE 'select max('||sequencedefs.columnname||') from ' || sequencedefs.tablename INTO c;
      IF c is null THEN c = 0; END IF;
      IF c is not null THEN c = c+ 1; END IF;
      EXECUTE 'alter sequence ' || sequencedefs.sequencename ||' restart  with ' || c;
   END LOOP;

   RETURN 1; END;
$body$ LANGUAGE plpgsql;

select rebuilt_sequences();

giải pháp người dùng456226:

--drop function IF EXISTS reset_sequence (text,text) RESTRICT;
CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text,columnname text) RETURNS bigint --"pg_catalog"."void"
AS
$body$
  DECLARE seqname character varying;
          c integer;
  BEGIN
    select tablename || '_' || columnname || '_seq' into seqname;
    EXECUTE 'SELECT max("' || columnname || '") FROM "' || tablename || '"' into c;
    if c is null then c = 0; end if;
    c = c+1; --because of substitution of setval with "alter sequence"
    --EXECUTE 'SELECT setval( "' || seqname || '", ' || cast(c as character varying) || ', false)'; DOES NOT WORK!!!
    EXECUTE 'alter sequence ' || seqname ||' restart with ' || cast(c as character varying);
    RETURN nextval(seqname)-1;
  END;
$body$ LANGUAGE 'plpgsql';

select sequence_name, PG_CLASS.relname, PG_ATTRIBUTE.attname,
       reset_sequence(PG_CLASS.relname,PG_ATTRIBUTE.attname)
from PG_CLASS
join PG_ATTRIBUTE on PG_ATTRIBUTE.attrelid = PG_CLASS.oid
join information_schema.sequences
     on information_schema.sequences.sequence_name = PG_CLASS.relname || '_' || PG_ATTRIBUTE.attname || '_seq'
where sequence_schema='public';

4

Kiểm tra lại tất cả các chuỗi trong chức năng lược đồ công cộng

CREATE OR REPLACE FUNCTION public.recheck_sequence (
)
RETURNS void AS
$body$
DECLARE
  _table_name VARCHAR;
  _column_name VARCHAR;  
  _sequence_name VARCHAR;
BEGIN
  FOR _table_name IN SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = 'public' LOOP
    FOR _column_name IN SELECT column_name FROM information_schema.columns WHERE table_name = _table_name LOOP
        SELECT pg_get_serial_sequence(_table_name, _column_name) INTO _sequence_name;
        IF _sequence_name IS NOT NULL THEN 
            EXECUTE 'SELECT setval('''||_sequence_name||''', COALESCE((SELECT MAX('||quote_ident(_column_name)||')+1 FROM '||quote_ident(_table_name)||'), 1), FALSE);';
        END IF;
    END LOOP;   
  END LOOP;
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;

3

Để khởi động lại tất cả chuỗi thành 1 sử dụng:

-- Create Function
CREATE OR REPLACE FUNCTION "sy_restart_seq_to_1" (
    relname TEXT
)
RETURNS "pg_catalog"."void" AS
$BODY$

DECLARE

BEGIN
    EXECUTE 'ALTER SEQUENCE '||relname||' RESTART WITH 1;';
END;
$BODY$

LANGUAGE 'plpgsql';

-- Use Function
SELECT 
    relname
    ,sy_restart_seq_to_1(relname)
FROM pg_class
WHERE relkind = 'S';

2

Câu trả lời Klaus là hữu ích nhất, được thực hiện cho một chút bỏ lỡ: bạn phải thêm DISTINCT trong câu lệnh chọn.

Tuy nhiên, nếu bạn chắc chắn rằng không có tên bảng + cột nào có thể tương đương với hai bảng khác nhau, bạn cũng có thể sử dụng:

select sequence_name, --PG_CLASS.relname, PG_ATTRIBUTE.attname
       reset_sequence(split_part(sequence_name, '_id_seq',1))
from PG_CLASS
join PG_ATTRIBUTE on PG_ATTRIBUTE.attrelid = PG_CLASS.oid
join information_schema.sequences
     on information_schema.sequences.sequence_name = PG_CLASS.relname || '_' || PG_ATTRIBUTE.attname
where sequence_schema='public';

đó là phần mở rộng của giải pháp user456226 cho trường hợp khi một số tên cột quan tâm không phải là 'ID'.


... tất nhiên, cũng cần một thay đổi trong "reset_ resultence", đó là thêm một tham số "cột tên", để sử dụng thay vì "id".
mauro

2

Nếu bạn thấy lỗi này khi bạn đang tải dữ liệu SQL tùy chỉnh để khởi tạo, một cách khác để tránh điều này là:

Thay vì viết:

INSERT INTO book (id, name, price) VALUES (1 , 'Alchemist' , 10),

Xóa id(khóa chính) khỏi dữ liệu ban đầu

INSERT INTO book (name, price) VALUES ('Alchemist' , 10),

Điều này giữ cho chuỗi Postgres đồng bộ!


2

Câu trả lời này là một bản sao từ mauro.

drop function IF EXISTS rebuilt_sequences() RESTRICT;
CREATE OR REPLACE FUNCTION  rebuilt_sequences() RETURNS integer as
$body$
  DECLARE sequencedefs RECORD; c integer ;
  BEGIN
    FOR sequencedefs IN Select
      DISTINCT(constraint_column_usage.table_name) as tablename,
      constraint_column_usage.column_name as columnname,
      replace(replace(columns.column_default,'''::regclass)',''),'nextval(''','') as sequencename
      from information_schema.constraint_column_usage, information_schema.columns
      where constraint_column_usage.table_schema ='public' AND 
      columns.table_schema = 'public' AND columns.table_name=constraint_column_usage.table_name
      AND constraint_column_usage.column_name = columns.column_name
      AND columns.column_default is not null 
      ORDER BY sequencename
   LOOP    
      EXECUTE 'select max('||sequencedefs.columnname||') from ' || sequencedefs.tablename INTO c;
      IF c is null THEN c = 0; END IF;
      IF c is not null THEN c = c+ 1; END IF;
      EXECUTE 'alter sequence ' || sequencedefs.sequencename ||' minvalue '||c ||' start ' || c ||' restart  with ' || c;
   END LOOP;

   RETURN 1; END;
$body$ LANGUAGE plpgsql;

select rebuilt_sequences();

2

Tôi đã dành một giờ để cố gắng để câu trả lời của djsnowsill hoạt động với cơ sở dữ liệu bằng cách sử dụng các bảng và cột của Case Case, sau đó tình cờ tìm ra giải pháp nhờ một nhận xét từ Manuel Darveau, nhưng tôi nghĩ rằng tôi có thể làm cho nó rõ ràng hơn cho mọi người:

CREATE OR REPLACE FUNCTION "reset_sequence" (tablename text, columnname text)
RETURNS "pg_catalog"."void" AS
$body$
DECLARE
BEGIN
EXECUTE format('SELECT setval(pg_get_serial_sequence(''%1$I'', %2$L),
        (SELECT COALESCE(MAX(%2$I)+1,1) FROM %1$I), false)',tablename,columnname);
END;
$body$  LANGUAGE 'plpgsql';

SELECT format('%s_%s_seq',table_name,column_name), reset_sequence(table_name,column_name) 
FROM information_schema.columns WHERE column_default like 'nextval%';

Điều này có lợi ích của:

  • không giả sử cột ID được đánh vần một cách cụ thể.
  • không giả sử tất cả các bảng có một chuỗi.
  • làm việc cho tên bảng / cột trường hợp hỗn hợp.
  • sử dụng định dạng để ngắn gọn hơn.

Để giải thích, vấn đề là pg_get_serial_sequencecó các chuỗi để tìm ra những gì bạn đang đề cập đến, vì vậy nếu bạn làm:

"TableName" --it thinks it's a table or column
'TableName' --it thinks it's a string, but makes it lower case
'"TableName"' --it works!

Điều này đạt được bằng cách sử dụng ''%1$I''trong chuỗi định dạng, ''làm cho dấu nháy đơn 1$có nghĩa là đối số đầu tiên và Icó nghĩa là trong dấu ngoặc kép


2
select 'SELECT SETVAL(' || seq [ 1] || ', COALESCE(MAX('||column_name||')+1, 1) ) FROM '||table_name||';'
from (
       SELECT table_name, column_name, column_default, regexp_match(column_default, '''.*''') as seq
       from information_schema.columns
       where column_default ilike 'nextval%'
     ) as sequense_query

4
Mặc dù mã này có thể trả lời câu hỏi, cung cấp ngữ cảnh bổ sung về lý do và / hoặc cách mã này trả lời câu hỏi cải thiện giá trị lâu dài của nó.
yeya

1

Hack xấu xí để sửa nó bằng một số phép thuật vỏ, không phải là một giải pháp tuyệt vời nhưng có thể truyền cảm hứng cho những người khác có vấn đề tương tự :)

pg_dump -s <DATABASE> | grep 'CREATE TABLE' | awk '{print "SELECT setval(#" $3 "_id_seq#, (SELECT MAX(id) FROM " $3 "));"}' | sed "s/#/'/g" | psql <DATABASE> -f -

0

Hãy thử reindex .

CẬP NHẬT: Như đã chỉ ra trong các ý kiến, đây là câu trả lời cho câu hỏi ban đầu.


reindex không hoạt động, nó dường như chỉ tăng chỉ số lên 1
meleyal

3
reindex không hoạt động vì nó đang trả lời câu hỏi ban đầu của bạn, về các chỉ mục cơ sở dữ liệu, không phải theo trình tự
Vinko Vrsalovic

0

SELECT setval... làm cho JDBC bork, vì vậy đây là một cách tương thích với Java để làm điều này:

-- work around JDBC 'A result was returned when none was expected.'
-- fix broken nextval due to poorly written 20140320100000_CreateAdminUserRoleTables.sql
DO 'BEGIN PERFORM setval(pg_get_serial_sequence(''admin_user_role_groups'', ''id''), 1 + COALESCE(MAX(id), 0), FALSE) FROM admin_user_role_groups; END;';

0

Phương pháp cập nhật tất cả các chuỗi trong lược đồ của bạn được sử dụng làm ID:

DO $$ DECLARE
  r RECORD;
BEGIN
FOR r IN (SELECT tablename, pg_get_serial_sequence(tablename, 'id') as sequencename
          FROM pg_catalog.pg_tables
          WHERE schemaname='YOUR_SCHEMA'
          AND tablename IN (SELECT table_name 
                            FROM information_schema.columns 
                            WHERE table_name=tablename and column_name='id')
          order by tablename)
LOOP
EXECUTE
        'SELECT setval(''' || r.sequencename || ''', COALESCE(MAX(id), 1), MAX(id) IS NOT null)
         FROM ' || r.tablename || ';';
END LOOP;
END $$;

0

Chỉ cần chạy bên dưới lệnh:

SELECT setval('my_table_seq', (SELECT max(id) FROM my_table));

0

Có rất nhiều câu trả lời tốt ở đây. Tôi có cùng nhu cầu sau khi tải lại cơ sở dữ liệu Django.

Nhưng tôi cần:

  • Tất cả trong một Chức năng
  • Có thể sửa một hoặc nhiều lược đồ cùng một lúc
  • Có thể sửa tất cả hoặc chỉ một bảng tại một thời điểm
  • Cũng muốn một cách tốt đẹp để xem chính xác những gì đã thay đổi, hoặc không thay đổi

Điều này có vẻ rất cần với những gì yêu cầu ban đầu là cho.
Cảm ơn Baldiry và Mauro đã đưa tôi đi đúng hướng.

drop function IF EXISTS reset_sequences(text[], text) RESTRICT;
CREATE OR REPLACE FUNCTION reset_sequences(
    in_schema_name_list text[] = '{"django", "dbaas", "metrics", "monitor", "runner", "db_counts"}',
    in_table_name text = '%') RETURNS text[] as
$body$
  DECLARE changed_seqs text[];
  DECLARE sequence_defs RECORD; c integer ;
  BEGIN
    FOR sequence_defs IN
        select
          DISTINCT(ccu.table_name) as table_name,
          ccu.column_name as column_name,
          replace(replace(c.column_default,'''::regclass)',''),'nextval(''','') as sequence_name
          from information_schema.constraint_column_usage ccu,
               information_schema.columns c
          where ccu.table_schema = ANY(in_schema_name_list)
            and ccu.table_schema = c.table_schema
            AND c.table_name = ccu.table_name
            and c.table_name like in_table_name
            AND ccu.column_name = c.column_name
            AND c.column_default is not null
          ORDER BY sequence_name
   LOOP
      EXECUTE 'select max(' || sequence_defs.column_name || ') from ' || sequence_defs.table_name INTO c;
      IF c is null THEN c = 1; else c = c + 1; END IF;
      EXECUTE 'alter sequence ' || sequence_defs.sequence_name || ' restart  with ' || c;
      changed_seqs = array_append(changed_seqs, 'alter sequence ' || sequence_defs.sequence_name || ' restart with ' || c);
   END LOOP;
   changed_seqs = array_append(changed_seqs, 'Done');

   RETURN changed_seqs;
END
$body$ LANGUAGE plpgsql;

Sau đó để Thực thi và Xem các thay đổi chạy:

select *
from unnest(reset_sequences('{"django", "dbaas", "metrics", "monitor", "runner", "db_counts"}'));

Trả về

activity_id_seq                          restart at 22
api_connection_info_id_seq               restart at 4
api_user_id_seq                          restart at 1
application_contact_id_seq               restart at 20
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.