Использование CONVERT в параметризованном запросе

#java #sql #sql-server #parameterized-query

#java #sql #sql-сервер #параметризованный запрос

Вопрос:

У меня есть некоторый Java-код, который взаимодействует с базой данных Oracle, и я конвертирую его для работы с SQL Server.

Код:

 String parameterisedSQL = "INSERT INTO T(a,b) VALUES (?,?)";
try (Connection con = DriverManager.getConnection(connectionString, databaseUser, databasePassword);
PreparedStatement stmt = con.prepareStatement(parameterisedSQL);)
{       
    stmt.setString(0,"test");
    stmt.setString(1,"CONVERT(DATE, '20201230', 112)");

    stmt.executeUpdate();
    return true;
} 
  

Очевидно, что когда это был код Oracle, код считывался stmt.setString(1,"TO_DATE('20201230','YYYYMMDD')");

Код Oracle для базы данных Oracle работал нормально, но теперь я запускаю код SQL Server для базы данных SQL Server, я получаю исключение

Ошибка преобразования при преобразовании даты и / или времени из символьной строки

Исходя из этого, я предполагаю, что SQL Server неправильно анализирует ПРЕОБРАЗОВАНИЕ в качестве параметра. Есть ли какой-нибудь способ заставить его сделать это? (Мой код на самом деле более общий, чем показано, Поэтому его будет сложно использовать .setDate и т. Д.)

Комментарии:

1. Вы пытались захватить запрос, чтобы проверить, верно ли ваше предположение?

2. @HoneyBadger как мне это сделать в SQL Server?

3. Старая школа использует профилировщик (доступен в SSMS, хотя и не в самой новой версии). Современный способ — это настройка расширенного события.

4. Я использую базу данных Azure, и SSMS не позволяет мне профилировать ее

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

Ответ №1:

Способ передачи функции интерпретируется следующим образом:

 INSERT INTO T(a,b) VALUES ('test', 'CONVERT(DATE, '20201230', 112)')
  

что, очевидно, неправильно, потому что вы пытаетесь вставить значение 'CONVERT(DATE, '20201230', 112)' в date столбец.

Вы должны сделать это:

 String parameterisedSQL = "INSERT INTO T(a,b) SELECT ?, CONVERT(DATE, ?, 112)";
  

и:

 stmt.setString(1,"test");
stmt.setString(2,"20201230");
  

Индексы основаны на 1.

Комментарии:

1. ЗНАЧЕНИЯ @simonalexander2005 не работают с инструкцией, как я ее написал. Для этого нужен другой синтаксис. И я не могу сказать, почему это сработало для Oracle.

Ответ №2:

@forpas дал подробный ответ.

Кроме того, для вашего условия вам не нужно преобразовывать формат короткой даты (ISO8601) перед вставкой в таблицу. Вы можете напрямую вставить.

О формате даты ISO8601

Описание ISO 8601 ГГГГ-ММ-ДД

ГГГГММДД Такой же, как стандарт SQL. Этот формат является единственным форматом, определенным как международный стандарт.

 declare @TABLE table(T date)

insert into @TABLE (T) VALUES('20201230')

SELECT * FROM @table
  

для вашего случая,

 stmt.setString(0,"test");
stmt.setString(1,"20201230");
  

Ответ №3:

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

В этом случае, поскольку это значение только для даты, без временной части, вы должны использовать java.sql.Date или java.time.LocalDate , в зависимости от возможностей драйвера JDBC.

Также обратите внимание, что индексы параметров имеют 1-базовую базу в API JDBC.

 // Old code: Wrong
stmt.setString(0,"test");
stmt.setString(1,"CONVERT(DATE, '20201230', 112)");

// Using java.sql.Date
SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
stmt.setString(1, "test");
stmt.setDate(2, new java.sql.Date(fmt.parse("20201230")));

// Using java.time.LocalDate
DateTimeFormatter fmt = new DateTimeFormatter.ofPattern("uuuuMMdd");
stmt.setString(1, "test");
stmt.setObject(2, LocalDate.parse("20201230", fmt));
  

Комментарии:

1. Ах да, индексация была моей ошибкой, набрав код здесь. Что касается других вариантов, я согласен, что это технически правильный способ сделать это; но, как я уже сказал в своем вопросе, мой код более общий, поэтому я старался избегать использования разных .set методов, если мог. Спасибо, что указали правильный способ сделать это, хотя

2. Ваш ответ неверен. Вы можете отправить значение даты в виде строки в базу данных, потому что все реляционные базы данных могут преобразовывать строки в даты, а SQL Server имеет более 1 функции для выполнения этой работы: CONVERT() , CAST , TRY_CONVERT() и TRY_CAST() . Преобразование строк в даты является обычной практикой в SQL. Кроме того, в SQL Server нет необходимости преобразовывать строку типа «20201230» в дату. Это принимается как есть, поэтому нет необходимости в java.sql.Date или java.time. LocalDate. Прочитайте ответ Венкатарамана Р. Также прочитайте мой ответ, чтобы увидеть, как легко выполняется преобразование в SQL.

3. @simonalexander2005 это технически неправильный способ. Это всего лишь 1 способ сделать это. Также прочитайте мой предыдущий комментарий.

4. @forpas Думаю, я привык писать код, не зависящий от диалекта. Отправка значения даты в базу данных в виде строки зависит от функциональности, специфичной для СУБД, но отправка значения в виде объекта date будет работать на всех СУБД, поскольку драйвер JDBC будет обрабатывать различия, зависящие от диалекта.