sql >> Databasteknik >  >> RDS >> PostgreSQL

Postgres-funktionen returnerar en rad som JSON-värde

Jag ser två stora problem:
1. Du kan inte lägga in en UPDATE till en underfråga överhuvudtaget . Du kan lösa det med en datamodifiering CTE gillar Patrick demonstrerar , men det är dyrare och mer utförligt än nödvändigt för det aktuella fallet.
2. Du har en potentiellt farlig namnkonflikt , som inte har åtgärdats än.

Bättre fråga/funktion

Lämnar SQL-funktionsomslaget åt sidan för tillfället (vi återkommer till det). Du kan använda en enkel UPDATE med en RETURNING klausul:

UPDATE tbl
SET    value1 = 'something_new'
WHERE  id = 123
RETURNING row_to_json(ROW(value1, value2));

RETURNING satsen tillåter godtyckliga uttryck som involverar kolumner i den uppdaterade raden. Det är kortare och billigare än en datamodifierande CTE.

Det återstående problemet:radkonstruktorn ROW(...) bevarar inte kolumnnamn (vilket är en känd svaghet), så du får generiska nycklar i ditt JSON-värde:

row_to_json
{"f1":"something_new","f2":"what ever is in value2"}

I Postgres 9.3 skulle du behöva en CTE en annan funktion för att kapsla in det första steget eller en cast till en väldefinierad radtyp. Detaljer:

I Postgres 9.4 använd bara json_build_object() eller json_object() :

UPDATE tbl
SET    value1 = 'something_new'
WHERE  id = 123
RETURNING json_build_object('value1', value1, 'value2', value2);

Eller:

...
RETURNING json_object('{value1, value2}', ARRAY[value1, value2]);

Nu får du ursprungliga kolumnnamn eller vad du nu väljer som nyckelnamn:

row_to_json
{"value1":"something_new","value2":"what ever is in value2"}

Det är lätt att slå in det här i en funktion, vilket för oss till ditt andra problem ...

Namnkonflikt

I din ursprungliga funktion använder du identiska namn för funktionsparametrar och kolumnnamn. Detta är generellt en mycket dålig idé . Du måste noggrant förstå vilken identifierare som kommer först i vilken omfattning.

I det aktuella fallet är resultatet totalt nonsens:

Create Or Replace Function ExampleTable_Update (id bigint, value1 text) Returns 
...
    Update ExampleTable
    Set Value1 = value1
    Where id = id
    Returning Value1, Value2;
...
$$ Language SQL;

Medan du verkar förvänta dig att den andra instansen av id skulle referera till funktionsparametern, det gör den inte. Kolumnnamnet kommer först inom ramen för en SQL-sats, den andra instansen refererar till kolumnen. vilket resulterar i ett uttryck som alltid är true förutom NULL-värden i id . Följaktligen skulle du uppdatera alla rader , vilket kan leda till katastrofal förlust av data . Vad värre är, du kanske inte ens inser det förrän senare, eftersom SQL-funktionen kommer att returnera ett godtycklig rad som definieras av RETURNING sats i funktionen (returnerar ett rad, inte en uppsättning rader).

I det här specifika fallet skulle du ha "tur", eftersom du också har value1 = value1 , som skriver över kolumnen med dess redan existerande värde, vilket effektivt gör .. ingenting på ett mycket dyrt sätt (såvida inte triggers gör något). Du kan bli förbryllad över att få en godtycklig rad med ett oförändrat value1 som resultat.

Så gör inte det.

Undvik potentiella namnkonflikter som denna om du inte vet exakt vad du gör (vilket uppenbarligen inte är fallet). En konvention jag gillar är att lägga ett understreck för parameter- och variabelnamn i funktioner, medan kolumnnamn aldrig börjar med ett understreck. I många fall kan du bara använda positionsreferenser för att vara entydiga:$1 , $2 , ..., men det kringgår bara hälften av problemet. Vilken metod som helst är bra så länge du undviker namnkonflikter . Jag föreslår:

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;

Observera också att detta returnerar det faktiska kolumnvärdet i value1 efter UPDATE , som kanske inte är samma som din indataparameter _value1 . Det kan finnas databasregler eller triggers som stör ...



  1. Batch infoga SQL-sats

  2. PL/SQL Gateway i R11i

  3. SQL Välj Kommande födelsedagar

  4. C# Oracle Stored Procedure Parameter Order