From fb730a1abd7eda2f33c790e5586943ca6fd31802 Mon Sep 17 00:00:00 2001 From: Brandon Dahler Date: Thu, 20 Nov 2014 23:58:51 -0600 Subject: [PATCH] Improve TruncateTime canonical function performance. --- .../SqlGen/SqlFunctionCallHandler.cs | 66 +++++++++++-------- .../SqlGen/SqlGenerator.cs | 12 +--- .../Query/SqlCeCanonicalFunctionsTests.cs | 2 +- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/src/EntityFramework.SqlServer/SqlGen/SqlFunctionCallHandler.cs b/src/EntityFramework.SqlServer/SqlGen/SqlFunctionCallHandler.cs index 977c42dc8d..d3271ea2c1 100644 --- a/src/EntityFramework.SqlServer/SqlGen/SqlFunctionCallHandler.cs +++ b/src/EntityFramework.SqlServer/SqlGen/SqlFunctionCallHandler.cs @@ -1285,49 +1285,63 @@ private static void AppendConvertToVarchar(SqlGenerator sqlgen, SqlBuilder resul // // TruncateTime(DateTime X) - // PreKatmai: TRUNCATETIME(X) => CONVERT(DATETIME, CONVERT(VARCHAR(255), expression, 102), 102) - // Katmai: TRUNCATETIME(X) => CONVERT(DATETIME2, CONVERT(VARCHAR(255), expression, 102), 102) + // PreKatmai: TRUNCATETIME(X) => DATEADD(d, DATEDIFF(d, 0, expression), 0) + // Katmai: TRUNCATETIME(X) => CAST(CAST(expression AS DATE) as DATETIME2) // TruncateTime(DateTimeOffset X) - // TRUNCATETIME(X) => CONVERT(datetimeoffset, CONVERT(VARCHAR(255), expression, 102) - // + ' 00:00:00 ' + Right(convert(varchar(255), @arg, 121), 6), 102) + // Katmai only: TRUNCATETIME(X) => TODATETIMEOFFSET(CAST(expression AS DATE), DATEPART(tz, expression))) // private static ISqlFragment HandleCanonicalFunctionTruncateTime(SqlGenerator sqlgen, DbFunctionExpression e) { //The type that we need to return is based on the argument type. - string typeName = null; - var isDateTimeOffset = false; - var typeKind = e.Arguments[0].ResultType.GetPrimitiveTypeKind(); + var isDateTimeOffset = (typeKind == PrimitiveTypeKind.DateTimeOffset); - if (typeKind == PrimitiveTypeKind.DateTime) - { - typeName = sqlgen.IsPreKatmai ? "datetime" : "datetime2"; - } - else if (typeKind == PrimitiveTypeKind.DateTimeOffset) - { - typeName = "datetimeoffset"; - isDateTimeOffset = true; - } - else + if (!isDateTimeOffset && typeKind != PrimitiveTypeKind.DateTime) { Debug.Assert(true, "Unexpected type to TruncateTime" + typeKind.ToString()); } + var result = new SqlBuilder(); - result.Append("convert ("); - result.Append(typeName); - result.Append(", convert(varchar(255), "); - result.Append(e.Arguments[0].Accept(sqlgen)); - result.Append(", 102) "); - if (isDateTimeOffset) + if (sqlgen.IsPreKatmai) + { + if (isDateTimeOffset) + { + throw new NotSupportedException(Strings.SqlGen_CanonicalFunctionNotSupportedPriorSql10(e.Function.Name)); + } + + result.Append("dateadd(d, datediff(d, 0, "); + result.Append(e.Arguments[0].Accept(sqlgen)); + result.Append("), 0)"); + } + else { - result.Append("+ ' 00:00:00 ' + Right(convert(varchar(255), "); + if (!isDateTimeOffset) + { + result.Append("cast("); + } + else + { + result.Append("todatetimeoffset("); + } + + result.Append("cast("); result.Append(e.Arguments[0].Accept(sqlgen)); - result.Append(", 121), 6) "); + result.Append(" as date)"); + + if (!isDateTimeOffset) + { + result.Append(" as datetime2)"); + } + else + { + result.Append(", datepart(tz, "); + result.Append(e.Arguments[0].Accept(sqlgen)); + result.Append("))"); + } } - result.Append(", 102)"); return result; } diff --git a/src/EntityFramework.SqlServerCompact/SqlGen/SqlGenerator.cs b/src/EntityFramework.SqlServerCompact/SqlGen/SqlGenerator.cs index 4735e4f274..91d271b1af 100644 --- a/src/EntityFramework.SqlServerCompact/SqlGen/SqlGenerator.cs +++ b/src/EntityFramework.SqlServerCompact/SqlGen/SqlGenerator.cs @@ -3148,13 +3148,10 @@ private static void AppendConvertToNVarchar(SqlGenerator sqlgen, SqlBuilder resu // // TruncateTime(DateTime X) - // TRUNCATETIME(X) => CONVERT(DATETIME, CONVERT(VARCHAR(255), expression, 102), 102) + // TRUNCATETIME(X) => DATEADD(d, DATEDIFF(d, 0, expression), 0) // private static ISqlFragment HandleCanonicalFunctionTruncateTime(SqlGenerator sqlgen, DbFunctionExpression e) { - //The type that we need to return is based on the argument type. - string typeName = "datetime"; - PrimitiveTypeKind typeKind; TypeHelpers.TryGetPrimitiveTypeKind(e.ResultType, out typeKind); @@ -3164,13 +3161,10 @@ private static ISqlFragment HandleCanonicalFunctionTruncateTime(SqlGenerator sql } var result = new SqlBuilder(); - result.Append("convert ("); - result.Append(typeName); - result.Append(", convert(nvarchar(255), "); + result.Append("dateadd(d, datediff(d, 0, "); result.Append(e.Arguments[0].Accept(sqlgen)); - result.Append(", 102) "); + result.Append("), 0)"); - result.Append(", 102)"); return result; } diff --git a/test/EntityFramework/FunctionalTests/Query/SqlCeCanonicalFunctionsTests.cs b/test/EntityFramework/FunctionalTests/Query/SqlCeCanonicalFunctionsTests.cs index e9957423aa..bedc0b8089 100644 --- a/test/EntityFramework/FunctionalTests/Query/SqlCeCanonicalFunctionsTests.cs +++ b/test/EntityFramework/FunctionalTests/Query/SqlCeCanonicalFunctionsTests.cs @@ -514,7 +514,7 @@ public void CanonicalFunction_truncatetime_translated_properly_to_sql_function() using (var context = GetArubaCeContext()) { var query = context.CreateQuery(@"SELECT VALUE Edm.TruncateTime(A.c5_datetime) FROM ArubaCeContext.AllTypes AS A"); - Assert.Contains("CONVERT", query.ToTraceString().ToUpperInvariant()); + Assert.Contains("DATEADD(D, DATEDIFF(D,", query.ToTraceString().ToUpperInvariant()); Assert.Equal(new DateTime(1990, 2, 2, 0, 0, 0), query.First()); } }