Populating ListBox More Quickly - mysql

Is there a way to make populating ListBox fast, because the UI is freezing on form load upon populating the ListBox?
This is my form load code:
Dim abc As String = itemCount()
Dim output = Account_Get(a)
For Each s In output
ListBox1.Items.Add(s)
count1 += 1
If count1 = abc Then
ListBox1.Visible = True
End If
Next
This is the query in module:
Public Function Account_Get(ByVal chk As String) As List(Of String)
Dim result = New List(Of String)()
Try
cn.Open()
sql = "select column_name as str from table where status = 'New' order by rand()"
cmd = New MySqlCommand(sql, cn)
dr = cmd.ExecuteReader
While dr.Read
result.Add(dr("str").ToString())
End While
Return result
Catch ex As Exception
MsgErr(ex.Message, "Error Encounter")
Return Nothing
Finally
cn.Close()
End Try
End Function
this is working fine. but the fact that it loads too many datas. the ui is freezing on load. hoping someone could help me with this. thanks!

Since you are incrementing count1 I assume it is some sort of number. However, you are then comparing it to a string in the If statement. Please use Option Strict.
Changed the Function to return an Array of String. Took the random sort form the sql statement and moved it to a little linq at the end or the function.
You could add a Stopwatch to the data retrieval and the display sections to see where your bottleneck is. BeginUpdate and EndUpdate on the listbox prevents repainting on every addition.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim output = Account_Get()
ListBox2.BeginUpdate()
ListBox2.Items.AddRange(output)
ListBox2.EndUpdate()
End Sub
Private Rand As New Random
Public Function Account_Get() As String()
Dim dt As New DataTable
Dim result As String()
Using cn As New MySqlConnection("Your connection string")
Dim Sql = "select column_name as str from table where status = 'New'" 'order by rand()"
Using cmd = New MySqlCommand(Sql, cn)
Try
cn.Open()
dt.Load(cmd.ExecuteReader)
Catch ex As Exception
MessageBox.Show(ex.Message, "Error Encounter")
Return Nothing
End Try
End Using
End Using
result = (From dRow In dt.AsEnumerable()
Let field = dRow("str").ToString
Order By Rand.Next
Select field).ToArray
Return result
End Function

The query you are using contains a random order. Ordering records randomly can be a huge performance issue within MySQL as it has to go through all records in the table and then sort them randomly. The more records in the table, the bigger the performance penalty. There is also no limitation on the number of records in your query. So if there are thousands of items in your table the listbox will also be thousands of items in size, which could also take a long time.
If you really require the random ordering you could do something about it in your code. I'm now assuming here that you are: 1) using identifiers in your table, 2) you actually wish to limit the number of items in your listbox and not display all of them.
Get a grasp of the total number of records in the table by a query
Pick a random number from the range of items in your table
Fetch the nearest record
Hope this helps you to get going to find a solution

Related

Delete command using ID from DataGridView

