Обработка Derby значений NULL

Я новичок в Derby и заметил, что сталкиваюсь с теми же проблемами, что и при использовании СУБД DB2, в том, что касается значений null. В документации Derby указано, что значение null должно иметь связанный с ним тип (то, от чего DB2 окончательно избавилась в версии 9.7):

http://db.apache.org/derby/docs/10.7/ref/crefsqlj21305.html

Теперь я пытаюсь найти здесь общее решение этой проблемы, поскольку оно будет частью моей библиотеки абстракций базы данных jOOQ. Приведенный ниже пример просто документирует проблему. Вспомните любой другой (более сложный) пример. Не работает следующее:

insert into T_AUTHOR (
  ID, FIRST_NAME, LAST_NAME, 
  DATE_OF_BIRTH, YEAR_OF_BIRTH, ADDRESS) 
select 
  1000, 'Lukas', 'Eder', 
  '1981-07-10', null, null 
from SYSIBM.SYSDUMMY1

Так же как и это (что на самом деле делает jOOQ):

insert into T_AUTHOR (
  ID, FIRST_NAME, LAST_NAME, 
  DATE_OF_BIRTH, YEAR_OF_BIRTH, ADDRESS) 
select ?, ?, ?, ?, ?, ? 
from SYSIBM.SYSDUMMY1

Потому что два значения null не имеют связанного с ним типа. Решением было бы написать что-то вроде этого:

insert into T_AUTHOR (
  ID, FIRST_NAME, LAST_NAME, 
  DATE_OF_BIRTH, YEAR_OF_BIRTH, ADDRESS) 
select 
  1000, 'Lukas', 'Eder', 
  '1981-07-10', cast(null as int), cast(null as varchar(500)) 
from SYSIBM.SYSDUMMY1

Или вот так, соответственно

insert into T_AUTHOR (
  ID, FIRST_NAME, LAST_NAME, 
  DATE_OF_BIRTH, YEAR_OF_BIRTH, ADDRESS) 
select 
  ?, ?, ?, ?, cast(? as int), cast(? as varchar(500)) 
from SYSIBM.SYSDUMMY1

Но очень часто в Java тип, к которому следует привести null, неизвестен:

  • В этом примере типы могут быть получены из предложения вставки, но это может оказаться сложным или невозможным для более общих случаев использования.
  • В других примерах я мог бы просто выбрать любой тип для приведения (например, всегда приводить к int), но в этом примере это не сработает, так как вы не можете поместить значение cast(null as int) в ADDRESS.
  • С HSQLDB (еще одним кандидатом на эту проблему) я могу просто написать cast(null as object), что будет работать в большинстве случаев. Но у Дерби нет типа object.

Эта проблема раньше раздражала меня с DB2, и я еще не нашел решения. Кто-нибудь знает стабильное и общее решение этой проблемы для любой из этих СУБД?

  • дерби
  • DB2

person Lukas Eder    schedule 05.02.2011    source источник


Ответы (4)


Если вы используете предложение VALUES в своем INSERT, вам не нужно приводить значения NULL:

insert into T_AUTHOR (
  ID, FIRST_NAME, LAST_NAME, 
  DATE_OF_BIRTH, YEAR_OF_BIRTH, ADDRESS) 
VALUES ( 
  1000, 'Lukas', 'Eder', 
  '1981-07-10', null, null 
);

