Senkronizasyondan düştüğünde postgres'in birincil anahtar dizisi nasıl sıfırlanır?


523

Birincil anahtar dizimin tablo satırlarımla senkronize olmaması sorunuyla karşılaştım.

Yani, yeni bir satır eklediğimde seri veri türünde ima edilen sıra zaten var olan bir sayı döndürdüğü için yinelenen bir anahtar hatası alıyorum.

Bu, dizinin düzgün bir şekilde tutulmamasından kaynaklanan ithalat / geri yüklemelerden kaynaklanıyor gibi görünüyor.


Merak ediyorum .. geri yüklemeden önce db düşürüyor musun? Bu olayla ilgili hafif bir hatırlama var, ama yanlış olabilirim: P
Arthur Thomas

25
PostgreSQL wiki'de Dizileri Düzeltme hakkında bir sayfa vardır .
Brad Koch

14
Sadece googleability yardımcı olmak için, burada atılan hata mesajı: "yinelenen anahtar değeri benzersiz kısıtlamayı ihlal ..."
superluminary

4
Django'da sqlsequencereset bunu yapar: SELECT setval (pg_get_serial_sequence ("<table_name>", 'id'), birleşme (max ("id"), 1), max ("id") boş DEĞİLDİR "< tablo_ismi> ";
kullanıcı

Pg_get_serioal_sequence işlevinin çalışması için <tablo adı> nın ilk örneğinin tek tırnak içine alınması gerekir: SELECT setval (pg_get_serial_sequence ('<table_name>', 'id'), birleşme (max ("id"), 1) , max ("id") boş DEĞİL) "<table_name>" 'dan
nclu

Yanıtlar:


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;

Kaynak - Ruby Forum


12
Her durumda, 1'e MAX (id) eklemek, kimliklerinizde tek bir sayı boşluğu bırakacaktır, çünkü setval kümeleri bir sonraki değil dizinin son değeridir.
mikl

6
Tabloda hiç satır yoksa örneğiniz çalışmaz. Böylece feryat verilen SQL daha güvenlidir: SELECT setval ('your_table_id_seq', birleşme ((sizin_tablonuzdan max (id) +1 seçin), 1), true);
Valery Viktorovsky

10
@Valery: Ama yukarıdaki iki yorum tarafından belirtilen boşlukları önlemek için ihtiyacınız varSELECT setval('your_table_id_seq', coalesce((select max(id)+1 from your_table), 1), false);
Antony Hatchkins

20
Tüm sorunlar tek bir sorguda çözüldü ve birleştirildi:SELECT setval('your_seq',(SELECT GREATEST(MAX(your_id)+1,nextval('your_seq'))-1 FROM your_table))
Frunsi

15
Uygulamanız sekanslardaki boşlukları önemsiyorsa, uygulamanız bozulur. Dizileri boşluklar normaldir ve plansız veritabanı kapatmalar, hatasından sonra işlem geri almalar vs nedeniyle oluşabilir
Craig Zil

202

pg_get_serial_sequencedizi adı ile ilgili yanlış varsayımlardan kaçınmak için kullanılabilir. Bu, bir çekimde sekansı sıfırlar:

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

Ya da daha kısaca:

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

Ancak bu form boş tabloları düzgün işleyemez, çünkü max (id) boştur ve dizinin kapsamı dışında olacağı için 0 değerini de ayarlayamazsınız. Bunun bir çözümü ALTER SEQUENCEsözdizimine başvurmaktır.

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

Ancak ALTER SEQUENCE, sıra adı ve yeniden başlatma değeri ifadeler olamayacağı için sınırlı kullanımlıdır.

En iyi çok amaçlı çözüm, setval3. parametre olarak false ile çağırmak ve "kullanılacak sonraki değeri" belirtmemize izin vermek gibi görünüyor :

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

Bu benim tüm kutuları keneler:

  1. gerçek dizi adının sabit kodlanmasını önler
  2. boş tabloları doğru şekilde işler
  3. varolan verileri içeren tabloları işler ve sırada bir boşluk bırakmaz

