Is there a way to run a for loop query based on datagridview selected values in vb.net? - vb.net

Unable to get the excepted result due to the following error
Conversion from string " to type 'integer' is not valid'
I have been able to load values from the products table, add selected ones to Selected Products and then search all the selected products against the Customers table to find out how many customers ordered these products.
Try
Dim ListOfDiag As StringBuilder = New StringBuilder()
For Each row As DataGridViewRow In SelectedDiagDGV.Rows
ListOfDiag.Append(row.Cells(0).Value.ToString & "", "" & Environment.NewLine)
Next
Dim query As String = String.Empty
Dim SegmentConnectionString As String = "Data Source=Test-PC;Initial Catalog=TestDB;Integrated Security=True"
query = "SELECT Customers, ProductName from Customers WHERE ProductName in (" & ListOfDiag.ToString & ")"
Dim dTable As DataTable = New DataTable()
Dim dAdapter As SqlDataAdapter
dAdapter = New SqlDataAdapter(query, SegmentConnectionString)
dAdapter.Fill(dTable)
DataGridView1.DataSource = dTable
'Next
Catch ex As System. Exception
MsgBox(ex.Message.ToString)
End Try
Unable to perform a for loop search. Some of the values contain special characters example: Soft ’Drink’; Computer (Desk).
Error: Conversion from string " to type 'Integer' is not valid.

ListOfDiag.Append(row.Cells(0).Value.ToString & "", "" & Environment.NewLine)
There is no overload of StringBuilder.Append that takes (String, String) as arguments. the first string is row.Cells(0).Value.ToString & "" and then there is a comma between parameters and the second string is "" & Environment.NewLine Remember that "" is an empty string, not escape characters. Not sure what your intention was but this will not work.
You had the right approach; to build a string for the In clause. I used a List(Of String) to get the data from the rows then after the loop I used a .Join with a comma separator to get the value for the In clause.
I passed the connection string directly to the constructor of the Connection and passed the Select statement and the connection to the constructor of the Command. For the Select statement I used and Interpolated String (the string preceded by the $) You could also use String.Format in older version of Visual Studio.
The Using...End Using blocks ensure that your database objects are closed and disposed even if there is an error.
I think the only special character that could mess things up would be the presence of a comma in a Product Name.
Private Sub OPCode()
Dim dTable As New DataTable
Dim ListOfDiag As New List(Of String)
For Each row As DataGridViewRow In SelectedDiagDGV.Rows
ListOfDiag.Add(row.Cells(0).Value.ToString)
Next
Dim InData = String.Join(",", ListOfDiag)
Using cn As New SqlConnection("Data Source=Test-PC;Initial Catalog=TestDB;Integrated Security=True")
Using cmd As New SqlCommand($"SELECT Customers, ProductName from Customers WHERE ProductName in ({InData})", cn)
cn.Open()
dTable.Load(cmd.ExecuteReader)
End Using
End Using
DataGridView1.DataSource = dTable
End Sub

Related

How to concatenate single quote in MySQL query with VB.NET parameter?

