Подтвердить что ты не робот

Что означает, когда Statement.executeUpdate() возвращает -1?

Запрос, который работает в студии управления и в executeUpdate, делает тот же executeUpdate return -1, который undefined в любой документации, которую мы можем найти. Предполагается, что он возвращает только строку или 0. Что это значит? Драйвер - это мост JDBC-ODBC, если это имеет значение.

Пример:

String query = "IF NOT EXISTS (SELECT * FROM animals WHERE animal_name ='" + a +"') INSERT INTO " + table + " (animal_name, animal_desc, species_id) VALUES ('" + a + "', '" + b + "', " + c + ")";
int result = statement.executeUpdate(query);
System.out.println(result);

Запрос работает, поскольку строка добавляется в базу данных, просто странно, что она возвращает -1, где в документации говорится, что она вернет только 0 или строку (как я был исправлен).

UPDATE:

Запуск этого в результатах Management Studio с помощью команды "Команда успешно завершена".

IF NOT EXISTS (SELECT * FROM animals WHERE animal_name = 'a') 
INSERT INTO animals(animal_name, animal_desc, species_id) VALUES ('a', 'a', 1)

Это должно означать, что метод должен возвращать 0, потому что он ничего не возвращает, правильно?

4b9b3361

Ответ 1

Итак, 4 года спустя Microsoft открыла исходный драйвер JDBC в Github. Я получил уведомление об этом вопросе сегодня, и пошел и посмотрел, и я считаю, что нашел преступника здесь, mssql-jdbc/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java:1713.

В принципе, драйвер пытается понять, что отправляет SQL Server, если это не определенный набор результатов. Согласно комментариям, это выглядит следующим образом:

  • Сначала проверьте наличие ошибок. (ln 1669)

  • Не ошибка. Это результат? (ln 1680)

  • Не ошибка или результирующий набор. Может быть, результат инструкции T-SQL? То есть, одно из следующего:

    • положительный счетчик числа затронутых строк (из INSERT, UPDATE или DELETE),
    • ноль, указывающий отсутствие затронутых строк, или оператор был DDL, или
    • a -1, указывающий, что оператор преуспел, но отсутствует информация о количестве обновлений (преобразуется в Statement.SUCCESS_NO_INFO в массивы подсчета пакетных обновлений). (ln 1706)
  • Ничего из перечисленного. Последний шанс здесь... Перейдя в синтаксический анализатор выше, мы знаем, что больше результатов было изначально верно. Если мы вышли с moreResults false, мы нажмем маркер DONE (либо DONE (FINAL), либо DONE (RPC в пакетном режиме)), что указывает на то, что пакет был выполнен в целом, но что нет информации о подсчетах обновлений отдельных операторов. Это похоже на последний случай выше, за исключением того, что нет количества обновлений. То есть: у нас есть успешный результат (return true), но у нас нет другой информации об этом (updateCount = -1). (ln 1693)

  • Единственный способ добраться сюда (больше результатов все еще верно, но никаких очевидных результатов не существует), если TDSParser фактически не анализировал что-нибудь. То есть, мы находимся в EOF в ответе. В этом случае результатов действительно нет. Были сделаны. (ln 1717)

(Акцент мой)

Итак, вы, ребята, были правы в конце. SQL просто не может определить, сколько строк затронуто, и по умолчанию используется -1.:)

Ответ 2

Поскольку выполняемый оператор не является фактически DML (например, UPDATE, INSERT или EXECUTE)), но часть T-SQL, которая содержит DML, я подозреваю, что это не рассматривается как запрос на обновление.

В разделе 13.1.2.3 спецификации JDBC 4.1 указано что-то (довольно сложно интерпретировать btw):

Когда метод EXECUTE возвращает true, метод getResultSet вызывается получить объект ResultSet. Когда EXECUTE возвращает значение false, метод getUpdateCount возвращает int. Если это число больше или равно нулю, указывает количество обновлений, возвращаемое оператором. Если оно равно -1, это указывает на то, что больше нет результатов.

Учитывая эту информацию, я полагаю, что executeUpdate() внутренне выполняет execute(), а затем - как execute() возвращает false - он вернет значение getUpdateCount(), которое в этом случае - в соответствии с спецификацией JDBC - вернет -1.

Это еще раз подтверждает тот факт, что 1) что Javadoc для Statement.executeUpdate() говорит:

