2013-11-07 3 views
1

Вот мой кусок кода:Конверсия удалось при преобразовании даты - Linq2SQL ExecuteQuery

 #region Validate and prepare parameters 
     if (month > 12) 
     { 
      throw new ArgumentException("Value of 'month' could not be greater than 12."); 
     } 

     int yearEnd = year; 
     int monthEnd = 0; 
     if (month != 12) 
     { 
      monthEnd = month + 1; 
     } 
     else 
     { 
      monthEnd = 1; 
      yearEnd = year + 1; 
     } 
     #endregion 

     MyModelDataContext context = new MyModelDataContext(); 

     string sql = 
      @"select SUM(ORDERQTY * MULTIPLIER) AS VOL_USD 
       from Executions with (nolock) 
       where TRANSACTTIME >= '{0}-{1}-01 00:00:00' 
        and TRANSACTTIME < '{2}-{3}-01 00:00:00' 
        and MTCONTEXT in (5,6) 
        and ORDERQTY > 0 
        AND SOURCE = 'INTMT' 
        and LEFT(SYMBOL, 3) = 'USD'"; 

     decimal usd___Sum = context.ExecuteQuery<decimal>(sql, year, month, yearEnd, monthEnd).First(); 

Я получаю исключение:

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

, когда я называю ExecuteQuery метод. Значение года - 2013 год, а значение месяца - 9. Что я делаю неправильно?

Заранее спасибо.

+0

каковы значения 'года, месяц, КОНЦА, monthEnd' при выполнении запроса? – Ovidiu

+0

Я обновил вопрос @Ovidiu – anilca

ответ

3

Вы сравниваете строки с датами, заставляя базу данных «угадывать», что такое правильный формат. Формат, который вы используете, не является форматом ISO, поэтому в базе данных предполагается, что вы используете формат, соответствующий его сопоставлению. Готов поспорить, вы используете LATIN1 или что-то подобное.

Вместо передачи целых чисел, а затем преобразовывать их в строки, например, в TRANSACTTIME >= '{0}-{1}-01 00:00:00', просто передать даты:

var startDate=new DateTime(year,month,1); 
var endDate=new DateTime(yearEnd,monthEnd,1); 

string sql = 
     @"select SUM(ORDERQTY * MULTIPLIER) AS VOL_USD 
      from Executions with (nolock) 
      where TRANSACTTIME >= {0} 
       and TRANSACTTIME < {1} 
       and MTCONTEXT in (5,6) 
       and ORDERQTY > 0 
       AND SOURCE = 'INTMT' 
       and LEFT(SYMBOL, 3) = 'USD'"; 

decimal usd___Sum = context.ExecuteQuery<decimal>(sql, startDate,endDate).First(); 

UPDATE

Как следует Ovidiu, замена параметр не работает в строках, так '@ p0- @ p1-01' останется неизменным, даже если мы укажем значения параметров @ p0, @ p1. Нам нужно создать дату вне строки, объединив каждую часть.

В SQL Server 2012 у нас есть еще одна опция с DATETIMEFROMPARTS. Мы можем создать дату из его частей, например:

string sql = 
     @"select SUM(ORDERQTY * MULTIPLIER) AS VOL_USD 
      from Executions with (nolock) 
      where TRANSACTTIME >= DATETIMEFROMPARTS({0},{1},1,0,0,0,0) 
      ..." 

хотя прохождение фактической даты еще предпочтительнее

+0

Вы правы! Спасибо за ответ. – anilca

+0

@anilca Использование DateTime вместо строки является решением в этом случае, но объяснение, почему вы должны это делать, неверно. – Ovidiu

+0

@Ovidiu вам не следует сравнивать строки с значениями полей других типов, если вы хотите избежать ошибок интернационализации и производительности. –

1

Проблема путь context.ExecuteQuery отправляет запрос в базу данных. Хотя он использует такие аргументы, как {0}, аналогично string.Format, он фактически не заменяет эти аргументы строковыми значениями, а отправляет их как параметры в БД. Это немного запутанно.

Если открыть SQL Profiler, вы увидите, что ваш запрос переводится на что-то вроде

exec sp_executesql 
    N'select SUM(ORDERQTY * MULTIPLIER) AS VOL_USD 
    from Executions with (nolock) 
    where TRANSACTTIME >= ''@[email protected] 00:00:00'' 
    and TRANSACTTIME < ''@[email protected] 00:00:00''' 
,N'@p0 int,@p1 int,@p2 int,@p3 int' 
,@p0=2013,@p1=9,@p2=2013,@p3=10 

Какой будет бросать исключение в SQL, так как параметр @p0 появляется в одинарные кавычки, и, таким образом, интерпретируется как строка "@p0" и не заменяется фактическим значением 2013.Таким образом, вместо того, чтобы '2013-09-01 00:00:00' вы в конечном итоге с '@[email protected] 00:00:00'

Отправка DateTime объектов вместо строк будет лучшим решением

where TRANSACTTIME >= {0} and and TRANSACTTIME < {1} 
... 
context.ExecuteQuery<decimal>(sql, new DateTime(year, month, 1), new DateTime(yearEnd, monthEnd, 1)) 

Но если вы все еще хотите сохранить текущее строительство, вы должны держать в вид, что аргумент {0} становится параметром @p0 в SQL и использовать что-то вроде этого

where TRANSACTTIME >= convert(datetime, {0} + '-' + {1} + '-01 00:00:00') 
    and TRANSACTTIME < convert(datetime, {2} + '-' + {3} + '-01 00:00:00') 
... 
context.ExecuteQuery<decimal>(sql, year.ToString(), month.ToString(), yearEnd.ToString(), monthEnd.ToString()) 
+0

Результирующая строка такая же. Почему вы думаете, что это будет работать? Строка STILL не является ISO, и она будет STILL преобразована с использованием языка, который соответствует сопоставлению базы данных. –

+0

@PanagiotisKanavos Это не приведет к ошибке «Конверсия не удалась при преобразовании даты и/или времени из символьной строки.». И строка не то же самое. Строка, отправленная в БД OP, не является «2013-09-01 00: 00: 00'', но' '@ p0- @ p1-01 00: 00: 00'', которая не является действительной датой, независимо от того, сортировка. – Ovidiu

+0

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