Обновление поля Firebird BLOB с добавлением текста

Проблема

Невозможно добавить текст в поле большого двоичного объекта, если это значение равно NULL, с помощью простого объединения (||).

База документации: https://firebirdsql.org/refdocs/langrefupd21-blob.html

Тестовая среда

введите здесь описание изображения

Предполагаемые типы:

  • полеTarget ~ BLOB
  • tablePk ~ VARCHAR(5)

Нравится:

UPDATE tablename
SET fieldTarget = fieldTarget || :string
WHERE tablePk = :pkTarget;

После выполнения ни одна ошибка не возвращается, но поле по-прежнему null


person gbragamonte    schedule 07.03.2019    source источник
comment
Вы уверены, что на самом деле используете Firebird 2.1, это база данных диалекта 3? Каков полный тип fieldTarget? Что случается? Вы получаете ошибку? Какой язык и драйвер вы используете? Может быть, проблема в том, что :string тоже печатается как большой двоичный объект, и ваш драйвер не обрабатывает это должным образом?   -  person Mark Rotteveel    schedule 07.03.2019
comment
Каким-то образом я неоднократно перечитывал когда это значение равно NULL... любая операция с нулевым значением дает нуль.   -  person Mark Rotteveel    schedule 08.03.2019


Ответы (3)


Все строковые операции (такие как ||-конкатенация) ограничены максимальным ограничением размера VarChar (что составляет 32 КБ, что меньше 8200 букв в тексте в кодировке UTF-8).

Однако, похоже, есть BLOB-ориентированные функции. Также не нужно экранировать значения NULL с помощью COALESCE.

СПИСОК возвращает строку, состоящую из значений аргументов, отличных от NULL, в группе, разделенных либо запятой, либо заданным пользователем разделителем.

https://firebirdsql.org/refdocs/langrefupd21-aggrfunc-list.html

Следовательно, используя derived tables:

Select LIST(ALL X, '') From
 ( Select fieldTarget as x From tablename Where tablePk = :pkTarget
      UNION ALL
   Select Cast( :string AS VarChar(8191) ) From RDB$DATABASE ) 
As TMP

Приведение типов заключается в присвоении типа данных выражению, состоящему только из параметров.

P.S. в документации также утверждается, что

Порядок значений в списке не определен.

П.П.С. Готовый к запуску пример, проверенный на FB 2.1.7

Select LIST(ALL X, '') From
 ( Select Cast( :str_pre AS VarChar(8191) ) as X From RDB$DATABASE
      UNION ALL
   Select /* DATA column */ RDB$TRIGGER_SOURCE
          From /* DATA table */ RDB$TRIGGERS
          Where /* PK ID */ RDB$TRIGGER_NAME  = :pkTarget
      UNION ALL
   Select Cast( :str_post AS VarChar(8191) ) From RDB$DATABASE
)

Передача таких имен, как RDB$TRIGGER_1, даст вам system trigger, который не имеет исходного текста, поэтому имеет значение NULL в поле. Легко проверить, что здесь не нужен COALESCE-скрининг :-D

person Arioch 'The    schedule 07.03.2019
comment
Поскольку в Firebird 2.1 конкатенация (||) поддерживает (текстовые) большие двоичные объекты, в магии не должно быть необходимости. См. firebirdsql.org/refdocs/langrefupd25-concat.html. - person Mark Rotteveel; 07.03.2019
comment
@MarkRotteveel, но с двумя ограничениями: значения NULL не пропускаются, но приводят к результату NULL, и его могут использовать только небольшие BLOB-объекты размером с VARCHAR (IOW - это не расширение операторов до BLOB, а неявное приведение типов BLOB-to-VARCHAR). См. firebirdsql.org/file/documentation/release_notes/html/ (v.2.1) На различных уровнях оценки механизм теперь обрабатывает текстовые BLOB-объекты, размер строки которых не превышает 32 765 байт, как если бы они были типами VARCHAR. Операции, которые теперь позволяют текстовым BLOB вести себя как строки: присваивания, преобразования и конкатенации (оператор ||) - person Arioch 'The; 08.03.2019
comment
Что ж, это явно указано в сводке / оглавлении: «Короткие» BLOB могут маскироваться под длинные VARCHAR :-D // О, третье ограничение: этим путем идут только BLOB с подтипом текста, а не двоичные BLOB по умолчанию. Что, возможно, имеет смысл, учитывая проблемы с кодировкой - person Arioch 'The; 08.03.2019
comment
2.1 не перепроверял, но в более поздних версиях такого ограничения нет, насколько я знаю. Конкатенация больших двоичных объектов будет работать для текстовых двоичных объектов любой длины (а также для подстрок и других); Я не проверял бинарные блобы. Упомянутое вами нулевое поведение является стандартным и также применимо к конкатенации varchar. - person Mark Rotteveel; 08.03.2019
comment
Это поведение NULL, конечно, является стандартным, но это не то, чего хотел автор темы, судя как по его вопросу, так и по использованию COALESCE в его собственном, теперь удаленном ответе. @МаркРоттевил - person Arioch 'The; 09.03.2019

