2017-08-05 184 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; 
    } 
} 
相關問題