I have data shown in DataGridView. I made a Button when selecting IDs in the grid the code below runs but I keep getting an error.
Dim cnx As New MySqlConnection("datasource=localhost;database=bdgeststock;username=root;password=")
Dim cmd As MySqlCommand = cnx.CreateCommand
Dim resultat As Integer
If ConnectionState.Open Then
cnx.Close()
End If
cnx.Open()
If grid.SelectedCells.Count = 0 Then
MessageBox.Show("please select the ids that u want to delete")
Else
cmd.CommandText = "delete from utilisateur where idu= #P1"
cmd.Parameters.AddWithValue("#P1", grid.SelectedCells)
resultat = cmd.ExecuteNonQuery
If (resultat = 0) Then
MessageBox.Show("error")
Else
MessageBox.Show("success")
End If
End If
cnx.Close()
cmd.Dispose()
How does this make sense?
cmd.Parameters.AddWithValue("#P1", grid.SelectedCells)
As you tagged this question WinForms, you are presumably using a DataGridView rather than a DataGrid (names matter so use the right ones). In that case, the SelectedCells property is type DataGridViewSelectedCellCollection. How does it make sense to set your parameter value to that? How is that going to get compared to an ID in the database?
If you expect to use the values in those cells then you have to actually get those values out. You also need to decide whether you're going to use a single value or multiple. You are using = in your SQL query so that means only a single value is supported. If you want to use multiple values then you would need to use IN and provide a list, but that also means using multiple parameters. I wrote an example of this type of thing using a ListBox some time ago. You can find that here. You could adapt that code to your scenario like so:
Dim connection As New SqlConnection("connection string here")
Dim command As New SqlCommand
Dim query As New StringBuilder("DELETE FROM utilisateur")
Select Case grid.SelectedCells.Count
Case 1
query.Append(" WHERE idu = #idu")
command.Parameters.AddWithValue("#idu", grid.SelectedCells(0).Value)
Case Is > 1
query.Append(" WHERE idu IN (")
Dim paramName As String
For index As Integer = 0 To grid.SelectedCells.Count - 1 Step 1
paramName = "#idu" & index
If index > 0 Then
query.Append(", ")
End If
query.Append(paramName)
command.Parameters.AddWithValue(paramName, grid.SelectedCells(index).Value)
Next index
query.Append(")")
End Select
command.CommandText = query.ToString()
command.Connection = connection
SelectedCells is a collection of cells
so it never can be only one id, so you have to guess which was you want or only allow one row to be selected
grid.SelectedCells(0).Value.ToString()
Or you have to program a loop to delete all selected rows

Select random database record with a lowest value

very new to VB so apologies if I don't make a lot of sense.
I have a database of trivia questions with three fields: Question, Answer and Times Asked.
I want to be able to hit a button and receive a question from the database at random from among the records with the lowest Times Asked value, then add one to that record's Times Asked value.
Can anyone help me out? Here's what I've got so far:
Private DBCon As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0; "Data Source=Trivia.accdb;")
Public Sub GenerateQuestion()
DBCon.Open()
'grab how many questions there are total
Dim QuestionsQuery As String = "SELECT COUNT(*) FROM Questions"
Dim QuestionsCmd As New OleDbCommand(QuestionsQuery, DBCon)
Dim NumOfQuestions As Integer = QuestionsCmd.ExecuteScalar()
'grab the lowest number from Times Asked
Dim SeenQuery As String = "SELECT MIN(`Times`) FROM Questions"
Dim SeenCmd As New OleDbCommand(SeenQuery, DBCon)
Dim SeenReader As OleDb.OleDbDataReader = SeenCmd.ExecuteReader
SeenReader.Close()
End Sub
This sub is called from a button on another form and when I click the button I get an exception for no data existing in the row/column. Even if I manage to get this value though I have no idea what the next step is with actually grabbing a random question from among the minimum Times values and adding one to the Times value for that question.
For reference here is the table I'm working with atm:
http://i.imgur.com/2vwlVQR.jpg
EDIT: To anyone finding this in the future here is the code that fixed the problem:
Imports System.Data.OleDb
Dim rng As New Random
Dim connection As OleDb.OleDbConnection
Private Function GetQuestionWithAnswer() As Tuple(Of String, String)
'Use XML literals for the SQL statements.
Dim selectSql = <sql>
SELECT *
FROM Questions
WHERE Times =
(
SELECT MIN(Times)
FROM Questions
)
</sql>
Dim updateSql = <sql>
UPDATE Questions
SET Times = #Times
WHERE ID = #ID
</sql>
Using connection As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0; Data Source=Trivia.accdb;")
Dim adapter As New OleDbDataAdapter(selectSql.Value, connection)
Dim updateCommand = New OleDbCommand(updateSql.Value, connection)
With updateCommand.Parameters
.Add("#Times", OleDbType.Integer, 0, "Times")
.Add("#ID", OleDbType.Integer, 0, "ID")
End With
adapter.UpdateCommand = updateCommand
Dim table As New DataTable
'Getall the questions that have been asked the fewest number of times.
adapter.Fill(table)
'Select a question at random.
Dim record = table.Rows(rng.Next(table.Rows.Count))
'Increment the number of times the question has been asked and save the change.
record("Times") = CInt(record("Times")) + 1
adapter.Update(table)
'Package up the selected question.
LblQuestion.Text = record(1)
Return Tuple.Create(CStr(record("Question")), CStr(record("Answer")))
End Using
End Function
Here you can find the use of Rand in Mysql:http://jan.kneschke.de/projects/mysql/order-by-rand/
If you use Rand, it is not necessary to Select questions which are not shown mostly. It will be balanced if you use only Rand.

