Apache Derby: как сделать «вставить, если не существует»?

Я проверяю Apache Derby, он же JavaDB. Кажется, я не могу обойти повторяющиеся ключевые проблемы при вставке записей, которые, возможно, уже существуют. Есть ли в Дерби эквивалент «insert if not exists» или «merge»?

Точно так же есть способ сделать что-нибудь вроде "drop table foo if exists"?

Если вы не привязаны к Дерби, попробуйте H2 - это Быстрее, и в нем есть очень красивый и простой глагол ОБЪЕДИНЕНИЕ.

Tom Anderson 22.02.2011 16:15
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
13
1
16 302
7

Ответы 7

Я никогда не использовал apache derby, но общее решение, которое практически не зависит от базы данных, выглядит следующим образом:

Чтобы вставить значения 'a' и 'b' в таблицу foo (со столбцами с именами A, B), но только там, где значений еще нет, попробуйте что-то вроде

INSERT INTO foo (  
  SELECT 'a' as A, 'b' as B
  FROM foo  
  WHERE  
    A = 'a' AND B = 'b'  
  HAVING count(*)=0  
 )

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

Это полезный прием для создания идемпотентного сценария sql (который ничего не делает при втором запуске). Однако будьте осторожны при использовании этого в производственном коде, поскольку HAVING count(*)=0 может быть очень медленным на больших таблицах.

Хороший трюк. Можно ли заставить его работать с несколькими строками? Возможно, используя синтаксис VALUES, но я не уверен.

Tom Anderson 22.02.2011 16:11

Работает также для HSQLDB! Очень хорошо!

binwiederhier 15.03.2014 01:58

На самом деле использование MERGE намного лучше; посмотрите этот ответ: stackoverflow.com/questions/2609996/…

binwiederhier 15.03.2014 02:58

Это вызывает Error code 0, SQL state XJ001: Java exception: '8: java.lang.ArrayIndexOutOfBoundsException'. при использовании в качестве партии.

Hooli 29.07.2016 21:18

Стандартный способ, который я использовал с базами данных PostgreSQL, выглядит примерно так:

INSERT INTO foo ( col1, col2, col3, ... )
SELECT 'col1 value', 'col2 value', 'colc value', ...
WHERE NOT EXISTS (
  SELECT 0
  FROM foo
  WHERE col1 = 'col1 value'
  ...
)

Не уверен, насколько он портативен или строго соответствует ANSI. Отсутствие предложения FROM во внешнем операторе SELECT может быть нестандартным. Но попробуйте.

Для этого нет встроенной поддержки, чтобы обойти это, я использую eclipse-link, eclipse-link будет пытаться создать таблицы и игнорировать любые ошибки, возникающие при попытке создать таблицы, которые уже существуют.

Если вы вносите изменения в схему, вы можете указать eclipse link, чтобы удалить таблицы перед их созданием.

Я использую это решение, но вы должны использовать его, только если понимаете разницу между дубликат из представления базы данных и дубликат из пользовательского представления

  • Дубликаты из представления базы данных - это две записи с одним и тем же первичным ключом.
  • Дубликаты из пользовательского представления - это две записи со всеми идентичными полями.

        while (ResultSet.next()) {
        try {
        PreparedStatement insertion = myConn.prepareStatement("insert into table values (?)");
        insertion .setString(1, "test");
        insertion .executeUpdate();           
        } catch (SQLException e) {
            if (e.getSQLState().equals("23505"))//Found duplicate from database view
             {continue;}//ignore duplicate and continue with the insert statement
            else{try {                           
                  throw e;
            } catch (Exception ex) {  
                }
              }
            }
          }
    

Разве вторая попытка не гарантирует, что ничего никогда не будет брошено? По сути, весь блок catch (SQLException) будет вести себя точно так же, как если бы он был полностью пуст.

Newtopian 07.09.2016 20:41

Запрос на поддержку оператора MERGE SQL: 2003 регистрируется в системе отслеживания ошибок Derby как https://issues.apache.org/jira/browse/DERBY-3155.

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

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

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

Уф. SQL: 2003 MERGE ужасен. Стандартный и гибкий, но столько текста, чтобы вставить строку!

Tom Anderson 22.02.2011 16:12

Я согласен ... так много дублирования (то же самое касается классического трюка выше). Интересно, есть ли стандартная альтернатива, потому что, если бы я нашел ее в то время, я бы попросил ее вместо этого.

Trejkaz 29.07.2011 08:54

У меня была такая же проблема, и я получил ее для вставки только с одним значением / столбцом в Derby. (Я никогда не собирался тестировать его с другими, но у меня нет причин предполагать, что этого не должно быть):

INSERT INTO my_table (my_column)
    (SELECT 'new_value_to_insert'
    FROM my_table
    WHERE my_column = 'new_value_to_insert' 
    HAVING count(*)=0)

Идеально! Я знаю, что это разные базы данных, чем вопрос, но это решение работает для меня с PostgreSQL и HSQLDB.

Jeff Fairley 26.09.2014 00:29

Дерби реализовал MERGE в 10.11: https://db.apache.org/derby/docs/10.11/ref/rrefsqljmerge.html
Обратите внимание, что вам необходимо обновить вашу БД до 10.11 перед ее использованием: https://db.apache.org/derby/docs/10.11/devguide/cdevupgrades.html

Другие вопросы по теме