Extracting the First (Oldest) Value from Dataset Based on Column Value - sql

I don't have a great deal of experience working with DataSets and haven't been able to find the best way of achieving what I want to achieve.
I basically create a DataSet using a SQL Query and then I am trying to find a Specific Value in the 'Field' column and then if there is a 'Y' in the 'Flag' (as apposed to a 'N') Column on the same Row then I want it to change a check box's state to Checked as well as updating a labels text.
What I have seems to work however if no data is returned I get the below error:
Object reference not set to an instance of an object
If I change the code slightly from .FirstOrDefault() to .First() I get this error:
Sequence contains no elements
The part of the code that appears to be causing the problem is listed below. If you need to know anything else I will add it in.
Dim sSQL As String
sSQL =
<SQL>
SELECT MAX(UpdateTime) AS UpdateTime FROM AdminCS_Data_Current
WHERE UpdateUser = |##UpdateUser|
</SQL>
sSQL = Replace(sSQL, "##UpdateUser", AdminCB.Text)
Me.LastUserUpdate.Text = "Last Action: " & Format(ReturnDatabaseValue(sSQL, "UpdateTime", "Data"), "dd/MM/yyyy HH:mm:ss")
Dim EmployeeDataset As New DataSet
Try
sSQL =
<SQL>
SELECT * FROM AdminCS_Data_Current
WHERE UpdateUser = |##UpdateUser| AND CONVERT(DATE, UpdateTime) = CAST(GETDATE() AS DATE)
ORDER BY UpdateTime ASC
</SQL>
sSQL = Replace(sSQL, "##UpdateUser", AdminCB.Text)
EmployeeDataset = ReturnDataSet(sSQL, "Data")
If EmployeeDataset IsNot Nothing Then
Dim eData = EmployeeDataset.Tables(0)
If (eData.Select("Field = 'Timesheets Checked'").FirstOrDefault()("Flag")) IsNot Nothing Then
If eData.Select("Field = 'Timesheets Checked'").FirstOrDefault()("Flag").ToString.Trim = "Y" Then
TShtY.CheckState = CheckState.Checked
TShtTime.Text = Format(eData.Select("Field = 'Timesheets Checked'").First()("UpdateTime"), "HH:mm:ss")
Else
TShtN.CheckState = CheckState.Checked
End If
End If
' The above two IF statements would be repeated several times on each change of "Field"
End If