Это будет работать так, как вы ожидаете (т.е. база данных может определить, что значения NULL соответствуют целому числу и varchar(500). Это работает как в DB2, так и в Derby (и должно работать практически в любом другом механизме базы данных).

Вы также можете использовать VALUES с маркерами параметров без необходимости их CAST.

Причина, по которой вы должны выполнить приведение при выдаче оператора insert into ... select from, заключается в том, что часть SELECT имеет приоритет — оператор select возвращает определенные типы данных, независимо от того, совместимы ли они с таблицей, в которую вы пытаетесь их вставить. Если они несовместимы, вы либо получите сообщение об ошибке (со строго типизированными механизмами баз данных, такими как DB2 ‹= 9.5), либо механизм выполнит неявное преобразование типов (когда это возможно).

person Ian Bjorhovde    schedule 05.02.2011
comment
Спасибо Ян. Перефразировать SQL не вариант, так как это был только описательный пример. Мне придется ожидать любой тип SQL в любой форме. Но хороший намек на приоритет SELECT в INSERT .. SELECT. На самом деле у меня есть много интеграционных тестов, которые работают в моей библиотеке без сбоев, но некоторые из них терпят неудачу. Я надеялся найти общее решение этой проблемы... Но, может быть, я просто оставлю это открытым вопросом, надеясь, что Дерби исправит это, поскольку синтаксис INSERT .. SELECT не очень распространен. - person Lukas Eder; 06.02.2011

DB2 использует нулевой индикатор... например

EXEC SQL INSERT INTO TABNAM (FILD1, FILD2) 
                     VALUES (:HOSTVAR1, :HOSTVAR2:NULL-IND2)  END-EXEC.

обратите внимание на поле NULL-IND2, для которого можно установить значение -1, чтобы указать, что это поле в таблице базы данных должно быть нулевым.

Есть ли аналогичный индикатор для JDBC или Derby?

person Joe Cotton    schedule 16.11.2011
comment
Я не знал о таком индикаторе, приятно знать. Я должен это проанализировать. Насколько мне известно, в JDCB такого индикатора нет. В частности, именованные параметры также недоступны в JDBC. Но есть возможность явно установить NULL вместе с типом JDBC в setNull(). Насчет Дерби тоже не уверен - person Lukas Eder; 17.11.2011

Что, если вы оставите значения столбца как значения подстановки вопросительного знака в вашем PreparedStatement, а затем установите значения во время выполнения с помощью PreparedStatement.setObject(N, null)?

В общем, ваша библиотека предпочтет предоставлять значения столбцов путем замены значений параметров, чтобы обрабатывать такие проблемы, как кавычки в строках, преобразования типов данных и т. д., и я думаю, что этот общий механизм подстановки также должен решать ваши проблемы с нулевыми значениями.

person Bryan Pendleton    schedule 05.02.2011
comment
Спасибо за вклад Брайан. Я не знаю, почему я оставил переменные связывания. Конечно, моя библиотека всегда будет использовать переменные связывания для повышения производительности/безопасности и т. д., вместо того, чтобы встраивать значения непосредственно в SQL. Я адаптирую вопрос. - person Lukas Eder; 05.02.2011
comment
Не волнуйтесь. Возможно, вы столкнулись с issues.apache.org/jira/browse/DERBY-1938., который был исправлен совсем недавно. Вы используете последнюю версию Derby (10.7)? Если нет, попробуйте и посмотрите, работает ли setObject(null) лучше. - person Bryan Pendleton; 05.02.2011
comment
Да, я использую Дерби 10.7.1.1. Но на самом деле это хороший намек с setObject(null). У меня было сочетание двух ошибок: сначала вызов Connection.prepareStatement() не принимал литералы null или выражения cast(? as integer) (если целое число является неправильным типом). Когда я просто подготовил ? для нулевого значения, моя библиотека вызвала PreparedStatement.setNull(N, Object.class), что тоже не сработало. Но setObject(N, null) вроде работает. Я дважды проверю... - person Lukas Eder; 06.02.2011
comment
Хм, это работает именно в этом случае, но не является общим решением. Если я просто SELECT ? FROM SYSIBM.SYSDUMMY1, то Дерби жалуется, что ? не допускается в предложении select. Это действительно сложная проблема для фреймворка, абстрагирующего диалект SQL, для решения... - person Lukas Eder; 06.02.2011

Возможно, это решение поможет вам:

insert into T_AUTHOR (
    ID, 
    FIRST_NAME, 
    LAST_NAME, 
    DATE_OF_BIRTH, 
    YEAR_OF_BIRTH, 
    ADDRESS
) select 
    1000, 
    'Lukas', 
    'Eder', 
    '1981-07-10', 
    (select YEAR_OF_BIRTH from T_AUTHOR where true = false), 
    (select ADDRESS from T_AUTHOR where true = false) 
from SYSIBM.SYSDUMMY1

Этот метод не требует явного приведения типа null, потому что внутренний select вернет null с требуемым типом.

person David E. Veliev    schedule 07.10.2016