-1

I have two DateTimePickers (dateStart and dateStop) in a winform. I've set dateStart and dateSop's format like this:

    dateStart.Format = DateTimePickerFormat.Custom;
    dateStart.CustomFormat = "yyyy:MM:dd HH:mm:ss";
    dateStop.Format = DateTimePickerFormat.Custom;
    dateStop.CustomFormat = "yyyy:MM:dd HH:mm:ss";

I want to count number of rows from my datebase's table between dateStart and dateStop. My select string:

string stmt = "SELECT COUNT(*) FROM [dbo].[tbl_TagLogging] WHERE DateTime BETWEEN  " + Convert.ToString(dateStart.Value) + " AND " + Convert.ToString(dateStop.Value);

And it's result: enter image description here

This is how link DateStart and DateStop paramaters of my datebase with DateTimePickers.

 cmd.Parameters.Add("@DataStart", SqlDbType.DateTime).Value = dateStart.Value;// new DateTime(dateStart.Value);
 cmd.Parameters.Add("@DataStop", SqlDbType.DateTime).Value = dateStop.Value;// new DateTime(2020, 02, 05, 13, 12, 25, 703);//2020, 02, 05, 13, 12, 25, 703   //2020, 02, 05, 13, 06, 50, 700

The row counting works perfectly without WHERE clause.

count = (int)cmdCount.ExecuteScalar();//this is how I count the rows

I'm getting here a 'System.Data.SqlClient.SqlException: Incorect syntax near 9'.

I kinda don't understand why if dateStart's format is "yyyy:MM:dd HH:mm:ss", dateStart.value is returned in another format. On my UI, dateStart is displayed in the right format (the one from the code above). But I might have done something faulty in my stmt string. Any thoughts on what is wrong?

EDIT 1 (my entire code):

 private void button_Click(object sender, EventArgs e)
    {
        dateStart.Format = DateTimePickerFormat.Custom;
        dateStart.CustomFormat = "yyyy:MM:dd HH:mm:ss";
        dateStop.Format = DateTimePickerFormat.Custom;
        dateStop.CustomFormat = "yyyy:MM:dd HH:mm:ss";
        string stmt = "SELECT COUNT(*) FROM [dbo].[tbl_TagLogging] WHERE DateTime BETWEEN  @DataStart AND @DataStop";         // WHERE DateTime BETWEEN  " + Convert.ToString(dateStart.Value) + " AND " + Convert.ToString(dateStop.Value)
       // string stmt = "SELECT COUNT(*) FROM [dbo].[tbl_TagLogging] WHERE DateTime BETWEEN  '" + Convert.ToString(dateStart.Value) + "' AND '" + Convert.ToString(dateStop.Value) + "'";
        int count = 0;

        using (SqlConnection connection = new SqlConnection(@"Data Source=DESKTOP-JQSJAF8\SQLEXPRESS;Initial Catalog=TRTF_TagLogging;Integrated Security=True; MultipleActiveResultSets = true")) 
        {
            using (SqlCommand cmd = new SqlCommand("PS_TagLogging", connection))// used for stored proc
            {
                using (SqlCommand cmdCount = new SqlCommand(stmt, connection))//used to count number of rows of my table
                {
                    //yyyy:MM:dd hh:mm:ss
                    connection.Open();
                    cmd.CommandType = CommandType.StoredProcedure;                        
                    cmd.Parameters.Add("@DataStart", SqlDbType.DateTime).Value = dateStart.Value;
                    cmd.Parameters.Add("@DataStop", SqlDbType.DateTime).Value = dateStop.Value;
                    var values = new List<double>();
                    var valData = new List<DateTime>();
                    SqlDataReader reader = cmd.ExecuteReader();
                    while (reader.Read())
                    {
                        // values.Add(reader.GetInt64(0));//reads the values of first column (ID) from DB
                        values.Add(reader.GetFloat(1));//reads the values of second column (Tag1) from DB 
                        valData.Add(reader.GetDateTime(2));//reads the values of third column (DateTime) from DB 
                    }

                    arrDate = valData.ToArray();
                    arrVal = values.ToArray();

                    count = (int)cmdCount.ExecuteScalar();
                    plot1.XAxes[0].ScaleDisplay.TextFormatting.Style = Iocomp.Types.TextFormatDoubleStyle.DateTime;
                    plot1.XAxes[0].ScaleRange.Span = 1.0 / 120.0 ; //30sec Span;



                    for (int i=0; i<count; i++)
                    {
                        plot1.Channels.Trace[0].AddXY(arrDate[i], arrVal[i]);
                    }


                    connection.Close();
                }
            }
        }
        MessageBox.Show("Nr. lines: " + count);
         Random().NextDouble() * 100);
    }
