CTE'lerle SQL işlevleri üzerinde PostgreSQL 9.6 sütun bırakma ve yan etkiler


15

D, A, B ve D - 3 sütun içeren bir tablo vardı ve yeni bir tanıtmak zorunda - D D mevcut konumunu değiştirmek için C demek: Ben aşağıdaki yöntemi kullanırdım:

  1. C ve D2 olarak 2 yeni sütun girin.
  2. D'nin içeriğini D2'ye kopyalayın.
  3. Silindi.
  4. D2'yi D olarak yeniden adlandırın.

Yeni sipariş A, B, C ve D olacaktır.

Bunun şimdiye kadar hiçbir sorun üretmediği için meşru bir uygulama olduğunu düşündüm.

Ancak, bugün aynı tabloda bir ifade yürüten bir işlev aşağıdaki hatayı döndürdüğünde bir sorunla karşılaştım:

table row type and query-specified row type do not match

Ve aşağıdaki detay:

Query provides a value for a dropped column at ordinal position 13

PostgreSQL'i yeniden başlatmayı denedim VACUUM FULLve sonunda burada ve burada önerilen şekilde silme ve yeniden oluşturma işlevini denedim, ancak bu çözümler işe yaramadı (bir sistem tablosunun değiştirildiği bir durumla uğraşmaya çalıştıklarından başka).

Çok küçük bir veritabanı ile çalışma lüksüne sahip olarak onu dışa aktardım, sildim ve daha sonra tekrar içe aktardım ve bu benim fonksiyonumla ilgili sorunu düzeltti .


Burada görüldüğü gibi sistem tablolarını değiştirerek (elleri kirleten pg_attributevb.) Sütunların doğal düzeniyle uğraşmaması gerektiğinin farkındaydım :

Postgres'deki sütunların doğal sırasını değiştirmek mümkün müdür?

İşlevim tarafından atılan hataya bakılırsa, yöntemimle sütunların sırasını değiştirmenin de hayır-hayır olduğunu anlıyorum. Yaptığım şeyin neden yanlış olduğuna kim ışık tutabilir?


Postgres sürümü 9.6.0'dır.

İşte işlevi:

CREATE OR REPLACE FUNCTION "public"."__post_users" ("facebookid" text, "useremail" text, "username" text) RETURNS TABLE (authentication_code text, id integer, key text, stripe_id text) AS '

-- First, select the user:
WITH select_user AS
(SELECT
users.id
FROM
users
WHERE
useremail = users.email),

-- Second, update the user (if user exists):
update_user AS
(UPDATE
users
SET
authentication_code = GEN_RANDOM_UUID(),
authentication_date = current_timestamp,
facebook_id = facebookid
WHERE EXISTS (SELECT * FROM select_user)
AND
useremail = users.email
RETURNING
users.authentication_code,
users.id,
users.key,
users.stripe_id),

-- Third, insert the user (if user does not exist):
insert_user AS
(INSERT INTO
users (authentication_code, authentication_date, email, key, name, facebook_id)
SELECT
GEN_RANDOM_UUID(),
current_timestamp,
useremail,
GEN_RANDOM_UUID(),
COALESCE(username, SUBSTRING(useremail FROM ''([^@]+)'')),
facebookid
WHERE NOT EXISTS (SELECT * FROM select_user)
RETURNING
users.authentication_code,
users.id,
users.key,
users.stripe_id)

-- Finally, select the authentication code, ID, key and Stripe ID:
SELECT
*
FROM
update_user
UNION ALL
SELECT
*
FROM
insert_user' LANGUAGE "sql" COST 100 ROWS 1
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER

Her iki sütunda yeniden adlandırma / yeniden sıralama gerçekleştirdim facebook_idve stripe_id(bunlardan önce yeni bir sütun eklendi, bu yeniden adlandırma nedeni, ancak bu sorgu tarafından dokunulmamış).

Sütunların belirli bir sırada olması tamamen sipariş için ilgi odağı değildir. Bununla birlikte, bu soruyu sormanın nedeni, bir sütunun basit bir şekilde yeniden adlandırılması ve silinmesinin üretim modunda (kendime olduğu gibi) işlevleri kullanan biri için gerçek sorunları tetikleyebileceğinden endişe etmemektedir.


Bir göz atın Bir PostgreSQL veritabanı tablosundaki bir sütun konumunu değiştirmek nasıl? yığın sütunlarında yardımcı olabilir, ancak çoğunlukla sütunlarınızı yeniden sıralamamanızı önerir.
joanolo