Checking which row from DataGridView exists in MySQL takes too long

I'm executing a query which returns me around 400 records (orders) and I display them in DataGridView. Then I have to check each row and make a green every row which exists in other MySQL database (by ID). I think I'm not doing this optimally. Here is what I am doing:
For Each oRow As DataGridViewRow In dgv.Rows
Dim orderNumber As Integer = oRow.Cells(0).Value
Dim exist As Boolean = mySql.CheckIfExists(shop, orderNumber)
If Not exist Then
Continue For
End If
If exist Then
oRow.DefaultCellStyle.BackColor = Color.LightGreen
oRow.DefaultCellStyle.SelectionBackColor = Color.Green
oRow.DefaultCellStyle.SelectionForeColor = Color.LightCoral
Continue For
End If
Next
Here is CheckIfExists() method in MySQL class:
Public Function CheckIfExists(ByVal shop As String, ByVal orderNumber As Integer) As Boolean
Dim dt As New DataTable
Dim sql As String = "" 'sql query'
Connect(mSOH) 'msoh is a connection string from My.Settings'
Dim sqlCommand As New MySqlCommand
With sqlCommand
.Connection = connection
.CommandText = sql
End With
Try
Dim sqlReader As MySqlDataReader = sqlCommand.ExecuteReader()
While sqlReader.Read()
Return True
End While
Catch ex As MySqlException
Logi.LogInfo(ex)
Catch ex As Exception
Logi.LogInfo(ex)
Finally
Disconnect()
End Try
Return False
End Function
And Connect and Disconnect methods if they are important:
Private Sub Connect(shop As String)
Select Case shop
Case "jablotron"
connection = New MySqlConnection(csJablotron)
Case "bcs"
connection = New MySqlConnection(csBCS)
Case "mSOH"
connection = New MySqlConnection(csmSOH)
Case Else
connection = New MySqlConnection(shop)
End Select
Try
connection.Open()
Catch ex As MySqlException
Logi.LogInfo(ex)
Catch ex As Exception
Logi.LogInfo(ex)
End Try
End Sub
Private Sub Disconnect()
Try
connection.Dispose()
Catch ex As MySqlException
Logi.LogInfo(ex)
Catch ex As Exception
Logi.LogInfo(ex)
End Try
End Sub
So if I try to check each row by this way it takes some time (around <1 second if database is on localhost, and around 30 second if database is on remote server and I try to connect via VPN). Is this way being optimal - checking each row like this? Is it a good approach? Please give me some tip and advice :) I know that code is in VB.NET, but guys from C# can also help me :)
Use this logic: Pre-load your order numbers into lists and cache them. For how long? - this is something for you to decide, based on how volatile the data is. Then check against those lists.
As well, It will be faster if you just parse your grid data by database and build In statements for each of your databases and load all needed records at once. Then only loop through already loaded data. And only relative data will be loaded.
Dim existingOrders as New List(of String)
Dim shopOrders as New List(of String)
/* Collect orders into list */
For Each oRow As DataGridViewRow In dgv.Rows
Dim orderNumber As Integer = oRow.Cells(0).Value
shopOrders.Add(orderNumber)
Next
/* Get and accumulate existing orders from each shop */
For Each shop as string In shops
GetOrdersByShop(shop, shopOrders, existingOrders)
Next
For Each oRow As DataGridViewRow In dgv.Rows
If existingOrders.Contains(oRow.Cells(0).Value) Then
oRow.DefaultCellStyle.BackColor = Color.LightGreen
oRow.DefaultCellStyle.SelectionBackColor = Color.Green
oRow.DefaultCellStyle.SelectionForeColor = Color.LightCoral
End If
Next
/* ---------------------------------------------------- */
Private Sub GetOrdersByShop(shop As string, shopOrders As List(of String),
existingOrders as New List(of String))
/* here, turn shopOrders into In('ord1', 'ord2', . . . )
and after running query add found orders to existingOrders */
End Sub
You can also use dictionary - it will make search work faster than list and you may store which order is in which shop, etc.
First off, a DataGridView is a VIEW object. You should use it as such. For data, you use a data object.
Personally I would do something like this:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim dtMain = SqlReadDatatable(firstConnString, "SELECT * FROM SomeTable")
Dim lstGreenIds = (From row In SqlReadDatatable(secondConnString, "SELECT ID FROM SomeOtherTable")
Select CInt(row("ID"))).ToList
dtMain.Columns.Add(New DataColumn("Exists", GetType(Boolean)))
For Each dtRow As DataRow In (From row In dtMain.Rows Where lstGreenIds.Contains(CInt(row("ID"))))
dtRow("Exists") = True
Next
DataGridView1.DataSource = dtMain
End Sub
Private Function SqlReadDatatable(ByVal connStr As String, ByVal query As String) As DataTable
Dim dt As New DataTable
Using conn As New SqlConnection(connStr) : conn.Open()
Using cmd As New SqlCommand(query, conn)
dt.Load(cmd.ExecuteReader)
End Using
End Using
Return dt
End Function
Private Sub DataGridView1_RowPostPaint(sender As Object, e As DataGridViewRowPostPaintEventArgs) Handles DataGridView1.RowPostPaint
Dim dgv = CType(sender, DataGridView)
Dim dgvRow = dgv.Rows(e.RowIndex)
Dim dtRow = CType(dgvRow.DataBoundItem, DataRow)
If dtRow("Exists") Then
dgvRow.DefaultCellStyle.BackColor = Color.LightGreen
dgvRow.DefaultCellStyle.SelectionBackColor = Color.Green
dgvRow.DefaultCellStyle.SelectionForeColor = Color.LightCoral
End If
End Sub
This cleanly separates data from visualisation. The color is only applied when the row comes into view, no need to iterate over the whole bunch at once.
EDIT: I should also note that the cleanest way to do this would be server-side. In MySQL you can use cross-database queries if both databases are on the same server. If they are on different servers, you can create a FEDERATED TABLE on one of the servers and connect it to the remote server. You can read about those here: http://dev.mysql.com/doc/refman/5.0/en/federated-storage-engine.html . In the case of two databases on the same server, you could just do a join:
SELECT f.*, NOT ISNULL(s.OrderNum) AS Exists
FROM 'firstdb'.'firsttable' f
LEFT JOIN 'seconddb'.'secondtable' s ON f.OrderNum = s.OrderNum
And then use the Exists column in code to color the row.

