SQL transaction statement in vb.net - sql

I'm making a project that is linked to Microsoft SQLServer, used to enter, remove, edit data about customers and orders. The full system works, however I've been advised to use transactions rather than regular SQL statements to add/remove/edit data etc.
The trouble is, I've not used these before and from my research over the last few hours, I can't work out how to even begin them.
Can anybody advise me how to turn the following code into a transaction?
Public Shared Function SaveNewPerson(ByVal firstName As String, lastName As String, ByVal age As Integer, ByVal postcode As String, m_cn As OleDbConnection)
Dim Dc As New OleDbCommand
Dc.Connection = m_cn
m_cn.Open()
Dc.CommandText = "INSERT INTO tblPerson([firstName], [lastName], [age], [postcode]) VALUES('" & firstName & "', '" & lastName & "', '" & age & "', '" & postcode & "')"
Dc.ExecuteNonQuery()
Dim personID As Integer
Dc.CommandText = "SELECT ##IDENTITY"
Dc.CommandType = CommandType.Text
personID = CType(Dc.ExecuteScalar(), Integer)
m_cn.Close()
End Function

I've just been learning TSQL, see if this sort of code will work for you (note that you need to Dim tr (with a different variable name, if you like) and use it in multiple places, but unlike in some languages you don't need to set up objects for the different methods.
Public Shared Function SaveNewIncident(ByVal clientName As String, dateStart As Date, dateEnd As Date, ByVal incidentProblem As String, ByVal timeStart As String, ByVal timeEnd As String,
ByVal incidentSolved As Boolean, ByVal incidentSolution As String, _con As OleDbConnection)
Dim tr As OleDbTransaction = Nothing
Try
Dim Dc As New OleDbCommand
Dc.Connection = _con
tr = _con.BeginTransaction()
Dc.CommandType = CommandType.Text
Dc.CommandText = "INSERT INTO dbo.tblIncidents VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)"
Dc.Transaction = tr
Dc.Parameters.Add("#clientName", OleDbType.VarChar).Value = clientName
Dc.Parameters.Add("#dateStart", OleDbType.Date).Value = dateStart
Dc.Parameters.Add("#dateEnd", OleDbType.Date).Value = dateEnd
Dc.Parameters.Add("#incidentProblem", OleDbType.LongVarChar).Value = incidentProblem
Dc.Parameters.Add("#timeStart", OleDbType.VarChar).Value = timeStart
Dc.Parameters.Add("#timeEnd", OleDbType.VarChar).Value = timeEnd
Dc.Parameters.Add("#incidentSolved", OleDbType.Boolean).Value = incidentSolved
Dc.Parameters.Add("#incidentSolution", OleDbType.LongVarChar).Value = incidentSolution
Dim personID As Integer
Dc.CommandText = "SELECT SCOPE_IDENTITY() AS personID"
Dc.CommandType = CommandType.Text
personID = CType(Dc.ExecuteScalar(), Integer)
tr.Commit()
Catch ex As Exception
tr.Rollback()
Throw
End Try
End Function

Related

Return maximum ID number from a database