Yanıtlar:


16

9.6 ve 9.6.1'de olası hata

Bu tamamen benim için bir hata gibi görünüyor ...

Neden olduğunu bilmiyorum, ama olduğunu teyit edebilirim. Bu, sorunu yeniden oluşturan en basit kurulumdur (sürüm 9.6.0 ve 9.6.1'de).

CREATE TABLE users
(
    id SERIAL PRIMARY KEY,
    email TEXT NOT NULL,
    column_that_we_will_drop TEXT
) ;

-- Function that uses the previous table, and that has a CTE
CREATE OR REPLACE FUNCTION __post_users
    (_useremail text) 
RETURNS integer AS
$$
-- Need a CTE to produce the error. A 'constant' one suffices.
WITH something_even_if_useless(a) AS
(
    VALUES (1)
)
UPDATE
    users
SET
    id = id
WHERE 
    -- The CTE needs to be referenced, if the next
    -- condition were not in place, the problem is not reproduced
    EXISTS (SELECT * FROM something_even_if_useless)
    AND email = _useremail
RETURNING
    id
$$
LANGUAGE "sql" ;

Bu kurulumdan sonra, sonraki ifade çalışır

SELECT * FROM __post_users('a@b.com');

Bu noktada, bir sütun bırakıyoruz:

ALTER TABLE users 
    DROP COLUMN column_that_we_will_drop ;

Bu değişiklik, bir sonraki ifadeyi hata oluşturmak için yapar

SELECT * FROM __post_users('a@b.com');

@Andy tarafından belirtilenle aynı:

ERROR: table row type and query-specified row type do not match
SQL state: 42804
Detail: Query provides a value for a dropped column at ordinal position 3.
Context: SQL function "__post_users" statement 1
    SELECT * FROM __post_users('a@b.com');

İşlevin düşürülmesi ve yeniden oluşturulması sorunu çözmez.
VAKUM DOLU (tablo veya tüm veritabanı) sorunu çözmez.


Hata raporu uygun PostgreSQL posta listesine geçti ve çok hızlı bir yanıt aldık :

Bunu HEAD veya 9.6 dal ucunda çoğaltamıyorum. Ben zaten 9.6.1 sonra biraz gitti bu yama ile zaten sabit olduğuna inanıyorum:

https://git.postgresql.org/gitweb/?p=postgresql.git&a=commitdiff&h=f4d865f22

Ama rapor için teşekkürler!

selamlar, tom lane


Sürüm 9.6.2

2017-03-06'da, 9.6.2 sürümünde bu davranışı yeniden oluşturamadığımı doğrulayabilirim. Yani, hata bu sürümde düzeltilmiş gibi görünüyor.

GÜNCELLEME

@Jana yorum başına: "Ben hata 9.6.1 'de mevcut ve 9.6.2' de düzeltildi teyit edebilirim. Düzeltme ayrıca postgres yayın web sitesinde listelenir : Düzeltme sahte" sorgu bırakılan sütun için bir değer sağlar "hataları sırasında Sütunu atlanmış bir tabloya INSERT veya UPDATE "



4
9.6.0 kullanıyorum ve @joanolo doğru, yöntemi ile hata yeniden üretebilir. Tom bunu yeniden üretemezse, muhtemelen bu belirli sürümle izole edilmiş bir hataydı (ve 9.6.1 sanırım?). Cevapta belirtildiği üzere iken, konu görünür bırakarak bir tablodaki bir sütun ve CTE etkileyen bir fonksiyonu kullanarak bu tabloyu. Dolayısıyla, sorun sadece yeniden sıralama ile ilgili değildir ve sorumu ele almaya çalıştığım da buydu. Bunu yansıtmak için başlığı düzenleyeceğim.
Andy

Hatanın 9.6.1'de mevcut olduğunu ve 9.6.2'de düzeltildiğini onaylayabilirim. Düzeltme ayrıca postgres sürümü web sitesinde listelenir : Düzeltme sahte "sorgu bırakılan sütun için bir değer sağlar" INSERT veya UPDATE sırasında bırakılan sütun içeren bir hata
Jana

0

Bunu muhtemelen daha önce duyduğunuzu biliyorum, ama bu korkunç bir fikir.

  • Mantıksal sıralama yalnızca SELECT *
  • Mantıksal sıralamanın etkisi görünümle sınırlıdır.

Dolayısıyla, hiç fark etmemek sizi caydırmazsa ve Photoshop'u sadece satır yapısıyla oynadığımızı ve görüntüyü takıntı haline getirdiğimizi kabul edersek, biraz daha açıklayalım.

  • PostgreSQL'de mantıksal sıralama mevcut değil
  • Fiziksel siparişin gerçek faydaları vardır (masa ambalajı)
  • PostgreSQL her CREATE TABLEikisinden sonra size Fiziksel Sipariş üzerinde herhangi bir kontrol sağlamaz ( çok daha yüksek bir öncelik olsa da )

PostgreSQL kötü bir görüntü katmanıdır. Tüm bunların üzerine, iyi çalışırken ALTERejderhayı baştan çıkarmamalısınız. Hem CAVEATALTER TABLE hem de bundan bahsediyor,

Bazı DDL komutları, şu anda yalnızca TRUNCATEve tablo yeniden yazma formları ALTER TABLEMVCC için güvenli değildir. Bu, kesme veya yeniden yazma işlemi tamamlandıktan sonra, DDL komutunun yürütülmesinden önce çekilen bir anlık görüntü kullanıyorsa, tablonun eşzamanlı işlemlere boş görüneceği anlamına gelir. Bu, yalnızca DDL komutu başlamadan önce söz konusu tabloya erişmeyen bir işlem için bir sorun olacaktır - bunu yapan herhangi bir işlem, işlem tamamlanıncaya kadar DDL komutunu engelleyen en az bir ACCESS SHARE tablo kilidine sahip olacaktır. Bu nedenle, bu komutlar hedef tabloda ardışık sorgular için tablo içeriğinde herhangi bir görünür tutarsızlığa neden olmaz, ancak hedef tablonun içeriği ile veritabanındaki diğer tablolar arasında görünür tutarsızlığa neden olabilir.

Ve eğer bunların hepsi yeterli değilse ve hala bunun iyi bir fikir olduğunu, korkunç bir fikir olduğunu iddia etmek istiyorsanız. Sonra şunu deneyin,

  1. İle tablo dökümü pg_dump -t
  2. Çöplükte sütunları elle yeniden sıralayın.
  3. Öğeleri bir TEMP tablosuna yükleyin.
  4. BEGIN bir işlem
  5. DROP eski masa tamamen,
  6. RENAME eşya tablosuna geçici tablo.
  7. COMMIT

Bu seslerin hepsi aşırı olursa, (onlar farzederek veritabanında satırları güncelleme satırları yeniden yazma gerektirir unutmayın değil TOST . Veri ve yeniden inşa tablo şema ayrıştırmak yaşıyorsanız, ancak her iki durumda da yeniden yazma lazım satır. Ben ise vardı , bu görevi yerine getirecek ben bunu yapacağını nasıl.

Ancak, tüm bunlar genel olarak konuşur. Sonuçlarınızı hiç kimse üretmedi.

Kaçamayacağımız bir test davası verdin

ERROR:  column users.authentication_code does not exist
LINE 24: users.authentication_code,

Ve bize tam olarak bulunduğunuz sürümü söylemediniz .


Ayrıntılı açıklama için teşekkür ederim, soruyu belirli bir sürümü içerecek şekilde düzenledim.
Andy

4
Hata 9.6.0 sürümünde yeniden üretilebilir
ypercubeᵀᴹ

0

Veritabanımı yedekleyerek ve geri yükleyerek bu hatayı aldım.

Heroku için adımlar

  • Bakım modunu açın: heroku maintenance:on
  • Yedek veritabanı: heroku pg:backups:capture
  • Veritabanını geri yükle: heroku pg:backups:restore
  • Uygulamayı yeniden başlat: heroku restart
  • Bakım modunu kapatın: heroku maintenance:off

0

Ben de bu hatayla karşılaştım. DB'lerini tamamen yedeklemek / geri yüklemek istemeyenler için. Sadece tablo kopyalamanın işe yaradığını bilin. Yine de bir tablo kopyalamak için "büyülü" bir yolu yoktur. Bunu kullanarak yaptım:

SELECT * INTO mytable_copy FROM mytable;
ALTER TABLE mytable RENAME TO mytable_backup; -- just in case. you never know
ALTER TABLE mytable_copy RENAME TO mytable;

Bundan sonra, yine de dizinlerinizi, yabancı anahtarlarınızı ve varsayılanlarınızı manuel olarak yeniden oluşturmanız gerekir. Tabloyu bu şekilde yeniden oluşturmak hatayı ortadan kaldırdı.

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.