2017-08-05 136 views
-1

我实际上是创建一个方法来获取我的asp图表基于某个日期时间范围的详细信息。我看这2个链接,开始和月底12个月(一年)

First LinkSecond Link

我的方法获取数据,

//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日。本月实际上是8月份。我如何才能真正得到2017年的所有12个月(1月至12月)开始和结束月份(从粗体示例)?

如果我没有错,它不得不涉及for循环?哪里的最终数是12.但我很确定如何做到这一点..可能有一个重复的问题,但我似乎找不到一个适合我的要求..

欣赏任何帮助。谢谢!

+1

开始,特别是不使用AddWithValue与日期[我们可以停止使用addWithValue了吗?](HTTPS:/ /blogs.msmvps.com/jcoehoorn/blog/2014/05/12/can-we-stop-using-addwithvalue-already/) – Steve

+0

其次,你能解释一下数据库中两列的数据类型是什么?如果他们也存储时间部分你的代码可能会被窃听 – Steve

+0

@Steve谢谢你的建议和文章,将来会修改! DateTime是firstDT的数据类型和lastD – domster

回答

1

整个2017年日历年

整个日历年:

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:0031 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:0031 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 stament以在WHERE声明中包含< (less than)条件,而不用担心使用BETWEEN条件时的精确度,从而可以避免这种精确头痛。

的变化是这样的

SELECT COUNT(*) 
FROM AuditActivity 
WHERE Username = @getUsername 
    AND DateTimeActivity >= @getFirstDT 
    AND DateTimeActivity < @getLastDT 
    AND ActivityType = @getType 

这意味着你可以删除AddSeconds(-1)AddTicks(-1L)。例如:

// entire year 
DateTime SecondDT = FirstDT.AddYear(1); 
// month 
DateTime SecondDT = FirstDT.AddMonths(1); 
+1

执行'.AddMonths(1).AddSeconds(-1)'有点差。你应该做'.AddMonths(1).AddTicks(-1L)'来获得新月开始之前的最后一次可能的表示。 – Enigmativity

+1

@Enigmativity - OP在他的问题中特别询问了2017年1月1日00:00:00(秒)。但就你而言,你是正确的,“蜱”是正确的方式。我会在答案中注明。 – Svek

+0

对不起,你是对的。我真的应该对这个问题发表评论。 – Enigmativity

1

所以,我猜这里有两个不同的问题。

第一个问题是“如何根据年份和月份获取特定月份的日期范围?”

这里有一个办法:

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}"); 
} 

请记住,有一种DateTime没有Date - 只值。如果您像我一样省略构造函数中的时间部分,它将设置为当天的午夜。当使用这些值来过滤一些日期,您应该先调用.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.99700:00。您可能会错过一些记录,或多次包含一些记录(这将在本月和下一次的结果中)。

  • 你列datetime2

那么它可能是可能写出正确的查询,但同样为了把事情做对,你必须考虑你在SQL中使用什么样的精度(可以为该特定表的特定列自定义),并且应该通过C#传递什么值才能在SQL中获得正确的值。

我不明白它是如何值得的麻烦。

只需使用x.Date > firstDayOfMonth AND x.Date < firstDayOfNextMonth并忘掉它。

你也可以看看this answer

+0

嘿vorou我会尝试一下,看看我得到了什么,谢谢! – domster

+0

这将错过所有登录每个月的最后一天。 – Enigmativity

+0

@Enigmativity它取决于你如何使用这段代码来过滤登录;我会写'.Where(x => x.LoginDate.Date <= lastDayOfMonth)',不会错过任何东西 – vorou

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语句中给出约束条件。

+0

嘿史蒂夫,它会全部返回12个月吗? – domster

+0

如果每个月至少有一个DateTimeActivity,否则该月不会有记录。它可以很容易地在代码中处理。还有一种方法可以直接从sql server获得一个月的零,但我现在还没有一个例子。 – Steve

+0

哦,好吧..甜蜜,欢呼!谢谢! – domster

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; 
    } 
} 
相关问题