PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Sprawdź, czy klucz istnieje w JSON z PL/pgSQL?

Już wiesz, że możesz przetestować wyrażenie user_info->>'username' dla NULL. Ale Twoja funkcja jest nadal bardzo nieefektywna . I nadal istnieją niejasności .

Lepsze rozwiązanie w Postgresie 9.3

Wielokrotne aktualizowanie wiersza dla wielu kolumn jest kosztowne. Postgres zapisuje nową wersję wiersza dla każdej aktualizacji. Użyj singla UPDATE jeśli to w ogóle możliwe:

CREATE OR REPLACE FUNCTION sp_update_user(_user_id int, _user_info json)
  RETURNS json AS
$func$
BEGIN
   UPDATE users u
   SET    firstname = COALESCE(_user_info->>'firstname', u.firstname)
        , lastname  = COALESCE(_user_info->>'lastname' , u.lastname)
   WHERE  id = sp_update_user._user_id
   AND   ((_user_info->>'firstname') IS NOT NULL OR
          (_user_info->>'lastname')  IS NOT NULL);

   IF FOUND THEN
      RETURN '{"success":true}'::json;
   ELSE
      RETURN '{"success":false}'::json;
   END IF;
END
$func$  LANGUAGE plpgsql;

Zadzwoń:

SELECT sp_update_user(123, '{"firstname": "jon", "lastname": "doe"}')
  • Jest to znacznie szybsze w przypadku wielu kolumn, ponieważ tylko jedna UPDATE (co najwyżej) jest wykonywany. Jeśli WHERE klauzula nie ma wartości true , nie następuje żadna aktualizacja i otrzymujesz '{"success":false}' w rezultacie.

  • Jeśli czasami wartości w tabeli są już takie, na jakie są zmieniane, możliwa jest kolejna optymalizacja. Rozważ ostatni akapit tej powiązanej odpowiedzi:

  • Zmienna / parametr user_id brakuje w oryginale.

  • Nadal istnieje przypadek narożny niejednoznaczność . Jeśli element istnieje i jest ustawiony na JSON null , otrzymasz również SQL NULL w wyniku. Rozważ:

    SELECT ('{"b": null}'::json->>'b') IS NULL AS b_is_null
         , ('{"c": 2}'::json->>'b')    IS NULL AS b_missing;
    
  • Nie wiem, dlaczego używasz danych typu json jako typ zwrotu, po prostu go zachowałem. Ale jeśli funkcja się nie aktualizuje, nie możesz być pewien, dlaczego otrzymujesz false . Może nie być wiersza o podanym id , nazwy kluczy 'firstname' i 'lastname' może brakować - lub być null ...


Doskonałe rozwiązanie w Postgresie 9.4

Jest czysty i proste rozwiązanie w Postgresie 9.4 z jsonb z ? operator „istnienia” - który może nawet używać indeksu dla większych tabel (nie dotyczy Twojej funkcji):

SELECT ('{"b": null}'::jsonb ? 'b') AS b_is_null
     , ('{"c": 2}'::jsonb ? 'b')    AS b_missing;

A ?| i ?& warianty aby sprawdzić wiele kluczy naraz.
Więc możemy zaimplementować:

CREATE OR REPLACE FUNCTION sp_update_user(_user_id int, _user_info jsonb)
  RETURNS jsonb AS
$func$
BEGIN
   UPDATE users u
   SET    firstname = CASE WHEN _user_info ? 'firstname' THEN _user_info->>'firstname' ELSE u.firstname END
        , lastname  = CASE WHEN _user_info ? 'lastname'  THEN _user_info->>'lastname'  ELSE u.lastname  END
   WHERE  id = sp_update_user._user_id
   AND    _user_info ?| '{firstname,lastname}';

   IF FOUND THEN
      RETURN '{"success":true}'::jsonb;
   ELSE
      RETURN '{"success":false}'::jsonb;
   END IF;
END
$func$  LANGUAGE plpgsql;

Te połączenia działają teraz zgodnie z oczekiwaniami:

SELECT sp_update_user(123, '{"firstname": null, "lastname": "doe1"}'::jsonb);
SELECT sp_update_user(123, '{"firstname": "doris"}'::jsonb);


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Nazwa elementu XML z danych w Postgres

  2. Webinarium:Nowe funkcje w Postgres 12 [Kontynuacja]

  3. Serwer nieoczekiwanie zamyka połączenie podczas łączenia się z Azure Postgres

  4. BŁĄD:podzapytanie w FROM nie może odnosić się do innych relacji tego samego poziomu zapytania

  5. Odpytywanie DAU/MAU w czasie (codziennie)