MySQL String Not Converting to .NET Time - mysql

I am trying to convert a MySQL time to a string using VB.NET.
Dim adpt As New MySqlDataAdapter(dbcmdstring, connection)
Dim myDataTable As New DataTable()
adpt.Fill(myDataTable)
DataGridView1.DataSource = myDataTable
DataGridView1.Columns.Remove("ActivityID")
DataGridView1.Columns.Remove("ActivityDate")
DataGridView1.Columns.Remove("UserID")
DataGridView1.Columns(0).HeaderCell.Value = "Name"
DataGridView1.Columns(1).HeaderCell.Value = "Start Time"
DataGridView1.Columns(2).HeaderCell.Value = "End Time"
DataGridView1.Columns.Add("Duration", "Duration")
DataGridView1.RowHeadersVisible = False
Dim duration As New TimeSpan
Dim durationStr As String = ""
Dim i As Integer = 0
For Each row As DataGridViewRow In DataGridView1.Rows
duration = Date.Parse(row.Cells(2).Value.ToString).Subtract(Date.Parse(row.Cells(1).Value.ToString))
durationStr = Math.Round(duration.TotalMinutes).ToString & ":" & Math.Round(duration.TotalSeconds).ToString
row.Cells(3).Value = durationStr
Next
When the date is parsed during the construction of the duration variable, it throws an error:
An unhandled exception of type 'System.NullReferenceException'
occurred in WindowsApplicationSQL.exe
Additional information: Object reference not set to an instance of an
object.
I can successfully parse the date and show it in a messagebox, but not convert it to a useable string. I have also tried using just the .value of the time as well.
Any help?

There is a much easier way to get a Duration, and a much, much easier way.
Part of the problem is this: I am trying to convert a MySQL time to a string using VB.NET. There is no need to convert to string. If the column is in fact, a Time() column in MySQL, it's has a NET counterpart: TimeSpan and it can be easily used to calculate a difference using subtraction.
Much Easier Method
dtLog.Columns.Add(New DataColumn("Duration", GetType(TimeSpan)))
For Each r As DataRow In dtLog.Rows
r("Duration") = r.Field(Of TimeSpan)("EndTime") - r.Field(Of TimeSpan)("StartTime")
Next
If you are using a DataSource, it is rarely a good idea to manipulate the data thru the DataGridView. Note: no strings were needed.
Much, MUCH Easier Method
Perform the operation in SQL:
Dim sql = "SELECT ... StartTime, EndTime, TIMEDIFF(EndTime, StartTime) As Duration FROM ActivityLog"
This will create a Duration column in the DataTable containing the result. Similarly, if you dont want certain columns, you can omit them from the SQL to start rather than removing DGV Columns.
Object reference not set to an instance of an object.
Finally, this error probably has nothing to do with the conversion of MySQL to VB, strings or TimeSpans. By default, the DGV has that extra row at the bottom - the NewRow for the user to start adding data - but all the cells are Nothing. So:
For Each row As DataGridViewRow In DataGridView1.Rows
This will try to process the NewRow and poking around it's cells will result in an NRE. When you switched to the datatable, it went away because they dont have the extra row. You still don't need all those gyrations though.

I tried, instead, doing it via the DataTable rather than the DataGridView and it worked!
myDataTable.Columns.Add("Duration", GetType(String))
myDataTable.Columns.Remove("ActivityID")
myDataTable.Columns.Remove("ActivityDate")
myDataTable.Columns.Remove("UserID")
DataGridView1.DataSource = myDataTable
DataGridView1.RowHeadersVisible = False
DataGridView1.Columns(0).HeaderCell.Value = "Name"
DataGridView1.Columns(1).HeaderCell.Value = "Start Time"
DataGridView1.Columns(2).HeaderCell.Value = "End Time"
Dim duration As New TimeSpan
Dim durationStr As String = ""
Dim i As Integer = 0
For Each row As DataRow In myDataTable.Rows
duration = Date.Parse(row.Item("EndTime").ToString).Subtract(Date.Parse(row.Item("StartTime").ToString))
durationStr = Math.Round(duration.Minutes).ToString & ":" & Math.Round(duration.Seconds).ToString
row.Item("Duration") = durationStr
Next

Related

Checking if the fund is sufficient