It would appear that this code has introduced not just iunefficiency but also a bug:
If (eData.Select("Field = 'Timesheets Checked'").FirstOrDefault()("Flag")) IsNot Nothing Then
If eData.Select("Field = 'Timesheets Checked'").FirstOrDefault()("Flag").ToString.Trim = "Y" Then
TShtY.CheckState = CheckState.Checked
TShtTime.Text = Format(eData.Select("Field = 'Timesheets Checked'").First()("UpdateTime"), "HH:mm:ss")
Else
TShtN.CheckState = CheckState.Checked
End If
End If
It should have been written like this in the first place:
Dim row = eData.Select("Field = 'Timesheets Checked'").FirstOrDefault()
If row IsNot Nothing Then
If row("Flag").ToString.Trim = "Y" Then
TShtY.CheckState = CheckState.Checked
TShtTime.Text = Format(row("UpdateTime"), "HH:mm:ss")
Else
TShtN.CheckState = CheckState.Checked
End If
End If
Easier to read, more efficient and avoids that nasty bug.
Also, I'd much rather see this:
Dim row = eData.Select("Field = 'Timesheets Checked'").FirstOrDefault()
If row IsNot Nothing Then
If row("Flag").ToString.Trim = "Y" Then
TShtY.Checked = True
TShtTime.Text = CDate(row("UpdateTime").ToString("HH:mm:ss")
Else
TShtN.Checked = True
End If
End If
You should never use the CheckState of a Checkbox unless it's tri-state, which maybe yours are but I doubt it. As for Format, we're not in VB6 anymore Toto.

Related

Adding new row to unbound datagridview

I have an unbound datagridview. Because of the various things I am doing with the data in the grid I do not want to bind it. The columns are predefined in the settings (Edit Columns) of the datagridview.
I want to create a new row and then populate the grid row with data. I am trying to use the .Add.Rows method but it is failing with
{"Index was out of range. Must be non-negative and less than the size of the collection." & vbCrLf & "Parameter name: index"}
The following SQL retrieves data:
USE CCAP
declare #ScheduleName as varchar(30) = 'Walk-In Center April Wk 1 2019'
Select ShiftName, ScheduleStart, ScheduleEnd, Position, ADP_ID1,
Name1,ADP_ID2, Name2, ADP_ID3, Name3, ADP_ID4, Name4, ADP_ID5,
Name5, ADP_ID6, Name6, ADP_ID7, Name7
from FormattedSchedules
where ScheduleName = #ScheduleName;
and the rowcount is greater than 0 so it is getting results.
I do not understand what index is out of range or why the collection is 0
Code is below:
Tried .Rows.Add(1) and .Rows.Add() and .Rows.Add("")
Dim FSchedCmd As SqlCommand
Dim FSchedSQL As String
Dim FSchedConn As New SqlConnection()
Dim FSchedadapter As New SqlDataAdapter()
Dim i As Integer = 0
Dim rowIndex As Integer
Dim row As DataGridViewRow
AddedNewRow = 1
Dim dsFSched As New DataSet()
FSchedSQL = "Select ShiftName, ScheduleStart, ScheduleEnd, Position, ADP_ID1, Name1, ADP_ID2, Name2, ADP_ID3, Name3, ADP_ID4, Name4, ADP_ID5, Name5, ADP_ID6, Name6, ADP_ID7, Name7 from FormattedSchedules where ScheduleName = #ScheduleName;"
Try
If GlobalVariables.logProd = 1 Then
GlobalVariables.strConnection = "CCAPProdConnectionString"
Else
GlobalVariables.strConnection = "CCAPTestConnectionString"
End If
FSchedConn.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings(GlobalVariables.strConnection).ConnectionString
FSchedConn.Open()
FSchedCmd = New SqlCommand(FSchedSQL, FSchedConn)
FSchedCmd.Parameters.Add("#ScheduleName", SqlDbType.VarChar).Value = cboCreateScheduleName.Text
FSchedadapter.SelectCommand = FSchedCmd
FSchedadapter.Fill(dsFSched)
FSchedadapter.Dispose()
FSchedCmd.Dispose()
FSchedConn.Close()
'dgvCreateSchedule.DataSource = dsFSched.Tables(0)
dgvCreateSchedule.Rows.Clear()
With dgvCreateSchedule
Dim RowNo As Long = 0
'.RowCount = 0
While RowNo <= dsFSched.Tables(0).Rows.Count - 1
.Rows.Add(1)
.Rows(RowNo).Cells(0).Value = dsFSched.Tables(0).Rows(RowNo).Item(0) 'ShiftName
'.Rows(RowNo).Cells(1).Value = dsFSched.Tables(0).Rows(RowNo).Item(1) 'Start Time
.Rows(RowNo).Cells(1).Value = Convert.ToDateTime(dsFSched.Tables(0).Rows(RowNo).Item(1)).TimeOfDay
'.Rows(RowNo).Cells(2).Value = dsFSched.Tables(0).Rows(RowNo).Item(2) 'End Time
.Rows(RowNo).Cells(2).Value = Convert.ToDateTime(dsFSched.Tables(0).Rows(RowNo).Item(2)).TimeOfDay 'End Time
.Rows(RowNo).Cells(3).Value = dsFSched.Tables(0).Rows(RowNo).Item(3) 'Position
.Rows(RowNo).Cells(4).Value = dsFSched.Tables(0).Rows(RowNo).Item(4) 'ADP_ID1
.Rows(RowNo).Cells(5).Value = dsFSched.Tables(0).Rows(RowNo).Item(5) 'Name1
.Rows(RowNo).Cells(6).Value = dsFSched.Tables(0).Rows(RowNo).Item(6) 'ADP_ID2
.Rows(RowNo).Cells(7).Value = dsFSched.Tables(0).Rows(RowNo).Item(7) 'Name2
.Rows(RowNo).Cells(8).Value = dsFSched.Tables(0).Rows(RowNo).Item(8) 'ADP_ID3
.Rows(RowNo).Cells(9).Value = dsFSched.Tables(0).Rows(RowNo).Item(9) 'Name3
.Rows(RowNo).Cells(10).Value = dsFSched.Tables(0).Rows(RowNo).Item(10) 'ADP_ID4
.Rows(RowNo).Cells(11).Value = dsFSched.Tables(0).Rows(RowNo).Item(11) 'Name4
.Rows(RowNo).Cells(12).Value = dsFSched.Tables(0).Rows(RowNo).Item(12) 'ADP_ID5
.Rows(RowNo).Cells(13).Value = dsFSched.Tables(0).Rows(RowNo).Item(13) 'Name5
.Rows(RowNo).Cells(14).Value = dsFSched.Tables(0).Rows(RowNo).Item(14) 'ADP_ID6
.Rows(RowNo).Cells(15).Value = dsFSched.Tables(0).Rows(RowNo).Item(15) 'Name6
.Rows(RowNo).Cells(16).Value = dsFSched.Tables(0).Rows(RowNo).Item(16) 'ADP_ID7
.Rows(RowNo).Cells(17).Value = dsFSched.Tables(0).Rows(RowNo).Item(17) 'Name7
RowNo = RowNo + 1
End While
End With
If dgvCreateSchedule.RowCount > 0 Then
dgvCreateSchedule.Rows(0).Selected = True
dgvCreateSchedule.CurrentCell = dgvCreateSchedule.Rows(0).Cells(0)
'dgvCreateSchedule.FirstDisplayedScrollingRowIndex = dgvCreateSchedule.CurrentRow.Index
End If
Catch ex As Exception
MessageBox.Show("Cannot open FormattedSchedules to load grid")
End Try
AddedNewRow = 0
Error message from line: .Rows.Add(1)
Index was out of range. Must be non-negative and less than the size of the collection." & vbCrLf & "Parameter name: index
This should be the fastest option:
dgvCreateSchedule.Rows.Clear()
For Each xrow As DataRow In TempDataTable.dsFSched.Tables(0).Rows
dgvCreateSchedule.Rows.Add(xrow.ItemArray)
Next
What it does adds all "Cells" along with row.
And when editing cells, I prefer to use
dgvCreateSchedule(y,x).Value = somevalue
'Though it's a little bit strange, as it's column first then row for location hence y then x axis , opposed to usual row then column thats x then y axis
Add it like this, assuming there is the same column count/order
.Rows.Add(dsFSched.Tables(0).Rows(RowNo).ItemArray)
I changed the name of the DGV to DataGridView1 because that is what I happened to have in my test project.
You can use conditional compile statements to chose the correct connection string. Not necessary to keep a Boolean variable somewhere to determine correct string. I know I would forget to change it for the release version.
You did a good job closing and disposing of database objects but if there is an error all that good work will be for naught. A Using...End Using block will accomplish the close, dispose even if there is an error.
Pass the connection string directly to the constructor of the connection and pass the Sql statement and the connection directly to the constructor of the command.
Don't open your connection until the last minute. In the case of a DataAdapter.Fill, the connection is opened and closed for you however, if the adapter finds and open connection it leaves it open. In this case there is no need for an adapter or a DataSet.
I do not see anything wrong with your line .Rows.Add(1). The problem comes on the next line. The index of DataGridView.Rows is an Int32, Integer in vb.net, and you have declared RowNo as Long. Of course you will want to use the code suggested by #CruleD answer.
Private Sub OPCode()
Dim dt As New DataTable
Dim FSchedSQL = "Select ShiftName, ScheduleStart, ScheduleEnd, Position, ADP_ID1, Name1, ADP_ID2, Name2, ADP_ID3, Name3, ADP_ID4, Name4, ADP_ID5, Name5, ADP_ID6, Name6, ADP_ID7, Name7 from FormattedSchedules where ScheduleName = #ScheduleName;"
Try
#If Not DEBUG Then
GlobalVariables.strConnection = "CCAPProdConnectionString"
#Else
GlobalVariables.strConnection = "CCAPTestConnectionString"
#End If
Using FSchedConn As New SqlConnection(ConfigurationManager.ConnectionStrings(GlobalVariables.strConnection).ConnectionString)
Using FSchedCmd As New SqlCommand(FSchedSQL, FSchedConn)
FSchedCmd.Parameters.Add("#ScheduleName", SqlDbType.VarChar).Value = cboCreateScheduleName.Text
FSchedConn.Open()
dt.Load(FSchedCmd.ExecuteReader)
End Using
End Using
DataGridView1.Rows.Clear()
For Each xrow As DataRow In dt.Rows
DataGridView1.Rows.Add(xrow.ItemArray)
Next
If DataGridView1.RowCount > 0 Then
DataGridView1.Rows(0).Selected = True
DataGridView1.CurrentCell = DataGridView1.Rows(0).Cells(0)
End If
Catch ex As Exception
MessageBox.Show("Error loading grid")
End Try
End Sub

Working with 1-Result-Linq with For Each? Any alternative?

If I got a Linq-Query where I am knowing that it'll return 1 result only, like this:
Dim result = From g In Foo.Bar
Where g.keyID = 1
Select g
...do I still have to use the For Each loop to get the values or is there anything else I could use to work with 1-result-queries?
For Each x In result
TextBox1.Text = x.field1
TextBox2.Text = x.field2
TextBox3.Text = x.field3
Next
You can use Enumerable.First/ Enumerable.FirstOrDefault or Enumerable.Single/ Enumerable.SingleOrDefault(if it was exceptional if there were more than one).
Dim firstResult = result.FirstOrDefault()
If firstResult IsNot Nothing Then
TextBox1.Text = firstResult.field1
TextBox2.Text = firstResult.field2
TextBox3.Text = firstResult.field3
End If
So use First if it's possible that there are more than one but you want the first
Use Single if you want the first but it was a bug if there were more than one
The methods without OrDefault in the name will throw an exception if none was found
The OrDefault methods will return the default value(Nothing for reference types).

Fromat datagridview cells based on SQL logic

I have the following SQL statement that checks for duplicate date ranges within my data set.
SELECT *
FROM [DaisyServices].[dbo].[DaisyServicesIndigo] i
JOIN [DaisyServices].[dbo].[DaisyServicesIndigo] i2
on i.cli = i2.cli
and i.quantity = i2.quantity
and i.unitcost = i2.unitcost
and i.totalcost = i2.totalcost
and i.[description] = i2.[description]
and ((i.FromDate <= i2.ToDate) and (i.ToDate >= i2.FromDate))
WHERE i.id<>i2.id
AND (i2.[bill]=0 AND i2.[Billed Month] is Null AND i2.currentbill = 0)
I would like to use this SQL logic, to check each line of my data grid view and then format each line accordingly if the condition is met. i.e. the data ranges overlap.
Something like this, I am just not sure how to incorporate the SQL element?
For Each row As DataGridViewRow In DaisyServicesForm.DataGridView2.Rows
If "SQL condition for the specific line is true" Then
row.DefaultCellStyle.ForeColor = Color.Blue
End If
Next
Any help greatly appreciated.
Try this piece of code
For intcount=0 to DaisyServicesForm.DataGridView2.Rows.count-1
If checkdata(intcount)=true Then
datagridview2.rows(intcount).DefaultCellStyle.ForeColor = Color.Blue
End If
Next
and add a checking function in your code like:
Private Function checkdata(ByVal row As Integer) As Boolean
Dim da As SqlDataReader
Dim command As SqlCommand
command.CommandType = CommandType.Text
command.Connection = connection
command.CommandText = "Your query for checking if the item in specified row is in database"
da = command.ExecuteReader
If da.HasRows Then
Return True
Else
Return False
End If
End Function
specify the checking in the command text in fucntion.Just Provided this as an example,you may modify it upon our own needs.

Listview help needed

I am confused being a relatively new user to vb.net. Why my listview is not amending the value in the list? If I may, so I have a correct working of how listview displays its data from database. I have a general question in addition to my code problem.
I have five columns in my listview (0-4). Am I correct in saying that if my access database contained say 10 fields but I only needed to display five of them but one of them was field (9), then would code the list like my code below, which is not changing the value and will only display the list if I remove the 'else' statement.
What is the error? Many thanks
UPDATED CODE:
oledbCnn.ConnectionString = My.Settings.storageConnectionString
oledbCnn.Open()
'drcount = Convert.ToInt32(dr("RowCount"))
sql = "Select TOP 100 * from Requests ORDER BY [Date-time received] DESC"
Debug.Print(sql)
Dim oledbCmd As OleDbCommand = New OleDbCommand(sql, oledbCnn)
Using dr = oledbCmd.ExecuteReader()
'clear items in the list before populating with new values
'lvRequests.Items.Clear()
While dr.Read()
If dr.HasRows Then
Dim LVI As New ListViewItem
With LVI
.Text = dr(0).ToString()
.UseItemStyleForSubItems = False
.SubItems.Add(CDate(dr(5)).ToShortDateString())
.SubItems.Add(dr(1).ToString())
.SubItems.Add(dr(3).ToString())
If dr(3).ToString = "D" Then
.SubItems(3).Text = "Destroyed"
ElseIf dr(3).ToString = "O" Then
.SubItems(3).Text = "Out"
ElseIf dr(3).ToString = "I" Then
.SubItems(3).Text = "Intake"
End If
.SubItems.Add(dr(9).ToString())
If dr(9).ToString = "DEMO" Then
.SubItems(9).Text = "Done"
End If
End With
lvRequests.Items.Add(LVI)
lvcount += 1
End If
End While
End Using
There is not enough info to be really sure what is going on. It appears that you pull 10 columns from the DB, but if you only post 5 of them to the LV, then there should only be 5 subitems. So this is wrong:
If dr(9).ToString() = "DEMO" Then
lvRequests.Items(lvRequests.Items.Count - 1).SubItems(9).Text = "Done"
' should probably be:
If dr(9).ToString() = "DEMO" Then
lvRequests.Items(lvRequests.Items.Count - 1).SubItems(4).Text = "Done"
What might make it clearer at least in code would be to use Names for the subitems. If you instance a SubItem object rather than use a default constructor, you can assign a name and reference them that way instead:
Dim si As New ListViewItem.ListViewSubItem
With si
.Text = dr(X).ToString ' dunno whats in there
' this is probably not exactly right, but give the SubItem
' the same name as the db column
.Name = dr.Table.Columns(X).ColumnName
End With
thisItem.SubItems.Add(si) ' add sub to Item collection
Now, your code can use the name rather than index:
If dr(9).ToString() = "DEMO" Then
lvReq.Items(lvReq.Items.Count - 1).SubItems("TheColumnName").Text = "Done"
it no longer really matters how many columns or subitems there are. The ListViewItem.SubItems collection does not require sub item names to be unique, so it is possible to end up with 2 with the same name, but if you map from the DR/DT/DB correctly, it should take care of itself.
If the LV is bound to the datasource (you did not say), then there could be as many SubItems as db/datasource columns (never actually used an LV that way).

new records to add first row of datagrid view in vb.net

In my VB.Net application I am filling my data grid view like this:
Dim cmdd1 As New SqlCommand("DashBordFetch1", con.connect)
cmdd1.CommandType = CommandType.StoredProcedure
cmdd1.Parameters.Add("#locid", SqlDbType.Int).Value = locid
da1.SelectCommand = cmdd1 dr = cmdd1.ExecuteReader
While dr.Read
flag = False
carid1 = dr("Car_Id")
For j = 0 To dcnt1 - 2
If carid1 = dgv.Item(0, j).Value Then
flag = True
Exit For
End If
Next
If flag = False Then
If dr("Car_Id") Is DBNull.Value Then
carid1 = "null"
Else
carid1 = dr("Car_Id")
End If
If dr("Plate_Source") Is DBNull.Value Then
platesource1 = "Null"
Else
platesource1 = dr("Plate_Source")
End If
Dim row1 As String() = {carid1, platesource1}
DGVDeliverd.Rows.Add(row1)
End If
End While
..also i am using Timer..every 2 minutes timer will work .some time new records will added to my datagridview..while adding new record that is adding to last row of my datagrid view,,i want to add every time new records to my first row of datagridview. how i can do this?
I am using windows forms
You can use Insert instead of Add to specify the position for the new row. So always insert at position zero:
DGVDeliverd.Rows.Insert(0, row1)
See MSDN here for details (screenshot below).
Use:
Order by your primary id desc
to keep the latest data on top of the grid
update #1
DGVDeliverd.Controls[0].Controls.AddAt(0, row1);