This is probably an easy thing to do, but I'm only doing my first vb project so I'm not sure how to do this 100%, so apologies in advanced if the following problem is actually very simple.
Basically, what I need to do is, when loading a database table into an ultragrid, I need to retrieve the maximum integer that is stored in a field.
To explain this more clearly, each record in the database has it's own ID number, so I need to iterate each record and find the one with the highest ID number, and return the ID, so that this can then be used in other calculations.
I know that I can use SQL = SELECT MAX(supportID) FROM tblIncidents for example to retrieve the highest ID number stored in this table.
So, how do I go about declaring the result of this (so, the highest ID number) as variable so that I can firstly display it in a messagebox to prove to me that the query has worked, and secondly so that I can use the variable as means of using the ID throughout my code?
An example; This is the code to save a new record into the tblIncidents table.
Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
Dim incidentSolved As Boolean = False
If cboxSolved.Checked Then
incidentSolved = True
End If
If txtClientSave.Text = "" Then
MsgBox("Client name cannot be blank")
ElseIf rtbProblem.Text = "" Then
MsgBox("Problem cannot be blank")
ElseIf cboxSolved.Checked = True And rtbSolution.Text = "" Then
MsgBox("Please enter solution")
Else
database.SaveNewIncident(txtClientSave.Text, dtpStart.Value, dtpEnd.Value, rtbProblem.Text, dtpStartTime.Value, dtpEndTime.Value, cboxSolved.Checked, rtbSolution.Text, _con)
txtClientSave.Text = ""
rtbProblem.Text = ""
rtbSolution.Text = ""
dtpStart.Value = Date.Today
dtpEnd.Value = Date.Today
dtpStartTime.Value = DateTime.Now
dtpEndTime.Value = DateTime.Now
cboxSolved.Checked = False
End If
End Sub
Database function that is called
Public Shared Function SaveNewIncident(ByVal clientName As String, dateStart As Date, dateEnd As Date, ByVal incidentProblem As String, ByVal timeStart As String, ByVal timeEnd As String,
ByVal incidentSolved As Boolean, ByVal incidentSolution As String, _Con As OleDbConnection)
Dim tr As OleDbTransaction = Nothing
Try
tr = _Con.BeginTransaction()
Dim Dc As New OleDbCommand
Dc.Connection = _Con
Dc.CommandType = CommandType.Text
Dc.CommandText = "INSERT INTO dbo.tblIncidents VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)"
Dc.Transaction = tr
Dc.Parameters.Add("#clientName", OleDbType.VarChar).Value = clientName
Dc.Parameters.Add("#dateStart", OleDbType.Date).Value = dateStart
Dc.Parameters.Add("#dateEnd", OleDbType.Date).Value = dateEnd
Dc.Parameters.Add("#incidentProblem", OleDbType.LongVarChar).Value = incidentProblem
Dc.Parameters.Add("#timeStart", OleDbType.VarChar).Value = timeStart
Dc.Parameters.Add("#timeEnd", OleDbType.VarChar).Value = timeEnd
Dc.Parameters.Add("#incidentSolved", OleDbType.Boolean).Value = incidentSolved
Dc.Parameters.Add("#incidentSolution", OleDbType.LongVarChar).Value = incidentSolution
Dc.ExecuteNonQuery()
tr.Commit()
MsgBox("Save successful")
Catch ex As Exception
mdInit.errorLog(ex.Message, ex.StackTrace)
MsgBox("Failed to save data, refer to error log")
tr.Rollback()
End Try
End Function
what you can do in VB.net is define a SQLcommand which will require a SQL connection attached to it - since your query returns a single value you will be able to execute it with ExecuteScalar to return a single value eg
Dim cmd AS New SqlClient.SQLCommand("SELECT MAX(billID) FROM tblRecurring",connectionString)
then
cmd.connection.open
and then
dim x as MaxId = cmd.executeScalar
and finish off with
cmd.connection.dispose
of course

Preventing SQL from injection attacks

I have a program that contains SQL queries, and it was pointed out to me yesterday that it was wide open to SQL injection attacks. After doing some research, I could see that to fix this, I needed to use parameters instead.
I have the following code... How do paramterise this?
Public Shared Function SaveNewPerson(ByVal firstName As String, lastName As String, ByVal age As Integer, ByVal postcode As String, m_cn As OleDbConnection)
Dim tr As OleDbTransaction = Nothing
Try
tr = m_cn.BeginTransaction()
Dim Dc As New OleDbCommand
Dc.Connection = m_cn
Dc.CommandText = "INSERT INTO tblPerson([firstName], [lastName], [age], [postcode]) VALUES('" & firstName & "', '" & lastName & "', '" & age & "', '" & postcode & "')"
Dc.Transaction = tr
Dc.ExecuteNonQuery()
Dim personID As Integer
Dc.CommandText = "SELECT SCOPE_IDENTITY() AS personID"
Dc.CommandType = CommandType.Text
personID = CType(Dc.ExecuteScalar(), Integer)
tr.Commit()
Catch ex As Exception
tr.Rollback()
Throw
End Try
End Function
I would make a stored procedure first to insert into the sql server and then use
dc.commandText = "Your stored procedure name"
dc.commandType = CommandType.StoredProcedure
Dim myParam as oledb.OleDbParameter = dc.parameters.add("#personID", oledbtype.int)
myParam.Direction = ParameterDirection.ReturnValue
dc.Parameters.Add("#firstName", OleDbType.VarChar).Value = [firstname]
....
....
Dim returnId as Integer = Cint(dc.Parameters("#personID").Value)
Dc.CommandText = "INSERT INTO tblPerson([firstName], [lastName], [age], [postcode]) VALUES('" & firstName & "', '" & lastName & "', '" & age & "', '" & postcode & "')"
Change this to
Dc.CommandText = "INSERT INTO tblPerson([firstName], [lastName], [age], [postcode]) VALUES(?, ?, ?, ?)"
Dc.Parameters.Add("#first", OleDbType.VarChar, firstName)
Dc.Parameters.Add("#last", OleDbType.VarChar, lastName)
Dc.Parameters.Add("#age", OleDbType.Integer, age)
Dc.Parameters.Add("#postcode", OleDbType.VarChar, postcode )
(Check the right OldDbType value is passed.)
NB. the order in the parameters collection determines which parameter matches which ? placeholder. The names given to the parameters are (it seems) ignored.