Son olarak, pg_get_serial_sequenceyalnızca dizinin sütuna ait olması durumunda işe yaradığını unutmayın . Bu, artan sütunun bir serialtür olarak tanımlanması durumunda olacaktır , ancak dizi manuel olarak eklenmişse,ALTER SEQUENCE .. OWNED BY .

örneğin, serialtür tablo oluşturmak için kullanıldıysa, bunların tümü çalışmalıdır:

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;

Ancak sekanslar manuel olarak eklendiyse:

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
Sorgudaki '+1' öğesine gerek yoktur, setval()geçerli değeri ayarlar ve nextval()şimdiden +1 değerini döndürür.
Antony Hatchkins

1
Bir parametreyi alan bu yöntemi saran işlev - tablo_adı - aşağıdaki
cevabımda

@AntonyHatchkins şerefe. Sadece 1 hata başka tekrarı böylece nihayet iyi ben umut için swatted testere
tardate

99

En kısa ve en hızlı yol:

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

tbl_idserialTablonun sütunu olmak, sıradan tblçizim yapmak tbl_tbl_id_seq(varsayılan otomatik addır).

Eğer ekli dizinin adını (varsayılan şeklinde olmak zorunda değildir) bilmiyorum, kullanım pg_get_serial_sequence():

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

Burada tek tek hata yok. Belgelere göre:

İki parametreli form, dizinin last_valuealanını belirtilen değere ayarlar ve is_calledalanını true değerine ayarlar , yani bir sonrakinextval değer bir değer döndürmeden önce diziyi ilerletir .

Cesur vurgu benim.

Eğer tablo boş olabilir ve aslında bu durumda 1'den başlatın:

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

Sadece 2-paremater formunu kullanamayız ve 0sekansların alt sınırı 1 olduğundan başlayamayız. varsayılan olarak (özelleştirilmiş değilse).

eşzamanlılık

Yukarıdaki sorgularda eşzamanlı dizi faaliyetine veya yazma işlemlerine karşı henüz bir savunma yoktur. O alakalı ise, belki tabloyu kilitlemek özel modda. Senkronizasyona çalışırken eşzamanlı işlemlerin daha yüksek bir sayı yazmasını engeller. (Ayrıca maksimum sayı ile uğraşmayan zararsız yazıları geçici olarak engeller.)

Ancak, ana masada herhangi bir kilit olmadan sıra numaralarını önceden getirmiş olabilecek istemcileri dikkate almaz (bu olabilir). Buna izin vermek için, sadece dizinin mevcut değerini arttırın , asla azaltmayın. Paranoyak görünebilir, ancak bu, dizilerin doğası ve eşzamanlılık sorunlarına karşı savunma ile uyumludur.

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;

"STANDART topluluk-temel fonksiyonlar kütüphanesi" nerede? Bu cevabın EXECUTE format()(@ EB. Gibi) ikinci seçim cümlesi temel bir işlevdir! PostgreSQL'de bu standart kütüphane eksikliğini nasıl giderebilirim ?
Peter Krauss

Birer birer olup olmadığı önemli değil . Sekanslardaki boşluklar normaldir. Uygulamanız başa çıkamıyorsa, uygulamanız bozulur, çünkü işlem geri alımları, planlanmamış sunucu kapanmaları vb. Nedeniyle boşluklar da ortaya çıkabilir
Craig Ringer

1
@Craig: Ele aldığım tek seferlik hata (ve orada değil), aksi takdirde yinelenen bir anahtar hatası riskini alacağımız için önemli olurdu. Düşüncelerinizin tersi; bir yanlış anlama gibi görünüyor.
Erwin Brandstetter

ah, mantıklı.
Craig Ringer

Bu benim için çalışıyor
hectk

54

Bu, tüm sekansları tablodan veya sütun adlarıyla ilgili herhangi bir varsayımda bulunmadan herkesten sıfırlar. 8.4 sürümünde test edildi

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 çok kullanışlı bir işlev! Sıra isimlerimiz tablo isimleriyle tam olarak eşleşmedi, bu yüzden substring(column_default, '''(.*)''')onun yerine kullandım table_name || '_' || column_name || '_seq'. Mükemmel çalışıyor.
Chris Lercher