I am making a MySQL Select query using MySQLCommand object in VB.NET were I use parameters. I am facing an issue, in my where clause, I need to put the value for the criteria into single quote, I tried to use backslash ' (\') to escape the single quote, it does not work. I used double quotes as well, same issue. Can somebody help me? Is there something specific I need to do when using the MySQLCommand object in VB.NET with parameter and want my parameter value to be in single quote into a query?
Here is the Function in which I make the MySQL query:
Public Shared Function getGeographyUnits(critere As String, valeur As String) As List(Of geography_unit)
Dim conn As MySqlConnection = DBUtils.GetDBConnection()
Dim rdr As MySqlDataReader
conn.Open()
Dim cmd As MySqlCommand = New MySqlCommand("select ID,description from geography_unit where #critere = ''#valeur''", conn)
cmd.Parameters.AddWithValue("#critere", critere)
cmd.Parameters.AddWithValue("#valeur", valeur)
rdr = cmd.ExecuteReader()
Dim geography_units As New List(Of geography_unit)
While rdr.Read
Dim geography_unit As New geography_unit
Try
geography_unit.ID = CLng(rdr("Id"))
geography_unit.description = rdr("description")
Catch ex As Exception
End Try
geography_units.Add(geography_unit)
End While
rdr.Close()
conn.Close()
Return geography_units
End Function
Actually, I want the cmdText for my query to be something like this after rendering:
select ID,description from geography_unit where critere = 'valeur'
The issue comes mainly from the fact that I am using parameter, how can I solve it?
You need to fix your code with something like this. But please note a couple of things.
If the #valeur is enclosed in single quotes it is no more a parameter placeholder but a string constant and the parameter associated with the placeholder will not be used.
The connection should always enclosed in a using statement to avoid dangerous resources consuption on the server
If you want to have a variable list of field to which apply the valeur passed then you need to be absolutely sure that your user is not allowed to type the value for critere. You should provide some kind of control like combobox or dropdwonlist where the user could only choose between a prefixed set of values, then you can concatenate the critere variable to your sql command.
Public Shared Function getGeographyUnits(critere As String, valeur As String) As List(Of geography_unit)
Using conn As MySqlConnection = DBUtils.GetDBConnection()
Dim sqlText As String = "select ID,description from geography_unit"
conn.Open()
If Not String.IsNullOrEmpty(critere) Then
sqlText = sqlText & " where " & critere & " = #valeur"
End If
Dim cmd As MySqlCommand = New MySqlCommand(sqlText, conn)
cmd.Parameters.Add("#valeur", MySqlDbType.VarChar).Value = valeur
Using rdr As MySqlDataReader = cmd.ExecuteReader()
Dim geography_units As New List(Of geography_unit)
While rdr.Read
Dim geography_unit As New geography_unit
Try
geography_unit.ID = CLng(rdr("Id"))
geography_unit.description = rdr("description")
Catch ex As Exception
End Try
geography_units.Add(geography_unit)
End While
End Using
' rdr.Close() not needed when inside using
' conn.Close() not needed when inside using
Return geography_units
End Using
End Function
Also worth of note is the point in which I have used the Add method to add the parameter to the collection. The AddWithValue, while convenient, is the cause of a lot of bugs because it defines the type of the parameter looking at the argument received. This could end very badly when you pass dates or decimal numbers directly from a string.
Quite simply, as valeur is a string then your query needs to be as follows
"select ID,description from geography_unit where critere = '" & valeur & "'"
If valeur was numeric then the format should be as follows
"select ID,description from geography_unit where critere = " & valeur
Note the difference where single quotes are included within double quotes around the variable when it is a string.

Issue Comparing Access Column

Can someone please explain to me when the "size" column wont work for comparison but I can replace the ID column in my code and it works perfectly fine. Perhaps my formatting of the access database column for Size isnt correct?
I am basically just trying to see if the key and value in my dictionary match the conditions in the access database and if so to write one text, if not write another. The error I keep getting when I have size in my code is:
An unhandled exception of type 'System.Data.OleDb.OleDbException' occurred in System.Data.dll IErrorInfo.GetDescription failed with E_FAIL(0x80004005).
For Each KeyPair In dict
Dim key As String
Dim value As Integer
key = KeyPair.Key
value = KeyPair.Value
Dim sqlQry As String
sqlQry = "SELECT Item, Size FROM [Table] WHERE Item = '" & key & "'AND Size>" & value & " "
Console.WriteLine(sqlQry)
Dim topDecision As String
Dim cmd As OleDbCommand
cmd = New OleDbCommand(sqlQry, myconnection)
Dim myreader As OleDbDataReader
myreader = cmd.ExecuteReader()
If myreader.Read() Then
topDecision = "Order"
Else
topDecision = "Dont"
End If
myreader.Close()
Next
Connections and some other database objects provided by ADO.net use unmanaged code internally. They provide a .Dispose method where they release these resources. It is up to the coder to call the .Dispose method. Fortunately, .net provides Using...End Using blocks that handle this for us. Connections, commands and readers should be declared in the method where they are used so they can be properly closed and disposed.
Don't concatenate strings to build sql queries. Use parameters to avoid sql injection. We only need a single command and a single ParametersCollection. Only the values of the parameters change inside the loop.
A special note for OleDb parameters. The names of the parameters are ignored. The position of the parameter in the sql query should match the order that they are added to the ParametersCollection.
Declare KeyPair As KeyValuePair so you can access .Key and .Value properties.
I used a StringBuilder to collect the messages from your code. A StringBuilder is mutable (changeable) whereas a String is not. If I used a String the compiler would have to throw away a string and create a new one on each iteration. The garbage collector would be kept busy.
I used an interpolated string indicated by the $ before the string. It allows us to insert variables directly into the string if they are surrounded by braces { }.
If you follow this sample, be sure the text box at the end has Multiline = True.
Private Sub OPCode()
Dim sqlQry = "SELECT Item, Size FROM [Table] WHERE Item = #Key AND Size > #Value;"
Dim sb As New StringBuilder
Using cn As New OleDbConnection("Your connection string"),
cmd As New OleDbCommand(sqlQry, cn)
cmd.Parameters.Add("#Key", OleDbType.VarChar, 100)
cmd.Parameters.Add("#Value", OleDbType.Integer)
cn.Open()
For Each KeyPair As KeyValuePair(Of String, Integer) In dict
cmd.Parameters("#Key").Value = KeyPair.Key
cmd.Parameters("#Value").Value = KeyPair.Value
Dim topDecision As String
Using myreader = cmd.ExecuteReader()
If myreader.Read() Then
topDecision = "Order"
Else
topDecision = "Dont Order"
End If
End Using
sb.AppendLine($"{KeyPair.Key} - {topDecision}")
Next
End Using
TextBox1.Text = sb.ToString
End Sub

