1

这是我的代码片段:

        #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方法时。year 的值为 2013,month 的值为 9。我做错了什么?

提前致谢。

4

2 回答 2

3

您正在将字符串与日期进行比较,迫使数据库“猜测”正确的格式是什么。您使用的格式不是 ISO 格式,因此数据库假定您使用与其排序规则匹配的格式。我敢打赌你使用 LATIN1 或类似的东西。

而不是传递整数,然后将它们转换为字符串,例如 in 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();

更新

正如 Ovidiu 建议的那样,参数替换在字符串中不起作用,因此即使我们为 @p0、@p1 参数提供值,“@p0-@p1-01”也将保持不变。我们需要通过连接每个部分来在字符串之外创建日期。

在 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)
            ..."

尽管通过实际日期仍然是可取的

于 2013-11-07T09:07:56.097 回答
1

问题是context.ExecuteQuery将查询发送到数据库的方式。尽管它使用像 {0} 这样的参数,类似于string.Format,但它实际上并没有用字符串值替换这些参数,而是将它们作为参数发送到 DB。这有点令人困惑。

如果您打开 SQL Profiler,您会看到您的查询转换为类似

exec sp_executesql 
    N'select SUM(ORDERQTY * MULTIPLIER) AS VOL_USD
    from Executions with (nolock)
    where TRANSACTTIME >= ''@p0-@p1-01 00:00:00''
    and TRANSACTTIME < ''@p2-@p3-01 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'你最终得到'@p0-@p1-01 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}变成@p0SQL 中的参数并使用类似这样的东西

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())
于 2013-11-07T10:02:09.203 回答