本というテーブルがあります
CREATE TABLE book
(
id smallint NOT NULL DEFAULT 0,
bname text,
btype text,
bprices numeric(11,2)[],
CONSTRAINT key PRIMARY KEY (id )
)
そして関数save_book
CREATE OR REPLACE FUNCTION save_book(thebook book)
RETURNS text AS
$BODY$
DECLARE
myoutput text :='Nothing has occured';
BEGIN
update book set
bname=thebook.bname,
btype=thebook.btype,bprices=thebook.bprices WHERE id=thebook.id;
IF FOUND THEN
myoutput:= 'Record with PK[' || thebook.id || '] successfully updated';
RETURN myoutput;
END IF;
BEGIN
INSERT INTO book values(thebook.id,thebook.bname,thebook.btype,
thebook.bprices);
myoutput:= 'Record successfully added';
END;
RETURN myoutput;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
関数を呼び出すと
SELECT save_book('(179,the art of war,fiction,{190,220})'::book);
エラーが出る
ERROR: malformed array literal: "{190"
SQL state: 22P02
Character: 18
配列の形式にエラーが表示されないため、理解できません。ヘルプはありますか?
この種のものは複雑になります。現在、いくつかの関連プロジェクトに取り組んでいます。基本的なTweakは、PostgreSQLがタプル表現で内部的に二重引用符を使用してリテラル値を表す形式を使用しているためです。
SELECT save_book('(179,the art of war,fiction,"{190,220}")'::book);
うまくいくはずです。本質的には、巧妙なトリックはcsvを作成し、タプルまたは配列識別子で囲むことです。大きな問題は、エスケープに対処する必要があることです(必要に応じて、すべてのレベルで引用符を2倍にします)。したがって、以下はまったく同じです。
SELECT save_book('(179,"the art of war","fiction","{""190"",""220""}")'::book);
2番目のアプローチは、行コンストラクターを使用することです。
SELECT save_book(row(179,'the art of war','fiction', array[190,220])::book);
最初のソリューションには、CSVの生成とエスケープのために既存のプログラミングフレームワークを利用できるという明らかな利点があります。 2番目はSQLで最もクリーンです。それらを混合して一致させることができます。
行型の正しい構文について疑問がある場合は、Postgresに尋ねてください。知っておくべきこと:
SELECT b FROM book b LIMIT 1; -- or: WHERE id = 179;
これにより、有効な形式で行のテキスト表現が返されます。
(179,"the art of war",fiction,"{190,220}")
列の値は、引用符で囲まれておらず、カンマで区切られたリストとして表され、括弧で囲まれます。
二重引用符は、あいまいさがある可能性がある場合に値を囲むために使用されます-空白のあるテキストを含みます。この特定のケースでは、"the art of war"
を囲む二重引用符はオプションですが、"{190,220}"
を囲む二重引用符は配列に必要です。
文字列を一重引用符で囲み、変更してテストします。
SELECT '(333,the art of war,fiction,"{191,220,235}")'::book
関連する前述の質問の下で私たちが議論したことを考慮してください:
PSERT関数の複合型の問題
別の block (BEGIN .. END;
)は、EXCEPTION
が発生する可能性があるINSERT
をキャッチしたい場合にのみ役立ちます。例外のあるブロックはある程度のオーバーヘッドを伴うため、入力されない可能性のある別のブロックを用意することは理にかなっています。
CREATE OR REPLACE FUNCTION save_book(thebook book)
RETURNS text AS
$BODY$
BEGIN
UPDATE book
SET bname = thebook.bname
,btype = thebook.btype
,bprices = thebook.bprices
WHERE id = thebook.id;
IF FOUND THEN
RETURN format('Record with PK[%s] successfully updated', thebook.id);
END IF;
BEGIN
INSERT INTO book SELECT (thebook).*;
RETURN format('Record with PK[%s] successfully inserted', thebook.id);
EXCEPTION WHEN unique_violation THEN
UPDATE book
SET bname = thebook.bname
,btype = thebook.btype
,bprices = thebook.bprices
WHERE id = thebook.id;
END;
RETURN format('Record with PK[%s] successfully updated', thebook.id);
END
$BODY$ LANGUAGE plpgsql
それ以外の場合、simplify:
CREATE OR REPLACE FUNCTION save_book(thebook book)
RETURNS text AS
$BODY$
BEGIN
UPDATE book
SET bname = thebook.bname
,btype = thebook.btype
,bprices = thebook.bprices
WHERE id = thebook.id;
IF FOUND THEN
RETURN format('Record with PK[%s] successfully updated', thebook.id);
END IF;
INSERT INTO book SELECT (thebook).*;
RETURN format('Record with PK[%s] successfully inserted', thebook.id);
END
$BODY$ LANGUAGE plpgsql
INSERT
ステートメントも簡略化しました。特定の状況では、列リストをINSERTから省略しても安全です。
私はあなたのソリューションの本当の利点を見ていませんが、次のように個々の値を渡すのではなく、関数に行を渡すことを意味します
CREATE OR REPLACE FUNCTION save_book2(
integer
, text
, text
, integer[]
)
RETURNS text AS
...
とにかく、関数を正しく呼び出すと、ソリューションも同様に機能します。
SELECT ave_book((179, 'the art of war', 'fiction', '{190,220}')::book);
つまり、レコード式は引用符で囲む必要はありませんが、テキスト値と配列リテラルは引用します。