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

Rzucanie typu NULL podczas aktualizowania wielu wierszy

Z samodzielnym VALUES wyrażenie PostgreSQL nie ma pojęcia, jakie powinny być typy danych. Dzięki prostym literałom liczbowym system chętnie przyjmuje pasujące typy. Ale z innymi danymi wejściowymi (np. NULL ) musiałbyś rzucać jawnie - jak już się dowiedziałeś.

Możesz zapytać pg_catalog (szybki, ale specyficzny dla PostgreSQL) lub information_schema (powolny, ale standardowy SQL), aby znaleźć i przygotować zestawienie z odpowiednimi typami.

Możesz też użyć jednej z tych prostych „sztuczek” (najlepsze zapisałem na ostatni ):

0. Wybierz wiersz z LIMIT 0 , dołącz wiersze z UNION ALL VALUES

UPDATE foo f
SET    x = t.x
     , y = t.y
FROM  (
  (SELECT pkid, x, y FROM foo LIMIT 0) -- parenthesis needed with LIMIT
   UNION ALL
   VALUES
      (1, 20, NULL)  -- no type casts here
    , (2, 50, NULL)
   ) t               -- column names and types are already defined
WHERE  f.pkid = t.pkid;

Pierwszy wybór podrzędny podzapytania:

(SELECT x, y, pkid  FROM foo LIMIT 0)

pobiera nazwy i typy dla kolumn, ale LIMIT 0 zapobiega dodaniu rzeczywistego wiersza. Kolejne wiersze są przypisywane do dobrze zdefiniowanego typu wiersza - i natychmiast sprawdzane, czy pasują do typu. Powinno to być subtelne dodatkowe ulepszenie w stosunku do oryginalnego formularza.

Podczas podawania wartości dla wszystkich kolumny tabeli ta krótka składnia może być użyta w pierwszym wierszu:

(TABLE foo LIMIT 0)

Główne ograniczenie :Postgres rzutuje literały wejściowe niezależnych VALUES wyrażenie do typu "najlepszego wysiłku" natychmiast. Kiedy później spróbuje rzutować na podane typy pierwszego SELECT , może być już za późno dla niektórych typów, jeśli nie ma zarejestrowanego rzutowania przypisania między typem zakładanym a typem docelowym. Przykłady:text -> timestamp lub text -> json .

Pro:

  • Minimalne obciążenie.
  • Czytelny, prosty i szybki.
  • Musisz tylko znać odpowiednie nazwy kolumn tabeli.

Wada:

  • Rozdzielczość typów może się nie powieść w przypadku niektórych typów.

1. Wybierz wiersz z LIMIT 0 , dołącz wiersze za pomocą UNION ALL SELECT

UPDATE foo f
SET    x = t.x
     , y = t.y
FROM  (
  (SELECT pkid, x, y FROM foo LIMIT 0) -- parenthesis needed with LIMIT
   UNION ALL SELECT 1, 20, NULL
   UNION ALL SELECT 2, 50, NULL
   ) t               -- column names and types are already defined
WHERE  f.pkid = t.pkid;

Pro:

  • Jak 0. , ale pozwala uniknąć błędów w rozwiązywaniu typów.

Wada:

  • UNION ALL SELECT jest wolniejszy niż VALUES wyrażenie dla długich list wierszy, jakie znalazłeś w teście.
  • Pełna składnia na wiersz.

2. VALUES wyrażenie z typem na kolumnę

...
FROM  (
   VALUES 
     ((SELECT pkid FROM foo LIMIT 0)
    , (SELECT x    FROM foo LIMIT 0)
    , (SELECT y    FROM foo LIMIT 0))  -- get type for each col individually
   , (1, 20, NULL)
   , (2, 50, NULL)
   ) t (pkid, x, y)  -- columns names not defined yet, only types.
...

W przeciwieństwie do 0. pozwala to uniknąć przedwczesnego rozwiązywania typów.

Pierwszy wiersz w VALUES wyrażenie jest wierszem NULL wartości, które określają typ dla wszystkich kolejnych wierszy. Ten wiodący wiersz szumu jest filtrowany przez WHERE f.pkid = t.pkid później, więc nigdy nie ujrzy światła dziennego. W innych celach możesz wyeliminować dodany pierwszy wiersz za pomocą OFFSET 1 w podzapytaniu.

Pro:

  • Zazwyczaj szybciej niż 1. (lub nawet 0. )
  • Krótka składnia tabel z wieloma kolumnami i tylko kilka ma znaczenie.
  • Musisz tylko znać odpowiednie nazwy kolumn tabeli.