4
Bunun tek tırnak işareti içeren sıra adları veya adlarında büyük harf, boşluk vb. İçeren tablo adları ile başarısız olacağını unutmayın. Burada quote_literalve quote_identişlevleri ya da tercihen formatişlevi kullanılmalıdır.
Craig Ringer

2
Keşke birden fazla oy verebilseydim ... güzelce efendim. En azından benim için Postgres 9.1'de de harika çalışıyor.
peelman

1
Bu harika. Kullandığım substring(column_default from 'nextval\(''(.+)''::regclass\)')açıkça dizisi adını kapmak için. Bir cazibe gibi çalıştı.
Matthew MacDonald

Bu çözümü bir günden fazla bir süredir arıyordum, Çok teşekkürler, hatta @ChrisLercher tarafından önerilen yöntemi kullandım, metni değiştirmek içinsubstring(column_default, '''(.*)''') instead of table_name || '_' || column_name || '_seq'
Sushin Pv

43

ALTER SEQUENCE sıra_adı RESTART WITH (SELECT maks (id) FROM tablo_adı); Çalışmıyor.

@Tardate yanıtından kopyalandı:

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

8
bu benim için 8.4'te bir sözdizimi hatasıdır (at ^ (SELECT ...). RESTART WITH sadece sıralı bir değeri kabul ediyor gibi görünüyor. id) FROM table_name) +1);
tardate

1
Muruges'in çözümü de 9.4'te işe yaramıyor. Bu cevaba neden bu kadar çok oy verildiğini anlamıyorum. ALTER SEQUENCE alt sorgulara izin vermez. @Tardate çözümü mükemmel çalışıyor. Yanlış verileri kaldırmak için cevap düzenlendi.
Vladislav Rastrusny

ALTER SEQUENCE benim için mükemmel çalıştı. Bazı verileri getirmek için COPY kullanıyordum ve birincil anahtarlarda boşluklar vardı ve INSERT'ler yinelenen anahtar istisnaları atıyorlardı. Diziyi ayarlamak hile yaptı. 9.4
user542319

22

Bu komut yalnızca postgresql dosyasında otomatik oluşturulan anahtar sırası değerini değiştirir

ALTER SEQUENCE "your_sequence_name" RESTART WITH 0;

Sıfır yerine, diziyi yeniden başlatmak istediğiniz herhangi bir sayıyı koyabilirsiniz.

varsayılan sıra adı olacaktır "TableName_FieldName_seq". Örneğin, tablo adınız "MyTable"ve alan adınız ise "MyID", sıra adınız olacaktır "MyTable_MyID_seq".

Bu cevap @ murugesanponappan'ın cevabı ile aynıdır, ancak çözümünde bir sözdizimi hatası vardır. Eğer alt sorgu kullanamazsınız (select max()...)içinde alterkomuta. Böylece ya sabit sayısal değer kullanmalısınız ya da alt sorgu yerine bir değişken kullanmalısınız.


Bu mükemmel çözüm, çok teşekkür ederim efendim. Ama benim durumumda bir hata vardı, bu yüzden bunu ALTER SEQUENCE "your_sequence_name" olarak değiştirmek zorunda kaldım 1 İLE RESTART;
Deunz

18

Tüm sekmeleri sıfırla, her tablonun birincil anahtarının "id" olması dışında adlar hakkında varsayım yok:

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%';

9.1 sürümümde mükemmel çalıştı
Valentin Vasilyev

