Cara mengatur kolom tipe komposit menggunakan sql dinamis dalam prosedur pemicu

Saya memiliki fungsi pemicu yang dipanggil oleh beberapa tabel ketika KOLOM A diperbarui, sehingga KOLOM B dapat diperbarui berdasarkan nilai dari fungsi yang berbeda. (Lebih rumit untuk dijelaskan daripada yang sebenarnya). Fungsi trigger menggunakan col_a dan col_b karena keduanya berbeda untuk tabel yang berbeda.

IF needs_updated THEN
    sql = format('($1).%2$s = dbo.foo(($1).%1$s); ', col_a, col_b);

    EXECUTE sql USING NEW;
END IF;

Ketika saya mencoba menjalankan yang di atas, formatnya menghasilkan sql ini:

($1).NameText = dbo.foo(($1).Name); 

Ketika saya menjalankan SQL dengan USING saya mengharapkan sesuatu seperti ini terjadi (yang berfungsi ketika dieksekusi langsung tanpa sql dinamis):

NEW.NameText = dbo.foo(NEW.Name); 

Sebaliknya saya mendapatkan:

[42601] ERROR: kesalahan sintaksis pada atau di dekat "$1"

Bagaimana cara memperbarui kolom secara dinamis pada tipe rekaman/komposit BARU?


person Airn5475    schedule 25.02.2016    source sumber


Jawaban (2)


Ini tidak akan berhasil karena NEW.NameText = dbo.foo(NEW.Name); bukan kueri sql yang benar. Dan saya tidak dapat memikirkan cara Anda dapat memperbarui atribut variabel BARU secara dinamis. Saran saya adalah mendefinisikan perilaku untuk setiap tabel Anda secara eksplisit:

IF TG_TABLE_SCHEMA = 'my_schema' THEN
    IF TG_TABLE_NAME = 'my_table_1' THEN
        NEW.a1 = foo(NEW.b1);
    ELSE IF TG_TABLE_NAME = 'my_table_2' THEN
        NEW.a2 = foo(NEW.b2);
    ... etc ...
    END IF;
END IF;
person Ildar Musin    schedule 25.02.2016
comment
Ildar, terima kasih atas tanggapannya. Sayangnya, ada logika di balik needs_updated yang akan diulangi untuk setiap tabel (dan kolom tertentu) dan pada saat itu saya mungkin juga membuat Fungsi Pemicu terpisah untuk setiap tabel. Saya berharap untuk mengurangi logika yang berulang-ulang. - person Airn5475; 25.02.2016

Pertama: Ini adalah masalah besar di plpgsql. Jadi rekomendasi terbaik saya adalah melakukan ini di beberapa PL lain, seperti plpythonu atau plperl. Melakukan hal ini pada salah satu dari hal tersebut adalah hal yang sepele. Bahkan jika Anda tidak ingin melakukan seluruh pemicu di PL lain, Anda masih dapat melakukan sesuatu seperti:

v_new RECORD;
BEGIN
v_new := plperl_function(NEW, column_a...)

Kunci untuk melakukan hal ini di plpgsql adalah membuat CTE yang memiliki apa yang Anda perlukan di dalamnya:

c_new_old CONSTANT text := format(
  'WITH
    NEW AS (SELECT (r).* FROM (SELECT ($1)::%1$s r) s)
    , OLD AS (SELECT (r).* FROM (SELECT ($2)::%1$s r) s
    '
  , TG_RELID::regclass
);

Anda juga perlu mendefinisikan v_new yang merupakan catatan biasa. Anda kemudian dapat melakukan sesuatu seperti:

-- Replace 2nd field in NEW with a new value
sql := c_new_old || $$SELECT row(NEW.a, $3, NEW.c) FROM NEW$$
EXECUTE sql INTO v_new USING NEW, OLD, new_value;
person Jim Nasby    schedule 29.02.2016