How to detect it was duplicate entry on VB.NET? - sql-server

I have a problem on making avoid or detect duplicate data entry in VB.NET using SQL Server, and also pop up message when there is duplicate entry.
This is my add button code:
Dim sql = "INSERT INTO studentAttendence(studentID, date, time, subject, attendence) " &
"VALUES(#studentID, #date, #time, #subject, #attendence)" &
"WHERE NOT EXISTS(SELECT * FROM studentAttendence WHERE studentID = #studentID)"
Using cn As New SqlConnection("My sql Connection string")
Dim commands As SqlCommand = New SqlCommand(sql, cn)
commands.Parameters.Add("#studentID", SqlDbType.NVarChar).Value = TextBox1.Text
commands.Parameters.Add("#date", SqlDbType.NVarChar).Value = DateTimePicker1.Text
commands.Parameters.Add("#time", SqlDbType.NVarChar).Value = DateTimePicker2.Text
commands.Parameters.Add("#subject", SqlDbType.NVarChar).Value = ComboBox1.Text
commands.Parameters.Add("#attendence", SqlDbType.NVarChar).Value = ComboBox2.Text
commands.ExecuteNonQuery()
End Using
Or is there any other methods for making avoid duplicate entries. I just want to avoid same date and subject of a student to add. So I can only add subject on the next date if I already add subject on today's date of studentID.

