为什么转换 SQL MAX(DateTime) 命令在 SSMS 中有效,但在 C# 中无效?

Why does casting a SQL MAX(DateTime) command work in SSMS but not in C#?

以下查询在 Azure 托管的 SQL 数据库 table:

上的 MSSSMS 版本 18.1 中按预期工作
select Name, cast(MAX(DTStamp) as Datetime) AS Last_Seen FROM dbo.MyTable GROUP BY Name

示例输出:

Fairyflosser29   2021-11-11 19:26:00.323
GoofballAnticz   2021-11-25 14:43:57.443
WeirdAlJankyVic  2021-12-01 19:30:20.341

但是,C# 运行 中的相同 SQL 命令通过以下方法始终会从 DateTime 字段中减少毫秒数,尽管进行了强制转换,尽管我编写了强制转换的方式。这对我来说很重要,因为我的后续查询取决于 DateTime 字段的精度是否正确到毫秒,否则后续查询 return 没有。

该过程调用 GetLastSeenList(),其中 return 是一个以逗号分隔的记录列表,表示 [=64] 中每个 Name 的最新(按日期时间戳)记录=].使用填充的列表,稍后会调用另一个方法,该方法使用这些结果来提取每个项目的完整行。

因为日期时间戳包含毫秒,所以后续查询必须包含毫秒值,否则即使有记录,查询也会失败。

首先,获取姓名列表以及他们最后一次露面的时间。接下来,为最后看到的列表中的每条记录拉出整行(此处未包括在内,因为第一个查询未提供日期时间,因为它们在 table 中 - 完整无缺毫秒)。

我不是很擅长 SQL,所以请帮助我理解我在这里遗漏了什么。

[编辑]: 以防万一不清楚,sql 命令 sqlcmd_GetLastSeenList 与顶部的 SSMS 查询相同:

private static readonly string sqlcmd_GetLastSeenList =
  @"select Name, cast(MAX(DTStamp) as Datetime) AS Last_Seen FROM dbo.MyTable GROUP BY Name;";

GetLastSeen:

internal static async Task<string> GetLastSeenList()
{
    StringBuilder sb = new StringBuilder();
    var list = await ReadSQL(sqlcmd_GetLastSeenList, false);

    if(list != null && list.Count > 0)
    {
        // Just return a CSV list of ID's with last seen timestamps
        foreach(var record in list)
            sb.Append($"{record[0]} {record[1]},");
    }

    return sb.ToString().Trim(',');
}

阅读SQL:

private static async Task<List<object[]>> ReadSQL(string cmdText, bool isProd)
{
    List<object[]> response = new List<object[]>();
    string connect = string.Empty;

    if (isProd)
        connect = await SetConnectionProd(_uidProd, _p_pwd);
    else
        connect = await SetConnectionTest(_uidTest, _t_pwd);

    try
    {
        using (SqlConnection conn = new SqlConnection(connect))
        {
            using (SqlCommand cmd = new SqlCommand(cmdText, conn))
            {
                response = await ExecuteQuery2(cmd, conn);
            }
        }
    }
    catch (Exception ex)
    {
        //TODO: log it
        throw ex;
    }

    return response;
}

执行查询 2:

private static async Task<List<object[]>> ExecuteQuery2(SqlCommand cmd, SqlConnection conn)
{
    List<object[]> retval = new List<object[]>();

    try
    {
        object[] myVals = new object[MAXCOLUMNCOUNT];
        object[] myRow = null;
        
        cmd.Connection.Open();
        using(SqlDataReader sqlreader = cmd.ExecuteReader())
        {
            if(sqlreader.HasRows)
            {
                while(sqlreader.Read())
                {
                    var count = sqlreader.GetValues(myVals);
                    myRow = new object[count];
                    Array.Copy(myVals, myRow, count);

                    retval.Add(myRow);
                }
            }
        }
    }
    catch(Exception ex)
    {
        throw ex;
    }
    finally
    {
        cmd.Connection.Close();
    }

    return retval;
}

毫秒在那里。 DateTime.ToString() 默认不显示它们。例如:

    var cmd = new SqlCommand("select cast('2021-11-11 19:26:00.323' as datetime) d", con);
    using (var rdr = cmd.ExecuteReader())
    {
        rdr.Read();

        var vals = new object[rdr.FieldCount];
        rdr.GetValues(vals);

        var val = (DateTime)vals[0];

        Console.WriteLine($"{val.GetType().Name}   {val}   {val.ToString("O")}");
    }

产出

DateTime   11/11/2021 7:26:00 PM   2021-11-11T19:26:00.3230000

但也要确保你没有在 datetimedatetime2 之间来回转换,例如这个

select cast(cast('2021-11-11 19:26:00.323' as datetime) as datetime2) d

产出

d
---------------------------
2021-11-11 19:26:00.3233333

这种转换可能是后续查询中错误比较的来源。