Возвращает: либо (1) количество строк для операторов SQL Data Manipulation Language (DML), либо (2) 0 для операторов SQL, которые ничего не возвращают

И 2), что Javadoc для Statement.getUpdateCount() указывает:

текущий результат как счетчик обновлений; -1, если текущий результат является объектом ResultSet или результатов больше нет.

Просто, чтобы уточнить: учитывая Javadoc для executeUpdate(), поведение, вероятно, неверно, но это можно объяснить.

Также, как я прокомментировал в другом месте, -1 может просто указать: возможно, что-то было изменено, но мы просто не знаем, или мы не можем дать точное количество изменений (например, потому что в этом примере это часть T-SQL, который выполняется).

Ответ 3

Я тоже этого не видел, но мой инстинкт состоял в том, что это означает, что IF помешало выполнению всего оператора.

Попробуйте запустить оператор с базой данных, где проходит IF.

Также проверьте, есть ли какие-либо триггеры, которые могут изменить результат.

[EDIT] Когда стандарт говорит, что эта функция никогда не должна возвращать -1, что не обеспечить это. Java не имеет условий до и после. Драйвер JDBC мог возвращать случайное число, и не было способа его остановить.

Если важно знать, почему это происходит, запустите оператор в отношении другой базы данных, пока не попробуете все пути выполнения (то есть, где IF возвращает false и тот, где он возвращает true).

Если это не так важно, отметьте его как "умный трюк" инженера Microsoft и помните, как вам понравилось, когда вы чувствуете себя умнее в следующий раз.

Ответ 4

Для операторов executeUpdate для сервера DB2 for z/OS возвращаемое значение зависит от типа выполняемого оператора SQL:

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

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

0, если на операцию не влияет строка.

-1, если операция представляет собой массовое удаление в сегментированном табличном пространстве.

Для оператора DB2 CALL возвращается значение -1, так как сервер базы данных DB2 не может определить количество затронутых строк. Вызовы getUpdateCount или getMoreResults для оператора CALL также возвращают -1. Для любого другого оператора SQL возвращается значение -1. ​​

Ответ 5

Это не объясняет, почему так должно быть, но объясняет, почему это может произойти. Следующие байтовые коды устанавливают -1 во внутренний флаг updateCount в конструкторе SQLServerStatement:

// Method descriptor #401 (Lcom/microsoft/sqlserver/jdbc/SQLServerConnection;II)V
// Stack: 5, Locals: 8
SQLServerStatement(
  com.microsoft.sqlserver.jdbc.SQLServerConnection arg0, int arg1, int arg2) 
throws com.microsoft.sqlserver.jdbc.SQLServerException;

// [...]

34 aload_0 [this]
35 iconst_m1
36 putfield com.microsoft.sqlserver.jdbc.SQLServerStatement.updateCount:int [27]

Теперь я не буду анализировать все возможные потоки управления, но я бы просто сказал, что это внутреннее значение инициализации по умолчанию, которое каким-то образом протекает в клиентский код. Обратите внимание, что это также делается другими способами:

// Method descriptor #383 ()V
// Stack: 2, Locals: 1
final void resetForReexecute() 
throws com.microsoft.sqlserver.jdbc.SQLServerException;

// [...]

10 aload_0 [this]
11 iconst_m1
12 putfield com.microsoft.sqlserver.jdbc.SQLServerStatement.updateCount:int [27]

// Method descriptor #383 ()V
// Stack: 3, Locals: 3
final void clearLastResult();
0 aload_0 [this]
1 iconst_m1
2 putfield com.microsoft.sqlserver.jdbc.SQLServerStatement.updateCount:int [27]

Другими словами, вы, вероятно, считаете безопасным перевод -1 как те же, что и 0. Если вы полагаетесь на это значение результата, возможно, оставайтесь на безопасной стороне и выполните свои проверки следующим образом:

// No rows affected
if (stmt.executeUpdate() <= 0) {
}
// Rows affected
else {
}

ОБНОВЛЕНИЕ: при чтении Отметьте ответ Rotteveel, я с ним согласен, считая, что -1 является JDBC-совместимым значением для "неизвестных обновлений". Даже если это не описано в соответствующем методе Javadoc, оно описано в спецификации JDBC, глава 13.1.2.3 Возврат неизвестных или нескольких результатов. В этом случае можно сказать, что оператор IF .. INSERT .. будет иметь "неизвестный счетчик обновлений", поскольку этот оператор не соответствует стандарту SQL в любом случае.