Ionut
  • 724
  • 2
  • 9
  • 25
  • 12
    Use SQL parameters – apomene Feb 17 '20 at 08:34
  • Either parameters or ' around the datetimes, but you should always use parameters – colosso Feb 17 '20 at 08:35
  • You can use SQL parameters like @apomene said. Or you can change the format of your Dates. SQL cannot execute this, because it doesn't understand the Format. You can change the dateformat like this: Convert.ToString(d). Here you find every Format:https://learn.microsoft.com/en-us/dotnet/api/system.datetime.tostring?view=netframework-4.8 – Presi Feb 17 '20 at 08:39
  • 2
    @colosso No. Using parameters is the correct way. Anything else might be a security risk. Read about SQL Injection. – Zohar Peled Feb 17 '20 at 08:39
  • @apomene you mean instead of Convert.ToString(dateStart.Value)? I will update my question. – Ionut Feb 17 '20 at 08:39
  • @Lonut don't mess around with strings when you have datetime values. Just pass in the instance of `DateTime` as a parameter to SQL Server and that's it. For more information, read [this](https://stackoverflow.com/a/37291493/3094533), [that](https://stackoverflow.com/a/46950197/3094533), and [For the 1024 time – DateTime has no format!](https://zoharpeled.wordpress.com/2019/12/19/for-the-1024-time-datetime-has-no-format/) – Zohar Peled Feb 17 '20 at 08:42
  • Also - [What do between and the devil have in common?](https://sqlblog.org/2011/10/19/what-do-between-and-the-devil-have-in-common) by Aaron Bertrand – Zohar Peled Feb 17 '20 at 08:43
  • 1
    @ZoharPeled see my edited question. Isn't this what you refer to? cmd.Parameters.Add("@DataStart", SqlDbType.DateTime).Value = dateStart.Value; cmd.Parameters.Add("@DataStop", SqlDbType.DateTime).Value = dateStop.Value; – Ionut Feb 17 '20 at 08:47
  • @apomene what parameters are you referring to? As you can see in my edited question, I do use parameters. "@DataStart" and "@DataStop" are 2 paramaters of my stored procedure. So what should I be doing? – Ionut Feb 17 '20 at 08:56
  • @Ionut in your sql query you concatenate string with datepicker values. Datepicker values should be provided as SQL parameters https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.parameters?view=netframework-4.8 – apomene Feb 17 '20 at 08:59
  • Your parameters seems fine, but you don't use them in your SQL statement. Check out Oliver's answer. – Zohar Peled Feb 17 '20 at 09:24
  • @ZoharPeled I've seen it but am I'm facing another exception. See my answer for his post. – Ionut Feb 17 '20 at 09:27
  • It would be much better if you could [edit] your question to include all the relevant code as a single block - that way we wouldn't have to guess... – Zohar Peled Feb 17 '20 at 09:37
  • @ZoharPeled I have edited my question with the entire code – Ionut Feb 17 '20 at 09:45

2 Answers2

4

As already mentioned in the comments and proofed by your other command, you should use parameters. And here is how it should look in your case:

string stmt = "SELECT COUNT(*) FROM [dbo].[tbl_TagLogging] WHERE DateTime BETWEEN @DataStart AND @DataEnd";
using (var cmd = new SqlCommand(stmt))
{
    cmd.Parameters.Add("@DataStart", SqlDbType.DateTime).Value = dateStart.Value;
    cmd.Parameters.Add("@DataStop", SqlDbType.DateTime).Value = dateStop.Value;
    var reader = cmd.ExecuteReader();

    // Process the data from the reader.
}

But maybe you should think about to completely drop these things and use Entity Framework (Core) and model your DBContext and just work with LINQ-to-SQL to retrieve data from your database.

Update

After you updated your question and posted the whole function, the problem is quite easy. You simply missed to add the parameters to your second query and added it only to the first one. So please update accordingly to something like this:

cmdCount.Parameters.Add("@DataStart", SqlDbType.DateTime).Value = dateStart.Value;
cmdCount.Parameters.Add("@DataStop", SqlDbType.DateTime).Value = dateStop.Value;
count = (int)cmdCount.ExecuteScalar();

Nevertheless I hope this is not really your whole function, cause in that case requesting the count through this second query is completely useless. You use that value to iterate over the array you extracted from the first query. So my assumption would be that the variables arrDate.Length and arrVal.Length already contain the value that you need. So you should really check if these values (the length property of the array and the count result of your query) are the same and if not, why they differ and find the root cause for this issue. If they are the same, just use your local existing value and don't bother asking the SQL serverb for something you already know.

Oliver
  • 43,366
  • 8
  • 94
  • 151
  • As @apomene suggested, I thought of exact your solution, but I'm getting 'System.Data.SqlClient.SqlException: must declare the scalar variable "@DataStart" '. How could I solve this? I have two comands. One on which I do the counting, and other executes a stored procedure with these two paramaters. Could be this an inconvenient? – Ionut Feb 17 '20 at 09:15
  • @Ionut If you have more commands using the same values, then you need to add parameters (containing these values) to all these commands – Hans Kesting Feb 17 '20 at 09:30
  • @Ionut I don't know, you only showed a few loose lines of your code – Hans Kesting Feb 17 '20 at 09:34
  • Thank you all for your answers! Oliver updated post helped solve my problem. – Ionut Feb 17 '20 at 10:01
1

Everything people told is ok, but i think there are missing quotes. Try:

string stmt = "SELECT COUNT(*) FROM [dbo].[tbl_TagLogging] WHERE DateTime BETWEEN  '" + Convert.ToString(dateStart.Value) + "' AND '" + Convert.ToString(dateStop.Value) + "'";
Fran Cerezo
  • 940
  • 3
  • 8
  • 19
  • 1
    No. That's an open door to SQL Injection. – Zohar Peled Feb 17 '20 at 09:23
  • Yes. It solves my problem, but I want to try and @Oliver's solution. – Ionut Feb 17 '20 at 09:28
  • `Convert.ToString(dateStart.Value)` will not produce the SQL compatible date representation in most of cases. You have to use parameters and let `SqlCommand` to take care for c# `DateTime` to SQL `date` conversion. – oleksa Feb 17 '20 at 09:30
  • 1
    Not only sql injection, but also format issues: is 1/2/2020 really "1st of feb" or "jan 2nd"? – Hans Kesting Feb 17 '20 at 09:32