Tablo büyük harf içeriyorsa fiyat teklifi eklemeniz gerekir:pg_get_serial_sequence(''"' || tablename || '"''
Manuel Darveau

Bu en iyi fonksiyon! Biçimdeki alıntı sorunlarından kaçınabilirsiniz (ve zarafeti artırabilirsiniz) EXECUTE format( 'SELECT setval(pg_get_serial_sequence(%L, %L), coalesce(max(id),0) + 1, false) FROM %I;', $1,$2,$1 );
Peter Krauss

13

Bu işlevler, sıra adları, sütun adları, tablo adları veya şema adları boşluklar, noktalama işaretleri ve benzeri komik karakterlere sahip olduğunda tehlikelerle doludur. Bunu yazdım:

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; $$ ;

OID'yi ileterek tek bir sekans için çağırabilirsiniz ve sekansı varsayılan olarak içeren herhangi bir tablo tarafından kullanılan en yüksek sayıyı döndürür; veya veritabanınızdaki tüm sekansları sıfırlamak için böyle bir sorgu ile çalıştırabilirsiniz:

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

Farklı bir nitelik kullanarak yalnızca belirli bir şemadaki diziyi sıfırlayabilirsiniz, vb. Örneğin, "genel" şemadaki sekansları ayarlamak istiyorsanız:

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';

Setval () işlevinin nasıl çalıştığı nedeniyle sonuca 1 eklemeniz gerekmediğini unutmayın.

Bir kapanış notu olarak, bazı veritabanlarının, sistem kataloglarının bunlarla ilgili tam bilgiye sahip olmasına izin vermeyecek şekilde dizilere varsayılan olarak bağlandığını görmeliyim. Bu psql's \ d gibi şeyler gördüğünüzde olur:

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

Bu varsayılan yan tümcedeki nextval () çağrısının :: regclass dökümüne ek olarak :: metin dökümüne sahip olduğunu unutmayın. Bunun veritabanlarının eski PostgreSQL sürümlerinden pg_dump'ed olmasından kaynaklandığını düşünüyorum . Ne olacağı, yukarıdaki sequ_max_value () işlevinin böyle bir tabloyu yoksayacağıdır. Sorunu gidermek için, DEFAULT yantümcesini, doğrudan döküm olmadan diziye başvurmak üzere yeniden tanımlayabilirsiniz:

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

Sonra psql düzgün görüntüler:

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

Bunu düzelttiğiniz anda, işlev bu tablo ve aynı sırayı kullanabilecek diğer tüm öğeler için doğru şekilde çalışır.


Bu inanılmaz teşekkürler! Benim gibi newmax := r.max::bigint;düzgün çalışması için ödev (fonksiyon kodu satır 21) bir döküm eklemek gerektiğini belirtmek gerekir .
Tommy Bravo

Bu da değiştirmek zorunda kaldı: 'SELECT max(' || quote_ident(colname) || ') FROM ' => dinamik inşa sorgu içinde 'SELECT max(' || quote_ident(colname) || '::bigint) FROM ' eklenen ::bigintdöküm dikkat edin .
Tommy Bravo

9

Yine başka bir plpgsql - yalnızca 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;
$$
;

hatta satır yorum --execute format('alter sequenceyapmak aslında değeri sıfırlamak, liste verecektir


8

Tüm sekansı herkesten sıfırla

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';

Görünüşe göre bu yaklaşım sütun ve tablo isimleri hakkında varsayımlar yapıyor, bu yüzden benim için işe yaramadı
djsnowsill

Veritabanındaki verilere zarar vermez mi?
zennin

8

Postgres wiki'de bulunan bu çözümü öneriyorum. Tablolarınızın tüm sekanslarını günceller.

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;

Nasıl kullanılır (postgres wiki'den):

  • Bunu bir dosyaya kaydedin, 'reset.sql' deyin
  • Dosyayı çalıştırın ve çıktısını olağan üstbilgileri içermeyecek şekilde kaydedin, ardından bu çıktıyı çalıştırın. Misal:

Misal:

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

Orijinal makale (ayrıca dizi sahipliği düzeltmesi ile birlikte) burada


7

Burada gerçekten çok sert cevaplar, bunun istendiğinde gerçekten kötü olduğunu varsayıyorum, çünkü buradan bir sürü cevap 9.3 sürümü için işe yaramıyor. Dokümantasyon sürüm 8.0 beri bu çok soruya bir yanıt sağlar:

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

Ayrıca, büyük / küçük harfe duyarlı sıra adlarına dikkat etmeniz gerekiyorsa, bunu şu şekilde yapabilirsiniz:

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

7

Veritabanı oluşturmak ve sonra ilk verileri ile veritabanını tohumlamak için varlık çerçevesi kullanırken bu sorun benimle olur, bu dizi uyumsuzluk yapar.

Ben veritabanı tohumlama sonra çalıştırmak için bir komut dosyası oluşturarak çözüldü:

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
neden MAX("Id") + 1sekans maksimum = olduğunda bu benim için iyi çalışır.
bağlantı

6

Benim sürümüm ilk hata, bazı hata kontrolü ile kullanın ...

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;

Hata kontrolü için teşekkür ederiz! Tablo / sütun adları çok uzunsa, RAISE WARNINGbenim için tanımladığınız kısaltılmış olarak çok takdir edilir .
Nicholas Riley

5

Hepsini bir araya koy

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';

id'verilen tablonun sırasını düzeltir (örneğin django ile genellikle gerekli olduğu gibi).


4

henüz kodu denemeden önce: aşağıdaki Klaus ve benim sürümümde çalışan Klaus ve user457226 çözümleri için sql-kod sürümünü, Klaus bir ve benim sürüm için sadece bazı küçük ayarlamalar sonrası kullanıcı için 457226 bir.

Klaus çözümü:

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();

user457226 çözümü:

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

Genel şema işlevindeki tüm dizileri yeniden denetleme

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

Tüm diziyi 1'e yeniden başlatmak için şunu kullanın:

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

Klaus cevabı, küçük bir özledim için en yararlı, yürütmedir: select deyiminde DISTINCT eklemeniz gerekir.

Ancak, iki farklı tablo için hiçbir tablo + sütun adının eşdeğer olmadığından eminseniz, şunları da kullanabilirsiniz:

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';

ilgilenen bazı sütun adlarının 'ID' olmadığı durumlarda user457226 çözümünün bir uzantısıdır.


... tabii ki, "sıfırlama" yerine "reset_sequence" de bir değişiklik yapılması gerekiyor.
11'te mauro

2

Başlatma için özel SQL verileri yüklerken bu hatayı görürseniz, bundan kaçınmanın başka bir yolu:

Yazmak yerine:

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

idİlk verilerden (birincil anahtar) kaldırın

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

Bu, Postgres dizisini senkronize tutar!


2

Bu cevap mauro'nun bir kopyası.

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

Ben djsnowsill'in Karışık Vaka tabloları ve sütunları kullanarak bir veritabanı ile çalışmak için cevap almak için bir saat geçirdim, sonra sonunda Manuel Darveau bir yorum sayesinde çözüm üzerinde tökezledi, ama herkes için biraz daha açık hale getirebilir düşündüm:

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%';

Bunun faydası:

  • ID sütununun belirli bir şekilde yazıldığını varsaymamak.
  • tüm tabloların bir sıraya sahip olduğunu varsaymamak.
  • Karma Vaka tablosu / sütun adları için çalışma.
  • daha özlü olmak için biçimi kullanma.

Açıklamak için sorun, pg_get_serial_sequencene demek istediğinizi bulmak için dizeleri almasıydı, bu yüzden yaparsanız:

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

Bu, ''%1$I''biçim dizgisi kullanılarak elde edilir , ''kesme işaretini 1$ilk arg Ianlamına gelir ve tırnak işareti anlamına gelir


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
Bu kod soruyu cevaplayabilirken, bu kodun soruyu neden ve / veya nasıl cevapladığı konusunda ek bağlam sağlamak uzun vadeli değerini arttırır.
yeya

1

Çirkin bir kabuk büyüsü kullanarak düzeltmek için hack, harika bir çözüm değil ama benzer problemleri olanlara ilham verebilir :)

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

SELECT setval... JDBC bork yapar, bu yüzden bunu yapmanın Java uyumlu bir yolu:

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

Kimlik olarak kullanılan şemanızdaki tüm sekansları güncelleme yöntemi:

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

Sadece aşağıdaki komutu çalıştırın:

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

0

Burada çok iyi cevaplar var. Django veritabanımı yeniden yükledikten sonra aynı ihtiyacı vardı.

Ama ihtiyacım vardı:

  • Hepsi bir arada İşlev
  • Bir kerede bir veya daha fazla şemayı düzeltebilir
  • Bir kerede tümünü veya yalnızca bir tabloyu düzeltebilir
  • Ayrıca neyin değiştiğini veya değişmediğini görmek için güzel bir yol istedim

Bu, orijinal sorunun ne olduğuna çok benzer bir ihtiyaç gibi görünüyor.
Baldiry sayesinde ve Mauro beni doğru yola soktu.

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;

Sonra Çalıştırmak ve değişiklikleri görmek için çalıştırın:

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

İadeler

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