I have this code into a function:
Adp.SelectCommand = New SqlCommand()
Adp.SelectCommand.Connection = oConn
Adp.SelectCommand.CommandText = sp
Adp.SelectCommand.CommandType = CommandType.StoredProcedure
SqlCommandBuilder.DeriveParameters(Adp.SelectCommand)
table = New DataTable
Dim resultado = 0
Dim inputParamList As New List(Of SqlParameter)
For Each param As SqlParameter In Adp.SelectCommand.Parameters
If param.Direction = Data.ParameterDirection.Input OrElse _
param.Direction = Data.ParameterDirection.InputOutput Then
inputParamList.Add(param)
End If
Next
For Each parame As SqlParameter In inputParamList
Dim metodo() As String
Dim paramName As String = parame.ParameterName.ToString()
Dim paramValue As Object = DBNull.Value
metodo = parame.ParameterName.ToString().Split("#")
paramValue = Parametros(metodo(1))
Adp.SelectCommand.Parameters.AddWithValue(paramName, paramValue)
Next
resultado = Adp.SelectCommand.ExecuteNonQuery()
Adp.Fill(table)
Catch ex As Exception
MessageBox.Show("Ocurrió una excepción en: " + ex.Message + "", "SystemA", _
MessageBoxButtons.OK)
Adp.Dispose()
table.Dispose()
End Try
Return table
Basically, what I am trying to do is read directly to the database what parameters the stored procedure has and depending on the amount I have (input) I am creating them in this for cycle. So far so good, when I have to fill the DataTable with the result of the rows from the database I get the message: "The procedure has too many arguments specified". But if I debug the code, it assigns me the sql values and parameters correctly. If they are 3, 3 is created, if it is 1, 1 is created and so on.
I'm fix it:
Dim inputParamList As New List(Of SqlParameter)
For Each param As SqlParameter In Adp.SelectCommand.Parameters
If param.Direction = Data.ParameterDirection.Input OrElse param.Direction = Data.ParameterDirection.InputOutput Then
inputParamList.Add(param)
End If
Next
For Each parame As SqlParameter In inputParamList
Dim metodo() As String
Dim paramName As String
Dim paramValue As Object
metodo = parame.ParameterName.ToString().Split("#")
paramName = parame.ParameterName
paramValue = Parametros(metodo(1))
Adp.SelectCommand.Parameters(parame.ParameterName).Value = paramValue
==> 'Adp.SelectCommand.Parameters.Add(parame.ParameterName, parame.SqlDbType)
Next
Adp.Fill(table)
Catch ex As Exception
MessageBox.Show("Ocurrió una excepción en: " + ex.Message + "", "Sistema de Agencias", MessageBoxButtons.OK)
Adp.Dispose()
table.Dispose()
End Try
Return table
Just comment on the "Add" line and it works. This is because I was trying to add parameters and just enough to pass the value of the encapsulated methods to the parameter of type SQLParameter. Thanks for the answers and the help provided.
You seem to get the SqlCommandBuilder to download all the parameter information from the DB and add it to an SqlCommand witha populated parameter collection, but then you skip through it looking for input parameters, load them into a list and enumerate it adding even more parameters to the same command:
For Each parame As SqlParameter In inputParamList
Dim metodo() As String
Dim paramName As String = parame.ParameterName.ToString()
Dim paramValue As Object = DBNull.Value
metodo = parame.ParameterName.ToString().Split("#")
paramValue = Parametros(metodo(1))
'you're adding more parameters, with a slightly different but essentially same name!!?
Adp.SelectCommand.Parameters.AddWithValue(paramName, paramValue)
Next
Surely you should just enumerate the existing parameters and supply a value for them based on their name (the type will/ should already be correct? Parametros is a Dictionary(Of string, object) that has parameter names without # and values ?)
For Each param As SqlParameter In Adp.SelectCommand.Parameters
If param.Direction = Data.ParameterDirection.Input OrElse _
param.Direction = Data.ParameterDirection.InputOutput Then
param.Value = Parametros(param.ParameterName.TrimStart("#"c))
End If
Next
Throw all that List inputParam/second ForEach away
Related
I want to put data in SQL table through vb.net in two columns which are Txn_Amount and Post_Amount
where textbox3 = Txn_Amount
Post Amount = Textbox4 - textbox3
but I want if textbox4 = "" than Post amount should be 0
This is my code:
Call Get_TxnID()
Dim Txn_Amount As String = TextBox3.Text
Dim Post_Amount As String = Val(TextBox4.Text) - Val(TextBox3.Text)
Dim query As String = "Insert into Txn_Master values (#Txn_Amount, #Post_Amount)"
Using cmd As New SqlCommand(query, Connection)
cmd.Parameters.AddWithValue("Txn_Amount", Txn_Amount)
cmd.Parameters.AddWithValue("Post_Amount", Post_Amount)
Connection.Open()
cmd.ExecuteNonQuery()
Connection.Close()
End Using
MsgBox("Transaction Success", MsgBoxStyle.Information)
It work well when i have value in both boxes For example :- textbox3.text = 25000 and textbox4.text = 50000 then Post_Amount is 25000
but if textbox3.text = 25000 and textbox4.text = "" then it shows -25000 in post_amount but i want if textbox4 = "" then post amount should be "" or "0"
I have tried
Dim Txn_Amount As String = TextBox3.Text
If textbox4.text="" then
Dim Post_Amount As String = ""
Else
Dim Post_Amount As String = Val(TextBox4.Text) - Val(TextBox3.Text)
endif
Dim query As String = "Insert into Txn_Master values (#Txn_Amount, #Post_Amount)"
Using cmd As New SqlCommand(query, Connection)
cmd.Parameters.AddWithValue("Txn_Amount", Txn_Amount)
cmd.Parameters.AddWithValue("Post_Amount", Post_Amount)
Connection.Open()
cmd.ExecuteNonQuery()
Connection.Close()
End Using
MsgBox("Transaction Success", MsgBoxStyle.Information)
But it is now working, please help me with this
If you initialise a variable for "Post_Amount" to zero, then you can check if the appropriate TextBox has an entry before setting its value, something like this:
Dim txnAmount As Integer = 0
If Not Integer.TryParse(tbTxnAmount.Text, txnAmount) Then
' Prompt user to enter an appropriate value in the TextBox.
' Exit Sub
End If
Dim postAmount As Integer = 0
'TODO Use sensible names for tbAmountA and tbAmountB.
If Not String.IsNullOrWhiteSpace(tbAmountB.Text) Then
'TODO: Use sensible names for these variables.
Dim a = 0
Dim b = 0
If Integer.TryParse(tbAmountA.Text, a) AndAlso Integer.TryParse(tbAmountB.Text, b) Then
postAmount = b - a
End If
End If
Using conn As New SqlConnection("your connection string")
Dim sql = "INSERT INTO [Txn_Master] VALUES (#Txn_Amount, #Post_Amount)"
Using cmd As New SqlCommand(sql, conn)
cmd.Parameters.Add(New SqlParameter With {.ParameterName = "#Txn_Amount",
.SqlDbType = SqlDbType.Int,
.Value = txnAmount})
cmd.Parameters.Add(New SqlParameter With {.ParameterName = "#Post_Amount",
.SqlDbType = SqlDbType.Int,
.Value = postAmount})
conn.Open()
cmd.ExecuteNonQuery()
cmd.Clone()
End Using
End Using
I strongly recommend that you use meaningful names for the TextBoxes and variables. "tbAmountB" is your "TextBox4", but it still needs a better name.
Strictly speaking, it doesn't need the String.IsNullOrWhiteSpace test as such a string would fail the parsing, but it does leave the intent clear.
Also, to make your code easier for others to read, it is convention to use camelCase for variable names: Capitalization Conventions.
I read rows from a file, do a check on the first row and then I have to write the next rows of the file into a table called "TestTable" with a method that works on DataSet. It tells me that I can't insert the string type in DataSet.
Dim myStream As Stream = Nothing
Dim openFileDialog1 As New OpenFileDialog()
openFileDialog1.InitialDirectory = "Z:\FitalogItalia\KMaster\SPEKM" 'ATTENZIONE CAMBIARE IN "C:\"
openFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*"
openFileDialog1.FilterIndex = 2
openFileDialog1.RestoreDirectory = True
If openFileDialog1.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
Try
myStream = openFileDialog1.OpenFile()
If (myStream IsNot Nothing) Then
Dim objReader As New StreamReader(openFileDialog1.FileName)
Dim ControlLine As String = ""
Dim sLine As String = ""
Dim arrText As New ArrayList()
ControlLine = objReader.ReadLine
If (ControlLine.Contains("H06") And ControlLine.Contains("SPEKM")) Then
sLine = objReader.ReadLine
Dim indice As Integer = 0
Do
If Not sLine Is Nothing Then
arrText.Add(sLine)
DB_SQL.SetDBfromDataset("INSERT INTO TestTable (riga) VALUES (" + arrText.Item(indice) + ");", "TestTable")
indice = +1
End If
Loop Until objReader.ReadLine.Count - 1
End If
objReader.Close()
End If
Catch Ex As Exception
MessageBox.Show(Ex.Message)
Finally
' Check this again, since we need to make sure we didn't throw an exception on open.
If (myStream IsNot Nothing) Then
myStream.Close()
End If
End Try
End If
UPDATE I ADDED THE FUNCTION WITH WHICH I LOAD THE DATA ON THE DB. THE PROBLEM IS THE ARRAY LIST BECAUSE I NEED TO PASS A DATASET
Public Function SetDBfromDataset(ByVal ds As Data.DataSet, ByVal TN As String) As Integer
DBadapter.InsertCommand = New SqlCommand
TmpSQLstring = "INSERT INTO " & TN
' ottengo la lista dei campi
ListFields = " ("
For Each myCol In ds.Tables(0).Columns
If (Not IsPrimaryCol(myCol, PKcols)) And (NormalizeDataTypeToDBtype(myCol) <> SqlDbType.Timestamp) Then
ListFields = ListFields & Trim(myCol.ColumnName)
ListFields = ListFields & ","
End If
Next
ListFields = Mid$(ListFields, 1, Len(ListFields) - 1) & ")"
'ottengo la lista dei parametri
ListParam = " VALUES ("
For Each myCol In ds.Tables(0).Columns
If (Not IsPrimaryCol(myCol, PKcols)) And (NormalizeDataTypeToDBtype(myCol) <> SqlDbType.Timestamp) Then
ListParam = ListParam & "#" & Trim(myCol.ColumnName)
ListParam = ListParam & ","
DBadapter.InsertCommand.Parameters.Add(New SqlParameter("#" & Trim(myCol.ColumnName), NormalizeDataTypeToDBtype(myCol)))
DBadapter.InsertCommand.Parameters("#" & Trim(myCol.ColumnName)).SourceColumn = Trim(myCol.ColumnName)
End If
Next
ListParam = Mid$(ListParam, 1, Len(ListParam) - 1) & ")"
DBadapter.InsertCommand.CommandText = TmpSQLstring & ListFields & ListParam
DBadapter.InsertCommand.Connection = CType(DBadapter.SelectCommand.Connection, SqlConnection)
End Function
Streams need to be disposed by calling Dispose or placing in a Using block. This is a text file so you don't even need a stream.
ArrayList is around for backward compatibility but you shouldn't be used in new code. See https://learn.microsoft.com/en-us/dotnet/api/system.collections.arraylist?view=net-5.0#remarks A good replacement is List(Of T)
I have broken the code into 3 methods. You are doing too many separate tasks in a single method. I have used System.IO.File class to read the file. ReadAllLines returns an array of the lines in the file. Then a simple For Each loop to identify the lines you want to add to the database. I used a List(Of String) to collect the lines. Then pass the list to the database code in InsertText.
Only the value of the parameter changes in the loop.
Private Sub OPCode()
Dim FilePath = GetFilePath()
If FilePath Is Nothing Then
MessageBox.Show("No file selected.")
End If
Dim lst As New List(Of String)
Dim lines = File.ReadAllLines(FilePath)
For Each line In lines
If line.Contains("H06") AndAlso line.Contains("SPEKM")) Then
lst.Add(line)
End If
Next
InsertText(lst)
End Sub
Private Function GetFilePath() As String
Dim openFileDialog1 As New OpenFileDialog()
openFileDialog1.InitialDirectory = "Z:\FitalogItalia\KMaster\SPEKM" 'ATTENZIONE CAMBIARE IN "C:\"
openFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*"
openFileDialog1.FilterIndex = 2
openFileDialog1.RestoreDirectory = True
If openFileDialog1.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
Return openFileDialog1.FileName
Else
Return Nothing
End If
End Function
Private Sub InsertText(lst As List(Of String))
Using cn As New SqlConnection("Your connection string"),
cmd As New SqlCommand("INSERT INTO TestTable (riga) VALUES (#riga);", cn)
cmd.Parameters.Add("#riga", SqlDbType.NVarChar)
cn.Open()
For Each line In lst
cmd.Parameters("#riga").Value = line
cmd.ExecuteNonQuery()
Next
End Using 'closes the connection and disposes the command and the connection
End Sub
I am trying to get multiple data sets from SQL Server using a VB.NET application. The problem that every time I try to execute the query,
I get this message:
Cannot change property 'ConnectionString'. The current state of the connection is open
Then I tried to fix it by enabling MARS
<connectionStrings>
<add name="ConString"
providerName="System.Data.SqlClient"
connectionString="Data Source=my-PC;Initial Catalog=Project;Persist Security Info=True; MultipleActiveResultSets=true;User ID=user;Password=*****" />
</connectionStrings>
This is my code
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim obj, body
obj = TextBox1.Text
body = TextBox2.Text
For Each mail In getemail()
Send_mail(mail, obj, body, getattachment(mail))
Next
MsgBox("Traitement effectué")
End Sub
Function getemail() As List(Of String)
Dim strMailTo As New List(Of String)
Dim SQL As String = "Select EMail FROM [USER] WHERE EMail Is Not NULL And MatriculeSalarie Is Not NULL And [EMail] <> '' and EtatPaie = 3 and BulletinDematerialise = 1 "
Dim cmd As New SqlCommand
Dim sqLdr As SqlDataReader
Dim dr As DataRow
Try
ConnServer()
cmd.Connection = con
cmd.CommandText = SQL
Using sda As New SqlDataAdapter(cmd)
Using ds As New DataTable()
sda.Fill(ds)
sqLdr = cmd.ExecuteReader()
For i = 0 To ds.Rows.Count - 1
dr = ds.Rows(i)
strMailTo.Add(dr("EMail"))
Next
End Using
End Using
Return strMailTo
sqLdr.Close()
Catch ex As Exception
MsgBox(ex.Message.ToString)
End Try
closeCon()
Return strMailTo
End Function
Function getattachment(email) As String()
Dim SQL As String = "Select MatriculeSalarie FROM [USER] WHERE [EMail]='" & email & "'"
Dim cmd As New SqlCommand
Dim sqLdr As SqlDataReader
ConnServer()
cmd.Connection = con
cmd.CommandText = SQL
Dim mat As String
mat = ""
Dim Dir As String = ConfigurationManager.AppSettings("path1").ToString
Dim file()
sqLdr = cmd.ExecuteReader()
While sqLdr.Read
mat = sqLdr.GetValue(sqLdr.GetOrdinal("MatriculeSalarie"))
End While
file = IO.Directory.GetFiles(Dir, mat.Substring(1) & "*.pdf")
sqLdr.Close()
Return file
End Function
If all you are going to do is show a message box in a Catch, don't do it in the database code. Let the error bubble up to the user interface code and put the Try around where the method is called.
Do not declare variables without a DataType. The button code with Option Infer on sets the type of obj and body.
Private ConStr As String = "Your connection string"
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim obj = TextBox1.Text
Dim body = TextBox2.Text
Dim emails As New List(Of String)
Try
emails = getemail()
Catch ex As Exception
MessageBox.Show(ex.Message.ToString, "Error retrieving email list")
Exit Sub
End Try
For Each email In emails
Try
Send_mail(email, obj, body, getattachment(email))
Catch ex As Exception
MessageBox.Show(ex.Message, "Error getting attachments")
End Try
Next
MessageBox.Show("Traitement effectué")
End Sub
Parameters used by Sub and Function must have a DataType.
I don't know what you are doing here.
While sqLdr.Read
mat = sqLdr.GetValue(sqLdr.GetOrdinal("MatriculeSalarie"))
End While
Each iteration will overwrite the previous value of mat. I can only assume that you expect only a single value, in which case you can use ExecuteScalar to get the first column of the first row of the result set. Don't do anything with the data until after the connection is closed. Just get the raw data and close (End Using) the connection. Manipulate the data later.
Always use Parameters. Parameters are not treated as executable code by the database server. They are simply values. An example of executable code that could be inserted is "Drop table [USER];" where the value of a parameter belongs. Oops!
Function getemail() As List(Of String)
Dim SQL As String = "Select EMail FROM [USER]
WHERE EMail Is Not NULL
And MatriculeSalarie Is Not NULL
And [EMail] <> ''
And EtatPaie = 3
And BulletinDematerialise = 1;"
Dim dt As New DataTable
Using con As New SqlConnection("Your connection string"),
cmd As New SqlCommand(SQL, con)
con.Open()
Using reader = cmd.ExecuteReader
dt.Load(reader)
End Using
End Using
Dim strMailTo As New List(Of String)
strMailTo = (From row As DataRow In dt.AsEnumerable
Select row.Field(Of String)(0)).ToList
Return strMailTo
End Function
Function getattachment(email As String) As String()
Dim SQL As String = "Select MatriculeSalarie FROM [USER] WHERE [EMail]='" & email & "'"
Dim mat As String
Using con As New SqlConnection(ConStr),
cmd As New SqlCommand(SQL, con)
cmd.Parameters.Add("#email", SqlDbType.VarChar).Value = email
con.Open()
mat = cmd.ExecuteScalar().ToString()
End Using
Dim Dir As String = ConfigurationManager.AppSettings("path1").ToString
'Your original code was fine, no need for searchPattern.
'I added this so you could see if your search pattern was what you expected.
Dim searchPattern = mat.Substring(1) & "*.pdf"
Debug.Print(searchPattern) 'Appears in the Immediate window
Dim file = IO.Directory.GetFiles(Dir, searchPattern)
Return file
End Function
all. I am facing an issue on how to pass the dynamic parameter value from SQL query, that will be entered by the user into the textbox, to the SQL command to search on the parameter value on the datagridview datatable.
For my project, a textbox will be provided for the user to key in the SQL query dynamically to search on the database data. If the user keys in SQL query like
select *
from table
where a = #a
The user can search on #a parameter value; if the user keys in SQL query like
select *
from table
where a = #a and b = #b
The user can search the #a and #b parameter values by using textbox, which means that the parameter number that had been entered by the user needs to be calculated, retrieved, passed to the SQL command and allow the user to filter on the parameter by using textbox provided.
However, currently, due to the #a parameter and #b parameter will be key in by the user dynamically during runtime, so I faced difficulty to declare/define the parameter name on the cmd.Parameters.AddWithValue() statement.
Can anyone help me to solve my problem by providing me some solutions on codes? I had been stuck on this issue for a few days already. Thank you for all the help!
The code I had tried:
Private Sub btn1_Click(sender As Object, e As EventArgs) Handles btn1.Click
Sql = TextBox4.Text
Try
'open database
Dim con As New SqlConnection(dbstring)
con.Open()
Dim cmd As New SqlCommand(Sql, con)
If param IsNot Nothing Then
For Each para As SqlParameter In param
'cmd.Parameters.Add(para)
For m As Integer = 0 To param.Count - 1
cmd.Parameters.Add(New SqlParameter With {.ParameterName = para.ParameterName(m),
.Value = para.Value(m),
.SqlDbType = SqlDbType.NVarChar,
.Size = 99})
Next
cmd.ExecuteNonQuery()
Next
End If
Using sda = New SqlDataAdapter()
sda.SelectCommand = cmd
cmd.CommandText = Sql
Sql = cmd.ExecuteScalar()
Using ds As DataSet = New DataSet()
sda.Fill(ds)
con.Close()
DataGridView1.DataSource = ds.Tables(0)
End Using
End Using
con.Close()
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Screenshot of Window App Formhad been provided as a reference.
To avoid SQL Injection, im usally doing like this:
Dim text1 As String = "text1"
Dim objComm As SqlCommand = New SqlCommand
objComm.Parameters.AddWithValue("#text1", text1)
objComm.CommandText = "SELECT * FROM TABLE WHERE Text1 = #text1"
I am not sure why you would want the user to write Select statements. I assume this query is on a single table where you know the field names, types and field sizes where applicable. I made up the these items. You will have to check your database to get the correct information.
I used Optional parameters in case you have other datatypes like Booleans or numbers where you can supply the defaults. To pass no value for the parameter just leave a blank but insert the appropriate commans.
Private Function GetSearchResults(Optional FirstName As String = "", Optional LastName As String = "") As DataTable
Dim dt As New DataTable
Using con As New SqlConnection(dbstring),
cmd As New SqlCommand()
Dim sb As New StringBuilder
sb.Append("Select * From SomeTable Where 1 = 1")
If FirstName <> "" Then
cmd.Parameters.Add("#a", SqlDbType.NVarChar, 100).Value = FirstName
sb.Append(" And FirstName = #a")
End If
If LastName <> "" Then
cmd.Parameters.Add("#b", SqlDbType.NVarChar, 100).Value = LastName
sb.Append(" And LastName = #b ")
End If
sb.Append(";")
Debug.Print(sb.ToString)
cmd.CommandText = sb.ToString
cmd.Connection = con
con.Open()
dt.Load(cmd.ExecuteReader)
End Using
Return dt
End Function
Alternative useage:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim dt = GetSearchResults(TextBox1.Text, TextBox2.Text)
DataGridView1.DataSource = dt
End Sub
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim dt = GetSearchResults(, TextBox2.Text)
DataGridView1.DataSource = dt
End Sub
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
Dim dt = GetSearchResults(TextBox1.Text, )
DataGridView1.DataSource = dt
End Sub
How to concatenate
ORDER BY
clause in VB.NET?
Here's what I've tried:
Using command As New SqlCommand()
command.Connection = conn
Dim parameterNames As New List(Of String)(dt_data.RowCount - 2)
For i As Integer = 0 To dt_data.RowCount - 3
Dim parameterName As String = "#meter_num_" & i
Dim meter_number As String = dt_data.Rows(i).Cells(3).Value
command.Parameters.AddWithValue(parameterName, meter_number)
parameterNames.Add(parameterName)
Next
command.CommandText = String.Format("SELECT * FROM customer WHERE cycle = #cycle and meter_num IN ({0})", String.Join(",", parameterNames), ("ORDER BY Client_Name ASC"))
command.Parameters.AddWithValue("#cycle", cycle2last)
Dim da As New SqlDataAdapter(command)
Dim ds As New DataSet
da.Fill(ds, "customer")
Compare_Reading.dt_last2month.DataSource = ds.Tables(0)
End Using
I wanted it to be like this
Select * from table_name
where column_name = #column and column2 = #column2
ORDER BY column_name ASC
To do that in a single instruction you need to replace
Compare_Reading.dt_last2month.DataSource = ds.Tables(0)
With:
Compare_Reading.dt_last2month.DataSource = (From existing As DataRow In ds.Tables(0).Select Order By existing.Item("your_filed_name_to_order_here") Ascending).CopyToDataTable
One of the nice things about List(Of T) is that you don't need to know the size before adding items. It will expand as necessary. So, I deleted the int32 from the constructor.
I think you want dt_data.RowCount -2 in For loop or you will skip the last row. I changed your .AddWithValue to .Add. You will have to check your database for the correct datatypes (I guessed) and adjust the code accordingly.
I think your main problem was in String.Format. You added the 2 parameters but neglected to put in the {1}.
I added a Debug.Print so you can check that your Select looks like you expect. This will appear in the immediate window when you run in debug. It won't effect the release version.
You don't need a DataAdapter or DataSet just a DataTable. Assign the .DataSource outside of the Using block.
I don't think there is any concern about sql injection because you have properly used parameters for all the variables.
Private Sub OPCode(cycle2last As String)
Dim dt As New DataTable
Using conn As New SqlConnection("Your connection string")
Using command As New SqlCommand()
command.Connection = conn
Dim parameterNames As New List(Of String)
For i As Integer = 0 To DataGridView1.RowCount - 2
Dim parameterName As String = "#meter_num_" & i
Dim meter_number As String = DataGridView1.Rows(i).Cells(3).Value.ToString
command.Parameters.Add(parameterName, SqlDbType.VarChar).Value = meter_number
parameterNames.Add(parameterName)
Next
command.CommandText = String.Format("SELECT * FROM customer WHERE cycle = #cycle and meter_num IN ({0}) {1}", String.Join(",", parameterNames), ("ORDER BY Client_Name ASC;"))
command.Parameters.Add("#cycle", SqlDbType.VarChar).Value = cycle2last
Debug.Print(command.CommandText)
conn.Open()
dt.Load(command.ExecuteReader)
End Using
End Using
DataGridView2.DataSource = dt
End Sub