Has random have a limit? - sql-server

Im working on a program which generating random numbers. I have use random in generating the build number. I just wondering does random will have a limit? heres my code thank you in advance.
Private Sub agenerate_Click(sender As Object, e As EventArgs) Handles agenerate.Click
Dim rand As New Random
abuildnumber.Text = rand.Next
Dim exist As String = String.Empty
exist &= "select * from stocks "
exist &= "where build_number=#build"
Using conn As New SqlConnection("server=WIN10;user=admin;password=12345;database=pc_parts")
Using cmd As New SqlCommand
With cmd
.Connection = conn
.CommandType = CommandType.Text
.CommandText = exist
.Parameters.AddWithValue("#build", abuildnumber.Text)
End With
Try
conn.Open()
Dim reader As SqlDataReader = cmd.ExecuteReader
If reader.HasRows Then
reader.Close()
abuildnumber.Text = rand.Next
End If
abrand.Enabled = True
apart.Enabled = True
aquantity.Enabled = True
aday.Enabled = True
amonth.Enabled = True
ayear.Enabled = True
add.Enabled = True
conn.Close()
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Using
End Using
End Sub

The Random will never "run out" of numbers, if that's what you are asking, but it will generate duplicates. You should probably avoid creating a new Random instance every time though, as that can lead the the numbers being less randomly distributed across all possible values. You should create a single source of random numbers, and draw from that each time, without recreating it. A class-level field would work well for this in your example.
If you are interested on just "random looking" numbers, with an extremely low chance of duplicates, you may want to investigate Guid.
Your code does contain another issue that concern me, mainly that the line abuildnumber.Text = rand.Next shows that you have Option Strict set to Off. This is a very bad practice that you should try your hardest to avoid. It can introduce subtle bugs that can be hard to track down once your code becomes more complicated.
Good job with the Using statement! I'm impressed that you are using it correctly on both the SqlConnection and the SqlCommand, most people miss the latter.

Yes, Random Numbers have limit. because its return INT value so its limits as per Int data type
Parameters
minValue
Type: System.Int32
The inclusive lower bound of the random number returned.
maxValue
Type: System.Int32
The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue.
Return Value Type:
System.Int32 A 32-bit signed integer greater than
or equal to minValue and less than maxValue; that is, the range of
return values includes minValue but not maxValue. If minValue equals
maxValue, minValue is returned.
Here is Reference https://msdn.microsoft.com/en-us/library/2dx6wyd4(v=vs.100).aspx

Related

Do While Loop doesn't stop looping in VB.net

I'm trying to generate a random number that's not in the database. If the randomly generated number happens to already be in the database, a message box appears saying the number exists. When you click Ok, it generates another number and if it's still in the database, it will repeat the same process. With my code, it keeps showing the message box and generating a number even after it has already generated a number that's not in the database. This is my code:
Private Sub BtnOrder_Click(sender As Object, e As EventArgs) Handles BtnOrder.Click
Dim rand As New Random
Dim num As Integer
num = rand.Next(1, 30)
TxtOrder.Text = "#" + num.ToString("0000")
BtnOrder.Enabled = False
Dim connection As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Daily Sales.accdb;")
Dim command As New OleDbCommand("SELECT [Order No] FROM [Table1] WHERE [Order No] = orderno", connection)
Dim orderParam As New OleDbParameter("orderno", Me.TxtOrder.Text)
command.Parameters.Add(orderParam)
command.Connection.Open()
Dim reader As OleDbDataReader = command.ExecuteReader()
Do While reader.HasRows = True
If reader.HasRows = False Then
Exit Do
End If
MessageBox.Show("Order number exists.", "Invalid Order Number")
num = rand.Next(1, 30)
TxtOrder.Text = "#" + num.ToString("0000")
Loop
command.Connection.Close()
End Sub
I think many things need to be changed. Some are listed in comments on your question, but here they are
You hit the database multiple times.
You don't follow the Disposable pattern which is implemented by both the connection and the command.
You had a loop whose condition looked for rows, then immediately checks for no rows to exit, which can never happen.
You don't actually check that the order number is in the result set.
You only create random numbers from 1 to 29, but if they all exist, the loop will continue forever.
You perform the database interaction on the UI thread.
You may find that using Using blocks to properly dispose of the Disposable objects helps with memory, and moving the query off the UI helps your UI remain responsive. Also, I don't see a need to alert the user that a random number has found a match in the database - just find a proper random number without alerting the user. Lastly, throw an Exception if you can't generate another order number.
Dim lowerInclusive As Integer = 1, upperExclusive As Integer = 30
Private Async Sub BtnOrder_Click(sender As Object, e As EventArgs) Handles BtnOrder.Click
BtnOrder.Enabled = False
Try
Dim nextOrderNumber = Await GetNextOrderNumber()
TxtOrder.Text = $"#{nextOrderNumber:0000}"
Catch ex As Exception
TxtOrder.Text = "N/A"
MessageBox.Show(ex.Message)
Finally
BtnOrder.Enabled = True
End Try
End Sub
Private Function GetNextOrderNumber() As Task(Of Integer)
Return Task.Run(
Function()
Dim existingOrderNumbers As New List(Of Integer)()
Using connection As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Daily Sales.accdb;")
connection.Open()
Using command As New OleDbCommand("SELECT [Order No] FROM [Table1]", connection)
Dim reader As OleDbDataReader = command.ExecuteReader()
While reader.Read()
existingOrderNumbers.Add(reader.GetInt32(0))
End While
End Using
End Using
Dim nextOrderNumbers = Enumerable.Range(lowerInclusive, upperExclusive - lowerInclusive).Except(existingOrderNumbers).ToList()
If Not nextOrderNumbers.Any() Then Throw New Exception("All possible order numbers are already used.")
Dim rand As New Random()
Return nextOrderNumbers(rand.Next(0, nextOrderNumbers.Count()))
End Function)
End Function
Thinking about it again, the new order number never goes into the database. So when does that happen? You may want that to be an atomic operation, if this code can be used by multiple people - multiple threads. You can use a transaction to lock down the table... or perhaps just use a PK ID as part of a hash to generate some random number.
So if you're looking for unique numbers, I suggest you change the DB column to become an IDENTITY column. That way, SQL server takes care of creating unique (increasing) numbers for you.
Query the current highest order number:
SELECT MAX([Order No]) AS OrderNoCurrent FROM Table1
Query the next order number:
SELECT (MAX([Order No])+1) AS OrderNoNext FROM Table1

