Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add function mapping for Ticks to sqlserver #35465

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -48,6 +48,41 @@ public class SqlServerDateTimeMemberTranslator(
nameof(DateTime.Millisecond) => DatePart("millisecond"),
nameof(DateTime.Microsecond) => sqlExpressionFactory.Modulo(DatePart("microsecond"), sqlExpressionFactory.Constant(1000)),
nameof(DateTime.Nanosecond) => sqlExpressionFactory.Modulo(DatePart("nanosecond"), sqlExpressionFactory.Constant(1000)),
//Use datediff to get the minute difference of 0001-01-01 00:00:00.0, then multiply it by the scale of each minute,
//and then get the scale of seconds, milliseconds, microseconds, and nanoseconds respectively, and finally add them together.
//The nanoseconds obtained by DATEPART include milliseconds and microseconds.
nameof(DateTime.Ticks)
=> sqlExpressionFactory.Add(
sqlExpressionFactory.Multiply(
sqlExpressionFactory.Function(
"DATEDIFF",
arguments: new[]
{
sqlExpressionFactory.Fragment("minute"),
sqlExpressionFactory.Constant("0001-01-01T00:00:00.0"),
instance!
},
nullable: true,
argumentsPropagateNullability: new[] { false, false, true },
typeof(long)),
sqlExpressionFactory.Constant(600000000)),
sqlExpressionFactory.Add(
sqlExpressionFactory.Multiply(
sqlExpressionFactory.Function(
"DATEPART",
arguments: new[] { sqlExpressionFactory.Fragment("second"), instance! },
nullable: true,
argumentsPropagateNullability: Statics.FalseTrue,
typeof(int)),
sqlExpressionFactory.Constant(10000000)),
sqlExpressionFactory.Divide(
sqlExpressionFactory.Function(
"DATEPART",
arguments: new[] { sqlExpressionFactory.Fragment("nanosecond"), instance! },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am noting that this duplicate instance twice in the translation; if that's a complicated expression (e.g. with a subquery), that's bad for performance - for those cases, we tend to do the translation only if we know that's it's a simple column/parameter, rather than a complex expression. I also saw a brief discussion over multiple translation possibilities (possibly some not supported in older SQL Server), I'd lean towards one that doesn't duplicate expressions - even if it's not supported across all versions/variants - rather than one that's supported everything but duplicates.

(regardless, this is missing test coverage)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If use datediff_big to calculate the microsecond difference and then multiply it by 10, may lose one digit of precision because the datetime2 data type supports up to 7 decimal places. If use datediff_big to calculate the nanosecond difference and then divide it by 100, it will cause an overflow.
So, I can't think of a solution without using complex expressions.What are you suggesting?

nullable: true,
argumentsPropagateNullability: Statics.FalseTrue,
typeof(int)),
sqlExpressionFactory.Constant(100)))),

nameof(DateTime.Date)
=> sqlExpressionFactory.Function(
Original file line number Diff line number Diff line change
@@ -2532,7 +2532,7 @@ public override async Task Select_datetime_Ticks_component(bool async)

AssertSql(
"""
SELECT [o].[OrderDate]
SELECT DATEDIFF(minute, N'0001-01-01T00:00:00.0', [o].[OrderDate]) * CAST(600000000 AS bigint) + DATEPART(second, [o].[OrderDate]) * CAST(10000000 AS bigint) + DATEPART(nanosecond, [o].[OrderDate]) / CAST(100 AS bigint)
FROM [Orders] AS [o]
""");
}