Reusing SQLCOmmand - revisited :)
With regards to SQL-Injection attacks, I wasn't too concerned as it was a windows service I was writing. However, the poke about performance got me thinking. Would it really be any quicker with parameters, when I was just calling a sproc a few times.
Kirk Allen Evans (Good book btw) had these points to make
Second, you will get better performance by using a parameterized SQL statement by replacing the literal values with question marks and using parameters in place of the literal values. The parameters can then be reused as well, where you simply set the values during each iteration.
Third, you can enhance performance a little more if your provider supports the .Prepared property for the Command object. The .Prepared property pre-compiles the SQL statement and caches it, avoiding parses for invaalid SQL statements.
So, I needed to convert this module to run in a more optimised way.
Sub Main()
Dim startTime As Date = Date.Now
Dim myConn As SqlConnection = New SqlConnection("server=xxx; user id=xxx; password=xxx; database=xxx")
myConn.Open()
Dim main_startDate As Date = Date.Today.AddDays(5)
Dim startDate As Date = Date.Today.AddDays(5)
Console.WriteLine("Today=" & startDate.ToString("s"))
Dim mySQL As New SqlCommand("exec tcHolidays.dbo.TCHolidays_Get_Hotel_Costs 2,0,1,'" & startDate.ToString("s") & "',3,'','','Boston',9999,0", myConn)
mySQL.ExecuteNonQuery()
Dim resorts(4)
resorts(0) = "New York"
resorts(1) = "Orlando"
resorts(2) = "Miami"
resorts(3) = "Las Vegas"
resorts(4) = "Boston"
Dim resort As String
For Each resort In resorts
While startDate < Date.Today.AddDays(10)
mySQL.CommandText = "exec tcHolidays.dbo.TCHolidays_Get_Hotel_Costs 2,0,1,'" & startDate.ToString("s") & "',7,'','','" & resort & "',9999,0"
mySQL.ExecuteNonQuery()
mySQL.CommandText = "exec tcHolidays.dbo.TCHolidays_Get_Hotel_Costs 2,0,1,'" & startDate.ToString("s") & "',10,'','','" & resort & "',9999,0"
mySQL.ExecuteNonQuery()
mySQL.CommandText = "exec tcHolidays.dbo.TCHolidays_Get_Hotel_Costs 2,0,1,'" & startDate.ToString("s") & "',14,'','','" & resort & "',9999,0"
mySQL.ExecuteNonQuery()
mySQL.CommandText = "Exec tcHolidays.dbo.TCHolidays_get_Flights '" & startDate.ToString("s") & "','" & resort & "'"
mySQL.ExecuteNonQuery()
startDate = startDate.AddDays(1)
Console.WriteLine("Done flights and Hotels for " & startDate.ToShortDateString() & " in " & resort & ".")
End While
startDate = main_startDate
Next
myConn.Close()
Dim endTime As Date = Date.Now
Dim timeSpan As TimeSpan = endTime.Subtract(startTime)
Console.WriteLine("Process took " & timeSpan.ToString() & ".")
Console.WriteLine("Press enter..")
Console.ReadLine()
End Sub
Doing it this way took 1.21.044 (m.s.ms)
To answer Kirks first point, I needed to parametise two stored procedures. I did this outside of the main loop, meaning I could reuse the parameters as suggested.
Dim mySQL As SqlCommand = New SqlCommand
With mySQL
.CommandText = "tcHolidays.dbo.TCHolidays_get_hotel_costs"
.CommandType = CommandType.StoredProcedure
.Connection = myConn
.Parameters.Add(New SqlParameter("@num_Adults", SqlDbType.Int))
.Parameters("@num_Adults").Value = 2
.Parameters.Add(New SqlParameter("@num_Children", SqlDbType.Int))
.Parameters("@num_Children").Value = 0
.Parameters.Add(New SqlParameter("@num_Rooms", SqlDbType.Int))
.Parameters("@num_Rooms").Value = 1
.Parameters.Add(New SqlParameter("@theDate", SqlDbType.VarChar, 20))
.Parameters.Add(New SqlParameter("@nights", SqlDbType.Int))
.Parameters.Add(New SqlParameter("@hcode", SqlDbType.VarChar, 30))
.Parameters("@hcode").Value = ""
.Parameters.Add(New SqlParameter("@country", SqlDbType.VarChar, 30))
.Parameters("@country").Value = ""
.Parameters.Add(New SqlParameter("@resort", SqlDbType.VarChar, 30))
.Parameters.Add(New SqlParameter("@maxPrice", SqlDbType.Int))
.Parameters("@maxPrice").Value = 9999
.Parameters.Add(New SqlParameter("@minPrice", SqlDbType.Int))
.Parameters("@minPrice").Value = 0
End With
Inside the main loop, I then simply change the values of those parameters which change inside the loops:
With mySQL
.Parameters("@theDate").Value = startDate.ToString("s")
.Parameters("@nights").Value = 7
.Parameters("@resort").Value = resort
.Prepare()
.ExecuteNonQuery()
End With
Because I was reusing the mySQL.Command - I needed another sqlCommand object for the last sproc, which obtains flights.
With myFlight_SQL
.Parameters("@theDate").Value = startDate.ToString("s")
.Parameters("@resort").Value = resort
.Prepare()
.ExecuteNonQuery()
End With
Now. Doing it this way took 1.28.088... 4 seconds slower.
So, I extended it to run over a longer period (30 days, rather than 5). The results were much the same.
With parameters: 8.52.9988944
Without - 8.10.13825644
Can anyone tell me where my extra 40 seconds is coming from?
The upshot is that doing it parametized is *much* cleaner code. I can hand this console to anyone and they'll be able to see instantly what it is doing, and why. Downside is that it seems to be slower but I think that the extra sqlCommand has somethign to do with that.