You can use If Not Exists before the insert. This method only requires a single hit on the database. Add a comman at the end of the first line of the Using block, then delete Dim before commands. This will include commands in the Using block.
Private Function InsertAttendance() As Integer
Dim sql = "If Not Exists (Select 1 From studentAttendence
Where studnetID = #studentID And date = #date And subject = #subject)
INSERT INTO studentAttendence(studentID, date, time, subject, attendence)
VALUES(#studentID, #date, #time, #subject, #attendence);"
Dim NumRowsAffected As Integer
Using cn As New SqlConnection("My sql Connection string"),
commands As SqlCommand = New SqlCommand(sql, cn)
commands.Parameters.Add("#studentID", SqlDbType.NVarChar).Value = TextBox1.Text
commands.Parameters.Add("#date", SqlDbType.NVarChar).Value = DateTimePicker1.Text
commands.Parameters.Add("#time", SqlDbType.NVarChar).Value = DateTimePicker2.Text
commands.Parameters.Add("#subject", SqlDbType.NVarChar).Value = ComboBox1.Text
commands.Parameters.Add("#attendence", SqlDbType.NVarChar).Value = ComboBox2.Text
cn.Open()
NumRowsAffected = commands.ExecuteNonQuery()
End Using
Return NumRowsAffected
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
If InsertAttendance() = 1 Then
MessageBox.Show("Success")
Else
MessageBox.Show("Duplicate Entry")
End If
Catch ex As Exception
MessageBox.Show($"Error Entering Attendance {ex.Message}")
End Try
End Sub
Be sure to check the datatypes of your parameters with the database.

You should create a function that queries your data table to return the row count where the Date, Subject, and StudentId match what you're wanting to insert. If the returned result is greater than 0 then display the message.
Here is a function that returns -1 if something went wrong or the row count:
Private Function GetRowCount(ByVal studentIdParameter As String, ByVal dateParameter As DateTime, ByVal subjectParameter As String) As Integer
'Declare the object to return
Dim count As Integer = -1
'Declare the connection object
Dim con As OleDbConnection
'Wrap code in Try/Catch
Try
'Set the connection object to a new instance
'TODO: Change "My Connection String Here" with a valid connection string
con = New OleDbConnection("My Connection String Here")
'Create a new instance of the command object
'TODO: Change [ID] to a valid column
Using cmd As OleDbCommand = New OleDbCommand("SELECT Count([ID]) FROM [MyTable] WHERE studentID=#studentId, AND date=#date AND subject=#subject", con)
'Parameterize the query
cmd.Parameters.Add("#studentId", OleDbType.NVarChar).Value = studentIdParameter
cmd.Parameters.Add("#date", OleDbType.DBDate).Value = dateParameter.Date
cmd.Parameters.Add("#subject", OleDbType.NVarChar).Value = subjectParameter
'Open the connection
con.Open()
'Use ExecuteScalar to return a single value
count = Convert.ToInt32(cmd.ExecuteScalar())
'Close the connection
con.Close()
End Using
Catch ex As Exception
'Display the error
Console.WriteLine(ex.Message)
Finally
'Check if the connection object was initialized
If con IsNot Nothing Then
If con.State = ConnectionState.Open Then
'Close the connection if it was left open(exception thrown)
con.Close()
End If
'Dispose of the connection object
con.Dispose()
End If
End Try
'Return the row count
Return count
End Function
To implement it, you'd do something like:
Dim rowCount = GetRowCount(TextBox1.Text, DateTimePicker1.Value, ComboBox1.Text)
If (rowCount = -1) Then
MessageBox.Show("Something went wrong checking for duplicates")
ElseIf (rowCount = 0) Then
' Insert record
Else
MessageBox.Show("A record already exists with this studentId, date, and subject.")
End If

Related

How to resolve : 'There is already an open DataReader associated with this Command which must be closed first.'

I've been trying to insert data into my sql database but this problem always show up
i've tried redoing it again and the same problem occurs and i'm really stumped right now
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim conn As SqlConnection = New SqlConnection("Data Source=DESKTOP-OBQR58O\SQLEXPRESS;Initial Catalog=Accounts;Integrated Security=True")
Dim comm As SqlCommand = New SqlCommand("insert into User(username, password)values('" + TextBox1.Text + "', '" + TextBox3.Text + "')", conn)
Dim data As SqlDataAdapter = New SqlDataAdapter(comm)
Dim user = TextBox1.Text
Dim pass = TextBox2.Text
Dim cpass = TextBox3.Text
Dim reader As SqlDataReader
conn.Open()
Dim cmd As SqlCommand = New SqlCommand("select Username from [User] where Username ='" + TextBox1.Text + "'", conn)
conn.Close()
conn.Open()
reader = cmd.ExecuteReader
If user.Trim() = "" Or pass.Trim() = "" Or cpass.Trim() = "" Then
MessageBox.Show("Empty Fields", "Blank Spaces")
ElseIf Not String.Equals(pass, cpass) Then
MessageBox.Show("Passwords do not match", "ERROR")
conn.Close()
ElseIf reader.HasRows Then
MessageBox.Show("Username already exists!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning)
TextBox1.Clear()
conn.Close()
reader.Close()
Else
MessageBox.Show("Account created succesfully!", "Success")
Dim table As DataTable = New DataTable()
data.Fill(table) ' this is where the problem occurs.
TextBox1.Clear()
TextBox2.Clear()
TextBox3.Clear()
Dim log As New Login
Me.Close()
log.Show()
conn.Close()
End If
conn.Close()
End Sub
I honestly don't know what to do
You open your reader up at the top:
reader = cmd.ExecuteReader
So, it's open. And then, when you run the Fill command, it conflicts with the open reader!
The simplest fix - although, personally, I would restructure the code a bit, to bring the OpenReader nearer to where it is used - would be to add a Close to your reader right before the Fill.
Else
reader.Close() ' what you would add
MessageBox.Show("Account created succesfully!", "Success")
Dim table As DataTable = New DataTable()
data.Fill(table) ' this is where the problem occurs.
VERY IMPORTANT: If you're not familiar with the concept of "SQL Injection Attacks", read up on them, right away. You should NEVER execute SQL that's been built by constructing a string with unvalidated data from the user. You should pass parameters instead.
After all, what if I typed in the user name of "Irrelevant';DROP TABLE Users;--"? You'd wind up with a SQL Statement that contained "SELECT Username from [Users] WHERE [Username] = 'Irrelevant'; DROP TABLE Users; --'"
And, of course, you should validate the input as well, for things like embedded HTML and script! But that's more complicated than just using SQL Parameters.

How to pass dynamic parameter from SQL query to the SqlCommand after user input on the SQL query

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

Auto generate alpha numeric in VB.NET

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

Avoid Duplicate record across columns

I Have a code that will determine if a data is already existing. The problem is, it is still adding even already exists.
Already tried some code that will add and not add if data exists
If txtHostname.Text = "" Then
MsgBox("Please fill-up all fields!", MsgBoxStyle.Exclamation, "Inventory!")
Else
Dim theQuery As String = "SELECT * FROM Asset WHERE Monitor1=#Monitor1 AND Monitor2=#Monitor2"
Dim cmd1 As OleDbCommand = New OleDbCommand(theQuery, con)
cmd1.Parameters.AddWithValue("#Monitor1", txtMonitor1.Text)
cmd1.Parameters.AddWithValue("#Monitor2", txtMonitor2.Text)
Using reader As OleDbDataReader = cmd1.ExecuteReader()
If reader.HasRows Then
' User already exists
MsgBox("User Already Exist!", MsgBoxStyle.Exclamation, "Add New User!")
Else
' User does not exist, add them
Dim cmd As OleDbCommand = New OleDbCommand("Insert into Asset ([Monitor1],[Monitor2]) values ('" + txtMonitor1.Text + "','" + txtMonitor2.Text + "')", con2)
cmd.ExecuteNonQuery()
MsgBox("Records Successfully Added!", MsgBoxStyle.Information, "Add New Customer!")
txtMonitor1.Text = ""
txtMonitor2.Text = ""
End If
End Using
con.Close()
End If
It should be, when I search 1 data in column1 it should detect if data is already exists in column1 and column2. Not just in column1.
Well, if you want to search and return the result about whether Fields exist or not , you should not use OledbReader, also I did notice that reader doesn't Read (Even if it is not even correct to use it in this scenario). You could rather use ExecuteScalar and see if Fields exist or not (>0 or <0).
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim CMDText As String = ("SELECT COUNT(*) FROM Asset WHERE Monitor1=#Monitor1 AND Monitor2=#Monitor2")
Dim Found As Integer
If String.IsNullOrEmpty(txtHostname.Text) Then
MsgBox("Please fill-up all fields!", MsgBoxStyle.Exclamation, "Inventory!")
Else
Using CN As New OleDb.OleDbConnection With {.ConnectionString = "CON_STR"},
Cmd1 As New OleDb.OleDbCommand(CMDText, CN)
CN.Open()
With Cmd1.Parameters
.Add("#Monitor1", OleDb.OleDbType.VarChar).Value = txtMonitor1.Text
.Add("#Monitor2", OleDb.OleDbType.VarChar).Value = txtMonitor2.Text
End With
Found = Cmd1.ExecuteScalar()
End Using
If Found > 0 Then
' User already exists
MsgBox("User Already Exist!", MsgBoxStyle.Exclamation, "Add New User!")
Else
Dim CmdText1 As String =
("INSERT INTO Asset (Monitor1,Monitor2) VALUES (#Monitor1 ,#Monitor2)")
Using Cmd As New OleDb.OleDbCommand(CmdText1, CN)
With Cmd.Parameters
.Add("#Monitor1", OleDb.OleDbType.VarChar).Value = txtMonitor1.Text
.Add("#Monitor2", OleDb.OleDbType.VarChar).Value = txtMonitor2.Text
End With
Cmd.ExecuteNonQuery()
' User does not exist, add them
MsgBox("Records Successfully Added!", MsgBoxStyle.Information, "Add New Customer!")
txtMonitor1.Text = String.Empty
txtMonitor2.Text = String.Empty
Cmd.Parameters.Clear()
End Using
End If
End If
End Sub
In my example Code : "CON_STR" = My ConnectionString that I used to test my Code, as you did not provide any.

how to get each unique value from access database column and add to combobox items

I am not professional but i am trying to learn VB.net. I am making a project where i am stuck where i want to get each unique value from a column in access database and add it to my combobox. Can anybody help me ??
Private Sub showItems()
Dim comm As OleDbCommand
Dim commStr As String = "SELECT Item_Name FROM Add_Items WHERE (Item_Name <> '"
Dim RD As OleDbDataReader
conn = New OleDbConnection(connStr)
conn.Open()
If cbItemname.Items.Count = 0 Then
comm = New OleDbCommand("Select Item_Name from Add_Items", conn)
RD = comm.ExecuteReader
While RD.Read
cbItemname.Items.Add(RD.GetString(0))
End While
End If
For Each i As Object In cbItemname.Items
comm = New OleDbCommand(commStr & i & "')", conn)
RD = comm.ExecuteReader
While RD.Read
cbItemname.Items.Add(RD.GetString(0))
Exit While
End While
Next
conn.Close()
End Sub
comm = New OleDbCommand("Select DISTINCT Item_Name from Add_Items", conn)
http://office.microsoft.com/en-in/access-help/HV080760568.aspx
You can get this error because your database table name is incorrect. Make sure you are in the Tables tab and check the name of the table. DISTINCT and UNIQUE(for MySQL) is correct solution for this.
I followed instructions given by vipul. It was working nicely and i made some more private subs for brands and Models and it suddenly stopped working. Now, when my form loads from previous parent form it hangs.
Private Sub showItems()
Dim comm As OleDbCommand
Dim commStr As String = "SELECT DISTINCT Item_Name from Add_Items"
Dim ReadData As OleDbDataReader
itemnamecombo.Items.Clear()
ItemChkboxList.Items.Clear()
Try
conn = New OleDbConnection(ConnStr)
conn.Open()
comm = New OleDbCommand(commStr, conn)
ReadData = comm.ExecuteReader
While ReadData.Read
itemnamecombo.Items.Add(ReadData.GetString(0))
ItemChkboxList.Items.Add(ReadData.GetString(0))
End While
Catch ex As Exception
'MessageBox.Show(ex.Message)
Finally
conn.Dispose()
End Try
If itemnamecombo.Items.Count <> 0 Then
itemnamecombo.SelectedIndex = 0
End If
End Sub

Resources