Performance issue with SQLite database with VB.NET

I am Inserting the data-table into SQLite Database. I am doing like this.
First I Fetch the data with getdata function and insert it into datatable, then with For Each Loop i made the Insert Command and Execute It. I am having 50000 Records it will take 30 Minutes to run.
Please Guide the suitable approach. Here is the Code.
Dim xtable As DataTable = getdata("select * from tablename")
Dim str As String = Nothing
For Each r As DataRow In xtable.Rows ''''HERE IT WILL TAKE TOO MUCH TIME
str = str & ("insert into tablename values(" & r.Item("srno") & "," & r.Item("name"));")
Next
EXECUTEcmd(str)
Public Function getdata(ByVal Query As String) As DataTable
connectionString()
Try
Dim mds As New DataTable
Dim mycommand As New SQLiteCommand(DBConn)
mycommand.CommandText = Query
Dim reader As SQLiteDataReader = mycommand.ExecuteReader()
mds.Load(reader)
Return mds
Catch ex As Exception
MsgBox("DB Error", vbCritical, "")
MsgBox(Err.Description)
Return Nothing
End Try
End Function
Public Sub EXECUTEcmd(ByVal selectcmd As String)
Using cn = New SQLiteConnection(conectionString)
cn.Open()
Using transaction = cn.BeginTransaction()
Using cmd = cn.CreateCommand()
cmd.CommandText = selectcmd
cmd.ExecuteNonQuery()
End Using
transaction.Commit()
End Using
cn.Close()
End Using
End Sub
here the Conncection String is:
conStr = "Data Source=" & dbpath & ";Version=3;Compress=True; UTF8Encoding=True; PRAGMA journal_mode=WAL; cache=shared;"
Use a stringbuilder to build your string, not string concatenation
Dim strB As StringBuilder = New StringBuilder(100 * 50000)
For Each r As DataRow In xtable.Rows
strB.AppendLine($"insert into tablename values({r.Item("srno")},{r.Item("name")});")
Next
Strings cannot be changed in .net. Every time you make a new string VB has to copy everything out of the old string into a new one and add the new bit you want. If each of your insert statements is 100 bytes, that means it copies 100 bytes, then adds 100, then copies 200 bytes and adds 100, then copies 300 bytes, then 400 bytes, then 500 bytes. By the time it has done 10 strings it has made 5.5 kilobytes of copying. By the time it's done 50 thousand strings it has copied 125 gigabytes of data. No wonder it's slow!
Always use a StringBuilder to build massive strings
--
I'm willing to overlook the sql injection hacking nag for this one, because of the nature of the task, but please read http://bobby-tables.com - you should never, ever concatenate values into an SQL as a way of making an sql that has some varying effect.
This entire exercise would be better done as this (pseudocode) kind of thing:
Dim sel as New SQLiteCommand("SELECT a, b FROM table", conn)
Dim ins as New SQLiteCommand("INSERT INTO table VALUES(:a, :b)", conn)
ins.Parameters.Add("a" ...)
ins.Parameters.Add("b" ...)
Dim r = sel.ExecuteReader()
While r.Read()
ins.Parameters("a") = r.GetString(0)
ins.Parameters("b") = r.GetString(1)
ins.ExecuteNonQuery()
End While
That is to say, you minimize your memory by reading rows one at a time out of ther edaer and inserting them one at a time in the insert; the insert command is prepared once, you just change the parameter values, execute it, change them again, execute it ... It's what parameterized queries were designed for (as well as stopping your app getting hacked when someone puts SQL in your variable, or even just stopping it crashing when you have an person named O'Grady
Maybe you must refactor your code like this:
Dim xtable As DataTable = getdata("select * from tablename")
Using cn = New SQLiteConnection(conectionString)
cn.Open()
Using transaction = cn.BeginTransaction()
Try
Using cmd = cn.CreateCommand()
cmd.Transaction = transaction
For Each r As DataRow In xtable.Rows ''''HERE IT WILL TAKE TOO MUCH TIME
cmd.CommandText = "insert into tablename values(" & r.Item("srno") & "," & r.Item("name") & ")"
cmd.ExecuteNonQuery()
Next
End Using
transaction.Commit()
Catch ex As Exception
transaction.Rollback()
End Try
End Using
End Using
Public Function getdata(ByVal Query As String) As DataTable
connectionString()
Try
Dim mds As New DataTable
Dim mycommand As New SQLiteCommand(DBConn)
mycommand.CommandText = Query
Dim reader As SQLiteDataReader = mycommand.ExecuteReader()
mds.Load(reader)
Return mds
Catch ex As Exception
MsgBox("DB Error", vbCritical, "")
MsgBox(Err.Description)
Return Nothing
End Try
End Function
Instead of concatenate an possible giant string, wrap all your inserts into a single transaction, like above. This will reduce the memory used and also make sqlite perform faster.

