PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Прехвърляне на тип NULL при актуализиране на няколко реда

Със самостоятелен VALUES израз PostgreSQL няма представа какви трябва да бъдат типовете данни. С прости числови литерали системата е щастлива да приеме съвпадащи типове. Но с друг вход (като NULL ) ще трябва да го изпълните изрично - както вече разбрахте.

Можете да направите заявка за pg_catalog (бърз, но специфичен за PostgreSQL) или information_schema (бавен, но стандартен SQL), за да разберете и подготвите изявлението си с подходящи типове.

Или можете да използвате един от тези прости „трикове“ (запазих най-доброто за последно ):

0. Изберете ред с LIMIT 0 , добавете редове с 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;

Първият под-селекция на подзаявката:

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

получава имена и типове за колоните, но LIMIT 0 предотвратява добавянето на действителен ред. Следващите редове се принуждават към вече добре дефинирания тип ред - и незабавно се проверяват дали съвпадат с типа. Би трябвало да е фино допълнително подобрение спрямо оригиналния ви формуляр.

Докато предоставя стойности за всички колони на таблицата този кратък синтаксис може да се използва за първия ред:

(TABLE foo LIMIT 0)

Основно ограничение :Postgres предава входните литерали на свободно стоящите VALUES израз към тип "най-добри усилия" незабавно. Когато по-късно се опита да прехвърли към дадените типове на първия SELECT , може вече да е твърде късно за някои типове, ако няма регистрирано прехвърляне на присвояване между предполагаемия тип и целевия тип. Примери:text -> timestamp или text -> json .

Професионално:

  • Минимални режийни разходи.
  • Четим, лесен и бърз.
  • Трябва да знаете само съответните имена на колони в таблицата.

Против:

  • Разделителната способност на типа може да е неуспешна за някои типове.

1. Изберете ред с LIMIT 0 , добавете редове с 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;

Професионално:

  • Като 0. , но избягва неуспешното разделяне на типа.

Против:

  • UNION ALL SELECT е по-бавно от VALUES израз за дълги списъци с редове, както открихте във вашия тест.
  • Подробен синтаксис на ред.

2. VALUES израз с тип за колона

...
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.
...

Противно на 0. това избягва преждевременното разделяне на типа.

Първият ред в VALUES изразът е ред от NULL стойности, които определят типа за всички следващи редове. Този водещ ред с шум се филтрира от WHERE f.pkid = t.pkid по-късно, така че никога не вижда бял свят. За други цели можете да премахнете добавения първи ред с OFFSET 1 в подзаявка.

Професионално:

  • Обикновено по-бързо от 1. (или дори 0. )
  • Кратък синтаксис за таблици с много колони и само няколко са подходящи.
  • Трябва да знаете само съответните имена на колони в таблицата.

Против:

  • Подробен синтаксис само за няколко реда
  • По-малко четими (IMO).

3. VALUES израз с тип ред

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;

Явно знаете името на таблицата. Ако знаете и броя на колоните и реда им, можете да работите с това.

За всяка таблица в PostgreSQL автоматично се регистрира тип ред. Ако съвпадате с броя на колоните във вашия израз, можете да прехвърляте към типа ред на таблицата ('(1,50,)'::foo ), като по този начин имплицитно присвоява типове колони. Не поставяйте нищо зад запетая, за да въведете NULL стойност. Добавете запетая за всяка неуместна колона в края.
В следващата стъпка можете да получите достъп до отделни колони с демонстрирания синтаксис. Повече за Избор на поле в ръководството.

Или можете да добавите ред с NULL стойности и използвайте единен синтаксис за действителни данни:

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

Професионално:

  • Най-бърз (поне в моите тестове с няколко реда и колони).
  • Най-краткият синтаксис за няколко реда или таблици, където имате нужда от всички колони.
  • Не е нужно да изписвате колони от таблицата – всички колони автоматично имат съответстващото име.

Против:

  • Не толкова добре познат синтаксис за избор на поле от запис/ред/композитен тип.
  • Трябва да знаете броя и позицията на съответните колони в ред по подразбиране.

4. VALUES израз с разложен тип ред

Като 3. , но с разложени редове в стандартен синтаксис:

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

Или отново с водещ ред от стойности NULL:

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

Плюсове и минуси като 3. , но с по-известен синтаксис.
И трябва да изпишете имената на колони (ако имате нужда от тях).

5. VALUES израз с типове, извлечени от тип ред

Както коментира Unril, можем да комбинираме достойнствата на 2. и 4. за да предоставите само подмножество от колони:

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;

Плюсове и минуси като 4. , но можем да работим с всяко подмножество от колони и не е нужно да знаем пълния списък.

Също така показва кратък синтаксис за UPDATE това е удобно за случаи с много колони. Свързано:

  • Групова актуализация на всички колони

4. и 5. са моите любими.

db<>цигулка тук - демонстриране на всички



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Име на таблица като параметър на функцията на PostgreSQL

  2. Коментар символ/символи в postgres / postgresql / psql?

  3. Как да определя последния ден от предишния месец с помощта на PostgreSQL?

  4. каква е escape последователността за тире (-) в PostgreSQL

  5. алтернатива на mysql_insert_id за postgresql