3

In SQL Server, datetime2(7) has a precision of 7 decimal places, e.g.:

2020-10-20 19:12:22.5294497
                    \_____/
                       |
              seven decimal places
               (100 ns precision)

This corresponds to the precision of the Windows FILETIME structure in Windows, which is based on a number of 100 ns ticks.

  • FILETIME counts the number of 100 ns ticks since 1601-01-01
  • datetime2(7) counts the number of 100 ns ticks since midnight

It has a precision of 100ns, so ideally it has an accuracy of 100 ns. And that you would be able to get two calls to SYSDATETIME to return:

  • 2020-10-20 19:12:22.5294497
  • 2020-10-20 19:12:22.5294498

Edit: Yes, ideally it will have an accuracy that matches its precision. This might not happen in reality - but ideally it does have matching accuracy.

So we try it

We setup a tight loop, and wait for the call to SYSDATETIME to return a value different from the previous call:

DECLARE @rc int = 0;

DECLARE @dt1 datetime2(7) = SYSUTCDATETIME();

WHILE @rc < 20
BEGIN
    DECLARE @dt2 datetime2(7) = SYSUTCDATETIME();

    --Next the next highest datetime value
    WHILE @dt1 = @dt2
    BEGIN
        --PRINT 'Incremeting datetime due to collision: '+CAST(@dt1 AS varchar(50))+' = '+CAST(@dt2 AS varchar(50))
        SET @dt2 = SYSUTCDATETIME();
    END
    PRINT CAST(@dt2 AS varchar(50))+'   (delta: '+CAST(DATEDIFF(ns, @dt1, @dt2) AS varchar(50))+' ns)'
    SET @rc = @rc+1;
    SET @dt1 = @dt2;
END

And when we run this, we see that it consistently takes 15,569,800 ns for SYSDATETIME to return a different value:

2020-10-20 19:12:21.4707033 (delta: 15569800 ns)
2020-10-20 19:12:21.4862731 (delta: 15569800 ns)
2020-10-20 19:12:21.5018429 (delta: 15569800 ns)
2020-10-20 19:12:21.5174127 (delta: 15569800 ns)
2020-10-20 19:12:21.5329825 (delta: 15569800 ns)
2020-10-20 19:12:21.5485523 (delta: 15569800 ns)
2020-10-20 19:12:21.5641221 (delta: 15569800 ns)
2020-10-20 19:12:21.5796919 (delta: 15569800 ns)
2020-10-20 19:12:21.5952617 (delta: 15569800 ns)
2020-10-20 19:12:21.6108315 (delta: 15569800 ns)
2020-10-20 19:12:21.6264013 (delta: 15569800 ns)
2020-10-20 19:12:21.6419711 (delta: 15569800 ns)
2020-10-20 19:12:21.6575409 (delta: 15569800 ns)
2020-10-20 19:12:21.6731107 (delta: 15569800 ns)
2020-10-20 19:12:21.6886805 (delta: 15569800 ns)
2020-10-20 19:12:21.7042503 (delta: 15569800 ns)
2020-10-20 19:12:21.7198201 (delta: 15569800 ns)
2020-10-20 19:12:21.7353899 (delta: 15569800 ns)
2020-10-20 19:12:21.7509597 (delta: 15569800 ns)
2020-10-20 19:12:21.7665295 (delta: 15569800 ns)

That accuracy is familiar:

  • 15,569,800 ns
  • 15,569.8 μs
  • 15.5698 ms

We recognize it as a common hardware clock frequency - which limits the resolution of any clock to ~15 ms.

Which is why Microsoft created GetSystemTimePreciseAsFileTime, which allows getting the current time with an accuracy down to 100 ns.

Is there a way to obtain the current time as a datetime2 with a full accuracy of 0.0000001 seconds (100 ns)?

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
  • `so ideally it has an accuracy of 100 ns` that assumption is wrong. In fact, it's quite common for all types of devices and instruments to have greater precision than accuracy. Why do you want such accuracy anyway? – Panagiotis Kanavos Oct 20 '20 at 20:22
  • There is no T-SQL function that defers to `GetSystemTimePreciseAsFileTime`. You could write a CLR function that does so, if you really insisted. (Even then this would only work on Windows.) There are other approaches to generate unique, monotonically increasing values (which even a 100 ns timestamp is not), if that's the idea. These can be combined with less-precise timestamps. – Jeroen Mostert Oct 20 '20 at 20:23
  • You'd have to run at the kernel level, probably with interrupts disabled and the CPU set to avoid sleeping (which it does, all the time) just to ensure you can read the motherboard's real-time clock without significant delays. – Panagiotis Kanavos Oct 20 '20 at 20:26
  • Incidentally, the resolution of the system timer is not guaranteed to be that fabled 15 ms, as this depends on the resolution of the hardware timer used. On my client system, I can get a minimum delta of 1 ms, as the timer precision has been increased. (1 ms is still the limit in that case without going to `Precise`.) At 1 ms T-SQL is typically running slower than that delta, incidentally, so getting anywhere from 1 to 30 ms is possible (darn context switches). – Jeroen Mostert Oct 20 '20 at 20:26
  • 1
    This is speculation but I would not be surprised if Azure SQL's clock was more accurate than most server clocks – SteveC Oct 20 '20 at 21:24
  • @PanagiotisKanavos The real issue i'm trying to solve is that i need a *user-defined function* to have (essentially) a random number generator. Side-effecting functions like `newid()` are not allowed in a UDF. So i'm *essentially* hashing `SYSDATETIME` assuming it has 100ns accuracy (so it rolls over every 100 ns). I don't like to mention the specific problem i'm trying to solve, as people might try to solve the problem, rather than the question. I also avoid giving the actual problem, because the question is applicable to more than just my current use case. – Ian Boyd Oct 22 '20 at 14:20
  • There is no tax on questions; you can always open a separate one to get an actual problem solved. The answer to this question is going to be a fairly boring "you can't do that, except through a CLR function" -- and if you're willing to use those, it's much simpler to just write a CLR function that just exposes an RNG directly. Hashing the system time is a bad idea *even* with 100 ns accuracy, because it can easily produce duplicates if the clock is adjusted (which doesn't happen often on properly configured systems, but does happen). That tends to be bad even for pseudorandomness. – Jeroen Mostert Oct 27 '20 at 10:45

1 Answers1

0

The maximum precision datetime available is datetimeoffset(7). Per the Docs the time range offered is 00:00:00 through 23:59:59.9999999 and is accurate to 100 nanoseconds. You could create a table with a column of maximum precision something like this

drop table if exists #tTable;
go
create table #tTable(max_precision_dto   datetimeoffset(7));
insert #tTable(max_precision_dto) values (sysdatetimeoffset());

select * from #tTable;

Output

max_precision_dto
2020-10-20 20:06:34.6490280 +00:00
SteveC
  • 5,955
  • 2
  • 11
  • 24
  • `SYSDATETIMEOFFSET`, like `SYSDATETIME`, is only accurate to [~15 ms.](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeadjustment) – Ian Boyd Oct 20 '20 at 20:41
  • Well given the only tag in the question is SQL Server I'm not sure if there's a better option. You're correct it's not ideal for the purpose. – SteveC Oct 20 '20 at 20:47