How do I loop Through Each Records in SQL server with Time Gap

I am making a CMS portal for my company which will fetch records from SQL server using ASP.NET:
My problem is that when I fetch values it only shows the last one. But my need is that it should display one by one values with say 5-10 seconds gap in between here is my code:
Imports System
Imports System.Data.Sql
Imports System.Data.SqlClient
Partial Class _Default
Inherits System.Web.UI.Page
Dim connectionString As String = "Data Source=soemserv;Initial Catalog=data;User Id=master; Password=hushotn;"
Dim conn As New SqlConnection(connectionString)
Public Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
conn.Open()
Dim comm As New SqlCommand("select Queue,[Timing(IST)],[Status at],TAT,[Staffing at] from DTD_LCD_Queue_Status", conn)
Dim reader As SqlDataReader = comm.ExecuteReader
While reader.Read()
lblQueueName.Text = reader("Queue").ToString.Trim
lblTimingIST.Text = reader("Timing(IST)").ToString.Trim
lblStatusAt.Text = reader("Status at").ToString.Trim
lblTAT.Text = reader("TAT").ToString.Trim
lblStaffingAt.Text = reader("Staffing at").ToString.Trim
End While
End Sub
End Class
How do I loop though each record I tried Do and Loop but its not working...
Thanks in advance!
EDIT
One Step Ahead with no LUCK!!!!
I have written this code but still getting the last row only
For I As Integer = 0 To 15
lblTemp.Text = I.ToString
Dim comm As New SqlCommand("select Queue,[Timing(IST)],[Status at],TAT,[Staffing at] from DTD_LCD_Queue_Status where SrNo='" + lblTemp.Text + "';", conn)
Dim reader As SqlDataReader = comm.ExecuteReader
While reader.Read()
lblQueueName.Text = reader("Queue").ToString.Trim
lblTimingIST.Text = reader("Timing(IST)").ToString.Trim
lblStatusAt.Text = reader("Status at").ToString.Trim
lblTAT.Text = reader("TAT").ToString.Trim
lblStaffingAt.Text = reader("Staffing at").ToString.Trim
Thread.Sleep(2000)
End While
Next
Note I have dynamically given rows i.e 0 to 15
Please guide me!!
Only the last values are printed because : you are fetching a group of rows(data) through the readr and in first iteration of the while loop the first rows(data) are copied to the controls then for the second iteration the contents in the controls are over written with the second row. This will happens in each iteration hence only the last data are displayed.
- This can be avoided by using
Group of controls which are dynamically created(but not practical and good)
using DataGridView(grid) to display the items How to do this
**The method you ware suggested (using Timer) is also possible but it is not a good programming practice only the last values ware displayed all the others flashed for a duration of 5-10 seconds **