Update sqlite query not working in Visual Basic 2013

I am trying to insert row and update row (if row already exists), as following -
Dim con As SQLiteConnection
Dim sql As String
Dim cmd As New SQLiteCommand
Dim da As SQLiteDataAdapter
Dim ds As New DataSet
Dim NumberOfRows As Integer
Public Sub UpdateUserStatistics(ByVal UserId As Integer, ByVal QId As Integer, _
ByVal KeyName As String, ByVal KeyValue As Integer)
con = New SQLiteConnection("Data Source = " + AppDomain.CurrentDomain.BaseDirectory + "/user_statistics.db;Version=3;")
con.Open()
sql = "SELECT * FROM med_user_meta where user_id=" & UserId & " And qid=" & QId
da = New SQLiteDataAdapter(sql, con)
da.Fill(ds, "UserMeta")
NumberOfRows = ds.Tables("UserMeta").Rows.Count
ds.Clear()
If (NumberOfRows = 0) Then
Dim InsertQuery As SQLiteCommand = con.CreateCommand
InsertQuery.CommandText = "INSERT INTO med_user_meta " _
& "(user_id, qid, " _
& KeyName _
& ", timestamp) VALUES(?, ?, ?, datetime('now'))"
InsertQuery.Parameters.AddWithValue("user_id", UserId)
InsertQuery.Parameters.AddWithValue("qid", QId)
InsertQuery.Parameters.AddWithValue(KeyName, KeyValue)
RowInserted = InsertQuery.ExecuteNonQuery()
ElseIf (NumberOfRows = 1) Then
Dim UpdateQuery As SQLiteCommand = con.CreateCommand
UpdateQuery.CommandText = "UPDATE med_user_meta SET " _
& KeyName _
& " = ?, timestamp = datetime('now') Where qid = ?"
'UpdateQuery.Parameters.AddWithValue("user_id", UserId)
UpdateQuery.Parameters.AddWithValue("qid", QId)
UpdateQuery.Parameters.AddWithValue(KeyName, KeyValue)
RowUpdated = UpdateQuery.ExecuteNonQuery()
End If
con.Close()
End Sub
I am using this code in a module and calling UpdateUserStatistics wherever required
My problem is that, insert query works fine, but update query does Not work.
It's been more than 2 hours, but I couldn't find, where am I making mistake?
Also, is there any way to get the final update query into message box after adding parameters, so that I can check if my final update query is correct?
Edit - 1
I forgot to mention, value of RowUpdated is returning 0, i.e. ExecuteNonQuery is Not updating the row.
I have also confirmed that row corresponding to update query Do exists, and the ElseIf (NumberOfRows = 1) Then block is running too, but it's Not updating the row.

Quickest Way to Add New Rows to Datatable That Could Contain Duplicates