Основная проблема, с которой вы столкнулись, заключается в том, что исходное значение равно NULL. Такие операции, как сложение, конкатенация и т. д. со значением NULL, дадут NULL. Чтобы глубже погрузиться в детали NULL, прочитайте Нулевое руководство по Firebird.

Решение состоит в том, чтобы использовать COALESCE для предоставления значения по умолчанию (например, пустой строки), когда столбец имеет значение null*.

UPDATE tablename
SET fieldTarget = COALESCE(fieldTarget, '') || :string
WHERE tablePk = :pkTarget;

*: у вас уже было это в вашем теперь удаленном ответе, но это было немного скрыто приведением к varchar, которое не должно быть необходимым

person Community    schedule 08.03.2019
comment
я уже пробовал этот способ, но возвращает эту ошибку: SQL Error [335544454] [HY000]: filter not found to convert type 1 to type 2 [SQLState:HY000, ISC error code:335544454] - person gbragamonte; 08.03.2019
comment
@gbragamonte Это будет означать, что поле является не текстовым BLOB-объектом (он же sub_type 1), а BLOB-объектом sub_type 2 (BLR), который следует использовать только для внутренних целей Firebird (и вы даже не должны иметь возможность определять BLOB-объекты этого типа). , но я не уверен, что такая защита уже существовала в Firebird 2.1). - person Mark Rotteveel; 08.03.2019
comment
@gbragamonte Возможно, вы захотите исправить проблему и воссоздать большой двоичный объект как один из правильных типов, или если вы считаете, что это уже текст подтипа большого двоичного объекта (или подтип 1), тогда было бы очень полезно иметь полностью воспроизводимый пример. - person Mark Rotteveel; 08.03.2019
comment
@MarkRotteveel, по крайней мере, в FB2.5 (но я верю в любой FB2.x) можно преобразовать BLR-BLOB в VARCHAR, который вызывает встроенный дизассемблер Firebird для преобразования скомпилированного байт-кода в более или менее читаемую мнемонику. Было бы странно, если бы BLR-блобы можно было преобразовать в varchar, но не в txt-blob. P.S. может не поле, а параметр как-то получает подтип 2? - person Arioch 'The; 09.03.2019
comment
@Arioch'The Ошибка связана с типом 1 и типом 2, который может произойти в хранилище, а не с типа 2 на тип 1. Кроме того, способ выполнения OP (в DBeaver) приведет к тому, что значение будет включено как литерал перед выполнением, поэтому нет параметра. Я смог воспроизвести его только в Firebird 2.1, вручную установив подтип поля на 2 (используя обновление системной таблицы, поскольку вы не можете создать большой двоичный объект типа 2 с помощью DDL в 2.1). - person Mark Rotteveel; 09.03.2019
comment
@MarkRotteveel Я имел в виду, что если у нас есть выражения sub_type_1 || sub_type_2 или sub_type_2 || sub_type_1, то, скорее всего, один из операндов должен быть приведен к другому, но какой из них будет ведущим, а какой подлежит преобразованию, я не знаю. - person Arioch 'The; 10.03.2019

Вы можете попробовать использовать CAST (ваше пустое поле blob как тип MY BLOB DOMAIN OF SUBTYPE_1) внутри COALESCE, у меня это работает (проверено в firebird 2.1):

CAST Firebird

ОБЪЕДИНЕНИЕ Firebird

В случае, который я тестировал, у меня было пустое поле blob (давайте назовем его ПРИМЕЧАНИЯМИ) со следующей информацией о домене:

BLOB SUB_TYPE 0 РАЗМЕР СЕГМЕНТА 80

Выполнение COALESCE(NOTES, '') возвращает мне следующую ошибку:

Типы данных несопоставимы в выражении COALESCE

Хитрость заключалась в том, чтобы создать домен из BLOB SUB_TYPE 1 (назовем его TEXT) и выполнить CAST для этого домена, прежде чем объединить или получить его:

SELECT COALESCE(CAST(NOTES AS TYPE OF TEXT), '') FROM MY_TABLE

В вашем случае следующий случай должен работать правильно:

UPDATE tablename
SET fieldTarget = COALESCE(CAST(:fieldTarget AS TYPE TEXT), '') || :string
WHERE tablePk = :pkTarget;

Примите во внимание создание в базе данных домена TEXT в качестве большого двоичного объекта sub_type 1 ранее.

PD: Настоящим трюком было бы приведение к домену поля, которое будет обновлено.

person Gonsabb    schedule 12.05.2021