Saya melihat dua masalah utama:
1. Anda tidak dapat memasukkan UPDATE
menjadi subquery sama sekali . Anda dapat menyelesaikannya dengan pengubah data CTE
seperti Patrick mendemonstrasikan
, tetapi itu lebih mahal dan bertele-tele daripada yang diperlukan untuk kasus yang dihadapi.
2. Anda memiliki konflik penamaan yang berpotensi berbahaya , yang belum ditangani.
Kueri/fungsi yang lebih baik
Meninggalkan pembungkus fungsi SQL untuk saat ini (kita akan kembali ke sana). Anda dapat menggunakan UPDATE
sederhana dengan RETURNING
klausa:
UPDATE tbl
SET value1 = 'something_new'
WHERE id = 123
RETURNING row_to_json(ROW(value1, value2));
RETURNING
klausa memungkinkan ekspresi sewenang-wenang yang melibatkan kolom dari baris yang diperbarui. Itu lebih pendek dan lebih murah daripada CTE pengubah data.
Masalah yang tersisa:konstruktor baris ROW(...)
tidak mempertahankan nama kolom (yang merupakan kelemahan yang diketahui), jadi Anda mendapatkan kunci umum dalam nilai JSON Anda:
row_to_json
{"f1":"something_new","f2":"what ever is in value2"}
Di Postgres 9.3 Anda memerlukan CTE fungsi lain untuk merangkum langkah pertama atau gips ke tipe baris yang terdefinisi dengan baik. Detail:
Di Postgres 9.4 cukup gunakan json_build_object()
atau json_object()
:
UPDATE tbl
SET value1 = 'something_new'
WHERE id = 123
RETURNING json_build_object('value1', value1, 'value2', value2);
Atau:
...
RETURNING json_object('{value1, value2}', ARRAY[value1, value2]);
Sekarang Anda mendapatkan nama kolom asli atau apa pun yang Anda pilih sebagai nama kunci:
row_to_json
{"value1":"something_new","value2":"what ever is in value2"}
Sangat mudah untuk membungkus ini dalam sebuah fungsi, yang membawa kita ke masalah kedua Anda ...
Konflik penamaan
Dalam fungsi asli Anda, Anda menggunakan nama yang identik untuk parameter fungsi dan nama kolom. Ini biasanya merupakan ide yang sangat buruk . Anda perlu memahami secara mendalam pengidentifikasi mana yang muncul lebih dulu dalam cakupan mana.
Dalam kasus yang ada, hasilnya benar-benar tidak masuk akal:
Create Or Replace Function ExampleTable_Update (id bigint, value1 text) Returns
...
Update ExampleTable
Set Value1 = value1
Where id = id
Returning Value1, Value2;
...
$$ Language SQL;
Meskipun Anda tampaknya mengharapkan contoh kedua dari id
akan mereferensikan parameter fungsi, tidak. Nama kolom didahulukan dalam lingkup pernyataan SQL, instance kedua merujuk ke kolom. menghasilkan ekspresi yang selalu true
kecuali untuk nilai NULL di id
. Akibatnya, Anda akan memperbarui semua baris , yang dapat menyebabkan kehilangan data yang sangat besar .Yang lebih parah, Anda mungkin tidak menyadarinya sampai nanti, karena fungsi SQL akan mengembalikan satu baris arbitrer seperti yang didefinisikan oleh RETURNING
klausa fungsi (mengembalikan satu baris, bukan kumpulan baris).
Dalam kasus khusus ini, Anda akan mendapatkan "keberuntungan", karena Anda juga memiliki value1 = value1
, yang menimpa kolom dengan nilai yang sudah ada sebelumnya, secara efektif melakukan .. tidak ada dengan cara yang sangat mahal (kecuali pemicu melakukan sesuatu). Anda mungkin bingung untuk mendapatkan baris arbitrer dengan value1
unchanged yang tidak berubah sebagai hasilnya.
Jadi, jangan.
Hindari potensi konflik penamaan seperti ini kecuali Anda tahu persis apa yang Anda lakukan (yang jelas tidak demikian). Satu konvensi yang saya suka adalah menambahkan garis bawah untuk parameter dan nama variabel dalam fungsi, sementara nama kolom tidak pernah dimulai dengan garis bawah. Dalam banyak kasus, Anda dapat menggunakan referensi posisi agar tidak ambigu:$1
, $2
, ..., tetapi itu hanya mengabaikan setengah dari masalah. Metode apa pun bagus selama Anda menghindari konflik penamaan . Saya sarankan:
CREATE OR REPLACE FUNCTION foo (_id bigint, _value1 text)
RETURNS json AS
$func$
UPDATE tbl
SET value1 = _value1
WHERE id = _id
RETURNING json_build_object('value1', value1, 'value2', value2);
$func$ LANGUAGE sql;
Perhatikan juga bahwa ini mengembalikan nilai kolom yang sebenarnya di value1
setelah UPDATE
, yang mungkin atau mungkin tidak sama dengan parameter input Anda _value1
. Mungkin ada aturan database atau pemicu yang mengganggu ...