I have a table full of stock price data. Each row has a unique combination of Ticker symbols and dates. I load new data all the time by obtaining CSV files containing stock price data for everyday for every ticker. I know that there are duplicates in the CSV files. I only want to add that data that is not already in my data table. What is the quickest way to do this?
Should I try to add every row and catch each exception? Or, should I compare each row against my data table by reading my data table to see that line already exists? Or, is there another alternative?
Additional Info
This is what I have been doing. For each line in the CSV file I read my data table to see if it already exists.
Dim strURL As String
Dim strBuffer As String
strURL = "http://ichart.yahoo.com/table.csv?s=" & tickerValue
strBuffer = RequestWebData(strURL)
Dim sReader As New StringReader(strBuffer)
Dim List As New List(Of String)
Do While sReader.Peek >= 0
List.Add(sReader.ReadLine)
Loop
List.RemoveAt(0)
Dim lines As String() = List.ToArray
sReader.Close()
For Each line In lines
Dim checkDate = line.Split(",")(0).Trim()
Dim dr As OleDbDataReader
Dim cmd2 As New OleDb.OleDbCommand("SELECT * FROM " & tblName & " WHERE Ticker = ? AND [Date] = ?", con)
cmd2.Parameters.AddWithValue("?", tickerValue)
cmd2.Parameters.AddWithValue("?", checkDate)
dr = cmd2.ExecuteReader
If dr.Read() = 0 Then
Dim cmd3 As OleDbCommand = New OleDbCommand("INSERT INTO " & tblName & " (Ticker, [Date], [Open], High, Low, [Close], Volume, Adj_Close) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", con)
cmd3.Parameters.Add("#Ticker", OleDbType.VarChar).Value = tickerValue
cmd3.Parameters.Add("#[Date]", OleDbType.VarChar).Value = checkDate
cmd3.Parameters.Add("#[Open]", OleDbType.VarChar).Value = line.Split(",")(1).Trim
cmd3.Parameters.Add("#High", OleDbType.VarChar).Value = line.Split(",")(2).Trim
cmd3.Parameters.Add("#Low", OleDbType.VarChar).Value = line.Split(",")(3).Trim
cmd3.Parameters.Add("#[Close]", OleDbType.VarChar).Value = line.Split(",")(4).Trim
cmd3.Parameters.Add("#Volume", OleDbType.VarChar).Value = line.Split(",")(5).Trim
cmd3.Parameters.Add("#Adj_Close", OleDbType.VarChar).Value = line.Split(",")(6).Trim
cmd3.ExecuteNonQuery()
Else
End If
This is what I have switched to and it gives this exception: The changes you requested to the table were not successful because they would create duplicate values in the index, primary key, or relationship. Change the data in the field or fields that contain duplicate data, remove the index, or redefine the index to permit duplicate entries and try again. I could catch this exception every time and ignore it until I hit a line that is new.
Dim strURL As String = "http://ichart.yahoo.com/table.csv?s=" & tickerValue
Debug.WriteLine(strURL)
Dim strBuffer As String = RequestWebData(strURL)
Using streamReader = New StringReader(strBuffer)
Using reader = New CsvReader(streamReader)
reader.ReadHeaderRecord()
While reader.HasMoreRecords
Dim dataRecord As DataRecord = reader.ReadDataRecord()
Dim cmd3 As OleDbCommand = New OleDbCommand("INSERT INTO " & tblName & " (Ticker, [Date], [Open], High, Low, [Close], Volume, Adj_Close) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", con)
cmd3.Parameters.Add("#Ticker", OleDbType.VarChar).Value = tickerValue
cmd3.Parameters.Add("#[Date]", OleDbType.VarChar).Value = dataRecord.Item("Date")
cmd3.Parameters.Add("#[Open]", OleDbType.VarChar).Value = dataRecord.Item("Open")
cmd3.Parameters.Add("#High", OleDbType.VarChar).Value = dataRecord.Item("High")
cmd3.Parameters.Add("#Low", OleDbType.VarChar).Value = dataRecord.Item("Low")
cmd3.Parameters.Add("#[Close]", OleDbType.VarChar).Value = dataRecord.Item("Close")
cmd3.Parameters.Add("#Volume", OleDbType.VarChar).Value = dataRecord.Item("Volume")
cmd3.Parameters.Add("#Adj_Close", OleDbType.VarChar).Value = dataRecord.Item("Adj Close")
cmd3.ExecuteNonQuery()
End While
End Using
End Using
I just want to use the most efficient method.
Update
Per the answers below, this is the code I have so far:
Dim strURL As String = "http://ichart.yahoo.com/table.csv?s=" & tickerValue
Dim strBuffer As String = RequestWebData(strURL)
Using streamReader = New StringReader(strBuffer)
Using reader = New CsvReader(streamReader)
' the CSV file has a header record, so we read that first
reader.ReadHeaderRecord()
While reader.HasMoreRecords
Dim dataRecord As DataRecord = reader.ReadDataRecord()
Dim cmd3 As OleDbCommand = New OleDbCommand("INSERT INTO " & tblName & "(Ticker, [Date], [Open], High, Low, [Close], Volume, Adj_Close) " & "SELECT ?, ?, ?, ?, ?, ?, ?, ? " & "FROM DUAL " & "WHERE NOT EXISTS (SELECT 1 FROM " & tblName & " WHERE Ticker = ? AND [Date] = ?)", con)
cmd3.Parameters.Add("#Ticker", OleDbType.VarChar).Value = tickerValue
cmd3.Parameters.Add("#[Date]", OleDbType.VarChar).Value = dataRecord.Item("Date")
cmd3.Parameters.Add("#[Open]", OleDbType.VarChar).Value = dataRecord.Item("Open")
cmd3.Parameters.Add("#High", OleDbType.VarChar).Value = dataRecord.Item("High")
cmd3.Parameters.Add("#Low", OleDbType.VarChar).Value = dataRecord.Item("Low")
cmd3.Parameters.Add("#[Close]", OleDbType.VarChar).Value = dataRecord.Item("Close")
cmd3.Parameters.Add("#Volume", OleDbType.VarChar).Value = dataRecord.Item("Volume")
cmd3.Parameters.Add("#Adj_Close", OleDbType.VarChar).Value = dataRecord.Item("Adj Close")
cmd3.Parameters.Add("#Ticker", OleDbType.VarChar).Value = tickerValue
cmd3.Parameters.Add("#[Date]", OleDbType.VarChar).Value = dataRecord.Item("Date")
cmd3.ExecuteNonQuery()
End While
End Using
End Using
It gives me this error Data type mismatch in criteria expression.
Most DBMS support a (non-standard) clause for the INSERT command to ignore duplicates, e.g.:
MySQL: INSERT IGNORE INTO ...
SQLite: INSERT OR IGNORE INTO INTO ...
This is the quickest way in non-batch mode, as you don't have to read the database before you write.
You can do the same with standard SQL using:
INSERT INTO ...
SELECT <your values>
WHERE NOT EXISTS ( <query for your values by id> );
Or (when you explicitly need a FROM clause):
INSERT INTO ...
SELECT <your values>
FROM DUAL
WHERE NOT EXISTS ( <query for your values by id> );
EDIT
MS Access does not have a built-in DUAL table (i.e., a table that always contains just one single row), but Access requires a FROM clause. So you have to build your own DUAL table:
CREATE TABLE DUAL (DUMMY INTEGER);
INSERT INTO DUAL VALUES (1);
You just do this once and for all. Then, in your code you would do inserts like
INSERT INTO MyTable (A,B,C,D)
SELECT 123, 456, 'Hello', 'World'
FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM MyTable WHERE A = 123 AND B = 456);
Thus, for your example, use:
Dim cmd3 As OleDbCommand = New OleDbCommand(_
"INSERT INTO " & tblName & _
"(Ticker, [Date], [Open], High, Low, [Close], Volume, Adj_Close) " & _
"SELECT ?, ?, ?, ?, ?, ?, ?, ? " & _
"FROM DUAL " & _
"WHERE NOT EXISTS (SELECT 1 FROM tblName WHERE Ticker = ? AND [Date] = ? AND ...)", con)
(WHERE clause depending on your key columns)