How can I query by an value which is segmented, as 123-456-789 in mysql in vb.net?

My problem is, when I query by a usual ID, like number with no segments = 1234567890, it works nicely.
But I need to query by some kind of segmented values or ID as = 123-4567-890, when I try by 123-4567-890 this id it does not query anything in mysql although in the database this 123-4567-890 ID is present.
So what is the possible solution to search by segmented value in mysql in VB.NET
Here is below, my trying codes in vb
Public Sub student()
textbox1.text= "123-4567-890"
Try
dbConn()
Dim myAdapter As New MySqlDataAdapter("Select studentID, batchID, studentStatus from student where studentID= " & textbox1.text, ServerString)
Dim myDataTable As New DataTable
myAdapter.Fill(myDataTable)
If myDataTable.Rows.Count > 0 Then
vrSID = myDataTable.Rows(0).Item("studentID")
vrRecBatchID = myDataTable.Rows(0).Item("batchID")
vrAttendanceStatus = myDataTable.Rows(0).Item("studentStatus")
If vrSID = vrIDD Then
If vrAttendanceStatus = "Active" Then
Console.Beep()
batchRoutine()
Else
led3()
Console.Beep()
End If
End If
Else
Console.Beep()
teacher()
End If
Catch ex As Exception
Console.Beep()
MsgBox ("Error")
End Try
End Sub
When you are running into problems, the easiest way to solve them is to break things down to the simplest scenario. So instead of complicating things with table adapters and data tables, see what happens when you just send MySQL the query you 'think' works correctly.
So start out by seeing if you can get this to work, then go from there:
Dim sql As String = "select id from mytable where id = '123-4567-890'"
Using cnx As New MySqlConnection("connection_string")
Using cmd As New MySqlCommand(sql, cnx)
cnx.Open()
Using reader As MySqlDataReader = cmd.ExecuteReader()
Debug.Assert(reader.Read(), "No results")
Trace.WriteLine(reader.GetValue(reader.Item(0)))
End Using
End Using
End Using
if the column is in-fact a sort of student ID column (not social security I would expect), and the table has it without special characters, I would pre-strip the extra formatting (hyphens) from the string and pass the CLEANED value to the query as a parameter to get results.
Also, by explicitly adding the query string Plus the text box value, you are leaving yourself WIDE OPEN to sql injection. Look into Parameterized queries (my guess is it would be an object something like MySqlDataParameter data type.)