Я хочу знать причину, по которой моя дата С# больше даты SQL, хотя сначала выполняется код С#, а после этого SQL-запрос,
Логически дата SQL должна быть больше даты C#.
Для справки: приложение .NET и SQL Server находятся на моем локальном компьютере.
Код С#:
using System.Data;
using System.Data.SqlClient;
for (int i = 1; i <= 20; i++)
{
AddRecord();
}
Console.WriteLine("20 records added in database....");
void AddRecord()
{
try
{
string ConnectionString = @"data source=OM5\SQL2019; database=TestDb; integrated security=SSPI";
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
SqlCommand cmd = new SqlCommand()
{
CommandText = "SP_AddRecord",
Connection = connection,
CommandType = CommandType.StoredProcedure
};
SqlParameter param1 = new SqlParameter
{
ParameterName = "@CSharp_DateNow",
SqlDbType = SqlDbType.DateTime2,
Value = DateTime.Now,
Direction = ParameterDirection.Input
};
cmd.Parameters.Add(param1);
SqlParameter param2 = new SqlParameter
{
ParameterName = "@CSharp_DateUTCNow",
SqlDbType = SqlDbType.DateTime2,
Value = DateTime.UtcNow,
Direction = ParameterDirection.Input
};
cmd.Parameters.Add(param2);
connection.Open();
cmd.ExecuteNonQuery();
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception Occurred: {ex.Message}");
}
}
SQL:
CREATE TABLE [dbo].[Records](
[Id] [int] PRIMARY KEY IDENTITY(1,1) NOT NULL,
[SQL_SysDateTime] [datetime2](7) NOT NULL,
[CSharp_DateNow] [datetime2](7) NOT NULL,
[SQL_SysUTCDateTime] [datetime2](7) NOT NULL,
[CSharp_DateUTCNow] [datetime2](7) NOT NULL
)
CREATE OR ALTER PROCEDURE [dbo].[SP_AddRecord]
@CSharp_DateNow datetime2,
@CSharp_DateUTCNow datetime2
AS
BEGIN
SET NOCOUNT ON;
insert into Records(SQL_SysDateTime, CSharp_DateNow, SQL_SysUTCDateTime, CSharp_DateUTCNow) values
(SYSDATETIME(),@CSharp_DateNow,SYSUTCDATETIME(),@CSharp_DateUTCNow)
END
Результат в таблице
Мне нужна реальная причина или подлинный источник, который может это объяснить.
@ThomA - ОП сказал, что это один и тот же компьютер.
Я видел это, @Enigmativity, но они также используют строку подключения OM5\SQL2019
, а это означает, что это разные устройства (так как они должны использовать .\SQL2019
, если это локальное устройство). Меня также «обманули» достаточно раз, и я всегда задавался вопросом, когда пользователь говорит, что использует одно и то же устройство, хотя на самом деле это не так.
даже если вы вызываете их одновременно, у ЦП обычно есть планировщик, который создает внутреннюю очередь. иногда SQL первым получает поток, иногда C# получает его первым, иногда оба получают отдельные потоки одновременно.
Также отметим, что префикс sp_
зарезервирован Microsoft для специальных/системных процедур. Его не следует использовать для пользовательских процедур. Это связано с затратами на производительность и риском того, что ваша процедура просто не заработает через день после обновления/модернизации. Либо используйте другой префикс, либо (возможно, лучше) вообще не используйте префикс. Префикс sp_ по-прежнему запрещен?
@ShamvilKazmi Я также проверял это несколько раз и на другой машине, но результат тот же
@ThomA - Достаточно справедливо. Вы имеете смысл.
@ShamvilKazmi код C# должен выполняться перед SQL, поскольку он передает значение SQL.
Там, где вы заметили разницу в -4, на самом деле разница равна -3.
@ThomA Я хочу добавить еще одну вещь, которую я делаю, создавая Node Js API и используя ту же процедуру, но она дает правильное время. Их время JavaScript меньше, чем время Sql. Это тоже правильно.
Я предполагаю, что при переводе даты и времени C# в дату и время sql происходит какое-то округление, что делает его неправильным. Можете ли вы создать таблицу с datetime2 и опубликовать результаты? SYSUTCDATETIME можно использовать по умолчанию.
@Шивам, ты говоришь, что использовал datetime2, но никогда не публиковал такой код. Все несоответствия, кроме 3, можно объяснить точностью datetime
, и ничто из указанного в вопросе не указывает на иное. Нет CREATE TABLE
с использованием datetime2(7)
, нет хранимой процедуры с параметром datetime2(7)
и SYSUTCDATETIME
по умолчанию, нет кода C# с использованием SqlDbType.DateTime2
. Остальные 3 случая могут быть задержками при запуске, подключении или сети. Вы уже получили ответ на вопрос.
мне удалось воспроизвести эту проблему в своей системе. разница в МИКРОСЕКУНДАХ 98650 526 -835 -261 -666 -138 -553 -124 -618 -110 -508 -910 -191 -608 -98 -540 -94 -528 -940 -144
@Шивам - Он снова открыт.
@DaleK Да, я
Вам нужно использовать SqlDbType.DateTime2
, поскольку это обеспечивает более высокую точность.
Из документации
DateTime
— Данные о дате и времени в диапазоне от 1 января 1753 года до 31 декабря 9999 года с точностью до 3,33 миллисекунды. (большинство ваших результатов расхождения находятся в диапазоне 3-4)
DateTime2
- Данные о дате и времени. Диапазон значений даты — с 1,1 января нашей эры по 31 декабря 9999 года нашей эры. Диапазон значений времени — от 00:00:00 до 23:59:59,9999999 с точностью до 100 наносекунд.
И sysdatetime
потому что getdate
возвращает datetime
. И datetime2
в проке тоже.
Я уже пробовал это сделать, но результаты те же.
@Шивам - покажи нам аналогичную таблицу с datetime2
Эти значения являются точными, но не точными.
Если вы возьмете разные значения из SQL_SysDateTime
и сравните их...
SELECT MS_Diff = DATEDIFF(NANOSECOND, '2024-07-26 13:26:35.2898391', '2024-07-26 13:26:35.3054610')/1E6,
MS_Diff = DATEDIFF(NANOSECOND, '2024-07-26 13:26:35.3054610', '2024-07-26 13:26:35.3210815')/1E6,
MS_Diff = DATEDIFF(NANOSECOND, '2024-07-26 13:26:35.3210815', '2024-07-26 13:26:35.3367030')/1E6
Это возвращает 15.6219
, 15.6205
, 15.6215
как разницу между ними (в мс).
Как описано здесь SQL Server использует GetSystemTimeAsFileTime() для SYSDATETIME()
/SYSUTCDATETIME()
.
Рэймонд Чен указывает здесь, что по умолчанию GetSystemTimeAsFileTime()
не особенно точен, хотя упоминает периоды обновления по умолчанию для возвращаемого им значения 55 мс или 10 мс, а не 15,62, так что, по-видимому, с тех пор это изменилось.
На различных сайтах указано, что разрешение таймера по умолчанию в Windows 10 составляет 15,6 мс ( или, точнее, 15625000 нс), поэтому приведенные выше пробелы соответствуют этому значению.
Для C# документация DateTime.UtcNow
не выглядит более многообещающе
Разрешение этого свойства зависит от системного таймера, который зависит от базовой операционной системы. Обычно оно составляет от 0,5 и 15 миллисекунд.
Так что остается вопрос, как добиться большей точности.
Вы отметили ядро .NET. Для этого запроса на включение он теперь вызывает GetSystemTimePrecisionAsFileTime, когда он доступен (один из последних, упомянутых в сообщении Рэймонда Чена выше).
На моей локальной машине (Win 11) при выполнении следующего теста я обычно вижу разницу около 1 мс. (Но запуск powercfg -energy
говорит мне о том, что различные запущенные у меня процессы (включая chrome.exe
и MongoDB
) запросили низкий интервал времени для разрешения таймера платформы)
SET NOCOUNT ON;
DECLARE @Times TABLE(insert_time datetime2)
DECLARE @Counter INT = 0
WHILE @Counter < 10000
BEGIN
INSERT @Times VALUES (SYSUTCDATETIME())
SET @Counter+=1;
END
SELECT [rowcount] = COUNT(*),
insert_time,
prev_insert_time = LAG(insert_time) OVER (ORDER BY insert_time),
diff_ms = DATEDIFF(NANOSECOND,LAG(insert_time) OVER (ORDER BY insert_time), insert_time)/1e6
FROM @Times
GROUP BY insert_time
SQL Server в настоящее время не имеет встроенного способа вызова GetSystemTimePreciseAsFileTime
и возврата datetime2(7)
, поэтому, если это важно для вас, вам придется сделать это за пределами базы данных (вы также можете использовать для этого интеграцию CLR, но тогда сборку необходимо будет помечен как небезопасный для вызова функции WinAPI).
Выполнив приведенное выше в базе данных SQL Azure, я получил следующие результаты, поэтому не похоже, что она обновляется там чаще (и вы получаете только ~ 64 уникальных значения в секунду).
Интересно, заменив SYSUTCDATETIME()
на GETUTCDATE()
, я получаю разницу в 3.3333
/3.3334
мс, так что это выглядит менее точным, но более точным. Предположительно это результат упомянутой здесь «коррекции».
Мне эта ситуация кажется далеко не идеальной. Есть запрос обратной связи Попросите SYSDATETIME() вернуть значение из GetSystemTimePrecisionAsFileTime(), но он имеет только 3 голоса и помечен как «В архиве», поэтому не уверен, означает ли это, что он никогда не будет рассмотрен.
Но если C# и SQL считывают одни и те же системные часы, как SQL может показывать более низкое значение, чем C#? Я понимаю, что разрешение составляет 15 мс, но наверняка они должны считывать одно и то же значение? И как SQL может показывать одно и то же значение для нескольких вызовов одного и того же SP? Я думаю, это возможно, если все они находятся в пределах одного и того же интервала 15 мс?
Да, вполне ожидаемо, что SQL вернет те же значения, если он будет вызван до обновления времени. .NET, по-видимому, использует какой-то другой метод, лежащий в основе DateTime.UtcNow
, кроме вызова GetSystemTimeAsFileTime
. Возможно, один из последних, упомянутых в посте Рэймонда Чена.
Хорошо, это имеет смысл, хотите ли вы подвести итог этому в конце вашего вопроса, например. «Похоже, что SQL Server использует время обновления по умолчанию 15 мс, о чем свидетельствует тот факт, что его значение не меняется для нескольких вызовов. В то время как .NET, похоже, использует один из более точных методов и поэтому медленно опережает SQL до конца периода обновления».
хотя на самом деле .NET не документирован как более точный The resolution of this property depends on the system timer, which depends on the underlying operating system. It tends to be between 0.5 and 15 milliseconds.
Learn.microsoft.com/en-us/dotnet/api/…
Это немного странно думать об этом: 15 мс — не очень высокое разрешение, если вы запускаете много обновлений в SQL.
Странно, что .NET утверждает, что он такой же... но поведение, похоже, говорит об обратном. По крайней мере, это хорошая теория :)
Похоже, .NET Core улучшил эту область github.com/dotnet/coreclr/pull/9736
Хорошая находка! Хотя в два раза медленнее... надо меньше называть :)
Предположительно, время на хосте, на котором вы запускаете приложение, и на хосте, на котором размещен SQL Server, различаются, поэтому вы получаете разные значения.