What is wrong with my vb.net webmethod?

This webmethod retrieves plist, firstaname, lastname, orgid from database then insert 2 lines in 2 different tables. 1st sql is fine - 2nd doesnt run
<WebMethod()> _
Public Function Register(ByVal meetingid As String, ByVal myid As String, ByVal PartType As Integer, ByVal startDate As String) As String
Dim connection As New SqlConnection(ConfigurationManager.ConnectionStrings("EPBconnection").ConnectionString)
Dim sSQL As String
Dim plistid As String = ""
Dim LastName As String = ""
Dim FirstName As String = ""
Dim orgID As String = ""
'get plistID
sSQL = "select pl.PLIST_ID"
sSQL = sSQL + " From PERSON_LIST pl"
sSQL = sSQL + " Where MTG_ID = '" + meetingid + "'"
Dim myCommand As New SqlCommand(sSQL, connection)
connection.Open()
Dim myReader As SqlDataReader = myCommand.ExecuteReader
While myReader.Read()
plistid = myReader("PLIST_ID").ToString()
End While
connection.Close()
'get firstname, lastname, orgid
sSQL = "SELECT p.LASTNAME, p.FIRSTNAME, p.ORGA_ID FROM PERSON p WHERE PERSON_ID = '" + myid + "'"
myCommand = New SqlCommand(sSQL, connection)
connection.Open()
myReader = myCommand.ExecuteReader
While myReader.Read()
LastName = myReader("LASTNAME").ToString()
FirstName = myReader("FIRSTNAME").ToString()
orgID = myReader("ORGA_ID").ToString()
End While
connection.Close()
Return "You are registered for this meeting"
End Function
When i remove the following it returns the string but otherwise it doesnt (compiles without errors either way):
While myReader.Read()
LastName = myReader("LASTNAME").ToString()
FirstName = myReader("FIRSTNAME").ToString()
orgID = myReader("ORGA_ID").ToString()
End While
The problem seems to be coming from myReader.Read()??? I dont understand why
orgID = myReader("ORGA_ID").ToString()
You have defined orgId as Integer;
Dim orgID As Integer
Sorry every1 the problem was solved by itself over night. Still dont know what the problem was. LOL