Converting VBA function to VB.net to get sql data

I am trying to convert VBA code into VB.net and I have made it to a point but I can't convert resultset into vb.net. RS was 'dim as resultset' in VBA, thought i could just change it to dataset but am getting errors with the '.fields' and other options?
Function GetG(sDB As String, sServ As String, sJob As String) As String
'sDB = Database name, sServ = Server\Instance, path = job.path
Dim conString As String = ("driver={SQL Server};server = " &
TextBox1.Text & " ; uid = username;pwd=password:database = " &
TextBox2.Text)
Dim RS As DataSet
Dim conn As SqlConnection = New SqlConnection(conString)
Dim cmd As SqlCommand
conn.Open()
'This is where my problems are occuring
cmd = New SqlCommand("SELECT [ID],[Name] FROM dbo.PropertyTypes")
Do While Not RS.Tables(0).Rows.Count = 0
If RS.Fields(1).Value = sJob Then
GetG = RS.Fields(0).Value
GetG = Mid(GetG, 2, 36)
Exit Do
End If
DataSet.MoveNext
Loop
conn.Close
End Function
Based on my understanding and some guesswork, here is what I came up with for what I think you're wanting.
As I stated in my comment above, it appears you can just use a WHERE clause to get the exact record you want (assuming a single instance of sJob appears in the name column).
Build the connectionstring off the input arguments, not controls on your form. That is after all why you allow for arguments to be passed along. Also note that there is a SqlCommandBuilder object that may be of interest. But for now
Function GetG(sDB As String, sServ As String, sJob As String) As String
'we'll pretend your connectionstring is correct based off of the sDB and sServ arguments
Dim conStr As String = ("driver={SQL Server};server = " & sServ & " ; uid = username;pwd=password:database = " & sDB)
'Create a connection and pass it your conStr
Using con As New SqlConnection(conStr)
con.Open() 'open the connection
'create your sql statement and add the WHERE clause with a parameter for the input argument 'sJob'
Dim sql As String = "SELECT [ID], [Name] FROM dbo.PropertyTypes WHERE [Name] = #job"
'create the sqlCommand (cmd) and pass it your sql statement and connection
Using cmd As New SqlCommand(sql, con)
'add a parameter so the command knows what #job holds
cmd.Parameters.Add(New SqlParameter("#job", SqlDbType.VarChar)).Value = sJob
'Now that have the command built, we can pass it to a reader object
Using rdr As SqlDataReader = cmd.ExecuteReader
rdr.Read()
'i admin i'm a little confused here on what you are
'trying to achieve so ID may not be what you are
'really wanting to get a substring of.
Return rdr("ID").ToString.Substring(2, 36)
End Using
End Using
End Using
End Function
An example to see if this is working could be to call a messagebox do display the result. For this example, I'm going to pretend that TextBox3 holds the sJob you're wanting. With that knowledge, you could simply do:
MessageBox.Show(GetG(TextBox2.Text, TextBox1.Text, TextBox3.Text))
This should then produce the result in a messagebox.
It seems that you're not filling your DataSet. So, when you try to loop through it, it's uninitialized or empty.
Check this answer to see an example: Get Dataset from DataBase

