我实际上正在创建一种方法来根据特定的日期时间范围获取我的asp图表的详细信息。我看过这两个链接,
我检索数据的方法,
//Monthly Statistics
public int janLogin(String username)
{
int result = 0;
StringBuilder sqlCmd = new StringBuilder();
sqlCmd.AppendLine("SELECT COUNT(*) FROM AuditActivity WHERE Username = @getUsername AND DateTimeActivity BETWEEN @getFirstDT AND @getLastDT AND ActivityType = @getType");
try
{
SqlConnection myConn = new SqlConnection(DBConnectionStr);
myConn.Open();
SqlCommand cmd = new SqlCommand(sqlCmd.ToString(), myConn);
//DateTime
DateTime currentDT = DateTime.Today;
//Code Below gets only current Start and End of Current month
DateTime FirstDT = currentDT.AddDays(1 - currentDT.Day);
DateTime SecondDT = FirstDT.AddMonths(1).AddSeconds(-1);
cmd.Parameters.AddWithValue("@getUsername", username);
cmd.Parameters.AddWithValue("@getFirstDT", FirstDT);
cmd.Parameters.AddWithValue("@getLastDT", SecondDT);
cmd.Parameters.AddWithValue("@getType", "Login");
result = Convert.ToInt16(cmd.ExecuteScalar());
myConn.Close();
return result;
}
catch (SqlException ex)
{
logManager log = new logManager();
log.addLog("AuditNLoggingDAO.janLogin", sqlCmd.ToString(), ex);
return 0;
}
}
我正在尝试实际做的事情,你可以从方法名称janLogin看到基本上从 2017年1月1日00:00:00到2017年1月31日登录的人数。本月实际上是八月。我怎样才能真正获得2017年的所有12个月(1月至12月)的开始和结束月份(来自大胆的例子)?
如果我没错,它必须涉及for循环?最终计数是12.但我很确定如何做到这一点..可能有一个重复的问题,但我似乎找不到符合我要求的问题..
感谢任何帮助。谢谢!
答案 0 :(得分:1)
整个日历年:
int year = 2017
DateTime FirstDT = new DateTime(year, 1, 1); // jan 2017
DateTime SecondDT = FirstDT.AddYear(1).AddSeconds(-1); // last second of dec 2017
上面的示例会将
的答案底部的精度问题01 Jan 2017 00:00:00
返回31 Dec 2017 23:59:59
请注意31 DEC 2017 23:59:59.999999
为了获得不同的月份,您可以这样做,而不是使用当前的月份:
int year = 2017;
int month = 12;
DateTime FirstDT = new DateTime(year, month , 1); // december 2017
DateTime SecondDT = FirstDT.AddMonths(1).AddSeconds(-1); // last second of dec 2017
上面的示例会将
的答案底部的精度问题01 Dec 2017 00:00:00
返回31 Dec 2017 23:59:59
请注意31 DEC 2017 23:59:59.999999
然后你可以改变你的方法,将月份作为参数,如下所示:
public int LoginsByMonth(int year, int month, String username)
{
if (month < 1 || month > 12)
{
throw new ArgumentOutOfRangeException("month must be between 1 and 12.");
}
...
DateTime FirstDT = new DateTime(year, month, 1);
DateTime SecondDT = FirstDT.AddMonths(1).AddSeconds(-1);
...
}
你可以像单身一样打电话给你的方法:
int loginsNovember2017 = LoginsByMonth(2017, 11, "userA");
int loginsDecember2017 = LoginsByMonth(2017, 12, "userA");
如下面的评论中所述。您可以使用.AddTicks(-1L)
代替.AddSeconds(-1)
来获得更准确的过滤器。但是,根据您的要求,您可能更愿意在下一个选项中完全避免这种情况。
同样如下面的评论中所述。通过在SELECT
语句中添加< (less than)
条件而不是在使用WHERE
条件时担心精度,可以完全避免这种精确的头痛问题。
更改将如下所示
BETWEEN
这意味着您可以放弃SELECT COUNT(*)
FROM AuditActivity
WHERE Username = @getUsername
AND DateTimeActivity >= @getFirstDT
AND DateTimeActivity < @getLastDT
AND ActivityType = @getType
或AddSeconds(-1)
。例如:
AddTicks(-1L)
答案 1 :(得分:1)
所以,我想这里有两个不同的问题。
第一个是&#34;如何按年份和月份获取特定月份的日期范围?&#34;
这是一种方式:
var year = 2017;
for (int month = 1; month <= 12; month++)
{
var firstDayOfMonth = new DateTime(year, month, 1);
var lastDayOfMonth = new DateTime(year, month, DateTime.DaysInMonth(year, month));
Console.WriteLine($"{firstDayOfMonth} - {lastDayOfMonth}");
}
请注意,没有Date
- 仅DateTime
的值。如果你像我一样省略构造函数中的时间部分,它将被设置为当天的午夜。使用这些值过滤某些日期时,您应首先在其上调用.Date
:
var datesInThisMonth = someDates.Where(x => x >= firstDayOfMonth && x.Date <= lastDayOfMonth);
...或者在下个月的第一天使用严格的下限作为上限:
var datesInThisMonth = someDates.Where(x => x >= firstDayOfMonth && x <= lastDayOfMonth.AddDays(1));
第二个是如何使用该值来构建SQL查询。
您不应在此使用BETWEEN
。根据您用于列的数据类型,它难以消化或者只是完全错误。
这是您的替代选择:
datetime
类型然后它rounded到.000
,.003
,.007
秒。因此,您在BETWEEN
语句中使用的上限将四舍五入到第二天的23:59:59.997
或00:00
。您将错过一些记录,或多次包含一些记录(这将是本月和下一个记录的结果)。
datetime2
类型然后可以编写正确的查询,但为了做到这一点,你必须考虑在SQL中使用的精度(可以为这个特定表的特定列自定义)并且你应该从C#传递什么值才能在SQL中获得正确的值。
我不明白它是多么值得麻烦。
只需使用x.Date > firstDayOfMonth AND x.Date < firstDayOfNextMonth
并忘掉它。
您还可以查看this answer。
答案 2 :(得分:1)
为什么不直接使用SQL提供的功能?
@"SELECT MONTH(DateTimeActivity), COUNT(*) FROM AuditActivity
WHERE Username = @getUsername AND ActivityType = @getType
GROUP BY MONTH(DateTimeActivity)
ORDER BY MONTH(DateTimeActivity)"
这将为您提供12个记录,每个月的计数(假设您有所有年份的日期),只需一次调用数据库,而不是循环内的12个不同的调用。并且WHERE语句更简单,没有对DateTimeActivity字段进行计算
这种方法只有一个问题。如果WHERE语句没有找到具有给定约束的任何记录,则它不返回零值的记录。
但是在代码中相对容易处理最终缺失的月份
cmd.Parameters.AddWithValue("@getUsername", username);
cmd.Parameters.AddWithValue("@getType", "Login");
SqlDataReader reader = cmd.ExecuteReader();
int[] monthCount = new int[12];
while(reader.Read())
// -1 because arrays start at zero and January has value 1.
monthCount[reader.GetInt32(0) - 1] = reader.GetInt32(1);
最后你有一个整数数组,其中每个元素代表每个月的计数,在WHERE语句中的givens约束被应用之后没有记录的数月为零。
答案 3 :(得分:1)
这应该将当前年份的登录作为代表每个月的整数数组得到:)
public int[] yearLogin(String username)
{
string sqlCmd = @"SELECT COUNT(*)
FROM AuditActivity
WHERE Username = @getUsername AND DateTimeActivity
BETWEEN @getFirstDT AND @getLastDT AND ActivityType = @getType";
try
{
Func<SqlConnection, DateTime, DateTime, int> fetch = (c, f, t) =>
{
using (SqlCommand cmd = new SqlCommand(sqlCmd.ToString(), c))
{
cmd.Parameters.AddWithValue("@getUsername", username);
cmd.Parameters.AddWithValue("@getFirstDT", f);
cmd.Parameters.AddWithValue("@getLastDT", t);
cmd.Parameters.AddWithValue("@getType", "Login");
return Convert.ToInt16(cmd.ExecuteScalar());
}
};
using (SqlConnection myConn = new SqlConnection(DBConnectionStr))
{
myConn.Open();
DateTime currentDT = DateTime.Today;
DateTime FirstDT = currentDT.AddMonths(1 - currentDT.Month).AddDays(1 - currentDT.Day);
int[] result =
Enumerable
.Range(0, 12)
.Select(x => fetch(myConn, FirstDT.AddMonths(x), FirstDT.AddMonths(x + 1).AddTicks(-1L)))
.ToArray();
return result;
}
}
catch (SqlException ex)
{
logManager log = new logManager();
log.addLog("AuditNLoggingDAO.janLogin", sqlCmd.ToString(), ex);
return null;
}
}