I have been writing in VB.Net for a couple months now and have used SQL commands in my code many times successfully but am having trouble writing to one particular table in my database. I believe the problem lies in that i have a numeric column that i am trying to write to (I came to this conclusion because it is the only one i don't use very often) and my code keeps coming up with an exception: Additional information: SqlCommand.Prepare method requires all variable length parameters to have an explicitly set non-zero Size. with the code:
Dim cn As System.Data.SqlClient.SqlConnection
Dim command As System.Data.SqlClient.SqlCommand
Dim VL1 As System.Data.SqlClient.SqlParameter
Dim VL2 As System.Data.SqlClient.SqlParameter
Dim VL3 As System.Data.SqlClient.SqlParameter
Dim VL4 As System.Data.SqlClient.SqlParameter
Dim commandText As String
cn = New System.Data.SqlClient.SqlConnection(ConnectStr)
cn.Open()
commandText = "INSERT INTO [dbo].[HourMeterLog]" _
+ "([MachineName],[TotalHours],[HLRD],[DateTime])" _
+ "VALUES (#VL1,#VL2,#VL3,#VL4)"
command = New System.Data.SqlClient.SqlCommand(commandText, cn)
VL1 = command.Parameters.Add("#VL1", System.Data.SqlDbType.NVarChar, 25)
VL2 = command.Parameters.Add("#VL2", System.Data.SqlDbType.Float)
VL3 = command.Parameters.Add("#VL3", System.Data.SqlDbType.Int, 10)
VL4 = command.Parameters.Add("#VL4", System.Data.SqlDbType.DateTime2, 0)
command.Prepare()
VL1.Value = "MachineName"
VL2.Value = 0
VL3.Value = 735562
VL4.Value = Now()
command.ExecuteNonQuery()
cn.Close()
I know my code connects because I have at least 15 other queries that work using this format. I would prefer to not use float to write to the numeric field but i had it work the first time I launched this code (not once since then).
According to MSDN the Max character Length of the DateTime2 is 27. So I have set the Length of DateTime2 to 27 and its working fine.
VL4 = command.Parameters.Add("#VL4", System.Data.SqlDbType.DateTime2, 27)
And my working code is,
Dim cn As System.Data.SqlClient.SqlConnection
Dim command As System.Data.SqlClient.SqlCommand
Dim VL1 As System.Data.SqlClient.SqlParameter
Dim VL2 As System.Data.SqlClient.SqlParameter
Dim VL3 As System.Data.SqlClient.SqlParameter
Dim VL4 As System.Data.SqlClient.SqlParameter
Dim commandText As String
cn = New System.Data.SqlClient.SqlConnection(connectionString)
cn.Open()
commandText = "INSERT INTO [dbo].[HourMeterLog]" _
+ "([MachineName],[TotalHours],[HLRD],[DateTime])" _
+ "VALUES (#VL1,#VL2,#VL3,#VL4)"
command = New System.Data.SqlClient.SqlCommand(commandText, cn)
VL1 = command.Parameters.Add("#VL1", System.Data.SqlDbType.NVarChar, 25)
VL2 = command.Parameters.Add("#VL2", System.Data.SqlDbType.Float)
VL3 = command.Parameters.Add("#VL3", System.Data.SqlDbType.Int, 10)
VL4 = command.Parameters.Add("#VL4", System.Data.SqlDbType.DateTime2, 27)
command.Prepare()
VL1.Value = "MachineName"
VL2.Value = 0
VL3.Value = 735562
VL4.Value = Now()
command.ExecuteNonQuery()
cn.Close()
Okay so I guess I wrote the AddWithValue method wrong when i tried that. I got the command to write with:
Dim cn As System.Data.SqlClient.SqlConnection
Dim command As System.Data.SqlClient.SqlCommand
Dim VL1 As System.Data.SqlClient.SqlParameter
Dim VL2 As System.Data.SqlClient.SqlParameter
Dim VL3 As System.Data.SqlClient.SqlParameter
Dim VL4 As System.Data.SqlClient.SqlParameter
Dim commandText As String
cn = New System.Data.SqlClient.SqlConnection(ConnectStr)
cn.Open()
commandText = "INSERT INTO [dbo].[HourMeterLog]" _
+ "([MachineName],[TotalHours],[HLRD],[DateTime])" _
+ "VALUES (#VL1,#VL2,#VL3,#VL4)"
command = New System.Data.SqlClient.SqlCommand(commandText, cn)
VL1 = command.Parameters.AddWithValue("#VL1", "MachineName")
VL2 = command.Parameters.AddWithValue("#VL2", 0)
VL3 = command.Parameters.AddWithValue("#VL3", 735562)
VL4 = command.Parameters.AddWithValue("#VL4", Now())
'VL1 = command.Parameters.Add("#VL1", System.Data.SqlDbType.NVarChar, 25)
'VL2 = command.Parameters.Add("#VL2", System.Data.SqlDbType.Float)
'VL3 = command.Parameters.Add("#VL3", System.Data.SqlDbType.Int)
'VL4 = command.Parameters.Add("#VL4", System.Data.SqlDbType.DateTime2)
'command.Prepare()
'VL1.Value = "MachineName"
'VL2.Value = 0
'VL3.Value = 735562
'VL4.Value = Now()
command.ExecuteNonQuery()
cn.Close()
I would really prefer to use the format i had used before but i am happy that i at least am writing now. Thanks for the help guys. If anyone has any ideas on how to improve my original format to make it write correctly let me know. and for those who wanted a little info on the table I am writing too the field the Float was writing to is a Numeric(18,1) field. Other than that column the others can be extrapolated from the code.
For the error messge, without know anything about your table...
"SqlCommand.Prepare method requires all variable length parameters to have an explicitly set non-zero Size"
From MSDN (https://msdn.microsoft.com/en-us/library/40959t6x(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1)
'Declaration
Public Function Add ( _
parameterName As String, _
sqlDbType As SqlDbType, _
size As Integer _
) As SqlParameter
Maybe your problems comes from:
VL4 = command.Parameters.Add("#VL4", System.Data.SqlDbType.DateTime2, 0)
Change it to
VL4 = command.Parameters.Add("#VL4", System.Data.SqlDbType.DateTime2)
Here is a short example what the 'Size' parameter of SqlParameter has an influence.
Sample table is
CREATE TABLE [dbo].[DateTimeTest](
[CreatedAt] [datetime2](7) NOT NULL,
[SizeValue] [int] NOT NULL
) ON [PRIMARY]
Sample code in C# is
string connectionString = "Data Source=.;Integrated Security=SSPI;Initial Catalog=Sandbox";
using (SqlConnection connection = new(connectionString))
{
connection.Open();
string commandText = #"
INSERT INTO [dbo].[DateTimeTest]
([CreatedAt]
,[SizeValue])
VALUES
(#CreatedAt
,#SizeValue);
";
DateTime timestamp = DateTime.Now;
for (int i = 1; i < 28; i++)
{
SqlCommand command = new SqlCommand(commandText, connection);
SqlParameter createdAtParam = new SqlParameter("#CreatedAt", SqlDbType.DateTime2, i); // i = 0 will raise an exception on call to command.Prepare()
command.Parameters.Add(createdAtParam);
createdAtParam.Value = timestamp;
SqlParameter sizeValueParam = new SqlParameter("#SizeValue", SqlDbType.Int);
command.Parameters.Add(sizeValueParam);
sizeValueParam.Value = i;
command.Prepare();
command.ExecuteNonQuery();
}
}
As you can see, the value of 'Size' has no influence on the precision of the inserted DateTime value.
SqlParameter createdAtParam = new SqlParameter("#CreatedAt", SqlDbType.DateTime2, 0);
or
SqlParameter createdAtParam = new SqlParameter("#CreatedAt", SqlDbType.DateTime2);
will raise an exception on call to
command.Prepare();
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'm currently working on my project for which I used VB.NET 2019 and SQL server. I need to create a function which auto generates IDs.
I want my IDs to be like these: P001, P002, P003 etc. Can someone show me how to code it? Below is my code
Private Sub Form4_Load_1(sender As Object, e As EventArgs) Handles MyBase.Load
BindData()
Dim data As String = "Data Source=LAPTOP-M8KKSG0I;Initial Catalog=Oceania;Integrated Security=True"
Dim con As New SqlConnection(data)
Try
If Con.State = ConnectionState.Closed Then
con.Open()
End If
Dim sql As String = "Select Max(PatientID) from Patient"
Dim cmd As New SqlCommand(sql, con)
Dim Max As String = cmd.ExecuteScalar
If Max > 0 Then
TextBox1.Text = Max + 1
Else
TextBox1.Text = "P01"
End If
Catch ex As Exception
MsgBox(Err.Description)
End Try
End Sub
You can try like this. Here 1 is an auto-generated number that may be an identity key column value from a table in SQL Server.
Dim number As Integer = 1
Dim numberText As String = "P" & number.ToString().PadLeft(3, "0")
Live demo
You can add a computed column like this in your table for auto-generating the sequences. This will reduce the chances of duplicate value runtime once more than one person will do the entry simultaneously.
Alter table Patient ADD PatientCode AS ('P' + Convert(Varchar(3),CONCAT(REPLICATE('0', 3 - LEN(PatientID)), PatientID)) )
To get the column value dynamically you can try the below code to generate function.
Private Sub GenerateSequnce()
Dim constring As String = "Data Source=TestServer;Initial Catalog=TestDB;User id = TestUser;password=test#123"
Using con As New SqlConnection(constring)
Using cmd As New SqlCommand("Select Top 1 ISNULL(TaxCode, 0) from Tax_Mst Order By TaxCode Desc", con)
cmd.CommandType = CommandType.Text
Using sda As New SqlDataAdapter(cmd)
Using dt As New DataTable()
sda.Fill(dt)
Dim maxNumberCode = dt.Rows(0)("TaxCode").ToString()
If (maxNumberCode = "0") Then
maxNumberCode = "1"
End If
Dim numberText As String = "P" & maxNumberCode.ToString().PadLeft(3, "0")
End Using
End Using
End Using
End Using
End Sub
Here the column TaxCode is int with identity constraint.
With the minor correction in your code, you can achieve this as shown below.
Dim data As String = "Data Source=LAPTOP-M8KKSG0I;Initial Catalog=Oceania;Integrated Security=True"
Dim con As New SqlConnection(data)
Try
If con.State = ConnectionState.Closed Then
con.Open()
End If
Dim sql As String = "Select ISNULL(Max(PatientID), 0) from Patient"
Dim cmd As New SqlCommand(sql, con)
Dim Max As String = cmd.ExecuteScalar
If (Max = "0") Then
Max = "1"
Else
Max = CInt(Max) + 1
End If
Dim numberText As String = "P" & Max.ToString().PadLeft(3, "0")
TextBox1.Text = numberText
Catch ex As Exception
MsgBox(Err.Description)
End Try
OUTPUT
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
I have an unsecured query which allows for an injection, and I'm not sure how to go about parameterizing it to prevent said injection
Dim sInsertSQL As String
sInsertSQL = "Insert into tbl_userprop (Prop_Def) values "
Dim tempString As String() = PropertyDefinitions.Split("|")
For i As Integer = 1 To tempString.Length
If tempString(i - 1).Length > 0 Then
sInsertSQL = sInsertSQL + " ('" + tempString(i - 1) + "'),"
bInsert = True
End If
Next
There are up to 10 values stored in tempString and they are concatenated onto sInsertSQL as such: ('val1'), ('val2'), etc
Figured it out, update for the curious:
Dim lstParams As New Collections.Generic.List(Of SqlParameter)
Dim tempString As String() = PropertyDefinitions.Split("|")
For i As Integer = 1 To tempString.Length
If tempString(i - 1).Length > 0 Then
Dim sParamName As String = String.Format("#param{0}", i)
Dim sparam As New SqlParameter(sParamName, tempString(i - 1))
lstParams.Add(sparam)
sInsertSQL = sInsertSQL + " (" + sParamName + "),"
bInsert = True
End If
Next
Once you have split the string you know how many parameters there will be, so you can create the # items for the SQL. After that, you can add the parameters by going through the lists of parameter names and values:
Dim PropertyDefinitions = "abc|def|ghi|jkl|mno"
Dim values = PropertyDefinitions.Split({"|"c})
Dim paramNames = Enumerable.Range(0, values.Count()).Select(Function(n) $"#p{n}")
Dim paramList = String.Join(", ", paramNames.Select(Function(s) $"({s})"))
Dim sql = "INSERT INTO [tbl_userprop] (Prop_Def) VALUES " & paramList
' The following line with the sample data would output '
' INSERT INTO [tbl_userprop] (Prop_Def) VALUES (#p0), (#p1), (#p2), (#p3), (#p4)
'Console.WriteLine(sql)
Dim connStr = "YourConnectionStringHere"
Using conn As New SqlConnection(connStr)
Using cmd As New SqlCommand(sql, conn)
For i = 0 To values.Count() - 1
'TODO: Set the .SqlDbType and .Size to conform to the database definition of [tbl_userprop]. '
cmd.Parameters.Add(New SqlParameter With {.ParameterName = paramNames(i),
.Value = values(i),
.SqlDbType = SqlDbType.NVarChar,
.Size = 99})
Next
'conn.Open()
'cmd.ExecuteNonQuery()
End Using
End Using
There are 10 rows in primary_student_table.
When I execute the following code, the result was -1.
Dim count As Int16
con.Open()
query = "SELECT COUNT(roll) AS rollcount FROM primary_student_table WHERE admityear = 2011 AND batch = 1 "
cmd = New SqlCommand(query, con)
count = cmd.ExecuteNonQuery
MsgBox(count)
con.Close()
What's the problem in the above code?
You should be using ExecuteScalar() rather than ExecuteNonQuery() because you are fetching a value.
count = Convert.ToInt16(cmd.ExecuteScalar())
MsgBox(count.ToString())
SqlCommand.ExecuteScalar Method
For proper coding
use using statement for proper object disposal
use try-catch block to properly handle exceptions
Example Code:
Dim connStr As String = "connection string here"
Dim query As String = "SELECT COUNT(roll) AS rollcount FROM primary_student_table WHERE admityear = 2011 AND batch = 1"
Using conn As New SqlConnection(connStr)
Using cmd As New SqlCommand()
With cmd
.Connection = conn
.CommandText = query
.CommandType = CommandType.Text
End With
Try
conn.Open()
Dim count As Int16 = Convert.ToInt16(cmd.ExecuteScalar())
MsgBox(count.ToString())
Catch(ex As SqlException)
' put your exception here '
End Try
End Using
End Using
The solution is to replace
count = cmd.ExecuteNonQuery
with
count = cmd.ExecuteScalar
Like Robert Beaubien said in his comments
MysqlConn = New MySqlConnection
MysqlConn.ConnectionString = "server=localhost;userid=root;password=1234;database=dblms"
Dim READER As MySqlDataReader
Try
MysqlConn.Open()
Dim Query As String
Query = "Select * from dblms.accounts"
COMMAND = New MySqlCommand(Query, MysqlConn)
READER = COMMAND.ExecuteReader
Dim count As Integer
count = 0
While READER.Read
count = count + 1
End While
MysqlConn.Close()
Catch ex As MySqlException
MessageBox.Show(ex.Message)
Finally
MysqlConn.Dispose()
End Try
the value in count will be the number of rows in a table :) hope this helped