I am currently doing an ATM Management System, I want my program to check if the amount entered does not exceed the account's balance on database. Here is my code:
Dim w As Double
w = Val(txtwithdraw.Text)
adapter = New MySql.Data.MySqlClient.MySqlDataAdapter("SELECT `balance` FROM `jaagbank` WHERE
acctnum = '" & Form1.namebox.Text & "'", con)
dtable.Clear()
adapter.Fill(dtable)
If w > dtable.Rows.Count Then
MsgBox("Insufficient Balance")
txtwithdraw.Clear()
Return
End If
Decimal is a good datatype to used for money. Don't use the vb6 Val(). You can get unexpected results. Put Option Strict and Option Infer on. I used .TryParse to test the input in txtwithdraw. If it returns True, it puts the converted decimal value in WithdrawalAmount.
Keep your data objects local to the method where they are used. Connections and commands need to be closed and disposed. Using...End Using blocks handle this for us even if there is an error.
Always use parameters to avoid sql injection. If the code is running in Form1, do not qualify namebox with Form1. It seems a bit strange that a field named acctnum is not a number but a String containing a name. You will need to check your database for the correct datatype and field size. I had to guess.
Since you are retrieving a single piece of data, you can use .ExecuteScalar which will give you the first column of the first row.
Private Sub OPCode()
Dim WithdrawalAmount As Decimal
If Not Decimal.TryParse(txtwithdraw.Text, WithdrawalAmount) Then
MessageBox.Show("Please enter a valid withdrawal amount.")
Return
End If
Dim Balance As Object
Using con As New MySqlConnection(ConStr),
cmd As New MySqlCommand("SELECT `balance` FROM `jaagbank` WHERE acctnum = #Name;", con)
cmd.Parameters.Add("#Name", MySqlDbType.VarChar, 100).Value = Form1.namebox.Text
con.Open()
Balance = cmd.ExecuteScalar
End Using
If Balance Is Nothing Then
MessageBox.Show("Account not recognized.")
Return
End If
If WithdrawalAmount > CDec(Balance) Then
MsgBox("Insufficient Balance")
txtwithdraw.Clear()
Return
End If
End Sub

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

Populating ListBox More Quickly

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

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 **

Comparing datatables / identify identical datatables

I am working with VB.net and mysql, I have a function which fills a datatable with mysql content then posts it (as appropriate) to a listview. This function is currently on a timer which activates every 5 seconds which unfortunately does horrible destruction to the drag-and-drop features of my software.
The solution: I have decided after filling the content to the listview to copy the content from the active datatable to another datatable for comparison, every time the data is taken from the mysql db have it save to a datatable and compare the two datatables - if they are not identical the software should run the function however if they are identical there is no reason to update the listview with the same exact data.
Public pendrun As New DataTable
Public postrun As New DataTable
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
con.ConnectionString = "server=localhost;" _
& "user id=user;" _
& "password=password;" _
& "database=DMT"
adptr = New MySqlDataAdapter("Select * from data", con)
Try
adptr.Fill(pendrun)
Catch err As Exception
Dim strError As String = "Exception: & err.ToString()"
End Try
if pendrun <> postrun then
ListView2.Items.Clear()
pendrun.Clear()
' commands to add
' pendrun datatable information
' to listview
postrun = pendrun 'to transfer data to the holding datatable
end if
the problem is
if pendrun <> postrun then
is not a valid way to compare datatables.
How do I identify if the datatables are identical (all rows and columns identical)
There's no built-in understanding of "identical" for types you've defined. You'll need to write that logic.
Essentially you need to build a function which accepts two DataTables as input, compares them in accordance with whatever business logic you define, and returns a Boolean indicating whether or not they pass that logic. Something like this:
Function TablesAreEqual(ByVal firstTable As DataTable, ByVal secondTable As DataTable) As Boolean
' Compare your tables, probably by looping through rows/values
Return areEqual
End Function
Then you'd use that function in your comparison:
If TablesAreEqual(pendrun, postrun) Then
Here is the resulting code I created to deal with this problem - thanks to David for the direction
Function TablesAreEqual(ByVal firstTable As DataTable, ByVal secondTable As DataTable) As Boolean
dim db1 As String = ""
dim db2 As String = ""
For rown As Integer = 0 To firstTable.Rows.Count - 1
For cown As Integer = 0 To firstTable.Columns.Count - 1
db1 = db1 & firstTable.Rows(rown).Item(cown)
Next
Next
For rown As Integer = 0 To secondTable.Rows.Count - 1
For cown As Integer = 0 To secondTable.Columns.Count - 1
db2 = db2 & secondTable.Rows(rown).Item(cown)
Next
Next
Return db1 = db2
End Function
If you want to use your pendrun and postrun datatables
try to serialize "writeXml" datatables and compare them.
Anyway i suggest you to think about a different approach instead of using a polling timer.
Better yet why not put in logic to not get new data if it has not changed.
Don't know about MySQL but MSSQL has Notification and TimeStamp
The columns should only change with a schema change.
First compare count and if not the same for sure there was a change.
If counts are the same then loop through the columns and compare.
As soon as one does not compare then done immediately return false.
On rows same thing. First test row count.
After that need to compare row by row and column by column.
Remember to sort the select statement so you would catch rows out of order.
And again as soon as one does not compare stop and return false.
Right with less code than you have in your answer
My inserts are C#
Function TablesAreEqual(ByVal firstTable As DataTable, ByVal secondTable As DataTable) As Boolean
if(firstTable.Rows.Count != secondTable.Rows.Count) return false;
if(firstTable.Columns.Count != secondTable.Columns.Count) return false;
For rown As Integer = 0 To firstTable.Rows.Count - 1
For cown As Integer = 0 To firstTable.Columns.Count - 1
if (firstTable.Rows(rown).Item(cown) != secondTable.Rows(rown).Item(cown)) return false;
Next
Next
return true;
End Function