Wada:

  • Pełna składnia tylko dla kilku wierszy
  • Mniej czytelny (IMO).

3. VALUES wyrażenie z typem wiersza

UPDATE foo f
SET x = (t.r).x         -- parenthesis needed to make syntax unambiguous
  , y = (t.r).y
FROM (
   VALUES
      ('(1,20,)'::foo)  -- columns need to be in default order of table
     ,('(2,50,)')       -- nothing after the last comma for NULL
   ) t (r)              -- column name for row type
WHERE  f.pkid = (t.r).pkid;

Oczywiście znasz nazwę stołu. Jeśli znasz również liczbę kolumn i ich kolejność, możesz z tym pracować.

Dla każdej tabeli w PostgreSQL typ wiersza jest rejestrowany automatycznie. Jeśli dopasujesz liczbę kolumn w wyrażeniu, możesz rzutować na typ wiersza tabeli ('(1,50,)'::foo ), przypisując w ten sposób typy kolumn niejawnie. Nie umieszczaj nic za przecinkiem, aby wpisać NULL wartość. Dodaj przecinek dla każdej nieistotnej końcowej kolumny.
W następnym kroku możesz uzyskać dostęp do poszczególnych kolumn z zademonstrowaną składnią. Więcej informacji o wybieraniu pól w instrukcji.

Możesz też dodać wiersz wartości NULL i użyj jednolitej składni dla rzeczywistych danych:

...
  VALUES
      ((NULL::foo))  -- row of NULL values
    , ('(1,20,)')    -- uniform ROW value syntax for all
    , ('(2,50,)')
...

Pro:

  • Najszybszy (przynajmniej w moich testach z kilkoma wierszami i kolumnami).
  • Najkrótsza składnia dla kilku wierszy lub tabel, w których potrzebne są wszystkie kolumny.
  • Nie musisz przeliterować kolumn tabeli – wszystkie kolumny automatycznie mają pasującą nazwę.

Wada:

  • Niezbyt dobrze znana składnia do wyboru pola z rekordu/wiersza/typu złożonego.
  • Musisz znać liczbę i położenie odpowiednich kolumn w domyślnej kolejności.

4. VALUES wyrażenie z rozłożonym typ wiersza

Jak 3. , ale z rozłożonymi wierszami w standardowej składni:

UPDATE foo f
SET    x = t.x
     , y = t.y
FROM (
   VALUES
      (('(1,20,)'::foo).*)  -- decomposed row of values
    , (2, 50, NULL)
   ) t(pkid, x, y)  -- arbitrary column names (I made them match)
WHERE  f.pkid = t.pkid;     -- eliminates 1st row with NULL values

Lub ponownie z wiodącym wierszem wartości NULL:

...
   VALUES
      ((NULL::foo).*)  -- row of NULL values
    , (1, 20, NULL)    -- uniform syntax for all
    , (2, 50, NULL)
...

Wady i zalety jak 3. , ale z bardziej znaną składnią.
I musisz przeliterować nazwy kolumn (jeśli ich potrzebujesz).

5. VALUES wyrażenie z typami pobranymi z typu wiersza

Jak skomentował Unril, możemy połączyć zalety 2. i 4. podać tylko podzbiór kolumn:

UPDATE foo f
SET   (  x,   y)
    = (t.x, t.y)  -- short notation, see below
FROM (
   VALUES
      ((NULL::foo).pkid, (NULL::foo).x, (NULL::foo).y)  -- subset of columns
    , (1, 20, NULL)
    , (2, 50, NULL)
   ) t(pkid, x, y)       -- arbitrary column names (I made them match)
WHERE  f.pkid = t.pkid;

Wady i zalety jak 4. , ale możemy pracować z dowolnym podzbiorem kolumn i nie musimy znać pełnej listy.

Wyświetlam również krótką składnię UPDATE samo w sobie jest wygodne w przypadku przypadków z wieloma kolumnami. Powiązane:

  • Zbiorcza aktualizacja wszystkich kolumn

4. i 5. są moimi ulubionymi.

db<>graj tutaj - demonstrowanie wszystkich



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Java Crosstab — zapytanie przygotowanej instrukcji

  2. Niezdefiniowana metoda Railsów dla ActiveRecord_Associations_CollectionProxy

  3. Wykonaj odroczony wyzwalacz tylko raz na wiersz w PostgreSQL

  4. Jak odbudować niespójne serwery PostgreSQL?

  5. [Wideo] Integracja danych z PostgreSQL