Simple way to convert dbNull to a string in VB.NET

I'm looking for a simpler way to check if a value is dbNull and to convert it to an empty string if so.
An example of a situation where I need this would be:
Dim dt As New DataTable
Dim conn As New OleDbConnection(someConnStr)
Dim adap As New OleDbDataAdapter(qryCSSInfo, cssConn)
adap.Fill(dt)
Dim someStr As String = "The first column of the first row returned: " & dt.rows(0).item(0)
Msgbox(someStr)
The problem is that if dt.rows(0).item(0) is null in the database it will be returned as a dbNull value, which can apparently not be appended to a string.
My solution to this problem has been using if statements to replace the value with blank strings:
Dim dt As New DataTable
Dim conn As New OleDbConnection(someConnStr)
Dim adap As New OleDbDataAdapter(qryCSSInfo, cssConn)
adap.Fill(dt)
If Not isDBNull(dt.rows(0).item(0)) then
Dim someStr As String = "The first column of the first row returned: " & dt.rows(0).item(0)
Else
Dim someStr As String = "The first column of the first row returned: " & ""
End If
Msgbox(someStr)
This works fine for my purposes, but it gets overwhelming if I have to make this check for every column I need to use in the table. Say I had 10 columns from the table that I wanted to display with this string. I'd have to make this check on each one to ensure they weren't null. Is there an easier or simpler way of doing so?
For string types you can directly use it this way dt.rows(0).item(0).ToString(), without the If condition
adap.Fill(dt)
Dim someStr As String = "The first column of the first row returned: " & dt.rows(0).item(0).ToString()
MsgBox(somestr)
i.e. you can completely omit the if statement. As per MSDN any DBNull value will be converted to EmptyString with .ToString()
Also check this SO post Conversion from type 'DBNull' to type 'String'
However, for non-string database column types such as integers, doubles you must apply checks using IsDBNull to avoid any exceptions.
You can leverage the If Operator to reduce a few lines of code:
Dim someStr As String = "The first column of the first row returned: " & _
If(dt.rows(0).item(0) Is DbNull.Value, String.Empty, dt.rows(0).item(0))
You should be able to concatenate a null field with a string - it should convert to an empty string. That said row.IsNull(index) is a good test to use.
SQL = "Select top 10 Region, CompanyName FROM Suppliers"
Dim dt As DataTable = Gen.GetDataTable(SQL, scon)
For Each row As DataRow In dt.Rows
MsgBox(row("companyName") & " region: " & row("Region")) ' null allowed
If row.IsNull("region") Then ' .Net test for Null
MsgBox(row("companyName") & " region is null")
Else
'continue
End If
Next
You can also resolve this in the query - covert nulls to useful (or empty) strings. The example query is from SQL Server, I don't know if your DB supports COALESCE.
MsgBox("COALESCE") ' SQL Server - may not be the same in ODBC databases
SQL = "Select top 10 COALESCE(Region,'na') Region, CompanyName FROM Suppliers"
dt = Gen.GetDataTable(SQL, scon)
For Each row As DataRow In dt.Rows
MsgBox(row("companyName") & " region: " & row("Region"))
Next
Some coding notes:
Dim dt As New DataTable
Dim conn As New OleDbConnection(someConnStr)
Dim adap As New OleDbDataAdapter(qryCSSInfo, cssConn)
adap.Fill(dt)
If Not IsDBNull(dt.Rows(0).Item(0)) Then ' in OP
'...
End If
' save some typing if you know there will be only one record
' will throw exception is no rows are returned, check for expected count
Dim row As DataRow = dt.Rows(0)
If Not IsDBNull(row(0)) Then
'...
End If
' or
If Not row.IsNull(0) Then
'...
End If
' note the fields can be accessed by name so you can avoid hard coding field position
If Not row.IsNull("FieldName") Then
'...
End If
The simplest way to do it is just add a "" after the field or string.
Eg.:
dim EmptyString as string = Nullfield() & ""
if EmptyString = ""
' in the sample, it should.
end if
So, in your code you can use:
If dt.rows(0).item(0) & "" = "" then
' it should be...
end if
I got some null data into cells of a datagrid; to correctly retrieve that data
I concatenate the "" string to the cell value:
Dim readVal As String = "" & row.Cells(2).Value