Slow For Each Element in Array Loop with Instr

I need to check if values (approx 10.000 insecure passwords) stored in a one-dimensional array occur in a TextBox and display a warning if necessary.
But due to the constant checking after each keystroke the input into the TextBox is very delayed.
Does anyone know a faster way to do this?
Greetings Ronny
'ForbiddenPasswords = one-dimensional array
Private Sub txtAdminPassword_Change()
Dim VarDat As Variant
For Each VarDat In ForbiddenPasswords
If InStr(1, LCase(txtAdminPassword.Text), VarDat) > 0 Then
lblInsecureWarning.Visible = True
Exit For
End If
Next
End Sub
Rather than loading the list of banned passwords in an array and searching through it to find if it exists, consider using a table to store the (lower case) passwords. You can then open a recordset based on the text entered, and see if it returns any records. Something like:
Function fCheckBannedPWD(strPWD As String) As Boolean
' returns true if the entered password is banned, otherwise false
On Error GoTo E_Handle
Dim db As DAO.Database
Dim rsPWD As DAO.Recordset
Dim strSQL As String
Set db = CurrentDb
strSQL = "SELECT BannedPWD FROM tblPWD WHERE InStr('" & LCase(strPWD) & "',BannedPWD)>0;"
Set rsPWD = db.OpenRecordset(strSQL)
If Not (rsPWD.BOF And rsPWD.EOF) Then
fCheckBannedPWD = True
End If
fExit:
On Error Resume Next
rsPWD.Close
Set rsPWD = Nothing
Set db = Nothing
Exit Function
E_Handle:
MsgBox Err.Description & vbCrLf & vbCrLf & "fCheckBannedPWD", vbOKOnly + vbCritical, "Error: " & Err.Number
Resume fExit
End Function
Regards,
Arrays want to be iterated with For...Next; Object collections want to be iterated with For Each...Next. Using For Each to iterate a large array is going to be slower than it needs to be.
Converting 10K strings to lowercase is also taking a toll on performance.
Using InStr is verifying whether the password contains the forbidden password, not whether it is one of them: assuming you mean to check if the user's password exists in the forbidden passwords list, you don't need InStr.
If you want to stick with an array, use For...Next to iterate it, and specify Option Compare Text to have the = operator treat "string" the same as "STRING" (default is Option Compare Database in Access, which matches your database settings - in all other hosts it's Option Compare Binary, which is case-sensitive).
But you shouldn't need to iterate any array. Assuming the 10K strings are all unique, you can have them added to a keyed Collection, and then you can instantly know if the user's password is in the collection by trying to add the user's password to the collection:
Private Function IsForbiddenPassword(ByVal Value As String) As Boolean
'assuming a ForbiddenPasswords global collection exists
On Error Resume Next
ForbiddenPasswords.Add Value, Value 'if the key already exists, an error is raised
If Err.Number <> 0 Then
'key already exists: password is forbidden
IsForbiddenPassword = True
Else
'.Add was successful, password is not forbidden (gotta remove it now!)
ForbiddenPasswords.Remove Value
End If
On Error GoTo 0
End Function
Alternatively, use a Scripting.Dictionary and its Exists method, which returns True if a given key already exists:
Private Sub txtAdminPassword_Change()
'where ForbiddenPasswords is a Dictionary instance keyed with the passwords:
lblInsecureWarning.Visible = ForbiddenPasswords.Exists(Value)
End Sub
Try the next way, please:
Create a Private variable on top of the module (in the declarations area):
Private objExcel As Object
Copy the next function code:
Function passExists(strPass As String, arr) As Boolean
Dim lPath As String
lPath = UCase(strPass)
passExists = Not IsError(objExcel.Application.match(lPath, arr, 0))
End Function
It can be checked in the next way:
Sub checkPasswordInArray()
Dim ForbiddenPasswords, boolMatch As Boolean, strPass As String
If objExcel Is Nothing Then Set objExcel = CreateObject("Excel.Application")
ForbiddenPasswords = Split("pass1,pass2,pass3", ",")
strPass = "Pass2" 'txtAdminPassword.Text
Debug.Print passExists(strPass, ForbiddenPasswords)
End Sub
In order to be very fast, objExcel must not be created each time you need to use the function, but you must take care to quit after using it...
Just to be complete, 2 additions to the propositions found.
txtAdminPassword.Text LIKE vardat & "*" is about 6x faster than
InStr(1, LCase(txtAdminPassword.Text), VarDat) > 0
for even better perf with your array, you can use Filter:
Dim strSubNames As Variant
strSubNames = Filter(forbiddenPasswords, txtAdminPassword.Text)

reduce string to compare

I would like to know the most effective way to compare long strings, having a database sql server, such as:
long string -> compare to-> 5,000 records in database
I happened to convert the records and the string to compare, using crc32, but I'm not sure if it would be a more efficient method
If u have a database,then u can go with a DataTable/DataSet/DataReader,let's assume u are using a DataReader
Dim cmd as new SqlCommand("Select * from table",connectionstring)
Dim dr as SqlDataReader = cmd.ExecuteReader
While dr.Read
If dr(1).ToString = "MyString" Then
''''Something will happen
End if
End While
If there are no other partially identifying fields you could check then make a new field of, perhaps the first 10 characters of the string. Make an index on this field and then take the first 10 characters of string to insert as a parameter. This should reduce the number of long comparisons you need to do. Of course my selection of 10 characters is arbitrary. You would select a number that you can see from your data that would reduce the number of rows returned significantly but be as small as possible.
Private Function OKayToInsert(stringToInsert As String) As Boolean
Using connection As New SqlConnection("Your connection string")
Dim stringShort As String = stringToInsert.Substring(0, 10)
Using cmd = New SqlCommand("Select LongString From StringTable Where NewShortField = #ShortString", connection)
cmd.Parameters.Add("#ShorString", SqlDbType.NVarChar, 100).Value = stringShort
Using reader As SqlDataReader = cmd.ExecuteReader()
If reader.HasRows Then
While reader.Read
If stringToInsert = reader.GetString(0) Then
Return False
End If
End While
Return True
Else
Return True
End If
End Using
End Using
End Using
End Function

System.ArgumentOutOfRangeException in For loop due to entry deleted from database. Help fix it

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim TimeNow As DateTime
TimeNow = DateTime.Now
With ReservationTableDataGridView
For i As Integer = 0 To (.Rows.Count - 1)
ListView1.BackColor = Color.Black
Dim ToBeTested As DateTime
ToBeTested = (.Rows(i).Cells(1).Value) #
Console.WriteLine(ToBeTested)
If ToBeTested > TimeNow.AddMinutes(45) Then
Me.ReservationTableTableAdapter.Delete((.Rows(i).Cells(4).Value), (.Rows(i).Cells(1).Value), (.Rows(i).Cells(2).Value), (.Rows(i).Cells(3).Value))
Me.ReservationTableTableAdapter.Fill(Me.CustomerResDataBaseDataSet.ReservationTable)
End If
Next
End With
End Sub
I am trying to write some code to delete a reservation from a data base after it has been 45 minutes after the set reservation time has passed. I keep getting the System.ArgumentOutOfRangeException error. I believe this is because after it delete an item from the database it has less values then the index. Can anyone please help me to fix this?
This was the error gotten, {"Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index"}. I believe this was due to the '#" line.
Try going backwards - For i As Integer = (.Rows.Count - 1) to 0 Step -1.
The issue is that you are deleting rows in the ReservationTableDataGridView as you try to iterate over them, thus changing the index.
By going backwards you always have a valid index.

Retrieving Stored Procedure Results

I'm trying to switch a user between three different screens depending on what a stored procedure returns in a BtnView_Click procedure in asp.net VB. The SP would return a "0, 1, or NULL". Currently it's only returning a "1" and not the others. I'm having trouble with the Reader.Read area with the IF statement and i'm wondering if there's a simple fix to this so it directs everything accuratley.
This is what I currently have (updated)
Sub BtnView_Click(ByVal sender As Object, ByVal e As CommandEventArgs)
Session.Add("SvarRecord", e.CommandArgument)
Dim sb As New System.Text.StringBuilder()
Dim connectionString As String = ConfigurationManager.ConnectionStrings("CS_Connection").ConnectionString
Using connection As New SqlConnection(connectionString)
Dim myCommand As New SqlCommand("View", connection)
myCommand.CommandType = CommandType.StoredProcedure
Dim sqlRecord As SqlParameter = New SqlParameter("#Name", SqlDbType.VarChar)
sqlRecord.Value = Session("SvarRecord")
myCommand.Parameters.Add(sqlRecord)
connection.Open()
Using reader As SqlClient.SqlDataReader = myCommand.ExecuteReader
REM Read() returns True if data can be read
If reader.Read() Then
REM IsDbNull checks if given column (by ordinal) contains DbNull.
REM You need it because you can not convert DbNull to a number. As alternative
REM you may read it as object and compare by yourself.
If reader.IsDBNull(0) Then
Response.Redirect("Entry.Aspx")
REM We are sure it is not DbNull and we can assume it is an integer
ElseIf reader.GetInt32(0) = 0 Then
Response.Redirect("Negatives.Aspx")
ElseIf reader.GetInt32(0) = 1 Then
Response.Redirect("PrevEntry.Aspx")
End If
End If
reader.Close()
End Using
connection.Close()
connection.Dispose()
End Using
You're comparing HasRows property (a Boolean that indicates if recordset is empty or not), not value returned from your stored procedure.
Change your code to:
Using reader As SqlClient.SqlDataReader = myCommand.ExecuteReader
Rem Read() returns True if data can be read
If reader.Read() Then
Rem IsDbNull checks if given column (by ordinal) contains DbNull.
Rem You need it because you can not convert DbNull to a number.
Rem As alternative you may read it as object and compare by yourself.
If reader.IsDbNull(0) Then
Response.Redirect("Entry.Aspx")
Rem We are sure it is not DbNull and we can assume it is an integer
ElseIf reader.GetInt32(0) = 0 Then
Response.Redirect("Negatives.Aspx")
ElseIf reader.GetInt32(0) = 1 Then
Response.Redirect("PrevEntry.Aspx")
End If
End If
End Using
Here I assume your stored procedure returns an integer value. If it's not you can get/compare with right value or convert it to integer. First case (same for the other If):
ElseIf reader.GetString(0) = "0" Then
Second case:
ElseIf Convert.ToInt32(reader.GetObject(0)) = 0 Then
Last note about your code, as suggested by Jhon in his comment your code may fail for an unlimited number of reasons, you'd better to always wrap disposable objects in a Using statement like this:
Dim connectionString As String =
ConfigurationManager.ConnectionStrings("CS_Connection").ConnectionString)
Using connection As New SqlConnection(connectionString)
Rem Put here all code that uses connection
End Using
This will ensure connection and other shared (and limited!) resources will always be released even in case of error. Edit: compiler won't complain about comparison of a Boolean and a String because you didn't set OPTION STRICT to ON (see #Tim's answer for more details).
First, use the Using-statement to dispose/close the connection and anything else implementing IDisposable even on error. Second, you should really set OPTION STRICT to on globally, then this will not compile which is a good thing:
If reader.HasRows = "0" Then
The problem with that code is that HasRows is a Boolean but you are comparing it with a String. That would result in a compiler error normaly, but OPTION STRICT off allows it. The Boolean will be converted to a String implicitely. So this comparison seems to work but it does not.
Actually you have to read the field, you can use the Get... methods:
If reader.HasRows Then
If reader.IsDBNull(0) Then
Response.Redirect("Entry.Aspx")
ElseIf reader.GetInt32(0) = 1 Then
Response.Redirect("PrevEntry.Aspx")
ElseIf reader.GetInt32(0) = 0 Then
Response.Redirect("Negatives.Aspx")
End If
End If

Resources